[
  {
    "path": ".eslintignore",
    "content": "# folders\nartifacts/\nbin/\nbuild/\ncache/\ndeployments/\ngenfiles/\nnode_modules/\ntypechain/\n"
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  env: {\n    es2021: true,\n    mocha: true,\n    node: true\n  },\n  root: true,\n  extends: [\n    'prettier',\n    'eslint:recommended',\n    'plugin:@typescript-eslint/recommended',\n    'plugin:import/errors',\n    'plugin:import/warnings'\n  ],\n  plugins: ['@typescript-eslint'],\n  rules: {\n    '@typescript-eslint/no-empty-function': 'off',\n    '@typescript-eslint/no-unused-vars': 'error',\n    '@typescript-eslint/no-use-before-define': 'error',\n    'arrow-body-style': 'off',\n    camelcase: ['error', { allow: ['^.*__factory$'] }],\n    'import/extensions': [\n      'error',\n      'ignorePackages',\n      {\n        js: 'never',\n        ts: 'never'\n      }\n    ],\n    'import/no-extraneous-dependencies': 'off',\n    'import/no-useless-path-segments': 'off',\n    'import/prefer-default-export': 'off',\n    'no-console': 'off',\n    'no-empty-function': 'off',\n    'no-param-reassign': 'warn',\n    'no-plusplus': ['off'],\n    'no-underscore-dangle': 'warn',\n    'no-unused-vars': 'off',\n    'no-use-before-define': 'off',\n    'prefer-destructuring': 'off',\n    'prefer-template': 'off'\n  },\n  settings: {\n    'import/resolver': {\n      node: {\n        extensions: ['.d.ts', '.js', '.ts']\n      }\n    }\n  }\n};\n"
  },
  {
    "path": ".github/workflows/abigen.yml",
    "content": "# run solc/abigen for go binding\n\nname: gen-go-binding\non:\n  pull_request:\n    paths:\n      - '.github/workflows/abigen.yml'\n      - 'hardhat.config.ts'\n      - 'contracts/**'\n      - 'scripts/**'\njobs:\n  build:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ github.head_ref }} # so we get the topic branch to push to same PR, only available if triggered by pull request\n      - name: compile contracts and generate go binding\n        env:\n          PRID: ${{ github.event.number }}\n          BRANCH: ${{ github.head_ref }}\n          GH_TOKEN: ${{ secrets.GH_TOKEN }}\n        run: |\n          source scripts/solc_abigen.sh\n          setup_git\n          dld_solc && run_solc\n          dld_abigen && run_abigen\n"
  },
  {
    "path": ".github/workflows/pbsol.yml",
    "content": "# run pb gen sol if proto files change\n\nname: pb-gen-sol\non:\n  pull_request:\n    paths:\n      - '.github/workflows/pbsol.yml'\n      - 'hardhat.config.ts'\n      - 'contracts/libraries/proto/**'\n\njobs:\n  build:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          ref: ${{ github.head_ref }} # so we get the topic branch to push to same PR, only available if triggered by pull request\n      - name: run pb-gen-sol and push to same PR if Pbxxx.sol files are different\n        run: |\n          source scripts/pb_gen_sol.sh\n          prepare_tools && gen_sol && add_to_pr\n"
  },
  {
    "path": ".github/workflows/run_tests.yml",
    "content": "name: run-tests\non:\n  pull_request:\n    paths:\n      - '.github/workflows/run_tests.yml'\n      - 'hardhat.config.ts'\n      - 'contracts/**'\n      - 'test/**'\njobs:\n  test:\n    runs-on: ubuntu-20.04\n    steps:\n      - uses: actions/checkout@v2\n        with:\n          fetch-depth: 1\n      - name: Setup Node.js\n        uses: actions/setup-node@v2\n        with:\n          node-version: 22.3.0\n      - name: Installing dependencies\n        run: yarn install --frozen-lockfile\n      - name: Running tests\n        run: yarn test\n"
  },
  {
    "path": ".gitignore",
    "content": "artifacts/\nartifacts-zk/\nbin/\nbuild/\ncache/\ncache-zk/\ndeployments/hardhat/\ngenfiles/\nfailing_metadata/\nnode_modules/\ntypechain/\n\n*.pid\n*.log\n*.new\n*.bak\n\n.DS_Store\n\n.vscode/\n.idea/\n.env\nsolc-input-*.json\nflattened-*.sol\n\nmemo\n"
  },
  {
    "path": ".prettierrc.json",
    "content": "{\n  \"trailingComma\": \"none\",\n  \"tabWidth\": 2,\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"overrides\": [\n    {\n      \"files\": \"*.sol\",\n      \"options\": {\n        \"printWidth\": 120,\n        \"tabWidth\": 4,\n        \"useTabs\": false,\n        \"singleQuote\": false,\n        \"bracketSpacing\": false,\n        \"explicitTypes\": \"always\"\n      }\n    },\n    {\n      \"files\": [\"*.js\", \"*.ts\", \"*.md\"],\n      \"options\": {\n        \"printWidth\": 120\n      }\n    }\n  ]\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "# SGN Contracts\n\nContracts for the Celer State Guardian Network (SGN) V2.\n\n### Run unit tests\n\n```sh\nyarn test\n```\n\n### Benchmark gas cost\n\n```sh\nyarn report-gas:benchmark\nyarn report-gas:summary\n```\n\nCheck `reports/gas_usage`.\n\n### Update contract sizes\n\n```sh\nyarn size-contracts\n```\n\nCheck `reports/contract_sizes.txt`.\n\n## Deployments\n\n### Deployment Management\n\nContract deployments are tracked by hardhat through the files under ./deployments directory on deployment branches.\n\nTo deploy newest contracts to mainnet, staging, or testnet chains:\n\n1. `git checkout mainnet-deployment|staging-deployment|testnet-deployment`, correspondingly\n2. `git merge main` into the deployment branch\n3. deploy the contracts\n4. push the deployments file changes\n\nIf any contracts (e.g. libraries) are used for both mainnet and staging, follow the step above to deploy them on staging chains first, then cherry-pick the commit containing ONLY the deployment changes of these shared contracts to `mainnet-deployment`. Please be cautious with file changes when doing such operation.\n\nRules:\n\n1. ./deployments should NOT exist on main branch\n2. only merge main into the deployment branches\n3. only change the ./deployments directory on deployment branches so that there will always be no conflicts when merge main\n\n### Deploy contracts\n\n1. `cp .env.template .env`, then ensure all environment variables are set in `.env`.\n\n2. Replace `INFURA-PROJECT-ID` suffix of the network endpoint in `.env`, that you're going to use.\n\n3. Add private key of your account that would be used, in `.env`. Refer to `hardhat.config.ts` for env param key.\n\n4. Deploy SGN and Staking contracts:\n\n```sh\nhardhat deploy --network <network> --tags SGNStaking\n```\n\nDeploy Bridge contract:\n\n```sh\nhardhat deploy --network <network>  --tags Bridge\n```\n\nDeploy OriginalTokenVault contract:\n\nMake sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.\nSuch as:\nORIGINAL_TOKEN_VAULT_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22\n\nWhere 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address\n\n```sh\nhardhat deploy --network <network>  --tags OriginalTokenVault\n```\n\nDeploy PeggedTokenBridge contract:\n\nMake sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.\nSuch as:\n\nPEGGED_TOKEN_BRIDGE_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22\n\nWhere 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address\n\n```sh\nhardhat deploy --network <network>  --tags PeggedTokenBridge\n```\n\nDeploy OriginalTokenVaultV2 contract:\n\nMake sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.\nSuch as:\n\nORIGINAL_TOKEN_VAULT_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22\n\nWhere 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address\n\n```sh\nhardhat deploy --network <network>  --tags OriginalTokenVaultV2\n```\n\nDeploy PeggedTokenBridgeV2 contract:\n\nMake sure to set ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER in .env to the Bridge address when deploying.\nSuch as:\n\nPEGGED_TOKEN_BRIDGE_SIGS_VERIFIER=0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22\n\nWhere 0x67E5E3E54B2E4433CeDB484eCF4ef0f35Fe3Fb22 is the Bridge contract address\n\n```sh\nhardhat deploy --network <network>  --tags PeggedTokenBridgeV2\n```\n\n### Verify contracts on explorers\n\n#### On Etherscan variants via hardhat etherscan-verify\n\nThis is the recommended way for most mainnet Etherscan variants.\n\nMake sure the `ETHERSCAN_API_KEY` is set correctly in `.env`.\n\n```sh\nhardhat etherscan-verify --network <network> --license \"GPL-3.0\" --force-license\n```\n\n#### On Etherscan variants via solt\n\nThis is useful since most testnet Etherscan variants don't offer verification via the API.\n\n1. Generate the standard JSON input files:\n\n```sh\nsource scripts/solt.sh\nrun_solt_write\n```\n\n2. Then try:\n\n```sh\nsolt verify --license 5 --network <network> solc-input-<contract>.json <deployed address> <contract name>\n```\n\n3. If the second step fails, go to Etherscan and manually verify using the standard JSON input files.\n\n#### On Blockscout variants via sourcify\n\nThis is used if the Blockscout variant requires \"Sources and Metadata JSON\".\n\n```sh\nhardhat sourcify --network <network>\n```\n\n#### On Blockscout variants via flattened source files\n\nThis is used if the Blockscout variant requires a single source file, or in general as a last resort.\n\n1. Flatten the source files:\n\n```sh\nhardhat flatten <path-to-contract> > flattened.sol\n```\n\n2. Edit `flattened.sol`. Remove the duplicate `SPDX-License-Identifier` lines, keeping a single copy of\n\n```\n// SPDX-License-Identifier: GPL-3.0-only\n```\n\nand submit to Blockscout.\n\nSometimes you also need to remove the duplicate `pragma solidity` lines.\n\n## Upgradable contract via the proxy pattern\n\n### How it works\n\nproxy contract holds state and delegatecall all calls to actual impl contract. When upgrade, a new impl contract is deployed, and proxy is updated to point to the new contract. below from [openzeppelin doc](https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#upgrading-via-the-proxy-pattern)\n\n```\nUser ---- tx ---> Proxy ----------> Implementation_v0\n                     |\n                      ------------> Implementation_v1\n                     |\n                      ------------> Implementation_v2\n```\n\n### Add upgradable contract\n\nTo minimize code fork, we add a new contract that inherits existing contract, eg. `contract TokenUpgradable is Token`. Next we need to ensure that all states set in Token contract constructor (and its parent contracts) must be settable via a separate normal func like `init`. This will allow Proxy contract to delegeteCall init and set proper values in Proxy's state, not the impl contract state. See MintSwapCanonicalTokenUpgradable.sol for example. We also need to either shadow Ownable._owner because when proxy delegateCall, in proxy state, Ownable._owner is not set and there is no other way to set it. Or use our own Ownable.sol which has internal func initOwner\n\n### Add deploy scripts\n\nadd a new ts file for deploy, in deploy options, add proxy section, make sure the methodName and args match actual upgradable contract\n\n```ts\nproxy: {\n    proxyContract: \"OptimizedTransparentProxy\",\n      execute: {\n        // only called when proxy is deployed, it'll call Token contract.init\n        // with proper args\n        init: {\n          methodName: 'init',\n          args: [\n            process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n            process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL]\n        }\n      }\n}\n```\n\nsee deploy/pegged/tokens/008_mint_swap_canonical_token_upgradable.ts for example\n\n### Deploy and upgrade\n\nhardhat deploy plugin tries to be smart and deploy ProxyAdmin only once for each chain, deploy impl contract then proxy contract\n"
  },
  {
    "path": "benchmark/relay.ts",
    "content": "import '@nomicfoundation/hardhat-ethers';\n\nimport { parseUnits, solidityPackedKeccak256, toNumber, Wallet } from 'ethers';\nimport fs from 'fs';\nimport { ethers } from 'hardhat';\nimport path from 'path';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { deployBridgeContracts, getAccounts } from '../test/lib/common';\nimport { getRelayRequest } from '../test/lib/proto';\nimport { Bridge, TestERC20 } from '../typechain';\n\nconst GAS_USAGE_DIR = 'reports/gas_usage/';\nconst GAS_USAGE_LOG = path.join(GAS_USAGE_DIR, 'relay.txt');\n\ndescribe('Relay Gas Benchmark', function () {\n  if (!fs.existsSync(GAS_USAGE_DIR)) {\n    fs.mkdirSync(GAS_USAGE_DIR, { recursive: true });\n  }\n  fs.rmSync(GAS_USAGE_LOG, { force: true });\n  fs.appendFileSync(GAS_USAGE_LOG, '<validatorNum, quorumSigNum, gasCost> for cbr testErc20 relay tx\\n\\n');\n\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { bridge, token } = await deployBridgeContracts(admin);\n    return { admin, bridge, token };\n  }\n\n  let bridge: Bridge;\n  let token: TestERC20;\n  let admin: HardhatEthersSigner;\n  let accounts: Wallet[];\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    bridge = res.bridge;\n    token = res.token;\n    admin = res.admin;\n    accounts = await getAccounts(admin, [token], 21);\n    await token.transfer(bridge.getAddress(), parseUnits('1000000'));\n    await bridge.setEpochVolumeCaps([token.getAddress()], [parseUnits('100')]);\n    await bridge.setEpochLength(5);\n    await bridge.setDelayThresholds([token.getAddress()], [parseUnits('100')]);\n  });\n\n  it('benchmark relay gas cost for bridge', async function () {\n    let perSigCost;\n    for (let i = 5; i <= 21; i += 2) {\n      perSigCost = await doBenchmarkRelaySig(i);\n    }\n    fs.appendFileSync(GAS_USAGE_LOG, 'per sig cost: ' + perSigCost + '\\n');\n    fs.appendFileSync(GAS_USAGE_LOG, '\\n');\n\n    const perSignerCost = await doBenchmarkRelaySigner(21, 8);\n    fs.appendFileSync(GAS_USAGE_LOG, 'per validator cost: ' + perSignerCost + '\\n');\n  });\n\n  async function getPowers(\n    accounts: Wallet[],\n    signerNum: number,\n    quorumSigNum: number\n  ): Promise<{ signers: Wallet[]; addrs: string[]; powers: bigint[] }> {\n    const signers: Wallet[] = [];\n    const addrs: string[] = [];\n    const powers: bigint[] = [];\n    for (let i = 0; i < signerNum; i++) {\n      signers.push(accounts[i]);\n      addrs.push(accounts[i].address);\n      if (i == quorumSigNum - 1) {\n        powers.push(parseUnits('100'));\n      } else {\n        powers.push(parseUnits('1'));\n      }\n    }\n    return { signers, addrs, powers };\n  }\n\n  async function doBenchmarkRelaySig(signerNum: number) {\n    let firstCost = 0;\n    let lastCost = 0;\n    const maxQuorumSigNum = ((signerNum * 2) / 3 + 1) | 0;\n    for (let i = 3; i <= maxQuorumSigNum; i += 2) {\n      const gasUsed = await doBenchmarkRelay(signerNum, i);\n      if (i == 3) {\n        firstCost = toNumber(gasUsed);\n      }\n      lastCost = toNumber(gasUsed);\n    }\n    const perSigCost = Math.ceil((lastCost - firstCost) / (maxQuorumSigNum - 3));\n    return perSigCost;\n  }\n\n  async function doBenchmarkRelaySigner(maxSignerNum: number, quorumSigNum: number) {\n    let firstCost = 0;\n    let lastCost = 0;\n    const minSignerNum = ((quorumSigNum * 3) / 2) | 0;\n    for (let i = minSignerNum; i <= maxSignerNum; i++) {\n      const gasUsed = await doBenchmarkRelay(i, quorumSigNum);\n      if (i == minSignerNum) {\n        firstCost = toNumber(gasUsed);\n      }\n      lastCost = toNumber(gasUsed);\n    }\n    const perSignerCost = Math.ceil((lastCost - firstCost) / (maxSignerNum - minSignerNum));\n    return perSignerCost;\n  }\n\n  async function doBenchmarkRelay(signerNum: number, quorumSigNum: number) {\n    if (quorumSigNum > signerNum) {\n      quorumSigNum = signerNum;\n    }\n    const { signers, addrs, powers } = await getPowers(accounts, signerNum, quorumSigNum);\n    await bridge.notifyResetSigners();\n    await bridge.resetSigners(addrs, powers);\n\n    const sender = accounts[0];\n    const receiver = accounts[1];\n    const amount = parseUnits('1');\n    const chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n    const srcXferId = solidityPackedKeccak256(['uint64'], [Date.now()]); // fake src xfer id\n    const { relayBytes, sigs } = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      amount,\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n    const gasUsed = (await (await bridge.relay(relayBytes, sigs, addrs, powers)).wait())!.gasUsed;\n    fs.appendFileSync(GAS_USAGE_LOG, signerNum.toString() + '\\t' + quorumSigNum.toString() + '\\t' + gasUsed + '\\n');\n    return gasUsed;\n  }\n});\n"
  },
  {
    "path": "contracts/circle-usdc/CircleBridgeProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./FeeOperator.sol\";\nimport \"../interfaces/ICircleBridge.sol\";\nimport \"../safeguard/Governor.sol\";\nimport \"../safeguard/Pauser.sol\";\n\ncontract CircleBridgeProxy is FeeOperator, Governor, Pauser, ReentrancyGuard {\n    using SafeERC20 for IERC20;\n\n    address public immutable circleBridge;\n\n    uint32 public feePercGlobal; //in 1e6\n    // chainId => feePercOverride, support override fee perc by dst chain\n    mapping(uint64 => uint32) public feePercOverride;\n    /// per dest chain id executor fee in this chain's USDC token\n    mapping(uint64 => uint256) public dstTxFee;\n\n    // 0 is regarded as not registered. Set to a negative value if target domain is actually 0.\n    mapping(uint64 => int32) public chidToDomain;\n\n    event FeePercUpdated(uint64[] chainIds, uint32[] feePercs);\n    event TxFeeUpdated(uint64[] chainIds, uint256[] fees);\n    event ChidToDomainUpdated(uint64[] chainIds, int32[] domains);\n    event Deposited(\n        address sender,\n        bytes32 recipient,\n        uint64 dstChid,\n        uint256 amount,\n        uint256 txFee,\n        uint256 percFee,\n        uint64 nonce\n    );\n\n    constructor(address _circleBridge, address _feeCollector) FeeOperator(_feeCollector) {\n        circleBridge = _circleBridge;\n    }\n\n    function depositForBurn(\n        uint256 _amount,\n        uint64 _dstChid,\n        bytes32 _mintRecipient,\n        address _burnToken\n    ) external nonReentrant whenNotPaused returns (uint64 _nonce) {\n        int32 dstDomain = chidToDomain[_dstChid];\n        require(dstDomain != 0, \"dst domain not registered\");\n        if (dstDomain < 0) {\n            dstDomain = 0; // a negative value indicates the target domain is 0 actually.\n        }\n        (uint256 fee, uint256 txFee, uint256 percFee) = totalFee(_amount, _dstChid);\n        require(_amount > fee, \"fee not covered\");\n\n        IERC20(_burnToken).safeTransferFrom(msg.sender, address(this), _amount);\n        uint256 bridgeAmt = _amount - fee;\n        IERC20(_burnToken).safeIncreaseAllowance(circleBridge, bridgeAmt);\n        _nonce = ICircleBridge(circleBridge).depositForBurn(bridgeAmt, uint32(dstDomain), _mintRecipient, _burnToken);\n        IERC20(_burnToken).safeApprove(circleBridge, 0);\n        emit Deposited(msg.sender, _mintRecipient, _dstChid, _amount, txFee, percFee, _nonce);\n    }\n\n    function totalFee(uint256 _amount, uint64 _dstChid)\n        public\n        view\n        returns (\n            uint256 _fee,\n            uint256 _txFee,\n            uint256 _percFee\n        )\n    {\n        uint32 feePerc = feePercOverride[_dstChid];\n        if (feePerc == 0) {\n            feePerc = feePercGlobal;\n        }\n        _txFee = dstTxFee[_dstChid];\n        _percFee = (_amount * feePerc) / 1e6;\n        _fee = _txFee + _percFee;\n    }\n\n    function setFeePerc(uint64[] calldata _chainIds, uint32[] calldata _feePercs) external onlyGovernor {\n        require(_chainIds.length == _feePercs.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            require(_feePercs[i] < 1e6, \"fee percentage too large\");\n            if (_chainIds[i] == 0) {\n                feePercGlobal = _feePercs[i];\n            } else {\n                feePercOverride[_chainIds[i]] = _feePercs[i];\n            }\n        }\n        emit FeePercUpdated(_chainIds, _feePercs);\n    }\n\n    function setTxFee(uint64[] calldata _chainIds, uint256[] calldata _fees) external onlyGovernor {\n        require(_chainIds.length == _fees.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            dstTxFee[_chainIds[i]] = _fees[i];\n        }\n        emit TxFeeUpdated(_chainIds, _fees);\n    }\n\n    function setChidToDomain(uint64[] calldata _chainIds, int32[] calldata _domains) external onlyGovernor {\n        require(_chainIds.length == _domains.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            chidToDomain[_chainIds[i]] = _domains[i];\n        }\n        emit ChidToDomainUpdated(_chainIds, _domains);\n    }\n}\n"
  },
  {
    "path": "contracts/circle-usdc/CircleBridgeProxyV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./FeeOperator.sol\";\nimport \"../interfaces/ICircleBridge.sol\";\nimport \"../safeguard/Governor.sol\";\nimport \"../safeguard/Pauser.sol\";\n\ncontract CircleBridgeProxyV2 is FeeOperator, Governor, Pauser, ReentrancyGuard {\n    using SafeERC20 for IERC20;\n\n    address public immutable circleBridge;\n\n    uint32 public feePercGlobal; //in 1e6\n    // chainId => feePercOverride, support override fee perc by dst chain\n    mapping(uint64 => uint32) public feePercOverride;\n    /// per dest chain id executor fee in this chain's USDC token\n    mapping(uint64 => uint256) public dstTxFee;\n\n    // 0 is regarded as not registered. Set to a negative value if target domain is actually 0.\n    mapping(uint64 => int32) public chidToDomain;\n\n    event FeePercUpdated(uint64[] chainIds, uint32[] feePercs);\n    event TxFeeUpdated(uint64[] chainIds, uint256[] fees);\n    event ChidToDomainUpdated(uint64[] chainIds, int32[] domains);\n    event Deposited(\n        address sender,\n        bytes32 recipient,\n        uint64 dstChid,\n        uint256 amount,\n        uint256 txFee,\n        uint256 percFee,\n        uint32 minFinalityThreshold\n    );\n\n    constructor(address _circleBridge, address _feeCollector) FeeOperator(_feeCollector) {\n        circleBridge = _circleBridge;\n    }\n\n    function depositForBurn(\n        uint256 _amount,\n        uint64 _dstChid,\n        bytes32 _mintRecipient,\n        address _burnToken,\n        uint256 _maxFee,\n        uint32 _minFinalityThreshold\n    ) external nonReentrant whenNotPaused {\n        int32 dstDomain = chidToDomain[_dstChid];\n        require(dstDomain != 0, \"dst domain not registered\");\n        if (dstDomain < 0) {\n            dstDomain = 0; // a negative value indicates the target domain is 0 actually.\n        }\n        (uint256 fee, uint256 txFee, uint256 percFee) = totalFee(_amount, _dstChid);\n        require(_amount > fee, \"fee not covered\");\n\n        IERC20(_burnToken).safeTransferFrom(msg.sender, address(this), _amount);\n        uint256 bridgeAmt = _amount - fee;\n        IERC20(_burnToken).safeIncreaseAllowance(circleBridge, bridgeAmt);\n        ICircleBridge(circleBridge).depositForBurn(bridgeAmt, uint32(dstDomain), _mintRecipient, _burnToken, bytes32(0), _maxFee, _minFinalityThreshold);\n        IERC20(_burnToken).safeApprove(circleBridge, 0);\n        emit Deposited(msg.sender, _mintRecipient, _dstChid, _amount, txFee, percFee, _minFinalityThreshold);\n    }\n\n    function totalFee(uint256 _amount, uint64 _dstChid)\n        public\n        view\n        returns (\n            uint256 _fee,\n            uint256 _txFee,\n            uint256 _percFee\n        )\n    {\n        uint32 feePerc = feePercOverride[_dstChid];\n        if (feePerc == 0) {\n            feePerc = feePercGlobal;\n        }\n        _txFee = dstTxFee[_dstChid];\n        _percFee = (_amount * feePerc) / 1e6;\n        _fee = _txFee + _percFee;\n    }\n\n    function setFeePerc(uint64[] calldata _chainIds, uint32[] calldata _feePercs) external onlyGovernor {\n        require(_chainIds.length == _feePercs.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            require(_feePercs[i] < 1e6, \"fee percentage too large\");\n            if (_chainIds[i] == 0) {\n                feePercGlobal = _feePercs[i];\n            } else {\n                feePercOverride[_chainIds[i]] = _feePercs[i];\n            }\n        }\n        emit FeePercUpdated(_chainIds, _feePercs);\n    }\n\n    function setTxFee(uint64[] calldata _chainIds, uint256[] calldata _fees) external onlyGovernor {\n        require(_chainIds.length == _fees.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            dstTxFee[_chainIds[i]] = _fees[i];\n        }\n        emit TxFeeUpdated(_chainIds, _fees);\n    }\n\n    function setChidToDomain(uint64[] calldata _chainIds, int32[] calldata _domains) external onlyGovernor {\n        require(_chainIds.length == _domains.length, \"length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            chidToDomain[_chainIds[i]] = _domains[i];\n        }\n        emit ChidToDomainUpdated(_chainIds, _domains);\n    }\n}\n"
  },
  {
    "path": "contracts/circle-usdc/FeeOperator.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../safeguard/Ownable.sol\";\n\nabstract contract FeeOperator is Ownable {\n    using SafeERC20 for IERC20;\n\n    address public feeCollector;\n\n    event FeeCollectorUpdated(address from, address to);\n\n    modifier onlyFeeCollector() {\n        require(msg.sender == feeCollector, \"not fee collector\");\n        _;\n    }\n\n    constructor(address _feeCollector) {\n        feeCollector = _feeCollector;\n    }\n\n    function collectFee(address[] calldata _tokens, address _to) external onlyFeeCollector {\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            // use zero address to denote native token\n            if (_tokens[i] == address(0)) {\n                uint256 bal = address(this).balance;\n                (bool sent, ) = _to.call{value: bal, gas: 50000}(\"\");\n                require(sent, \"send native failed\");\n            } else {\n                uint256 balance = IERC20(_tokens[i]).balanceOf(address(this));\n                IERC20(_tokens[i]).safeTransfer(_to, balance);\n            }\n        }\n    }\n\n    function setFeeCollector(address _feeCollector) external onlyOwner {\n        address oldFeeCollector = feeCollector;\n        feeCollector = _feeCollector;\n        emit FeeCollectorUpdated(oldFeeCollector, _feeCollector);\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/GovernedOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./proxies/CommonOwnerProxy.sol\";\nimport \"./proxies/BridgeOwnerProxy.sol\";\nimport \"./proxies/MessageOwnerProxy.sol\";\nimport \"./proxies/SgnOwnerProxy.sol\";\nimport \"./proxies/UpgradeableOwnerProxy.sol\";\n\ncontract GovernedOwnerProxy is\n    CommonOwnerProxy,\n    BridgeOwnerProxy,\n    MessageOwnerProxy,\n    SgnOwnerProxy,\n    UpgradeableOwnerProxy\n{\n    constructor(address _initializer) OwnerProxyBase(_initializer) {}\n}\n"
  },
  {
    "path": "contracts/governed-owner/SimpleGovernance.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../libraries/Utils.sol\";\n\n// mainly used for governed multi owner to do infrequent owner operations,\n// relatively prefer easy-to-use over gas-efficiency\ncontract SimpleGovernance {\n    uint256 public constant THRESHOLD_DECIMAL = 100;\n    uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour\n    uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks\n\n    using SafeERC20 for IERC20;\n\n    enum ParamName {\n        ActivePeriod,\n        QuorumThreshold, // default threshold for votes to pass\n        FastPassThreshold // lower threshold for less critical operations\n    }\n\n    enum ProposalType {\n        ExternalDefault,\n        ExternalFastPass,\n        InternalParamChange,\n        InternalVoterUpdate,\n        InternalProxyUpdate,\n        InternalTransferToken\n    }\n\n    mapping(ParamName => uint256) public params;\n\n    struct Proposal {\n        bytes32 dataHash; // hash(proposalType, targetAddress, calldata)\n        uint256 deadline;\n        mapping(address => bool) votes;\n    }\n\n    mapping(uint256 => Proposal) public proposals;\n    uint256 public nextProposalId;\n\n    address[] public voters;\n    mapping(address => uint256) public voterPowers; // voter addr -> voting power\n\n    // NOTE: proxies must be audited open-source non-upgradable contracts with following requirements:\n    // 1. Truthfully pass along tx sender who called the proxy function as the governance proposer.\n    // 2. Do not allow arbitrary fastpass proposal with calldata constructed by the proxy callers.\n    // See ./proxies/CommonOwnerProxy.sol for example.\n    mapping(address => bool) public proposerProxies;\n\n    uint256 public nativeTokenTransferGas = 50000;\n\n    event Initiated(\n        address[] voters,\n        uint256[] powers,\n        address[] proxies,\n        uint256 activePeriod,\n        uint256 quorumThreshold,\n        uint256 fastPassThreshold\n    );\n\n    event ProposalCreated(\n        uint256 proposalId,\n        ProposalType proposalType,\n        address target,\n        bytes data,\n        uint256 deadline,\n        address proposer\n    );\n    event ProposalVoted(uint256 proposalId, address voter, bool vote);\n    event ProposalExecuted(uint256 proposalId);\n\n    event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value);\n    event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers);\n    event ProxyUpdateProposalCreated(uint256 proposalId, address[] addrs, bool[] ops);\n    event TransferTokenProposalCreated(uint256 proposalId, address receiver, address token, uint256 amount);\n\n    constructor(\n        address[] memory _voters,\n        uint256[] memory _powers,\n        address[] memory _proxies,\n        uint256 _activePeriod,\n        uint256 _quorumThreshold,\n        uint256 _fastPassThreshold\n    ) {\n        require(_voters.length > 0 && _voters.length == _powers.length, \"invalid init voters\");\n        require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, \"invalid active period\");\n        require(\n            _quorumThreshold < THRESHOLD_DECIMAL && _fastPassThreshold <= _quorumThreshold,\n            \"invalid init thresholds\"\n        );\n        for (uint256 i = 0; i < _voters.length; i++) {\n            _setVoter(_voters[i], _powers[i]);\n        }\n        for (uint256 i = 0; i < _proxies.length; i++) {\n            proposerProxies[_proxies[i]] = true;\n        }\n        params[ParamName.ActivePeriod] = _activePeriod;\n        params[ParamName.QuorumThreshold] = _quorumThreshold;\n        params[ParamName.FastPassThreshold] = _fastPassThreshold;\n        emit Initiated(_voters, _powers, _proxies, _activePeriod, _quorumThreshold, _fastPassThreshold);\n    }\n\n    /*********************************\n     * External and Public Functions *\n     *********************************/\n\n    function createProposal(address _target, bytes memory _data) external returns (uint256) {\n        return _createProposal(msg.sender, _target, _data, ProposalType.ExternalDefault);\n    }\n\n    // create proposal through proxy\n    function createProposal(\n        address _proposer,\n        address _target,\n        bytes memory _data,\n        ProposalType _type\n    ) external returns (uint256) {\n        require(proposerProxies[msg.sender], \"sender is not a valid proxy\");\n        require(_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass, \"invalid type\");\n        return _createProposal(_proposer, _target, _data, _type);\n    }\n\n    function createParamChangeProposal(ParamName _name, uint256 _value) external returns (uint256) {\n        bytes memory data = abi.encode(_name, _value);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange);\n        emit ParamChangeProposalCreated(proposalId, _name, _value);\n        return proposalId;\n    }\n\n    function createVoterUpdateProposal(address[] calldata _voters, uint256[] calldata _powers)\n        external\n        returns (uint256)\n    {\n        require(_voters.length == _powers.length, \"voters and powers length not match\");\n        bytes memory data = abi.encode(_voters, _powers);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate);\n        emit VoterUpdateProposalCreated(proposalId, _voters, _powers);\n        return proposalId;\n    }\n\n    function createProxyUpdateProposal(address[] calldata _addrs, bool[] calldata _ops) external returns (uint256) {\n        require(_addrs.length == _ops.length, \"_addrs and _ops length not match\");\n        bytes memory data = abi.encode(_addrs, _ops);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalProxyUpdate);\n        emit ProxyUpdateProposalCreated(proposalId, _addrs, _ops);\n        return proposalId;\n    }\n\n    function createTransferTokenProposal(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) external returns (uint256) {\n        bytes memory data = abi.encode(_receiver, _token, _amount);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalTransferToken);\n        emit TransferTokenProposalCreated(proposalId, _receiver, _token, _amount);\n        return proposalId;\n    }\n\n    function voteProposal(uint256 _proposalId, bool _vote) public {\n        require(voterPowers[msg.sender] > 0, \"invalid voter\");\n        Proposal storage p = proposals[_proposalId];\n        require(block.timestamp < p.deadline, \"deadline passed\");\n        p.votes[msg.sender] = _vote;\n        emit ProposalVoted(_proposalId, msg.sender, _vote);\n    }\n\n    function voteProposals(uint256[] calldata _proposalIds, bool[] calldata _votes) external {\n        require(_proposalIds.length == _votes.length, \"proposalIds and votes length not match\");\n        for (uint256 i = 0; i < _proposalIds.length; i++) {\n            voteProposal(_proposalIds[i], _votes[i]);\n        }\n    }\n\n    function executeProposal(\n        uint256 _proposalId,\n        ProposalType _type,\n        address _target,\n        bytes calldata _data\n    ) external {\n        require(voterPowers[msg.sender] > 0, \"only voter can execute a proposal\");\n        Proposal storage p = proposals[_proposalId];\n        require(block.timestamp < p.deadline, \"deadline passed\");\n        require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, \"data hash not match\");\n        p.deadline = 0;\n\n        p.votes[msg.sender] = true;\n        (, , bool pass) = countVotes(_proposalId, _type);\n        require(pass, \"not enough votes\");\n\n        if (_type == ProposalType.ExternalDefault || _type == ProposalType.ExternalFastPass) {\n            (bool success, bytes memory res) = _target.call(_data);\n            require(success, Utils.getRevertMsg(res));\n        } else if (_type == ProposalType.InternalParamChange) {\n            (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256));\n            params[name] = value;\n            if (name == ParamName.ActivePeriod) {\n                require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, \"invalid active period\");\n            } else if (name == ParamName.QuorumThreshold || name == ParamName.FastPassThreshold) {\n                require(\n                    params[ParamName.QuorumThreshold] >= params[ParamName.FastPassThreshold] &&\n                        value < THRESHOLD_DECIMAL &&\n                        value > 0,\n                    \"invalid threshold\"\n                );\n            }\n        } else if (_type == ProposalType.InternalVoterUpdate) {\n            (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[]));\n            for (uint256 i = 0; i < addrs.length; i++) {\n                if (powers[i] > 0) {\n                    _setVoter(addrs[i], powers[i]);\n                } else {\n                    _removeVoter(addrs[i]);\n                }\n            }\n        } else if (_type == ProposalType.InternalProxyUpdate) {\n            (address[] memory addrs, bool[] memory ops) = abi.decode((_data), (address[], bool[]));\n            for (uint256 i = 0; i < addrs.length; i++) {\n                if (ops[i]) {\n                    proposerProxies[addrs[i]] = true;\n                } else {\n                    delete proposerProxies[addrs[i]];\n                }\n            }\n        } else if (_type == ProposalType.InternalTransferToken) {\n            (address receiver, address token, uint256 amount) = abi.decode((_data), (address, address, uint256));\n            _transfer(receiver, token, amount);\n        }\n        emit ProposalExecuted(_proposalId);\n    }\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external {\n        require(voterPowers[msg.sender] > 0, \"invalid caller\");\n        nativeTokenTransferGas = _gasUsed;\n    }\n\n    receive() external payable {}\n\n    /**************************\n     *  Public View Functions *\n     **************************/\n\n    function getVoters() public view returns (address[] memory, uint256[] memory) {\n        address[] memory addrs = new address[](voters.length);\n        uint256[] memory powers = new uint256[](voters.length);\n        for (uint32 i = 0; i < voters.length; i++) {\n            addrs[i] = voters[i];\n            powers[i] = voterPowers[voters[i]];\n        }\n        return (addrs, powers);\n    }\n\n    function getVote(uint256 _proposalId, address _voter) public view returns (bool) {\n        return proposals[_proposalId].votes[_voter];\n    }\n\n    function countVotes(uint256 _proposalId, ProposalType _type)\n        public\n        view\n        returns (\n            uint256,\n            uint256,\n            bool\n        )\n    {\n        uint256 yesVotes;\n        uint256 totalPower;\n        for (uint32 i = 0; i < voters.length; i++) {\n            if (getVote(_proposalId, voters[i])) {\n                yesVotes += voterPowers[voters[i]];\n            }\n            totalPower += voterPowers[voters[i]];\n        }\n        uint256 threshold;\n        if (_type == ProposalType.ExternalFastPass) {\n            threshold = params[ParamName.FastPassThreshold];\n        } else {\n            threshold = params[ParamName.QuorumThreshold];\n        }\n        bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL);\n        return (totalPower, yesVotes, pass);\n    }\n\n    /**********************************\n     * Internal and Private Functions *\n     **********************************/\n\n    // create a proposal and vote yes\n    function _createProposal(\n        address _proposer,\n        address _target,\n        bytes memory _data,\n        ProposalType _type\n    ) private returns (uint256) {\n        require(voterPowers[_proposer] > 0, \"only voter can create a proposal\");\n        uint256 proposalId = nextProposalId;\n        nextProposalId += 1;\n        Proposal storage p = proposals[proposalId];\n        p.dataHash = keccak256(abi.encodePacked(_type, _target, _data));\n        p.deadline = block.timestamp + params[ParamName.ActivePeriod];\n        p.votes[_proposer] = true;\n        emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer);\n        return proposalId;\n    }\n\n    function _setVoter(address _voter, uint256 _power) private {\n        require(_power > 0, \"zero power\");\n        if (voterPowers[_voter] == 0) {\n            // add new voter\n            voters.push(_voter);\n        }\n        voterPowers[_voter] = _power;\n    }\n\n    function _removeVoter(address _voter) private {\n        require(voterPowers[_voter] > 0, \"not a voter\");\n        uint256 lastIndex = voters.length - 1;\n        for (uint256 i = 0; i < voters.length; i++) {\n            if (voters[i] == _voter) {\n                if (i < lastIndex) {\n                    voters[i] = voters[lastIndex];\n                }\n                voters.pop();\n                voterPowers[_voter] = 0;\n                return;\n            }\n        }\n        revert(\"voter not found\"); // this should never happen\n    }\n\n    function _transfer(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) private {\n        if (_token == address(0)) {\n            (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n            require(sent, \"failed to send native token\");\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/customized/MessageBusOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../libraries/Utils.sol\";\nimport \"../interfaces/IMessageOwner.sol\";\n\n// only allow set MsgFee and PreExecuteMessageGasUsage\n// disable contract upgrade or token bridge address updates\ncontract MessageBusOwner {\n    uint256 public constant THRESHOLD_DECIMAL = 100;\n    uint256 public constant MIN_ACTIVE_PERIOD = 3600; // one hour\n    uint256 public constant MAX_ACTIVE_PERIOD = 2419200; // four weeks\n\n    enum ParamName {\n        ActivePeriod,\n        QuorumThreshold // threshold for votes to pass\n    }\n\n    enum ProposalType {\n        External,\n        InternalParamChange,\n        InternalVoterUpdate\n    }\n\n    enum MsgFeeType {\n        PerByte,\n        Base\n    }\n\n    mapping(ParamName => uint256) public params;\n\n    struct Proposal {\n        bytes32 dataHash; // hash(proposalType, targetAddress, calldata)\n        uint256 deadline;\n        mapping(address => bool) votes;\n    }\n\n    mapping(uint256 => Proposal) public proposals;\n    uint256 public nextProposalId;\n\n    address[] public voters;\n    mapping(address => uint256) public voterPowers; // voter addr -> voting power\n\n    event Initiated(address[] voters, uint256[] powers, uint256 activePeriod, uint256 quorumThreshold);\n\n    event ProposalCreated(\n        uint256 proposalId,\n        ProposalType proposalType,\n        address target,\n        bytes data,\n        uint256 deadline,\n        address proposer\n    );\n    event ParamChangeProposalCreated(uint256 proposalId, ParamName name, uint256 value);\n    event VoterUpdateProposalCreated(uint256 proposalId, address[] voters, uint256[] powers);\n    event SetMsgFeeProposalCreated(uint256 proposalId, address target, MsgFeeType feeType, uint256 fee);\n    event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage);\n\n    event ProposalVoted(uint256 proposalId, address voter, bool vote);\n    event ProposalExecuted(uint256 proposalId);\n\n    constructor(\n        address[] memory _voters,\n        uint256[] memory _powers,\n        uint256 _activePeriod,\n        uint256 _quorumThreshold\n    ) {\n        require(_voters.length > 0 && _voters.length == _powers.length, \"invalid init voters\");\n        require(_activePeriod <= MAX_ACTIVE_PERIOD && _activePeriod >= MIN_ACTIVE_PERIOD, \"invalid active period\");\n        require(_quorumThreshold < THRESHOLD_DECIMAL, \"invalid init thresholds\");\n        for (uint256 i = 0; i < _voters.length; i++) {\n            _setVoter(_voters[i], _powers[i]);\n        }\n        params[ParamName.ActivePeriod] = _activePeriod;\n        params[ParamName.QuorumThreshold] = _quorumThreshold;\n        emit Initiated(_voters, _powers, _activePeriod, _quorumThreshold);\n    }\n\n    /*********************************\n     * External and Public Functions *\n     *********************************/\n\n    function proposeParamChange(ParamName _name, uint256 _value) external returns (uint256) {\n        bytes memory data = abi.encode(_name, _value);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalParamChange);\n        emit ParamChangeProposalCreated(proposalId, _name, _value);\n        return proposalId;\n    }\n\n    function proposeVoterUpdate(address[] calldata _voters, uint256[] calldata _powers) external returns (uint256) {\n        require(_voters.length == _powers.length, \"voters and powers length not match\");\n        bytes memory data = abi.encode(_voters, _powers);\n        uint256 proposalId = _createProposal(msg.sender, address(0), data, ProposalType.InternalVoterUpdate);\n        emit VoterUpdateProposalCreated(proposalId, _voters, _powers);\n        return proposalId;\n    }\n\n    function proposeSetMsgFee(\n        address _target,\n        MsgFeeType _feeType,\n        uint256 _fee\n    ) external returns (uint256) {\n        bytes4 selector;\n        if (_feeType == MsgFeeType.PerByte) {\n            selector = IMessageOwner.setFeePerByte.selector;\n        } else if (_feeType == MsgFeeType.Base) {\n            selector = IMessageOwner.setFeeBase.selector;\n        } else {\n            revert(\"invalid fee type\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _fee);\n        uint256 proposalId = _createProposal(msg.sender, _target, data, ProposalType.External);\n        emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee);\n        return proposalId;\n    }\n\n    function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external {\n        bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage);\n        uint256 proposalId = _createProposal(msg.sender, _target, data, ProposalType.External);\n        emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage);\n    }\n\n    function voteProposal(uint256 _proposalId, bool _vote) external {\n        require(voterPowers[msg.sender] > 0, \"invalid voter\");\n        Proposal storage p = proposals[_proposalId];\n        require(block.timestamp < p.deadline, \"deadline passed\");\n        p.votes[msg.sender] = _vote;\n        emit ProposalVoted(_proposalId, msg.sender, _vote);\n    }\n\n    function executeProposal(\n        uint256 _proposalId,\n        ProposalType _type,\n        address _target,\n        bytes calldata _data\n    ) external {\n        require(voterPowers[msg.sender] > 0, \"only voter can execute a proposal\");\n        Proposal storage p = proposals[_proposalId];\n        require(block.timestamp < p.deadline, \"deadline passed\");\n        require(keccak256(abi.encodePacked(_type, _target, _data)) == p.dataHash, \"data hash not match\");\n        p.deadline = 0;\n\n        p.votes[msg.sender] = true;\n        (, , bool pass) = countVotes(_proposalId);\n        require(pass, \"not enough votes\");\n\n        if (_type == ProposalType.External) {\n            (bool success, bytes memory res) = _target.call(_data);\n            require(success, Utils.getRevertMsg(res));\n        } else if (_type == ProposalType.InternalParamChange) {\n            (ParamName name, uint256 value) = abi.decode((_data), (ParamName, uint256));\n            params[name] = value;\n            if (name == ParamName.ActivePeriod) {\n                require(value <= MAX_ACTIVE_PERIOD && value >= MIN_ACTIVE_PERIOD, \"invalid active period\");\n            } else if (name == ParamName.QuorumThreshold) {\n                require(value < THRESHOLD_DECIMAL && value > 0, \"invalid threshold\");\n            }\n        } else if (_type == ProposalType.InternalVoterUpdate) {\n            (address[] memory addrs, uint256[] memory powers) = abi.decode((_data), (address[], uint256[]));\n            for (uint256 i = 0; i < addrs.length; i++) {\n                if (powers[i] > 0) {\n                    _setVoter(addrs[i], powers[i]);\n                } else {\n                    _removeVoter(addrs[i]);\n                }\n            }\n        }\n        emit ProposalExecuted(_proposalId);\n    }\n\n    /**************************\n     *  Public View Functions *\n     **************************/\n\n    function getVoters() public view returns (address[] memory, uint256[] memory) {\n        address[] memory addrs = new address[](voters.length);\n        uint256[] memory powers = new uint256[](voters.length);\n        for (uint32 i = 0; i < voters.length; i++) {\n            addrs[i] = voters[i];\n            powers[i] = voterPowers[voters[i]];\n        }\n        return (addrs, powers);\n    }\n\n    function getVote(uint256 _proposalId, address _voter) public view returns (bool) {\n        return proposals[_proposalId].votes[_voter];\n    }\n\n    function countVotes(uint256 _proposalId)\n        public\n        view\n        returns (\n            uint256,\n            uint256,\n            bool\n        )\n    {\n        uint256 yesVotes;\n        uint256 totalPower;\n        for (uint32 i = 0; i < voters.length; i++) {\n            if (getVote(_proposalId, voters[i])) {\n                yesVotes += voterPowers[voters[i]];\n            }\n            totalPower += voterPowers[voters[i]];\n        }\n        uint256 threshold = params[ParamName.QuorumThreshold];\n        bool pass = (yesVotes >= (totalPower * threshold) / THRESHOLD_DECIMAL);\n        return (totalPower, yesVotes, pass);\n    }\n\n    /**********************************\n     * Internal and Private Functions *\n     **********************************/\n\n    // create a proposal and vote yes\n    function _createProposal(\n        address _proposer,\n        address _target,\n        bytes memory _data,\n        ProposalType _type\n    ) private returns (uint256) {\n        require(voterPowers[_proposer] > 0, \"only voter can create a proposal\");\n        uint256 proposalId = nextProposalId;\n        nextProposalId += 1;\n        Proposal storage p = proposals[proposalId];\n        p.dataHash = keccak256(abi.encodePacked(_type, _target, _data));\n        p.deadline = block.timestamp + params[ParamName.ActivePeriod];\n        p.votes[_proposer] = true;\n        emit ProposalCreated(proposalId, _type, _target, _data, p.deadline, _proposer);\n        return proposalId;\n    }\n\n    function _setVoter(address _voter, uint256 _power) private {\n        require(_power > 0, \"zero power\");\n        if (voterPowers[_voter] == 0) {\n            // add new voter\n            voters.push(_voter);\n        }\n        voterPowers[_voter] = _power;\n    }\n\n    function _removeVoter(address _voter) private {\n        require(voterPowers[_voter] > 0, \"not a voter\");\n        uint256 lastIndex = voters.length - 1;\n        for (uint256 i = 0; i < voters.length; i++) {\n            if (voters[i] == _voter) {\n                if (i < lastIndex) {\n                    voters[i] = voters[lastIndex];\n                }\n                voters.pop();\n                voterPowers[_voter] = 0;\n                return;\n            }\n        }\n        revert(\"voter not found\"); // this should never happen\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/interfaces/IBridgeOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IBridgeOwner {\n    // for bridges\n\n    function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external;\n\n    function notifyResetSigners() external;\n\n    function increaseNoticePeriod(uint256 _period) external;\n\n    function setWrap(address _token) external;\n\n    function setSupply(address _token, uint256 _supply) external;\n\n    function increaseSupply(address _token, uint256 _delta) external;\n\n    function decreaseSupply(address _token, uint256 _delta) external;\n\n    function addGovernor(address _account) external;\n\n    function removeGovernor(address _account) external;\n\n    // for bridge tokens\n\n    function updateBridge(address _bridge) external;\n\n    function updateBridgeSupplyCap(address _bridge, uint256 _cap) external;\n\n    function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external;\n}\n"
  },
  {
    "path": "contracts/governed-owner/interfaces/ICommonOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ICommonOwner {\n    function transferOwnership(address _newOwner) external;\n\n    function addPauser(address _account) external;\n\n    function removePauser(address _account) external;\n}\n"
  },
  {
    "path": "contracts/governed-owner/interfaces/IMessageOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IMessageOwner {\n    function setFeePerByte(uint256 _fee) external;\n\n    function setFeeBase(uint256 _fee) external;\n\n    function setLiquidityBridge(address _addr) external;\n\n    function setPegBridge(address _addr) external;\n\n    function setPegVault(address _addr) external;\n\n    function setPegBridgeV2(address _addr) external;\n\n    function setPegVaultV2(address _addr) external;\n\n    function setPreExecuteMessageGasUsage(uint256 _usage) external;\n}\n"
  },
  {
    "path": "contracts/governed-owner/interfaces/ISgnOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ISgnOwner {\n    function setWhitelistEnabled(bool _whitelistEnabled) external;\n\n    function addWhitelisted(address _account) external;\n\n    function removeWhitelisted(address _account) external;\n\n    function setGovContract(address _addr) external;\n\n    function setRewardContract(address _addr) external;\n\n    function setMaxSlashFactor(uint256 _maxSlashFactor) external;\n}\n"
  },
  {
    "path": "contracts/governed-owner/interfaces/IUpgradeableOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IUpgradeableOwner {\n    function changeProxyAdmin(address _proxy, address _newAdmin) external;\n\n    function upgrade(address _proxy, address _implementation) external;\n\n    function upgradeAndCall(\n        address _proxy,\n        address _implementation,\n        bytes calldata _data\n    ) external;\n\n    function upgradeTo(address _implementation) external;\n\n    function upgradeToAndCall(address _implementation, bytes calldata _data) external;\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/BridgeOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/IBridgeOwner.sol\";\nimport {SimpleGovernance as sg} from \"../SimpleGovernance.sol\";\nimport {OwnerDataTypes as dt} from \"./OwnerDataTypes.sol\";\n\nabstract contract BridgeOwnerProxy is OwnerProxyBase {\n    // for bridges\n    event ResetSignersProposalCreated(uint256 proposalId, address target, address[] signers, uint256[] powers);\n    event NotifyResetSignersProposalCreated(uint256 proposalId, address target);\n    event IncreaseNoticePeriodProposalCreated(uint256 proposalId, address target, uint256 period);\n    event SetNativeWrapProposalCreated(uint256 proposalId, address target, address token);\n    event UpdateSupplyProposalCreated(\n        uint256 proposalId,\n        address target,\n        dt.Action action,\n        address token,\n        uint256 supply\n    );\n    event UpdateGovernorProposalCreated(uint256 proposalId, address target, dt.Action action, address account);\n\n    // for bridge tokens\n    event UpdateBridgeProposalCreated(uint256 proposalId, address target, address bridgeAddr);\n    event UpdateBridgeSupplyCapProposalCreated(uint256 proposalId, address target, address bridge, uint256 cap);\n    event SetBridgeTokenSwapCapProposalCreated(uint256 proposalId, address target, address bridgeToken, uint256 cap);\n\n    function proposeResetSigners(\n        address _target,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.resetSigners.selector, _signers, _powers);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit ResetSignersProposalCreated(proposalId, _target, _signers, _powers);\n    }\n\n    function proposeNotifyResetSigners(address _target) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.notifyResetSigners.selector);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit NotifyResetSignersProposalCreated(proposalId, _target);\n    }\n\n    function proposeIncreaseNoticePeriod(address _target, uint256 _period) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.increaseNoticePeriod.selector, _period);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit IncreaseNoticePeriodProposalCreated(proposalId, _target, _period);\n    }\n\n    function proposeSetNativeWrap(address _target, address _token) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.setWrap.selector, _token);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetNativeWrapProposalCreated(proposalId, _target, _token);\n    }\n\n    function proposeUpdateSupply(\n        address _target,\n        dt.Action _action,\n        address _token,\n        uint256 _supply\n    ) external {\n        bytes4 selector;\n        if (_action == dt.Action.Set) {\n            selector = IBridgeOwner.setSupply.selector;\n        } else if (_action == dt.Action.Add) {\n            selector = IBridgeOwner.increaseSupply.selector;\n        } else if (_action == dt.Action.Remove) {\n            selector = IBridgeOwner.decreaseSupply.selector;\n        } else {\n            revert(\"invalid action\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _token, _supply);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit UpdateSupplyProposalCreated(proposalId, _target, _action, _token, _supply);\n    }\n\n    function proposeUpdateGovernor(\n        address _target,\n        dt.Action _action,\n        address _account\n    ) external {\n        bytes4 selector;\n        if (_action == dt.Action.Add) {\n            selector = IBridgeOwner.addGovernor.selector;\n        } else if (_action == dt.Action.Remove) {\n            selector = IBridgeOwner.removeGovernor.selector;\n        } else {\n            revert(\"invalid action\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _account);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit UpdateGovernorProposalCreated(proposalId, _target, _action, _account);\n    }\n\n    function proposeUpdateBridgeSupplyCap(\n        address _target,\n        address _bridge,\n        uint256 _cap\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.updateBridgeSupplyCap.selector, _bridge, _cap);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit UpdateBridgeSupplyCapProposalCreated(proposalId, _target, _bridge, _cap);\n    }\n\n    function proposeSetBridgeTokenSwapCap(\n        address _target,\n        address _bridgeToken,\n        uint256 _swapCap\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IBridgeOwner.setBridgeTokenSwapCap.selector, _bridgeToken, _swapCap);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetBridgeTokenSwapCapProposalCreated(proposalId, _target, _bridgeToken, _swapCap);\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/CommonOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/ICommonOwner.sol\";\nimport {SimpleGovernance as sg} from \"../SimpleGovernance.sol\";\nimport {OwnerDataTypes as dt} from \"./OwnerDataTypes.sol\";\n\nabstract contract CommonOwnerProxy is OwnerProxyBase {\n    event TransferOwnershipProposalCreated(uint256 proposalId, address target, address newOwner);\n    event UpdatePauserProposalCreated(uint256 proposalId, address target, dt.Action action, address account);\n\n    function proposeTransferOwnership(address _target, address _newOwner) external {\n        bytes memory data = abi.encodeWithSelector(ICommonOwner.transferOwnership.selector, _newOwner);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit TransferOwnershipProposalCreated(proposalId, _target, _newOwner);\n    }\n\n    function proposeUpdatePauser(\n        address _target,\n        dt.Action _action,\n        address _account\n    ) external {\n        bytes4 selector;\n        if (_action == dt.Action.Add) {\n            selector = ICommonOwner.addPauser.selector;\n        } else if (_action == dt.Action.Remove) {\n            selector = ICommonOwner.removePauser.selector;\n        } else {\n            revert(\"invalid action\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _account);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit UpdatePauserProposalCreated(proposalId, _target, _action, _account);\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/MessageOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/IMessageOwner.sol\";\nimport {SimpleGovernance as sg} from \"../SimpleGovernance.sol\";\nimport {OwnerDataTypes as dt} from \"./OwnerDataTypes.sol\";\n\nabstract contract MessageOwnerProxy is OwnerProxyBase {\n    event SetMsgFeeProposalCreated(uint256 proposalId, address target, dt.MsgFeeType feeType, uint256 fee);\n    event SetBridgeAddressProposalCreated(\n        uint256 proposalId,\n        address target,\n        dt.BridgeType bridgeType,\n        address bridgeAddr\n    );\n    event SetPreExecuteMessageGasUsageProposalCreated(uint256 proposalId, address target, uint256 usage);\n\n    function proposeSetMsgFee(\n        address _target,\n        dt.MsgFeeType _feeType,\n        uint256 _fee\n    ) external {\n        bytes4 selector;\n        if (_feeType == dt.MsgFeeType.PerByte) {\n            selector = IMessageOwner.setFeePerByte.selector;\n        } else if (_feeType == dt.MsgFeeType.Base) {\n            selector = IMessageOwner.setFeeBase.selector;\n        } else {\n            revert(\"invalid fee type\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _fee);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit SetMsgFeeProposalCreated(proposalId, _target, _feeType, _fee);\n    }\n\n    function proposeSetBridgeAddress(\n        address _target,\n        dt.BridgeType _bridgeType,\n        address _bridgeAddr\n    ) external {\n        bytes4 selector;\n        if (_bridgeType == dt.BridgeType.Liquidity) {\n            selector = IMessageOwner.setLiquidityBridge.selector;\n        } else if (_bridgeType == dt.BridgeType.PegBridge) {\n            selector = IMessageOwner.setPegBridge.selector;\n        } else if (_bridgeType == dt.BridgeType.PegVault) {\n            selector = IMessageOwner.setPegVault.selector;\n        } else if (_bridgeType == dt.BridgeType.PegBridgeV2) {\n            selector = IMessageOwner.setPegBridgeV2.selector;\n        } else if (_bridgeType == dt.BridgeType.PegVaultV2) {\n            selector = IMessageOwner.setPegVaultV2.selector;\n        } else {\n            revert(\"invalid bridge type\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _bridgeAddr);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetBridgeAddressProposalCreated(proposalId, _target, _bridgeType, _bridgeAddr);\n    }\n\n    function proposeSetPreExecuteMessageGasUsage(address _target, uint256 _usage) external {\n        bytes memory data = abi.encodeWithSelector(IMessageOwner.setPreExecuteMessageGasUsage.selector, _usage);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetPreExecuteMessageGasUsageProposalCreated(proposalId, _target, _usage);\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/OwnerDataTypes.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary OwnerDataTypes {\n    enum Action {\n        Set,\n        Add,\n        Remove\n    }\n\n    enum MsgFeeType {\n        PerByte,\n        Base\n    }\n\n    enum BridgeType {\n        Liquidity,\n        PegBridge,\n        PegVault,\n        PegBridgeV2,\n        PegVaultV2\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/OwnerProxyBase.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../SimpleGovernance.sol\";\n\nabstract contract OwnerProxyBase {\n    SimpleGovernance public gov;\n    address private initializer;\n\n    constructor(address _initializer) {\n        initializer = _initializer;\n    }\n\n    function initGov(SimpleGovernance _gov) public {\n        require(msg.sender == initializer, \"only initializer can init\");\n        require(address(gov) == address(0), \"gov addr already set\");\n        gov = _gov;\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/SgnOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/ISgnOwner.sol\";\nimport {SimpleGovernance as sg} from \"../SimpleGovernance.sol\";\nimport {OwnerDataTypes as dt} from \"./OwnerDataTypes.sol\";\n\nabstract contract SgnOwnerProxy is OwnerProxyBase {\n    event SetWhitelistEnableProposalCreated(uint256 proposalId, address target, bool enabled);\n    event UpdateWhitelistedProposalCreated(uint256 proposalId, address target, dt.Action action, address account);\n    event SetGovContractProposalCreated(uint256 proposalId, address target, address addr);\n    event SetRewardContractProposalCreated(uint256 proposalId, address target, address addr);\n    event SetMaxSlashFactorProposalCreated(uint256 proposalId, address target, uint256 maxSlashFactor);\n    event DrainTokenProposalCreated(uint256 proposalId, address target, address token, uint256 amount);\n\n    function proposeSetWhitelistEnable(address _target, bool _enable) external {\n        bytes memory data = abi.encodeWithSelector(ISgnOwner.setWhitelistEnabled.selector, _enable);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetWhitelistEnableProposalCreated(proposalId, _target, _enable);\n    }\n\n    function proposeUpdateWhitelisted(\n        address _target,\n        dt.Action _action,\n        address _account\n    ) external {\n        bytes4 selector;\n        if (_action == dt.Action.Add) {\n            selector = ISgnOwner.addWhitelisted.selector;\n        } else if (_action == dt.Action.Remove) {\n            selector = ISgnOwner.removeWhitelisted.selector;\n        } else {\n            revert(\"invalid action\");\n        }\n        bytes memory data = abi.encodeWithSelector(selector, _account);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalFastPass);\n        emit UpdateWhitelistedProposalCreated(proposalId, _target, _action, _account);\n    }\n\n    function proposeSetGovContract(address _target, address _addr) external {\n        bytes memory data = abi.encodeWithSelector(ISgnOwner.setGovContract.selector, _addr);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetGovContractProposalCreated(proposalId, _target, _addr);\n    }\n\n    function proposeSetRewardContract(address _target, address _addr) external {\n        bytes memory data = abi.encodeWithSelector(ISgnOwner.setRewardContract.selector, _addr);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetRewardContractProposalCreated(proposalId, _target, _addr);\n    }\n\n    function proposeSetMaxSlashFactor(address _target, uint256 _maxSlashFactor) external {\n        bytes memory data = abi.encodeWithSelector(ISgnOwner.setMaxSlashFactor.selector, _maxSlashFactor);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit SetMaxSlashFactorProposalCreated(proposalId, _target, _maxSlashFactor);\n    }\n\n    function proposeDrainToken(\n        address _target,\n        address _token,\n        uint256 _amount\n    ) external {\n        bytes memory data;\n        if (_token == address(0)) {\n            data = abi.encodeWithSelector(bytes4(keccak256(bytes(\"drainToken(uint256\"))), _amount);\n        } else {\n            data = abi.encodeWithSelector(bytes4(keccak256(bytes(\"drainToken(address,uint256\"))), _token, _amount);\n        }\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit DrainTokenProposalCreated(proposalId, _target, _token, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/governed-owner/proxies/UpgradeableOwnerProxy.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./OwnerProxyBase.sol\";\nimport \"../interfaces/IUpgradeableOwner.sol\";\nimport {SimpleGovernance as sg} from \"../SimpleGovernance.sol\";\nimport {OwnerDataTypes as dt} from \"./OwnerDataTypes.sol\";\n\nabstract contract UpgradeableOwnerProxy is OwnerProxyBase {\n    event ChangeProxyAdminProposalCreated(uint256 proposalId, address target, address proxy, address newAdmin);\n    event UpgradeProposalCreated(uint256 proposalId, address target, address proxy, address implementation);\n    event UpgradeAndCallProposalCreated(\n        uint256 proposalId,\n        address target,\n        address proxy,\n        address implementation,\n        bytes data\n    );\n    event UpgradeToProposalCreated(uint256 proposalId, address target, address implementation);\n    event UpgradeToAndCallProposalCreated(uint256 proposalId, address target, address implementation, bytes data);\n\n    function proposeChangeProxyAdmin(\n        address _target,\n        address _proxy,\n        address _newAdmin\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.changeProxyAdmin.selector, _proxy, _newAdmin);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit ChangeProxyAdminProposalCreated(proposalId, _target, _proxy, _newAdmin);\n    }\n\n    function proposeUpgrade(\n        address _target,\n        address _proxy,\n        address _implementation\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgrade.selector, _proxy, _implementation);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit UpgradeProposalCreated(proposalId, _target, _proxy, _implementation);\n    }\n\n    function proposeUpgradeAndCall(\n        address _target,\n        address _proxy,\n        address _implementation,\n        bytes calldata _data\n    ) external {\n        bytes memory data = abi.encodeWithSelector(\n            IUpgradeableOwner.upgradeAndCall.selector,\n            _proxy,\n            _implementation,\n            _data\n        );\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit UpgradeAndCallProposalCreated(proposalId, _target, _proxy, _implementation, _data);\n    }\n\n    function proposeUpgradeTo(address _target, address _implementation) external {\n        bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeTo.selector, _implementation);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit UpgradeToProposalCreated(proposalId, _target, _implementation);\n    }\n\n    function proposeUpgradeToAndCall(\n        address _target,\n        address _implementation,\n        bytes calldata _data\n    ) external {\n        bytes memory data = abi.encodeWithSelector(IUpgradeableOwner.upgradeToAndCall.selector, _implementation, _data);\n        uint256 proposalId = gov.createProposal(msg.sender, _target, data, sg.ProposalType.ExternalDefault);\n        emit UpgradeToAndCallProposalCreated(proposalId, _target, _implementation, _data);\n    }\n}\n"
  },
  {
    "path": "contracts/integration-examples/ContractAsLP.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"../interfaces/IPool.sol\";\nimport \"../interfaces/IWithdrawInbox.sol\";\nimport \"../safeguard/Pauser.sol\";\n\n/**\n * @title Example contract to provide liquidity to {Bridge}. Supports withdrawing liquidity via {WithdrawInbox}.\n */\ncontract ContractAsLP is ReentrancyGuard, Pauser {\n    using SafeERC20 for IERC20;\n\n    address public bridge;\n    address public inbox;\n\n    event Deposited(address depositor, address token, uint256 amount);\n\n    constructor(address _bridge, address _inbox) {\n        bridge = _bridge;\n        inbox = _inbox;\n    }\n\n    /**\n     * @notice Deposit tokens.\n     * @param _token The deposited token address.\n     * @param _amount The amount to deposit.\n     */\n    function deposit(address _token, uint256 _amount) external nonReentrant whenNotPaused onlyOwner {\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit Deposited(msg.sender, _token, _amount);\n    }\n\n    /**\n     * @notice Add liquidity to the pool-based bridge.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The address of the token.\n     * @param _amount The amount to add.\n     */\n    function addLiquidity(address _token, uint256 _amount) external whenNotPaused onlyOwner {\n        require(IERC20(_token).balanceOf(address(this)) >= _amount, \"insufficient balance\");\n        IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n        IPool(bridge).addLiquidity(_token, _amount);\n    }\n\n    /**\n     * @notice Withdraw liquidity from the pool-based bridge.\n     * NOTE: Each of your withdrawal request should have different _wdSeq.\n     * NOTE: Tokens to withdraw within one withdrawal request should have the same symbol.\n     * @param _wdSeq The unique sequence number to identify this withdrawal request.\n     * @param _receiver The receiver address on _toChain.\n     * @param _toChain The chain Id to receive the withdrawn tokens.\n     * @param _fromChains The chain Ids to withdraw tokens.\n     * @param _tokens The token to withdraw on each fromChain.\n     * @param _ratios The withdrawal ratios of each token.\n     * @param _slippages The max slippages of each token for cross-chain withdraw.\n     */\n    function withdraw(\n        uint64 _wdSeq,\n        address _receiver,\n        uint64 _toChain,\n        uint64[] calldata _fromChains,\n        address[] calldata _tokens,\n        uint32[] calldata _ratios,\n        uint32[] calldata _slippages\n    ) external whenNotPaused onlyOwner {\n        IWithdrawInbox(inbox).withdraw(_wdSeq, _receiver, _toChain, _fromChains, _tokens, _ratios, _slippages);\n    }\n}\n"
  },
  {
    "path": "contracts/integration-examples/ContractAsSender.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\nimport \"../libraries/BridgeTransferLib.sol\";\nimport \"../safeguard/Pauser.sol\";\n\n/**\n * @title Example contract to send cBridge transfers. Supports the liquidity pool-based {Bridge}, the {OriginalTokenVault} for pegged\n * deposit and the {PeggedTokenBridge} for pegged burn. Includes handling of refunds for failed transfers.\n * @notice For the bad Bridge.send/PeggedTokenBridge.deposit of native token(eg.ETH) or wrapped native token(eg.WETH),\n * its refund asset depends on whether the nativeWrap of Bridge/PeggedTokenBridge is set or not AT THE MOMENT OF REFUNDING.\n * If the nativeWrap is set, the refund asset would always be native token (eg.ETH), even though the original sending asset\n * is wrapped native token. If the nativeWrap isn't set, the refund asset would always be wrapped native token.\n */\ncontract ContractAsSender is ReentrancyGuard, Pauser {\n    using SafeERC20 for IERC20;\n\n    mapping(BridgeTransferLib.BridgeSendType => address) public bridges;\n    mapping(bytes32 => address) public records;\n\n    address public nativeWrap;\n    uint256 public nativeTokenTransferGas = 50000;\n\n    event Deposited(address depositor, address token, uint256 amount);\n    event BridgeUpdated(BridgeTransferLib.BridgeSendType bridgeSendType, address bridgeAddr);\n\n    /**\n     * @notice Send a cross-chain transfer either via liquidity pool-based bridge or in form of mint/burn.\n     * @param _receiver The address of the receiver.\n     * @param _token The address of the token.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {BridgeSendType.Liquidity}.\n     * @param _bridgeSendType The type of bridge used by this transfer. One of the {BridgeSendType} enum.\n     */\n    function transfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000\n        BridgeTransferLib.BridgeSendType _bridgeSendType\n    ) external nonReentrant whenNotPaused onlyOwner returns (bytes32) {\n        address _bridgeAddr = bridges[_bridgeSendType];\n        require(_bridgeAddr != address(0), \"unknown bridge type\");\n        bytes32 transferId = BridgeTransferLib.sendTransfer(\n            _receiver,\n            _token,\n            _amount,\n            _dstChainId,\n            _nonce,\n            _maxSlippage,\n            _bridgeSendType,\n            _bridgeAddr\n        );\n        require(records[transferId] == address(0), \"record exists\");\n        records[transferId] = msg.sender;\n        return transferId;\n    }\n\n    /**\n     * @notice Refund a failed cross-chain transfer.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeSendType The type of bridge used by this failed transfer. One of the {BridgeSendType} enum.\n     */\n    function refund(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        BridgeTransferLib.BridgeSendType _bridgeSendType\n    ) external nonReentrant whenNotPaused onlyOwner returns (bytes32) {\n        address _bridgeAddr = bridges[_bridgeSendType];\n        require(_bridgeAddr != address(0), \"unknown bridge type\");\n        BridgeTransferLib.ReceiveInfo memory refundInfo = BridgeTransferLib.receiveTransfer(\n            _request,\n            _sigs,\n            _signers,\n            _powers,\n            BridgeTransferLib.bridgeRefundType(_bridgeSendType),\n            _bridgeAddr\n        );\n        require(refundInfo.receiver == address(this), \"invalid refund\");\n        address _receiver = records[refundInfo.refid];\n        require(_receiver != address(0), \"unknown transfer id or already refunded\");\n        delete records[refundInfo.refid];\n        _sendToken(_receiver, refundInfo.token, refundInfo.amount);\n        return refundInfo.transferId;\n    }\n\n    /**\n     * @notice Send token to user. For native token and wrapped native token, this contract may not have enough _token to\n     * send to _receiver. This may caused by others refund an original transfer that is sent from this contract via cBridge\n     * contract right before you call refund function of this contract and then the nativeWrap of cBridge contract is\n     * modified right after that the refund triggered by that guy completes.\n     * As a consequence, native token and wrapped native token possessed by this contract are mixed. But don't worry,\n     * the total sum of two tokens keeps correct. So in order to avoid deadlocking any token, we'd better have a\n     * balance check before sending out native token or wrapped native token. If the balance of _token is not sufficient,\n     * we change to sent the other token.\n     */\n    function _sendToken(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) internal {\n        if (_token == address(0)) {\n            // refund asset is ETH\n            if (address(this).balance >= _amount) {\n                (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n                require(sent, \"failed to send native token\");\n            } else {\n                // in case of refund asset is WETH\n                IERC20(_token).safeTransfer(_receiver, _amount);\n            }\n        } else if (_token == nativeWrap) {\n            // refund asset is WETH\n            if (IERC20(_token).balanceOf(address(this)) >= _amount) {\n                IERC20(_token).safeTransfer(_receiver, _amount);\n            } else {\n                // in case of refund asset is ETH\n                (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n                require(sent, \"failed to send native token\");\n            }\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n\n    // ----------------------Admin operation-----------------------\n\n    /**\n     * @notice Lock tokens.\n     * @param _token The deposited token address.\n     * @param _amount The amount to deposit.\n     */\n    function deposit(address _token, uint256 _amount) external nonReentrant whenNotPaused onlyOwner {\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit Deposited(msg.sender, _token, _amount);\n    }\n\n    function setBridgeAddress(BridgeTransferLib.BridgeSendType _bridgeSendType, address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        bridges[_bridgeSendType] = _addr;\n        emit BridgeUpdated(_bridgeSendType, _addr);\n    }\n\n    // set nativeWrap\n    function setWrap(address _weth) external onlyOwner {\n        nativeWrap = _weth;\n    }\n\n    // setNativeTransferGasUsed, native transfer will use this config.\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyOwner {\n        nativeTokenTransferGas = _gasUsed;\n    }\n\n    // This is needed to receive ETH if a refund asset is ETH\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/interfaces/IBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IBridge {\n    function send(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage\n    ) external;\n\n    function sendNative(\n        address _receiver,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage\n    ) external payable;\n\n    function relay(\n        bytes calldata _relayRequest,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n\n    function transfers(bytes32 transferId) external view returns (bool);\n\n    function withdraws(bytes32 withdrawId) external view returns (bool);\n\n    function withdraw(\n        bytes calldata _wdmsg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n\n    /**\n     * @notice Verifies that a message is signed by a quorum among the signers.\n     * @param _msg signed message\n     * @param _sigs list of signatures sorted by signer addresses in ascending order\n     * @param _signers sorted list of current signers\n     * @param _powers powers of current signers\n     */\n    function verifySigs(\n        bytes memory _msg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external view;\n}\n"
  },
  {
    "path": "contracts/interfaces/ICircleBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ICircleBridge {\n    /**\n     * @notice Deposits and burns tokens from sender to be minted on destination domain.\n     * Emits a `DepositForBurn` event.\n     * @dev reverts if:\n     * - given burnToken is not supported\n     * - given destinationDomain has no CircleBridge registered\n     * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n     * to this contract is less than `amount`.\n     * - burn() reverts. For example, if `amount` is 0.\n     * - MessageTransmitter returns false or reverts.\n     * @param _amount amount of tokens to burn\n     * @param _destinationDomain destination domain (ETH = 0, AVAX = 1)\n     * @param _mintRecipient address of mint recipient on destination domain\n     * @param _burnToken address of contract to burn deposited tokens, on local domain\n     * @return _nonce unique nonce reserved by message\n     */\n    function depositForBurn(\n        uint256 _amount,\n        uint32 _destinationDomain,\n        bytes32 _mintRecipient,\n        address _burnToken\n    ) external returns (uint64 _nonce);\n\n    /**\n     * @notice Deposits and burns tokens from sender to be minted on destination domain.\n     * Emits a `DepositForBurn` event.\n     * @dev reverts if:\n     * - given burnToken is not supported\n     * - given destinationDomain has no TokenMessenger registered\n     * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance\n     * to this contract is less than `amount`.\n     * - burn() reverts. For example, if `amount` is 0.\n     * - maxFee is greater than or equal to `amount`.\n     * - MessageTransmitterV2#sendMessage reverts.\n     * @param amount amount of tokens to burn\n     * @param destinationDomain destination domain to receive message on\n     * @param mintRecipient address of mint recipient on destination domain\n     * @param burnToken token to burn `amount` of, on local domain\n     * @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),\n     * any address can broadcast the message.\n     * @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken\n     * @param minFinalityThreshold the minimum finality at which a burn message will be attested to.\n     */\n    function depositForBurn(\n        uint256 amount,\n        uint32 destinationDomain,\n        bytes32 mintRecipient,\n        address burnToken,\n        bytes32 destinationCaller,\n        uint256 maxFee,\n        uint32 minFinalityThreshold\n    ) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/IDelayedTransfer.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\ninterface IDelayedTransfer {\n    struct delayedTransfer {\n        address receiver;\n        address token;\n        uint256 amount;\n        uint256 timestamp;\n    }\n\n    function delayedTransfers(bytes32 transferId) external view returns (delayedTransfer memory);\n}\n"
  },
  {
    "path": "contracts/interfaces/IOriginalTokenVault.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IOriginalTokenVault {\n    /**\n     * @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge\n     * @param _token local token address\n     * @param _amount locked token amount\n     * @param _mintChainId destination chainId to mint tokens\n     * @param _mintAccount destination account to receive minted tokens\n     * @param _nonce user input to guarantee unique depositId\n     */\n    function deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external;\n\n    /**\n     * @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge\n     * @param _amount locked token amount\n     * @param _mintChainId destination chainId to mint tokens\n     * @param _mintAccount destination account to receive minted tokens\n     * @param _nonce user input to guarantee unique depositId\n     */\n    function depositNative(\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external payable;\n\n    /**\n     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\n     * @param _request The serialized Withdraw protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n\n    function records(bytes32 recordId) external view returns (bool);\n}\n"
  },
  {
    "path": "contracts/interfaces/IOriginalTokenVaultV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IOriginalTokenVaultV2 {\n    /**\n     * @notice Lock original tokens to trigger mint at a remote chain's PeggedTokenBridge\n     * @param _token local token address\n     * @param _amount locked token amount\n     * @param _mintChainId destination chainId to mint tokens\n     * @param _mintAccount destination account to receive minted tokens\n     * @param _nonce user input to guarantee unique depositId\n     */\n    function deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external returns (bytes32);\n\n    /**\n     * @notice Lock native token as original token to trigger mint at a remote chain's PeggedTokenBridge\n     * @param _amount locked token amount\n     * @param _mintChainId destination chainId to mint tokens\n     * @param _mintAccount destination account to receive minted tokens\n     * @param _nonce user input to guarantee unique depositId\n     */\n    function depositNative(\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external payable returns (bytes32);\n\n    /**\n     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\n     * @param _request The serialized Withdraw protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external returns (bytes32);\n\n    function records(bytes32 recordId) external view returns (bool);\n}\n"
  },
  {
    "path": "contracts/interfaces/IPeggedToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedToken {\n    function mint(address _to, uint256 _amount) external;\n\n    function burn(address _from, uint256 _amount) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/IPeggedTokenBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedTokenBridge {\n    /**\n     * @notice Burn tokens to trigger withdrawal at a remote chain's OriginalTokenVault\n     * @param _token local token address\n     * @param _amount locked token amount\n     * @param _withdrawAccount account who withdraw original tokens on the remote chain\n     * @param _nonce user input to guarantee unique depositId\n     */\n    function burn(\n        address _token,\n        uint256 _amount,\n        address _withdrawAccount,\n        uint64 _nonce\n    ) external;\n\n    /**\n     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\n     * @param _request The serialized Mint protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function mint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n\n    function records(bytes32 recordId) external view returns (bool);\n}\n"
  },
  {
    "path": "contracts/interfaces/IPeggedTokenBridgeV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPeggedTokenBridgeV2 {\n    /**\n     * @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens at a remote chain's\n     * OriginalTokenVault, or mint at another remote chain\n     * @param _token The pegged token address.\n     * @param _amount The amount to burn.\n     * @param _toChainId If zero, withdraw from original vault; otherwise, the remote chain to mint tokens.\n     * @param _toAccount The account to receive tokens on the remote chain\n     * @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function burn(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) external returns (bytes32);\n\n    // same with `burn` above, use openzeppelin ERC20Burnable interface\n    function burnFrom(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) external returns (bytes32);\n\n    /**\n     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\n     * @param _request The serialized Mint protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function mint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external returns (bytes32);\n\n    function records(bytes32 recordId) external view returns (bool);\n}\n"
  },
  {
    "path": "contracts/interfaces/IPeggedTokenBurnFrom.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\n// used for pegged token with openzeppelin ERC20Burnable interface\n// only compatible with PeggedTokenBridgeV2\ninterface IPeggedTokenBurnFrom {\n    function mint(address _to, uint256 _amount) external;\n\n    function burnFrom(address _from, uint256 _amount) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/IPool.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IPool {\n    function addLiquidity(address _token, uint256 _amount) external;\n\n    function withdraws(bytes32 withdrawId) external view returns (bool);\n\n    function withdraw(\n        bytes calldata _wdmsg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/ISigsVerifier.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface ISigsVerifier {\n    /**\n     * @notice Verifies that a message is signed by a quorum among the signers.\n     * @param _msg signed message\n     * @param _sigs list of signatures sorted by signer addresses in ascending order\n     * @param _signers sorted list of current signers\n     * @param _powers powers of current signers\n     */\n    function verifySigs(\n        bytes memory _msg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external view;\n}\n"
  },
  {
    "path": "contracts/interfaces/IUniswapV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IUniswapV2 {\n    function swapTokensForExactTokens(\n        uint256 amountOut,\n        uint256 amountInMax,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external returns (uint256[] memory amounts);\n\n    function swapExactTokensForTokens(\n        uint256 amountIn,\n        uint256 amountOutMin,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external returns (uint256[] memory amounts);\n}\n"
  },
  {
    "path": "contracts/interfaces/IWETH.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IWETH {\n    function deposit() external payable;\n\n    function withdraw(uint256) external;\n}\n"
  },
  {
    "path": "contracts/interfaces/IWithdrawInbox.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IWithdrawInbox {\n    function withdraw(\n        uint64 _wdSeq,\n        address _receiver,\n        uint64 _toChain,\n        uint64[] calldata _fromChains,\n        address[] calldata _tokens,\n        uint32[] calldata _ratios,\n        uint32[] calldata _slippages\n    ) external;\n}\n"
  },
  {
    "path": "contracts/libraries/BridgeTransferLib.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"./PbBridge.sol\";\nimport \"./PbPegged.sol\";\nimport \"./PbPool.sol\";\nimport \"../interfaces/IBridge.sol\";\nimport \"../interfaces/IOriginalTokenVault.sol\";\nimport \"../interfaces/IOriginalTokenVaultV2.sol\";\nimport \"../interfaces/IPeggedTokenBridge.sol\";\nimport \"../interfaces/IPeggedTokenBridgeV2.sol\";\n\ninterface INativeWrap {\n    function nativeWrap() external view returns (address);\n}\n\nlibrary BridgeTransferLib {\n    using SafeERC20 for IERC20;\n\n    enum BridgeSendType {\n        Null,\n        Liquidity,\n        PegDeposit,\n        PegBurn,\n        PegV2Deposit,\n        PegV2Burn,\n        PegV2BurnFrom\n    }\n\n    enum BridgeReceiveType {\n        Null,\n        LqRelay,\n        LqWithdraw,\n        PegMint,\n        PegWithdraw,\n        PegV2Mint,\n        PegV2Withdraw\n    }\n\n    struct ReceiveInfo {\n        bytes32 transferId;\n        address receiver;\n        address token; // 0 address for native token\n        uint256 amount;\n        bytes32 refid; // reference id, e.g., srcTransferId for refund\n    }\n\n    // ============== Internal library functions called by apps ==============\n\n    /**\n     * @notice Send a cross-chain transfer of ERC20 token either via liquidity pool-based bridge or in the form of pegged mint / burn.\n     * @param _receiver The address of the receiver.\n     * @param _token The address of the token.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {BridgeSendType.Liquidity}.\n     * @param _bridgeSendType The type of the bridge used by this transfer. One of the {BridgeSendType} enum.\n     * @param _bridgeAddr The address of the bridge used.\n     */\n    function sendTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000\n        BridgeSendType _bridgeSendType,\n        address _bridgeAddr\n    ) internal returns (bytes32) {\n        bytes32 transferId;\n        IERC20(_token).safeIncreaseAllowance(_bridgeAddr, _amount);\n        if (_bridgeSendType == BridgeSendType.Liquidity) {\n            IBridge(_bridgeAddr).send(_receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);\n            transferId = keccak256(\n                abi.encodePacked(address(this), _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))\n            );\n        } else if (_bridgeSendType == BridgeSendType.PegDeposit) {\n            IOriginalTokenVault(_bridgeAddr).deposit(_token, _amount, _dstChainId, _receiver, _nonce);\n            transferId = keccak256(\n                abi.encodePacked(address(this), _token, _amount, _dstChainId, _receiver, _nonce, uint64(block.chainid))\n            );\n        } else if (_bridgeSendType == BridgeSendType.PegBurn) {\n            IPeggedTokenBridge(_bridgeAddr).burn(_token, _amount, _receiver, _nonce);\n            transferId = keccak256(\n                abi.encodePacked(address(this), _token, _amount, _receiver, _nonce, uint64(block.chainid))\n            );\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(_bridgeAddr, 0);\n        } else if (_bridgeSendType == BridgeSendType.PegV2Deposit) {\n            transferId = IOriginalTokenVaultV2(_bridgeAddr).deposit(_token, _amount, _dstChainId, _receiver, _nonce);\n        } else if (_bridgeSendType == BridgeSendType.PegV2Burn) {\n            transferId = IPeggedTokenBridgeV2(_bridgeAddr).burn(_token, _amount, _dstChainId, _receiver, _nonce);\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(_bridgeAddr, 0);\n        } else if (_bridgeSendType == BridgeSendType.PegV2BurnFrom) {\n            transferId = IPeggedTokenBridgeV2(_bridgeAddr).burnFrom(_token, _amount, _dstChainId, _receiver, _nonce);\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(_bridgeAddr, 0);\n        } else {\n            revert(\"bridge send type not supported\");\n        }\n        return transferId;\n    }\n\n    /**\n     * @notice Send a cross-chain transfer of native token either via liquidity pool-based bridge or in the form of pegged mint / burn.\n     * @param _receiver The address of the receiver.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {BridgeSendType.Liquidity}.\n     * @param _bridgeSendType The type of the bridge used by this transfer. One of the {BridgeSendType} enum.\n     * @param _bridgeAddr The address of the bridge used.\n     */\n    function sendNativeTransfer(\n        address _receiver,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000\n        BridgeSendType _bridgeSendType,\n        address _bridgeAddr\n    ) internal returns (bytes32) {\n        require(\n            _bridgeSendType == BridgeSendType.Liquidity ||\n                _bridgeSendType == BridgeSendType.PegDeposit ||\n                _bridgeSendType == BridgeSendType.PegV2Deposit,\n            \"Lib: invalid bridge send type\"\n        );\n        address _token = INativeWrap(_bridgeAddr).nativeWrap();\n        bytes32 transferId;\n        if (_bridgeSendType == BridgeSendType.Liquidity) {\n            IBridge(_bridgeAddr).sendNative{value: msg.value}(_receiver, _amount, _dstChainId, _nonce, _maxSlippage);\n            transferId = keccak256(\n                abi.encodePacked(address(this), _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))\n            );\n        } else if (_bridgeSendType == BridgeSendType.PegDeposit) {\n            IOriginalTokenVault(_bridgeAddr).depositNative{value: msg.value}(_amount, _dstChainId, _receiver, _nonce);\n            transferId = keccak256(\n                abi.encodePacked(address(this), _token, _amount, _dstChainId, _receiver, _nonce, uint64(block.chainid))\n            );\n        } else {\n            // _bridgeSendType == BridgeSendType.PegV2Deposit\n            transferId = IOriginalTokenVaultV2(_bridgeAddr).depositNative{value: msg.value}(\n                _amount,\n                _dstChainId,\n                _receiver,\n                _nonce\n            );\n        }\n        return transferId;\n    }\n\n    /**\n     * @notice Receive a cross-chain transfer.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeReceiveType The type of the received transfer. One of the {BridgeReceiveType} enum.\n     * @param _bridgeAddr The address of the bridge used.\n     */\n    function receiveTransfer(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        BridgeReceiveType _bridgeReceiveType,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        if (_bridgeReceiveType == BridgeReceiveType.LqRelay) {\n            return receiveLiquidityRelay(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else if (_bridgeReceiveType == BridgeReceiveType.LqWithdraw) {\n            return receiveLiquidityWithdraw(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else if (_bridgeReceiveType == BridgeReceiveType.PegWithdraw) {\n            return receivePegWithdraw(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else if (_bridgeReceiveType == BridgeReceiveType.PegMint) {\n            return receivePegMint(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else if (_bridgeReceiveType == BridgeReceiveType.PegV2Withdraw) {\n            return receivePegV2Withdraw(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else if (_bridgeReceiveType == BridgeReceiveType.PegV2Mint) {\n            return receivePegV2Mint(_request, _sigs, _signers, _powers, _bridgeAddr);\n        } else {\n            revert(\"bridge receive type not supported\");\n        }\n    }\n\n    /**\n     * @notice Receive a liquidity bridge relay.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of liquidity bridge.\n     */\n    function receiveLiquidityRelay(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbBridge.Relay memory request = PbBridge.decRelay(_request);\n        recv.transferId = keccak256(\n            abi.encodePacked(\n                request.sender,\n                request.receiver,\n                request.token,\n                request.amount,\n                request.srcChainId,\n                uint64(block.chainid),\n                request.srcTransferId\n            )\n        );\n        recv.refid = request.srcTransferId;\n        recv.receiver = request.receiver;\n        recv.token = request.token;\n        recv.amount = request.amount;\n        if (!IBridge(_bridgeAddr).transfers(recv.transferId)) {\n            IBridge(_bridgeAddr).relay(_request, _sigs, _signers, _powers);\n        }\n        return recv;\n    }\n\n    /**\n     * @notice Receive a liquidity bridge withdrawal.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of liquidity bridge.\n     */\n    function receiveLiquidityWithdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbPool.WithdrawMsg memory request = PbPool.decWithdrawMsg(_request);\n        recv.transferId = keccak256(\n            abi.encodePacked(request.chainid, request.seqnum, request.receiver, request.token, request.amount)\n        );\n        recv.refid = request.refid;\n        recv.receiver = request.receiver;\n        if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {\n            recv.token = address(0);\n        } else {\n            recv.token = request.token;\n        }\n        recv.amount = request.amount;\n        if (!IBridge(_bridgeAddr).withdraws(recv.transferId)) {\n            IBridge(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);\n        }\n        return recv;\n    }\n\n    /**\n     * @notice Receive an OriginalTokenVault withdrawal.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of OriginalTokenVault.\n     */\n    function receivePegWithdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);\n        recv.transferId = keccak256(\n            abi.encodePacked(\n                request.receiver,\n                request.token,\n                request.amount,\n                request.burnAccount,\n                request.refChainId,\n                request.refId\n            )\n        );\n        recv.refid = request.refId;\n        recv.receiver = request.receiver;\n        if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {\n            recv.token = address(0);\n        } else {\n            recv.token = request.token;\n        }\n        recv.amount = request.amount;\n        if (!IOriginalTokenVault(_bridgeAddr).records(recv.transferId)) {\n            IOriginalTokenVault(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);\n        }\n        return recv;\n    }\n\n    /**\n     * @notice Receive a PeggedTokenBridge mint.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of PeggedTokenBridge.\n     */\n    function receivePegMint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbPegged.Mint memory request = PbPegged.decMint(_request);\n        recv.transferId = keccak256(\n            abi.encodePacked(\n                request.account,\n                request.token,\n                request.amount,\n                request.depositor,\n                request.refChainId,\n                request.refId\n            )\n        );\n        recv.refid = request.refId;\n        recv.receiver = request.account;\n        recv.token = request.token;\n        recv.amount = request.amount;\n        if (!IPeggedTokenBridge(_bridgeAddr).records(recv.transferId)) {\n            IPeggedTokenBridge(_bridgeAddr).mint(_request, _sigs, _signers, _powers);\n        }\n        return recv;\n    }\n\n    /**\n     * @notice Receive an OriginalTokenVaultV2 withdrawal.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A request must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of OriginalTokenVaultV2.\n     */\n    function receivePegV2Withdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);\n        if (IOriginalTokenVaultV2(_bridgeAddr).records(request.refId)) {\n            recv.transferId = keccak256(\n                abi.encodePacked(\n                    request.receiver,\n                    request.token,\n                    request.amount,\n                    request.burnAccount,\n                    request.refChainId,\n                    request.refId,\n                    _bridgeAddr\n                )\n            );\n        } else {\n            recv.transferId = IOriginalTokenVaultV2(_bridgeAddr).withdraw(_request, _sigs, _signers, _powers);\n        }\n        recv.refid = request.refId;\n        recv.receiver = request.receiver;\n        if (INativeWrap(_bridgeAddr).nativeWrap() == request.token) {\n            recv.token = address(0);\n        } else {\n            recv.token = request.token;\n        }\n        recv.amount = request.amount;\n        return recv;\n    }\n\n    /**\n     * @notice Receive a PeggedTokenBridgeV2 mint.\n     * @param _request The serialized request protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A request must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     * @param _bridgeAddr The address of PeggedTokenBridgeV2.\n     */\n    function receivePegV2Mint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        address _bridgeAddr\n    ) internal returns (ReceiveInfo memory) {\n        ReceiveInfo memory recv;\n        PbPegged.Mint memory request = PbPegged.decMint(_request);\n        if (IPeggedTokenBridgeV2(_bridgeAddr).records(request.refId)) {\n            recv.transferId = keccak256(\n                abi.encodePacked(\n                    request.account,\n                    request.token,\n                    request.amount,\n                    request.depositor,\n                    request.refChainId,\n                    request.refId,\n                    _bridgeAddr\n                )\n            );\n        } else {\n            recv.transferId = IPeggedTokenBridgeV2(_bridgeAddr).mint(_request, _sigs, _signers, _powers);\n        }\n        recv.refid = request.refId;\n        recv.receiver = request.account;\n        recv.token = request.token;\n        recv.amount = request.amount;\n        return recv;\n    }\n\n    function bridgeRefundType(BridgeSendType _bridgeSendType) internal pure returns (BridgeReceiveType) {\n        if (_bridgeSendType == BridgeSendType.Liquidity) {\n            return BridgeReceiveType.LqWithdraw;\n        }\n        if (_bridgeSendType == BridgeSendType.PegDeposit) {\n            return BridgeReceiveType.PegWithdraw;\n        }\n        if (_bridgeSendType == BridgeSendType.PegBurn) {\n            return BridgeReceiveType.PegMint;\n        }\n        if (_bridgeSendType == BridgeSendType.PegV2Deposit) {\n            return BridgeReceiveType.PegV2Withdraw;\n        }\n        if (_bridgeSendType == BridgeSendType.PegV2Burn || _bridgeSendType == BridgeSendType.PegV2BurnFrom) {\n            return BridgeReceiveType.PegV2Mint;\n        }\n        return BridgeReceiveType.Null;\n    }\n}\n"
  },
  {
    "path": "contracts/libraries/Pb.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\n// runtime proto sol library\nlibrary Pb {\n    enum WireType {\n        Varint,\n        Fixed64,\n        LengthDelim,\n        StartGroup,\n        EndGroup,\n        Fixed32\n    }\n\n    struct Buffer {\n        uint256 idx; // the start index of next read. when idx=b.length, we're done\n        bytes b; // hold serialized proto msg, readonly\n    }\n\n    // create a new in-memory Buffer object from raw msg bytes\n    function fromBytes(bytes memory raw) internal pure returns (Buffer memory buf) {\n        buf.b = raw;\n        buf.idx = 0;\n    }\n\n    // whether there are unread bytes\n    function hasMore(Buffer memory buf) internal pure returns (bool) {\n        return buf.idx < buf.b.length;\n    }\n\n    // decode current field number and wiretype\n    function decKey(Buffer memory buf) internal pure returns (uint256 tag, WireType wiretype) {\n        uint256 v = decVarint(buf);\n        tag = v / 8;\n        wiretype = WireType(v & 7);\n    }\n\n    // count tag occurrences, return an array due to no memory map support\n    // have to create array for (maxtag+1) size. cnts[tag] = occurrences\n    // should keep buf.idx unchanged because this is only a count function\n    function cntTags(Buffer memory buf, uint256 maxtag) internal pure returns (uint256[] memory cnts) {\n        uint256 originalIdx = buf.idx;\n        cnts = new uint256[](maxtag + 1); // protobuf's tags are from 1 rather than 0\n        uint256 tag;\n        WireType wire;\n        while (hasMore(buf)) {\n            (tag, wire) = decKey(buf);\n            cnts[tag] += 1;\n            skipValue(buf, wire);\n        }\n        buf.idx = originalIdx;\n    }\n\n    // read varint from current buf idx, move buf.idx to next read, return the int value\n    function decVarint(Buffer memory buf) internal pure returns (uint256 v) {\n        bytes10 tmp; // proto int is at most 10 bytes (7 bits can be used per byte)\n        bytes memory bb = buf.b; // get buf.b mem addr to use in assembly\n        v = buf.idx; // use v to save one additional uint variable\n        assembly {\n            tmp := mload(add(add(bb, 32), v)) // load 10 bytes from buf.b[buf.idx] to tmp\n        }\n        uint256 b; // store current byte content\n        v = 0; // reset to 0 for return value\n        for (uint256 i = 0; i < 10; i++) {\n            assembly {\n                b := byte(i, tmp) // don't use tmp[i] because it does bound check and costs extra\n            }\n            v |= (b & 0x7F) << (i * 7);\n            if (b & 0x80 == 0) {\n                buf.idx += i + 1;\n                return v;\n            }\n        }\n        revert(); // i=10, invalid varint stream\n    }\n\n    // read length delimited field and return bytes\n    function decBytes(Buffer memory buf) internal pure returns (bytes memory b) {\n        uint256 len = decVarint(buf);\n        uint256 end = buf.idx + len;\n        require(end <= buf.b.length); // avoid overflow\n        b = new bytes(len);\n        bytes memory bufB = buf.b; // get buf.b mem addr to use in assembly\n        uint256 bStart;\n        uint256 bufBStart = buf.idx;\n        assembly {\n            bStart := add(b, 32)\n            bufBStart := add(add(bufB, 32), bufBStart)\n        }\n        for (uint256 i = 0; i < len; i += 32) {\n            assembly {\n                mstore(add(bStart, i), mload(add(bufBStart, i)))\n            }\n        }\n        buf.idx = end;\n    }\n\n    // return packed ints\n    function decPacked(Buffer memory buf) internal pure returns (uint256[] memory t) {\n        uint256 len = decVarint(buf);\n        uint256 end = buf.idx + len;\n        require(end <= buf.b.length); // avoid overflow\n        // array in memory must be init w/ known length\n        // so we have to create a tmp array w/ max possible len first\n        uint256[] memory tmp = new uint256[](len);\n        uint256 i = 0; // count how many ints are there\n        while (buf.idx < end) {\n            tmp[i] = decVarint(buf);\n            i++;\n        }\n        t = new uint256[](i); // init t with correct length\n        for (uint256 j = 0; j < i; j++) {\n            t[j] = tmp[j];\n        }\n        return t;\n    }\n\n    // move idx pass current value field, to beginning of next tag or msg end\n    function skipValue(Buffer memory buf, WireType wire) internal pure {\n        if (wire == WireType.Varint) {\n            decVarint(buf);\n        } else if (wire == WireType.LengthDelim) {\n            uint256 len = decVarint(buf);\n            buf.idx += len; // skip len bytes value data\n            require(buf.idx <= buf.b.length); // avoid overflow\n        } else {\n            revert();\n        } // unsupported wiretype\n    }\n\n    // type conversion help utils\n    function _bool(uint256 x) internal pure returns (bool v) {\n        return x != 0;\n    }\n\n    function _uint256(bytes memory b) internal pure returns (uint256 v) {\n        require(b.length <= 32); // b's length must be smaller than or equal to 32\n        assembly {\n            v := mload(add(b, 32))\n        } // load all 32bytes to v\n        v = v >> (8 * (32 - b.length)); // only first b.length is valid\n    }\n\n    function _address(bytes memory b) internal pure returns (address v) {\n        v = _addressPayable(b);\n    }\n\n    function _addressPayable(bytes memory b) internal pure returns (address payable v) {\n        require(b.length == 20);\n        //load 32bytes then shift right 12 bytes\n        assembly {\n            v := div(mload(add(b, 32)), 0x1000000000000000000000000)\n        }\n    }\n\n    function _bytes32(bytes memory b) internal pure returns (bytes32 v) {\n        require(b.length == 32);\n        assembly {\n            v := mload(add(b, 32))\n        }\n    }\n\n    // uint[] to uint8[]\n    function uint8s(uint256[] memory arr) internal pure returns (uint8[] memory t) {\n        t = new uint8[](arr.length);\n        for (uint256 i = 0; i < t.length; i++) {\n            t[i] = uint8(arr[i]);\n        }\n    }\n\n    function uint32s(uint256[] memory arr) internal pure returns (uint32[] memory t) {\n        t = new uint32[](arr.length);\n        for (uint256 i = 0; i < t.length; i++) {\n            t[i] = uint32(arr[i]);\n        }\n    }\n\n    function uint64s(uint256[] memory arr) internal pure returns (uint64[] memory t) {\n        t = new uint64[](arr.length);\n        for (uint256 i = 0; i < t.length; i++) {\n            t[i] = uint64(arr[i]);\n        }\n    }\n\n    function bools(uint256[] memory arr) internal pure returns (bool[] memory t) {\n        t = new bool[](arr.length);\n        for (uint256 i = 0; i < t.length; i++) {\n            t[i] = arr[i] != 0;\n        }\n    }\n}\n"
  },
  {
    "path": "contracts/libraries/PbBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: bridge.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbBridge {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct Relay {\n        address sender; // tag: 1\n        address receiver; // tag: 2\n        address token; // tag: 3\n        uint256 amount; // tag: 4\n        uint64 srcChainId; // tag: 5\n        uint64 dstChainId; // tag: 6\n        bytes32 srcTransferId; // tag: 7\n    } // end struct Relay\n\n    function decRelay(bytes memory raw) internal pure returns (Relay memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.sender = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.receiver = Pb._address(buf.decBytes());\n            } else if (tag == 3) {\n                m.token = Pb._address(buf.decBytes());\n            } else if (tag == 4) {\n                m.amount = Pb._uint256(buf.decBytes());\n            } else if (tag == 5) {\n                m.srcChainId = uint64(buf.decVarint());\n            } else if (tag == 6) {\n                m.dstChainId = uint64(buf.decVarint());\n            } else if (tag == 7) {\n                m.srcTransferId = Pb._bytes32(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder Relay\n}\n"
  },
  {
    "path": "contracts/libraries/PbFarming.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/libraries/proto/farming.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbFarming {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct FarmingRewards {\n        address recipient; // tag: 1\n        address[] tokenAddresses; // tag: 2\n        uint256[] cumulativeRewardAmounts; // tag: 3\n    } // end struct FarmingRewards\n\n    function decFarmingRewards(bytes memory raw) internal pure returns (FarmingRewards memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256[] memory cnts = buf.cntTags(3);\n        m.tokenAddresses = new address[](cnts[2]);\n        cnts[2] = 0; // reset counter for later use\n        m.cumulativeRewardAmounts = new uint256[](cnts[3]);\n        cnts[3] = 0; // reset counter for later use\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.recipient = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.tokenAddresses[cnts[2]] = Pb._address(buf.decBytes());\n                cnts[2]++;\n            } else if (tag == 3) {\n                m.cumulativeRewardAmounts[cnts[3]] = Pb._uint256(buf.decBytes());\n                cnts[3]++;\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder FarmingRewards\n}\n"
  },
  {
    "path": "contracts/libraries/PbPegged.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/libraries/proto/pegged.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbPegged {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct Mint {\n        address token; // tag: 1\n        address account; // tag: 2\n        uint256 amount; // tag: 3\n        address depositor; // tag: 4\n        uint64 refChainId; // tag: 5\n        bytes32 refId; // tag: 6\n    } // end struct Mint\n\n    function decMint(bytes memory raw) internal pure returns (Mint memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.token = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.account = Pb._address(buf.decBytes());\n            } else if (tag == 3) {\n                m.amount = Pb._uint256(buf.decBytes());\n            } else if (tag == 4) {\n                m.depositor = Pb._address(buf.decBytes());\n            } else if (tag == 5) {\n                m.refChainId = uint64(buf.decVarint());\n            } else if (tag == 6) {\n                m.refId = Pb._bytes32(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder Mint\n\n    struct Withdraw {\n        address token; // tag: 1\n        address receiver; // tag: 2\n        uint256 amount; // tag: 3\n        address burnAccount; // tag: 4\n        uint64 refChainId; // tag: 5\n        bytes32 refId; // tag: 6\n    } // end struct Withdraw\n\n    function decWithdraw(bytes memory raw) internal pure returns (Withdraw memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.token = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.receiver = Pb._address(buf.decBytes());\n            } else if (tag == 3) {\n                m.amount = Pb._uint256(buf.decBytes());\n            } else if (tag == 4) {\n                m.burnAccount = Pb._address(buf.decBytes());\n            } else if (tag == 5) {\n                m.refChainId = uint64(buf.decVarint());\n            } else if (tag == 6) {\n                m.refId = Pb._bytes32(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder Withdraw\n}\n"
  },
  {
    "path": "contracts/libraries/PbPool.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/libraries/proto/pool.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbPool {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct WithdrawMsg {\n        uint64 chainid; // tag: 1\n        uint64 seqnum; // tag: 2\n        address receiver; // tag: 3\n        address token; // tag: 4\n        uint256 amount; // tag: 5\n        bytes32 refid; // tag: 6\n    } // end struct WithdrawMsg\n\n    function decWithdrawMsg(bytes memory raw) internal pure returns (WithdrawMsg memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.chainid = uint64(buf.decVarint());\n            } else if (tag == 2) {\n                m.seqnum = uint64(buf.decVarint());\n            } else if (tag == 3) {\n                m.receiver = Pb._address(buf.decBytes());\n            } else if (tag == 4) {\n                m.token = Pb._address(buf.decBytes());\n            } else if (tag == 5) {\n                m.amount = Pb._uint256(buf.decBytes());\n            } else if (tag == 6) {\n                m.refid = Pb._bytes32(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder WithdrawMsg\n}\n"
  },
  {
    "path": "contracts/libraries/PbSgn.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/libraries/proto/sgn.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbSgn {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct Withdrawal {\n        address account; // tag: 1\n        address token; // tag: 2\n        uint256 cumulativeAmount; // tag: 3\n    } // end struct Withdrawal\n\n    function decWithdrawal(bytes memory raw) internal pure returns (Withdrawal memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.account = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.token = Pb._address(buf.decBytes());\n            } else if (tag == 3) {\n                m.cumulativeAmount = Pb._uint256(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder Withdrawal\n}\n"
  },
  {
    "path": "contracts/libraries/PbStaking.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// Code generated by protoc-gen-sol. DO NOT EDIT.\n// source: contracts/libraries/proto/staking.proto\npragma solidity 0.8.17;\nimport \"./Pb.sol\";\n\nlibrary PbStaking {\n    using Pb for Pb.Buffer; // so we can call Pb funcs on Buffer obj\n\n    struct StakingReward {\n        address recipient; // tag: 1\n        uint256 cumulativeRewardAmount; // tag: 2\n    } // end struct StakingReward\n\n    function decStakingReward(bytes memory raw) internal pure returns (StakingReward memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.recipient = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.cumulativeRewardAmount = Pb._uint256(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder StakingReward\n\n    struct Slash {\n        address validator; // tag: 1\n        uint64 nonce; // tag: 2\n        uint64 slashFactor; // tag: 3\n        uint64 expireTime; // tag: 4\n        uint64 jailPeriod; // tag: 5\n        AcctAmtPair[] collectors; // tag: 6\n    } // end struct Slash\n\n    function decSlash(bytes memory raw) internal pure returns (Slash memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256[] memory cnts = buf.cntTags(6);\n        m.collectors = new AcctAmtPair[](cnts[6]);\n        cnts[6] = 0; // reset counter for later use\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.validator = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.nonce = uint64(buf.decVarint());\n            } else if (tag == 3) {\n                m.slashFactor = uint64(buf.decVarint());\n            } else if (tag == 4) {\n                m.expireTime = uint64(buf.decVarint());\n            } else if (tag == 5) {\n                m.jailPeriod = uint64(buf.decVarint());\n            } else if (tag == 6) {\n                m.collectors[cnts[6]] = decAcctAmtPair(buf.decBytes());\n                cnts[6]++;\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder Slash\n\n    struct AcctAmtPair {\n        address account; // tag: 1\n        uint256 amount; // tag: 2\n    } // end struct AcctAmtPair\n\n    function decAcctAmtPair(bytes memory raw) internal pure returns (AcctAmtPair memory m) {\n        Pb.Buffer memory buf = Pb.fromBytes(raw);\n\n        uint256 tag;\n        Pb.WireType wire;\n        while (buf.hasMore()) {\n            (tag, wire) = buf.decKey();\n            if (false) {}\n            // solidity has no switch/case\n            else if (tag == 1) {\n                m.account = Pb._address(buf.decBytes());\n            } else if (tag == 2) {\n                m.amount = Pb._uint256(buf.decBytes());\n            } else {\n                buf.skipValue(wire);\n            } // skip value of unknown tag\n        }\n    } // end decoder AcctAmtPair\n}\n"
  },
  {
    "path": "contracts/libraries/Utils.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary Utils {\n    // https://ethereum.stackexchange.com/a/83577\n    // https://github.com/Uniswap/v3-periphery/blob/v1.0.0/contracts/base/Multicall.sol\n    function getRevertMsg(bytes memory _returnData) internal pure returns (string memory) {\n        // If the _res length is less than 68, then the transaction failed silently (without a revert message)\n        if (_returnData.length < 68) return \"Transaction reverted silently\";\n        assembly {\n            // Slice the sighash.\n            _returnData := add(_returnData, 0x04)\n        }\n        return abi.decode(_returnData, (string)); // All that remains is the revert string\n    }\n}\n"
  },
  {
    "path": "contracts/libraries/proto/README.md",
    "content": "# about soltype\n\nDue to pb gen sol only supports soltype field option defined in the same proto file. When we have multiple proto files that need soltype, it will break on go side. The short term solution is to manually sync proto files to go repo, and remove soltype related.\n\n## proper solution\n\nhave a single file like soltype.proto define the field option, then other proto files just import it. pb gen sol need to parse imported proto and ExtName will have soltype.proto package prefix eg. opt.soltype instead of just soltype. unless all protos have same package which will cause issue for pb gen sol as it outputs .sol files based on proto package name.\n\nor maybe we just register the fieldoption w/ proto team officially? still need to import another proto but package will be celer.opt\nhttps://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/descriptor.proto#L325\n\n# Generating Solidity bindings\n\nFrom the project repo root, run:\n\n```sh\nprotoc --sol_out=importpb=true:contracts/libraries contracts/libraries/proto/{filename}.proto\n```\n"
  },
  {
    "path": "contracts/libraries/proto/bridge.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage bridge;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\nmessage Relay {\n  bytes sender = 1 [(soltype) = \"address\"];\n  bytes receiver = 2 [(soltype) = \"address\"];\n  bytes token = 3 [(soltype) = \"address\"];  // asset address on dest chain\n  bytes amount = 4 [(soltype) = \"uint256\"];\n  uint64 src_chain_id = 5;\n  uint64 dst_chain_id = 6;\n  bytes src_transfer_id = 7 [(soltype) = \"bytes32\"];\n}\n"
  },
  {
    "path": "contracts/libraries/proto/farming.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage farming;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\nmessage FarmingRewards {\n  // recipient defines the reward recipient\n  bytes recipient = 1 [(soltype) = \"address\"];\n  // token_addresses defines the list of reward token addresses\n  repeated bytes token_addresses = 2 [(soltype) = \"address\"];\n  // cumulative_reward_amounts defines the cumulative amount of rewards\n  repeated bytes cumulative_reward_amounts = 3 [(soltype) = \"uint256\"];\n}\n"
  },
  {
    "path": "contracts/libraries/proto/pegged.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage pegged;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\n// mint pegged token\n// triggered by deposit at the original token vault\nmessage Mint {\n  bytes token = 1 [(soltype) = \"address\"];\n  bytes account = 2 [(soltype) = \"address\"];\n  bytes amount = 3 [(soltype) = \"uint256\"];\n  // depositor defines the account address that made deposit at the original token chain,\n  // or the account address that burned tokens at another remote chain\n  // Not applicable to governance-triggered mints.\n  bytes depositor = 4 [(soltype) = \"address\"];\n  // ref_chain_id defines the reference chain ID, taking values of:\n  // 1. The common case: the chain ID on which the corresponding deposit or burn happened;\n  // 2. Governance-triggered mint: the chain ID on which the minting will happen.\n  uint64 ref_chain_id = 5;\n  // ref_id defines a unique reference ID, taking values of:\n  // 1. The common case of deposit/burn-mint: the deposit or burn ID;\n  // 2. Governance-triggered mint: ID as needed.\n  bytes ref_id = 6 [(soltype) = \"bytes32\"];\n}\n\n// withdraw locked original tokens\n// triggered by burn at the pegged token bridge\nmessage Withdraw {\n  bytes token = 1 [(soltype) = \"address\"];\n  bytes receiver = 2 [(soltype) = \"address\"];\n  bytes amount = 3 [(soltype) = \"uint256\"];\n  // burn_account defines the account that burned the pegged token.\n  // Not applicable to fee claims and governance-triggered withdrawals.\n  bytes burn_account = 4 [(soltype) = \"address\"];\n  // ref_chain_id defines the reference chain ID, taking values of:\n  // 1. The common case of burn-withdraw: the chain ID on which the corresponding burn happened;\n  // 2. Pegbridge fee claim: zero / Not applicable;\n  // 3. Other governance-triggered withdrawals: the chain ID on which the withdrawal will happen.\n  uint64 ref_chain_id = 5;\n  // ref_id defines a unique reference ID, taking values of:\n  // 1. The common case of burn-withdraw: the burn ID;\n  // 2. Pegbridge fee claim: a per-account nonce;\n  // 3. Refund for wrong deposit: the deposit ID;\n  // 4. Governance-triggered withdrawal: ID as needed.\n  bytes ref_id = 6 [(soltype) = \"bytes32\"];\n}\n"
  },
  {
    "path": "contracts/libraries/proto/pool.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage pool;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\nmessage WithdrawMsg {\n  uint64 chainid = 1;\n  uint64 seqnum = 2;  // global unique across chains\n  bytes receiver = 3 [(soltype) = \"address\"];\n  bytes token = 4 [(soltype) = \"address\"];\n  bytes amount = 5 [(soltype) = \"uint256\"];\n  // reference id\n  // 1. if refund, set as xfer_id\n  // 2. if claim fee, set as 0x1\n  // 3. if LP withdraw liquidity, set as 0x0\n  bytes refid = 6 [(soltype) = \"bytes32\"];\n}"
  },
  {
    "path": "contracts/libraries/proto/sgn.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage sgn;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\nmessage Withdrawal {\n  bytes account = 1 [(soltype) = \"address\"];\n  bytes token = 2 [(soltype) = \"address\"];\n  bytes cumulative_amount = 3 [(soltype) = \"uint256\"];\n}\n"
  },
  {
    "path": "contracts/libraries/proto/staking.proto",
    "content": "syntax = \"proto3\";\noption go_package = \"github.com/celer-network/sgn-v2/proto/eth\";\n\nimport \"google/protobuf/descriptor.proto\";\n\npackage staking;\n\nextend google.protobuf.FieldOptions {\n  string soltype = 1004;\n}\n\nmessage StakingReward {\n  bytes recipient = 1 [(soltype) = \"address\"];\n  bytes cumulative_reward_amount = 2 [(soltype) = \"uint256\"];\n}\n\nmessage Slash {\n  bytes validator = 1 [(soltype) = \"address\"];\n  uint64 nonce = 2;\n  uint64 slash_factor = 3;\n  uint64 expire_time = 4;  // block timestamp\n  uint64 jail_period = 5;  // block number\n  repeated AcctAmtPair collectors = 6;\n}\n\nmessage AcctAmtPair {\n  bytes account = 1 [(soltype) = \"address\"];\n  bytes amount = 2 [(soltype) = \"uint256\"];\n}\n"
  },
  {
    "path": "contracts/liquidity-bridge/Bridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../libraries/PbBridge.sol\";\nimport \"./Pool.sol\";\n\n/**\n * @title The liquidity-pool based bridge.\n */\ncontract Bridge is Pool {\n    using SafeERC20 for IERC20;\n\n    // liquidity events\n    event Send(\n        bytes32 transferId,\n        address sender,\n        address receiver,\n        address token,\n        uint256 amount,\n        uint64 dstChainId,\n        uint64 nonce,\n        uint32 maxSlippage\n    );\n    event Relay(\n        bytes32 transferId,\n        address sender,\n        address receiver,\n        address token,\n        uint256 amount,\n        uint64 srcChainId,\n        bytes32 srcTransferId\n    );\n    // gov events\n    event MinSendUpdated(address token, uint256 amount);\n    event MaxSendUpdated(address token, uint256 amount);\n\n    mapping(bytes32 => bool) public transfers;\n    mapping(address => uint256) public minSend; // send _amount must > minSend\n    mapping(address => uint256) public maxSend;\n\n    // min allowed max slippage uint32 value is slippage * 1M, eg. 0.5% -> 5000\n    uint32 public minimalMaxSlippage;\n\n    /**\n     * @notice Send a cross-chain transfer via the liquidity pool-based bridge.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _receiver The address of the receiver.\n     * @param _token The address of the token.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     * Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the\n     * transfer can be refunded.\n     */\n    function send(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage // slippage * 1M, eg. 0.5% -> 5000\n    ) external nonReentrant whenNotPaused {\n        bytes32 transferId = _send(_receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit Send(transferId, msg.sender, _receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);\n    }\n\n    /**\n     * @notice Send a cross-chain transfer via the liquidity pool-based bridge using the native token.\n     * @param _receiver The address of the receiver.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A unique number. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     * Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the\n     * transfer can be refunded.\n     */\n    function sendNative(\n        address _receiver,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage\n    ) external payable nonReentrant whenNotPaused {\n        require(msg.value == _amount, \"Amount mismatch\");\n        require(nativeWrap != address(0), \"Native wrap not set\");\n        bytes32 transferId = _send(_receiver, nativeWrap, _amount, _dstChainId, _nonce, _maxSlippage);\n        IWETH(nativeWrap).deposit{value: _amount}();\n        emit Send(transferId, msg.sender, _receiver, nativeWrap, _amount, _dstChainId, _nonce, _maxSlippage);\n    }\n\n    function _send(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage\n    ) private returns (bytes32) {\n        require(_amount > minSend[_token], \"amount too small\");\n        require(maxSend[_token] == 0 || _amount <= maxSend[_token], \"amount too large\");\n        require(_maxSlippage > minimalMaxSlippage, \"max slippage too small\");\n        bytes32 transferId = keccak256(\n            // uint64(block.chainid) for consistency as entire system uses uint64 for chain id\n            // len = 20 + 20 + 20 + 32 + 8 + 8 + 8 = 116\n            abi.encodePacked(msg.sender, _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))\n        );\n        require(transfers[transferId] == false, \"transfer exists\");\n        transfers[transferId] = true;\n        return transferId;\n    }\n\n    /**\n     * @notice Relay a cross-chain transfer sent from a liquidity pool-based bridge on another chain.\n     * @param _relayRequest The serialized Relay protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function relay(\n        bytes calldata _relayRequest,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Relay\"));\n        verifySigs(abi.encodePacked(domain, _relayRequest), _sigs, _signers, _powers);\n        PbBridge.Relay memory request = PbBridge.decRelay(_relayRequest);\n        // len = 20 + 20 + 20 + 32 + 8 + 8 + 32 = 140\n        bytes32 transferId = keccak256(\n            abi.encodePacked(\n                request.sender,\n                request.receiver,\n                request.token,\n                request.amount,\n                request.srcChainId,\n                request.dstChainId,\n                request.srcTransferId\n            )\n        );\n        require(transfers[transferId] == false, \"transfer exists\");\n        transfers[transferId] = true;\n        _updateVolume(request.token, request.amount);\n        uint256 delayThreshold = delayThresholds[request.token];\n        if (delayThreshold > 0 && request.amount > delayThreshold) {\n            _addDelayedTransfer(transferId, request.receiver, request.token, request.amount);\n        } else {\n            _sendToken(request.receiver, request.token, request.amount);\n        }\n\n        emit Relay(\n            transferId,\n            request.sender,\n            request.receiver,\n            request.token,\n            request.amount,\n            request.srcChainId,\n            request.srcTransferId\n        );\n    }\n\n    function setMinSend(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minSend[_tokens[i]] = _amounts[i];\n            emit MinSendUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxSend(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxSend[_tokens[i]] = _amounts[i];\n            emit MaxSendUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMinimalMaxSlippage(uint32 _minimalMaxSlippage) external onlyGovernor {\n        minimalMaxSlippage = _minimalMaxSlippage;\n    }\n\n    // This is needed to receive ETH when calling `IWETH.withdraw`\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/liquidity-bridge/FarmingRewards.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../libraries/PbFarming.sol\";\nimport \"../safeguard/Pauser.sol\";\n\n/**\n * @title A contract to hold and distribute farming rewards.\n */\ncontract FarmingRewards is Pauser {\n    using SafeERC20 for IERC20;\n\n    ISigsVerifier public immutable sigsVerifier;\n\n    // recipient => tokenAddress => amount\n    mapping(address => mapping(address => uint256)) public claimedRewardAmounts;\n\n    event FarmingRewardClaimed(address indexed recipient, address indexed token, uint256 reward);\n    event FarmingRewardContributed(address indexed contributor, address indexed token, uint256 contribution);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Claim rewards\n     * @dev Here we use cumulative reward to make claim process idempotent\n     * @param _rewardsRequest rewards request bytes coded in protobuf\n     * @param _sigs list of signatures sorted by signer addresses in ascending order\n     * @param _signers sorted list of current signers\n     * @param _powers powers of current signers\n     */\n    function claimRewards(\n        bytes calldata _rewardsRequest,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"FarmingRewards\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _rewardsRequest), _sigs, _signers, _powers);\n        PbFarming.FarmingRewards memory rewards = PbFarming.decFarmingRewards(_rewardsRequest);\n        bool hasNewReward;\n        for (uint256 i = 0; i < rewards.tokenAddresses.length; i++) {\n            address token = rewards.tokenAddresses[i];\n            uint256 cumulativeRewardAmount = rewards.cumulativeRewardAmounts[i];\n            uint256 newReward = cumulativeRewardAmount - claimedRewardAmounts[rewards.recipient][token];\n            if (newReward > 0) {\n                hasNewReward = true;\n                claimedRewardAmounts[rewards.recipient][token] = cumulativeRewardAmount;\n                IERC20(token).safeTransfer(rewards.recipient, newReward);\n                emit FarmingRewardClaimed(rewards.recipient, token, newReward);\n            }\n        }\n        require(hasNewReward, \"No new reward\");\n    }\n\n    /**\n     * @notice Contribute reward tokens to the reward pool\n     * @param _token the address of the token to contribute\n     * @param _amount the amount of the token to contribute\n     */\n    function contributeToRewardPool(address _token, uint256 _amount) external whenNotPaused {\n        address contributor = msg.sender;\n        IERC20(_token).safeTransferFrom(contributor, address(this), _amount);\n\n        emit FarmingRewardContributed(contributor, _token, _amount);\n    }\n\n    /**\n     * @notice Owner drains tokens when the contract is paused\n     * @dev emergency use only\n     * @param _token the address of the token to drain\n     * @param _amount drained token amount\n     */\n    function drainToken(address _token, uint256 _amount) external whenPaused onlyOwner {\n        IERC20(_token).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/liquidity-bridge/Pool.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"../interfaces/IWETH.sol\";\nimport \"../libraries/PbPool.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\nimport \"./Signers.sol\";\n\n/**\n * @title Liquidity pool functions for {Bridge}.\n */\ncontract Pool is Signers, ReentrancyGuard, Pauser, VolumeControl, DelayedTransfer {\n    using SafeERC20 for IERC20;\n\n    uint64 public addseq; // ensure unique LiquidityAdded event, start from 1\n    mapping(address => uint256) public minAdd; // add _amount must > minAdd\n\n    // map of successful withdraws, if true means already withdrew money or added to delayedTransfers\n    mapping(bytes32 => bool) public withdraws;\n\n    // erc20 wrap of gas token of this chain, eg. WETH, when relay ie. pay out,\n    // if request.token equals this, will withdraw and send native token to receiver\n    // note we don't check whether it's zero address. when this isn't set, and request.token\n    // is all 0 address, guarantee fail\n    address public nativeWrap;\n\n    // when transfer native token after wrap, use this gas used config.\n    uint256 public nativeTokenTransferGas = 50000;\n\n    // liquidity events\n    event LiquidityAdded(\n        uint64 seqnum,\n        address provider,\n        address token,\n        uint256 amount // how many tokens were added\n    );\n    event WithdrawDone(\n        bytes32 withdrawId,\n        uint64 seqnum,\n        address receiver,\n        address token,\n        uint256 amount,\n        bytes32 refid\n    );\n    event MinAddUpdated(address token, uint256 amount);\n\n    /**\n     * @notice Add liquidity to the pool-based bridge.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The address of the token.\n     * @param _amount The amount to add.\n     */\n    function addLiquidity(address _token, uint256 _amount) external nonReentrant whenNotPaused {\n        require(_amount > minAdd[_token], \"amount too small\");\n        addseq += 1;\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit LiquidityAdded(addseq, msg.sender, _token, _amount);\n    }\n\n    /**\n     * @notice Add native token liquidity to the pool-based bridge.\n     * @param _amount The amount to add.\n     */\n    function addNativeLiquidity(uint256 _amount) external payable nonReentrant whenNotPaused {\n        require(msg.value == _amount, \"Amount mismatch\");\n        require(nativeWrap != address(0), \"Native wrap not set\");\n        require(_amount > minAdd[nativeWrap], \"amount too small\");\n        addseq += 1;\n        IWETH(nativeWrap).deposit{value: _amount}();\n        emit LiquidityAdded(addseq, msg.sender, nativeWrap, _amount);\n    }\n\n    /**\n     * @notice Withdraw funds from the bridge pool.\n     * @param _wdmsg The serialized Withdraw protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be\n     * signed-off by +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdraw(\n        bytes calldata _wdmsg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"WithdrawMsg\"));\n        verifySigs(abi.encodePacked(domain, _wdmsg), _sigs, _signers, _powers);\n        // decode and check wdmsg\n        PbPool.WithdrawMsg memory wdmsg = PbPool.decWithdrawMsg(_wdmsg);\n        // len = 8 + 8 + 20 + 20 + 32 = 88\n        bytes32 wdId = keccak256(\n            abi.encodePacked(wdmsg.chainid, wdmsg.seqnum, wdmsg.receiver, wdmsg.token, wdmsg.amount)\n        );\n        require(withdraws[wdId] == false, \"withdraw already succeeded\");\n        withdraws[wdId] = true;\n        _updateVolume(wdmsg.token, wdmsg.amount);\n        uint256 delayThreshold = delayThresholds[wdmsg.token];\n        if (delayThreshold > 0 && wdmsg.amount > delayThreshold) {\n            _addDelayedTransfer(wdId, wdmsg.receiver, wdmsg.token, wdmsg.amount);\n        } else {\n            _sendToken(wdmsg.receiver, wdmsg.token, wdmsg.amount);\n        }\n        emit WithdrawDone(wdId, wdmsg.seqnum, wdmsg.receiver, wdmsg.token, wdmsg.amount, wdmsg.refid);\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        _sendToken(transfer.receiver, transfer.token, transfer.amount);\n    }\n\n    function setMinAdd(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minAdd[_tokens[i]] = _amounts[i];\n            emit MinAddUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function _sendToken(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) internal {\n        if (_token == nativeWrap) {\n            // withdraw then transfer native to receiver\n            IWETH(nativeWrap).withdraw(_amount);\n            (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n            require(sent, \"failed to send native token\");\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n\n    // set nativeWrap, for relay requests, if token == nativeWrap, will withdraw first then transfer native to receiver\n    function setWrap(address _weth) external onlyOwner {\n        nativeWrap = _weth;\n    }\n\n    // setNativeTransferGasUsed, native transfer will use this config.\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyGovernor {\n        nativeTokenTransferGas = _gasUsed;\n    }\n}\n"
  },
  {
    "path": "contracts/liquidity-bridge/Signers.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"../safeguard/Ownable.sol\";\nimport \"../interfaces/ISigsVerifier.sol\";\n\n/**\n * @title Multi-sig verification and management functions for {Bridge}.\n */\ncontract Signers is Ownable, ISigsVerifier {\n    using ECDSA for bytes32;\n\n    bytes32 public ssHash;\n    uint256 public triggerTime; // timestamp when last update was triggered\n\n    // reset can be called by the owner address for emergency recovery\n    uint256 public resetTime;\n    uint256 public noticePeriod; // advance notice period as seconds for reset\n    uint256 constant MAX_INT = 2 ** 256 - 1;\n\n    event SignersUpdated(address[] _signers, uint256[] _powers);\n\n    event ResetNotification(uint256 resetTime);\n\n    /**\n     * @notice Verifies that a message is signed by a quorum among the signers\n     * The sigs must be sorted by signer addresses in ascending order.\n     * @param _msg signed message\n     * @param _sigs list of signatures sorted by signer addresses in ascending order\n     * @param _signers sorted list of current signers\n     * @param _powers powers of current signers\n     */\n    function verifySigs(\n        bytes memory _msg,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) public view override {\n        bytes32 h = keccak256(abi.encodePacked(_signers, _powers));\n        require(ssHash == h, \"Mismatch current signers\");\n        _verifySignedPowers(keccak256(_msg).toEthSignedMessageHash(), _sigs, _signers, _powers);\n    }\n\n    /**\n     * @notice Update new signers.\n     * @param _newSigners sorted list of new signers\n     * @param _curPowers powers of new signers\n     * @param _sigs list of signatures sorted by signer addresses in ascending order\n     * @param _curSigners sorted list of current signers\n     * @param _curPowers powers of current signers\n     */\n    function updateSigners(\n        uint256 _triggerTime,\n        address[] calldata _newSigners,\n        uint256[] calldata _newPowers,\n        bytes[] calldata _sigs,\n        address[] calldata _curSigners,\n        uint256[] calldata _curPowers\n    ) external {\n        // use trigger time for nonce protection, must be ascending\n        require(_triggerTime > triggerTime, \"Trigger time is not increasing\");\n        // make sure triggerTime is not too large, as it cannot be decreased once set\n        require(_triggerTime < block.timestamp + 3600, \"Trigger time is too large\");\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"UpdateSigners\"));\n        verifySigs(abi.encodePacked(domain, _triggerTime, _newSigners, _newPowers), _sigs, _curSigners, _curPowers);\n        _updateSigners(_newSigners, _newPowers);\n        triggerTime = _triggerTime;\n    }\n\n    /**\n     * @notice reset signers, only used for init setup and emergency recovery\n     */\n    function resetSigners(address[] calldata _signers, uint256[] calldata _powers) external onlyOwner {\n        require(block.timestamp > resetTime, \"not reach reset time\");\n        resetTime = MAX_INT;\n        _updateSigners(_signers, _powers);\n    }\n\n    function notifyResetSigners() external onlyOwner {\n        resetTime = block.timestamp + noticePeriod;\n        emit ResetNotification(resetTime);\n    }\n\n    function increaseNoticePeriod(uint256 period) external onlyOwner {\n        require(period > noticePeriod, \"notice period can only be increased\");\n        noticePeriod = period;\n    }\n\n    // separate from verifySigs func to avoid \"stack too deep\" issue\n    function _verifySignedPowers(\n        bytes32 _hash,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) private pure {\n        require(_signers.length == _powers.length, \"signers and powers length not match\");\n        uint256 totalPower; // sum of all signer.power\n        for (uint256 i = 0; i < _signers.length; i++) {\n            totalPower += _powers[i];\n        }\n        uint256 quorum = (totalPower * 2) / 3 + 1;\n\n        uint256 signedPower; // sum of signer powers who are in sigs\n        address prev = address(0);\n        uint256 index = 0;\n        for (uint256 i = 0; i < _sigs.length; i++) {\n            address signer = _hash.recover(_sigs[i]);\n            require(signer > prev, \"signers not in ascending order\");\n            prev = signer;\n            // now find match signer add its power\n            while (signer > _signers[index]) {\n                index += 1;\n                require(index < _signers.length, \"signer not found\");\n            }\n            if (signer == _signers[index]) {\n                signedPower += _powers[index];\n            }\n            if (signedPower >= quorum) {\n                // return early to save gas\n                return;\n            }\n        }\n        revert(\"quorum not reached\");\n    }\n\n    function _updateSigners(address[] calldata _signers, uint256[] calldata _powers) private {\n        require(_signers.length == _powers.length, \"signers and powers length not match\");\n        address prev = address(0);\n        for (uint256 i = 0; i < _signers.length; i++) {\n            require(_signers[i] > prev, \"New signers not in ascending order\");\n            prev = _signers[i];\n        }\n        ssHash = keccak256(abi.encodePacked(_signers, _powers));\n        emit SignersUpdated(_signers, _powers);\n    }\n}\n"
  },
  {
    "path": "contracts/liquidity-bridge/WithdrawInbox.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../safeguard/Ownable.sol\";\n\n/**\n * @title A contract to initiate withdrawal requests for contracts that provide liquidity to {Bridge}.\n */\ncontract WithdrawInbox is Ownable {\n    // min allowed max slippage uint32 value is slippage * 1M, eg. 0.5% -> 5000\n    uint32 public minimalMaxSlippage;\n    // the period of time during which a withdrawal request is intended to be valid\n    uint256 public validityPeriod;\n\n    // contract LP withdrawal request\n    event WithdrawalRequest(\n        uint64 seqNum,\n        address sender,\n        address receiver,\n        uint64 toChain,\n        uint64[] fromChains,\n        address[] tokens,\n        uint32[] ratios,\n        uint32[] slippages,\n        uint256 deadline\n    );\n\n    constructor() {\n        // default validityPeriod is 2 hours\n        validityPeriod = 7200;\n    }\n\n    /**\n     * @notice Withdraw liquidity from the pool-based bridge.\n     * NOTE: Each of your withdrawal request should have different _wdSeq.\n     * NOTE: Tokens to withdraw within one withdrawal request should have the same symbol.\n     * @param _wdSeq The unique sequence number to identify this withdrawal request.\n     * @param _receiver The receiver address on _toChain.\n     * @param _toChain The chain Id to receive the withdrawn tokens.\n     * @param _fromChains The chain Ids to withdraw tokens.\n     * @param _tokens The token to withdraw on each fromChain.\n     * @param _ratios The withdrawal ratios of each token.\n     * @param _slippages The max slippages of each token for cross-chain withdraw.\n     */\n    function withdraw(\n        uint64 _wdSeq,\n        address _receiver,\n        uint64 _toChain,\n        uint64[] calldata _fromChains,\n        address[] calldata _tokens,\n        uint32[] calldata _ratios,\n        uint32[] calldata _slippages\n    ) external {\n        require(_fromChains.length > 0, \"empty withdrawal request\");\n        require(\n            _tokens.length == _fromChains.length &&\n                _ratios.length == _fromChains.length &&\n                _slippages.length == _fromChains.length,\n            \"length mismatch\"\n        );\n        for (uint256 i = 0; i < _ratios.length; i++) {\n            require(_ratios[i] > 0 && _ratios[i] <= 1e8, \"invalid ratio\");\n            require(_slippages[i] >= minimalMaxSlippage, \"slippage too small\");\n        }\n        uint256 _deadline = block.timestamp + validityPeriod;\n        emit WithdrawalRequest(\n            _wdSeq,\n            msg.sender,\n            _receiver,\n            _toChain,\n            _fromChains,\n            _tokens,\n            _ratios,\n            _slippages,\n            _deadline\n        );\n    }\n\n    // ------------------------Admin operations--------------------------\n\n    function setMinimalMaxSlippage(uint32 _minimalMaxSlippage) external onlyOwner {\n        minimalMaxSlippage = _minimalMaxSlippage;\n    }\n\n    function setValidityPeriod(uint256 _validityPeriod) external onlyOwner {\n        validityPeriod = _validityPeriod;\n    }\n}\n"
  },
  {
    "path": "contracts/message/CHANGELOG.md",
    "content": "# 0.2.0 (2022-03-28)\n\n## MsgDataTypes\n\n- Most message bridge related data types are now in a separate library `MsgDataTypes`. Some types have their naming changed but there is no structural change.\n\n- `MsgDataTypes.BridgeSendType` (originally `BridgeType`) now has three more types `PegV2Deposit`, `PegV2Burn`, and `PegV2BurnFrom`.\n\n## MessageReceiverApp\n\n- Receiver functions `executeMessageWithTransfer`, `executeMessageWithTransferFallback`, `executeMessageWithTransferRefund`, `executeMessage` now has an extra param `address _executor` that the app developer could use to check who submitted the execution.\n\n- Receiver functions' required return value also changed from a `boolean success` to `ExecutionStatus` to accommodate a third status `ExecutionStatus.Retry`. This status indicates that the message processing should not be regarded as \"processed\" by MessageBus and the processing should simply be ignored. This status can be used in conjunction with the aforementioned `address _executor` to completely ignore executions that are not originated from a specific executor.\n\n## MessageBus (MessageBusReceiver)\n\n- Added a helper function `refund()` to aggregate refund call to `Bridge.withdraw()` and refund functions in other bridges and `MessageBus.executeMessageWithTransferRefund()` into one call.\n\n- Added a new event `NeedRetry`, emitted when the execution logic in an app contract returns `ExecutionStatus.Retry`.\n\n- Added a field `srcTxHash` in `Executed` and `NeedRetry` event to enable third parties to co-verify a whether a transfer/message send does happen on the source chain (in the name of not completely trusting message bus).\n"
  },
  {
    "path": "contracts/message/README.md",
    "content": "[Celer Inter-chain Message (IM) Developer Docs](https://im-docs.celer.network/)\n"
  },
  {
    "path": "contracts/message/apps/RFQ.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport \"../framework/MessageSenderApp.sol\";\nimport \"../framework/MessageReceiverApp.sol\";\nimport \"../../safeguard/Pauser.sol\";\nimport \"../../safeguard/Governor.sol\";\nimport \"../../message/interfaces/IMessageBus.sol\";\nimport \"../../interfaces/IWETH.sol\";\n\n/** @title rfq contract */\ncontract RFQ is MessageSenderApp, MessageReceiverApp, Pauser, Governor {\n    using SafeERC20 for IERC20;\n    using ECDSA for bytes32;\n\n    struct Quote {\n        uint64 srcChainId;\n        address srcToken;\n        uint256 srcAmount;\n        uint256 srcReleaseAmount;\n        uint64 dstChainId;\n        address dstToken;\n        uint256 dstAmount;\n        uint64 deadline;\n        uint64 nonce;\n        address sender;\n        address receiver;\n        address refundTo;\n        address liquidityProvider;\n    }\n\n    enum QuoteStatus {\n        Null,\n        SrcDeposited, // sender deposited\n        SrcReleased, // released ERC20 token to liquidity provider\n        SrcReleasedNative, // released native token to liquidity provider\n        SrcRefunded, // refunded ERC20 token to refundTo/sender\n        SrcRefundedNative, // refunded native token to refundTo/sender\n        DstRefundInitiated, // refund initiated\n        DstTransferred, // transferred ERC20 token to receiver\n        DstTransferredNative // transferred native token to receiver\n    }\n\n    enum MessageType {\n        Null,\n        Release,\n        Refund\n    }\n\n    address public nativeWrap;\n    uint256 public nativeTokenTransferGas = 50000;\n\n    mapping(uint64 => address) public remoteRfqContracts;\n    // msg => bool\n    mapping(bytes32 => bool) public unconsumedMsg;\n    // quoteHash => QuoteStatus\n    mapping(bytes32 => QuoteStatus) public quotes;\n\n    address public treasuryAddr;\n    uint32 public feePercGlobal;\n    // chainId => feePercOverride, support override fee perc of this chain\n    mapping(uint64 => uint32) public feePercOverride;\n    // tokenAddr => feeBalance\n    mapping(address => uint256) public protocolFee;\n\n    // market maker => allowed signer\n    mapping(address => address) public allowedSigner;\n\n    event SrcDeposited(bytes32 quoteHash, Quote quote);\n    event DstTransferred(bytes32 quoteHash, address receiver, address dstToken, uint256 amount);\n    event RefundInitiated(bytes32 quoteHash);\n    event SrcReleased(bytes32 quoteHash, address liquidityProvider, address srcToken, uint256 amount);\n    event Refunded(bytes32 quoteHash, address refundTo, address srcToken, uint256 amount);\n    event RfqContractsUpdated(uint64[] chainIds, address[] remoteRfqContracts);\n    event FeePercUpdated(uint64[] chainIds, uint32[] feePercs);\n    event TreasuryAddrUpdated(address treasuryAddr);\n    event FeeCollected(address treasuryAddr, address token, uint256 amount);\n\n    constructor(address _messageBus) {\n        messageBus = _messageBus;\n    }\n\n    function srcDeposit(Quote calldata _quote, uint64 _submissionDeadline)\n        external\n        payable\n        whenNotPaused\n        returns (bytes32)\n    {\n        bytes32 quoteHash = _srcDeposit(_quote, _submissionDeadline, msg.value);\n        IERC20(_quote.srcToken).safeTransferFrom(msg.sender, address(this), _quote.srcAmount);\n        return quoteHash;\n    }\n\n    function srcDepositNative(Quote calldata _quote, uint64 _submissionDeadline)\n        external\n        payable\n        whenNotPaused\n        returns (bytes32)\n    {\n        require(nativeWrap != address(0), \"Rfq: native wrap not set\");\n        require(_quote.srcToken == nativeWrap, \"Rfq: src token mismatch\");\n        require(msg.value >= _quote.srcAmount, \"Rfq: insufficient amount\");\n        bytes32 quoteHash = _srcDeposit(_quote, _submissionDeadline, msg.value - _quote.srcAmount);\n        IWETH(nativeWrap).deposit{value: _quote.srcAmount}();\n        return quoteHash;\n    }\n\n    function _srcDeposit(\n        Quote calldata _quote,\n        uint64 _submissionDeadline,\n        uint256 _msgFee\n    ) private returns (bytes32) {\n        require(\n            _submissionDeadline > block.timestamp && _quote.deadline > _submissionDeadline,\n            \"Rfq: inappropriate deadline\"\n        );\n        require(\n            _quote.receiver != address(0) && _quote.liquidityProvider != address(0),\n            \"Rfq: invalid receiver or liquidityProvider\"\n        );\n        require(_quote.srcChainId == uint64(block.chainid), \"Rfq: src chainId mismatch\");\n        require(_quote.sender == msg.sender, \"Rfq: sender mismatch\");\n        bytes32 quoteHash = getQuoteHash(_quote);\n        require(quotes[quoteHash] == QuoteStatus.Null, \"Rfq: quote hash exists\");\n        uint256 rfqFee = getRfqFee(_quote.dstChainId, _quote.srcAmount);\n        require(rfqFee <= _quote.srcAmount - _quote.srcReleaseAmount, \"Rfq: insufficient protocol fee\");\n\n        quotes[quoteHash] = QuoteStatus.SrcDeposited;\n        if (_quote.srcChainId != _quote.dstChainId) {\n            address msgReceiver = remoteRfqContracts[_quote.dstChainId];\n            require(msgReceiver != address(0), \"Rfq: dst contract not set\");\n            bytes memory message = abi.encodePacked(quoteHash);\n            sendMessage(msgReceiver, _quote.dstChainId, message, _msgFee);\n        }\n        emit SrcDeposited(quoteHash, _quote);\n        return quoteHash;\n    }\n\n    function dstTransfer(Quote calldata _quote) external payable whenNotPaused {\n        (bytes32 quoteHash, address msgReceiver) = _dstTransferCheck(_quote);\n        quotes[quoteHash] = QuoteStatus.DstTransferred;\n        bytes memory message = abi.encodePacked(keccak256(abi.encodePacked(quoteHash, MessageType.Release)));\n        sendMessage(msgReceiver, _quote.srcChainId, message, msg.value);\n        IERC20(_quote.dstToken).safeTransferFrom(msg.sender, _quote.receiver, _quote.dstAmount);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    function dstTransferNative(Quote calldata _quote) external payable whenNotPaused {\n        require(_quote.dstToken == nativeWrap, \"Rfq: dst token mismatch\");\n        require(msg.value >= _quote.dstAmount, \"Rfq: insufficient amount\");\n        (bytes32 quoteHash, address msgReceiver) = _dstTransferCheck(_quote);\n        quotes[quoteHash] = QuoteStatus.DstTransferredNative;\n        bytes memory message = abi.encodePacked(keccak256(abi.encodePacked(quoteHash, MessageType.Release)));\n        sendMessage(msgReceiver, _quote.srcChainId, message, msg.value - _quote.dstAmount);\n        _transferNativeToken(_quote.receiver, _quote.dstAmount);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    // As transferFrom is not available for native token, dstTransferNativeWithSig is not supported\n    function dstTransferWithSig(Quote calldata _quote, bytes calldata _sig) external payable whenNotPaused {\n        (bytes32 quoteHash, address msgReceiver) = _dstTransferCheck(_quote);\n        verifySigOfQuoteHash(_quote.liquidityProvider, quoteHash, _sig);\n        quotes[quoteHash] = QuoteStatus.DstTransferred;\n        bytes memory message = abi.encodePacked(keccak256(abi.encodePacked(quoteHash, MessageType.Release)));\n        sendMessage(msgReceiver, _quote.srcChainId, message, msg.value);\n        IERC20(_quote.dstToken).safeTransferFrom(_quote.liquidityProvider, _quote.receiver, _quote.dstAmount);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    function sameChainTransfer(Quote calldata _quote, bool _releaseNative) external payable whenNotPaused {\n        require(_quote.srcChainId == _quote.dstChainId, \"Rfq: not same chain swap\");\n        (bytes32 quoteHash, ) = _dstTransferCheck(_quote);\n        IERC20(_quote.dstToken).safeTransferFrom(msg.sender, _quote.receiver, _quote.dstAmount);\n        _srcRelease(_quote, quoteHash, _releaseNative);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    function sameChainTransferNative(Quote calldata _quote, bool _releaseNative) external payable whenNotPaused {\n        require(_quote.srcChainId == _quote.dstChainId, \"Rfq: not same chain swap\");\n        require(_quote.dstToken == nativeWrap, \"Rfq: dst token mismatch\");\n        require(msg.value == _quote.dstAmount, \"Rfq: native token amount mismatch\");\n        (bytes32 quoteHash, ) = _dstTransferCheck(_quote);\n        _transferNativeToken(_quote.receiver, _quote.dstAmount);\n        _srcRelease(_quote, quoteHash, _releaseNative);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    // As transferFrom is not available for native token, sameChainTransferNativeWithSig is not supported\n    function sameChainTransferWithSig(\n        Quote calldata _quote,\n        bool _releaseNative,\n        bytes calldata _sig\n    ) external payable whenNotPaused {\n        require(_quote.srcChainId == _quote.dstChainId, \"Rfq: not same chain swap\");\n        (bytes32 quoteHash, ) = _dstTransferCheck(_quote);\n        verifySigOfQuoteHash(_quote.liquidityProvider, quoteHash, _sig);\n        IERC20(_quote.dstToken).safeTransferFrom(_quote.liquidityProvider, _quote.receiver, _quote.dstAmount);\n        _srcRelease(_quote, quoteHash, _releaseNative);\n        emit DstTransferred(quoteHash, _quote.receiver, _quote.dstToken, _quote.dstAmount);\n    }\n\n    function _dstTransferCheck(Quote calldata _quote) private view returns (bytes32, address) {\n        require(_quote.deadline > block.timestamp, \"Rfq: transfer deadline passed\");\n        require(_quote.dstChainId == uint64(block.chainid), \"Rfq: dst chainId mismatch\");\n        bytes32 quoteHash = getQuoteHash(_quote);\n        address msgReceiver = remoteRfqContracts[_quote.srcChainId];\n        if (_quote.srcChainId != _quote.dstChainId) {\n            require(quotes[quoteHash] == QuoteStatus.Null, \"Rfq: quote already executed\");\n            require(msgReceiver != address(0), \"Rfq: dst rfq contract not set\");\n        } else {\n            require(quotes[quoteHash] == QuoteStatus.SrcDeposited, \"Rfq: no deposit on same chain\");\n        }\n        return (quoteHash, msgReceiver);\n    }\n\n    function srcRelease(Quote calldata _quote, bytes calldata _execMsgCallData) external whenNotPaused {\n        bytes32 quoteHash = _srcReleaseCheck(_quote, _execMsgCallData);\n        _srcRelease(_quote, quoteHash, false);\n    }\n\n    function srcReleaseNative(Quote calldata _quote, bytes calldata _execMsgCallData) external whenNotPaused {\n        require(_quote.srcToken == nativeWrap, \"Rfq: src token mismatch\");\n        bytes32 quoteHash = _srcReleaseCheck(_quote, _execMsgCallData);\n        _srcRelease(_quote, quoteHash, true);\n    }\n\n    function _srcReleaseCheck(Quote calldata _quote, bytes calldata _execMsgCallData) private returns (bytes32) {\n        bytes32 quoteHash = getQuoteHash(_quote);\n        require(quotes[quoteHash] == QuoteStatus.SrcDeposited, \"Rfq: incorrect quote hash\");\n        _receiveMessage(_execMsgCallData, quoteHash, MessageType.Release);\n        return quoteHash;\n    }\n\n    function _srcRelease(\n        Quote calldata _quote,\n        bytes32 _quoteHash,\n        bool _releaseNative\n    ) private {\n        protocolFee[_quote.srcToken] += (_quote.srcAmount - _quote.srcReleaseAmount);\n        if (_releaseNative) {\n            quotes[_quoteHash] = QuoteStatus.SrcReleasedNative;\n            _withdrawNativeToken(_quote.liquidityProvider, _quote.srcReleaseAmount);\n        } else {\n            quotes[_quoteHash] = QuoteStatus.SrcReleased;\n            IERC20(_quote.srcToken).safeTransfer(_quote.liquidityProvider, _quote.srcReleaseAmount);\n        }\n        emit SrcReleased(_quoteHash, _quote.liquidityProvider, _quote.srcToken, _quote.srcReleaseAmount);\n    }\n\n    function requestRefund(Quote calldata _quote) external payable whenNotPaused {\n        require(_quote.deadline < block.timestamp, \"Rfq: transfer deadline not passed\");\n        require(_quote.dstChainId == uint64(block.chainid), \"Rfq: dst chainId mismatch\");\n        address _receiver = remoteRfqContracts[_quote.srcChainId];\n        require(_receiver != address(0), \"Rfq: src rfq contract not set\");\n        bytes32 quoteHash = getQuoteHash(_quote);\n        require(quotes[quoteHash] == QuoteStatus.Null, \"Rfq: quote already executed\");\n\n        quotes[quoteHash] = QuoteStatus.DstRefundInitiated;\n        bytes memory message = abi.encodePacked(keccak256(abi.encodePacked(quoteHash, MessageType.Refund)));\n        sendMessage(_receiver, _quote.srcChainId, message, msg.value);\n        emit RefundInitiated(quoteHash);\n    }\n\n    function executeRefund(Quote calldata _quote, bytes calldata _execMsgCallData) external whenNotPaused {\n        (bytes32 quoteHash, address receiver) = _executeRefund(_quote, _execMsgCallData);\n        quotes[quoteHash] = QuoteStatus.SrcRefunded;\n        IERC20(_quote.srcToken).safeTransfer(receiver, _quote.srcAmount);\n        emit Refunded(quoteHash, receiver, _quote.srcToken, _quote.srcAmount);\n    }\n\n    function executeRefundNative(Quote calldata _quote, bytes calldata _execMsgCallData) external whenNotPaused {\n        require(_quote.srcToken == nativeWrap, \"Rfq: src token mismatch\");\n        (bytes32 quoteHash, address receiver) = _executeRefund(_quote, _execMsgCallData);\n        quotes[quoteHash] = QuoteStatus.SrcRefundedNative;\n        _withdrawNativeToken(receiver, _quote.srcAmount);\n        emit Refunded(quoteHash, receiver, _quote.srcToken, _quote.srcAmount);\n    }\n\n    function _executeRefund(Quote calldata _quote, bytes calldata _execMsgCallData) private returns (bytes32, address) {\n        bytes32 quoteHash = getQuoteHash(_quote);\n        require(quotes[quoteHash] == QuoteStatus.SrcDeposited, \"Rfq: incorrect quote hash\");\n        if (_quote.srcChainId != _quote.dstChainId) {\n            _receiveMessage(_execMsgCallData, quoteHash, MessageType.Refund);\n        } else {\n            require(_quote.deadline < block.timestamp, \"Rfq: transfer deadline not passed\");\n        }\n        address receiver = (_quote.refundTo == address(0)) ? _quote.sender : _quote.refundTo;\n        return (quoteHash, receiver);\n    }\n\n    function executeMessage(\n        address _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        require(_message.length == 32, \"Rfq: incorrect message length\");\n        address expectedSender = remoteRfqContracts[_srcChainId];\n        if (expectedSender != _sender) {\n            return ExecutionStatus.Retry;\n        }\n        unconsumedMsg[bytes32(_message)] = true;\n        return ExecutionStatus.Success;\n    }\n\n    function collectFee(address _token) external {\n        require(treasuryAddr != address(0), \"Rfq: treasury address not set\");\n        uint256 feeAmount = protocolFee[_token];\n        protocolFee[_token] = 0;\n        IERC20(_token).safeTransfer(treasuryAddr, feeAmount);\n        emit FeeCollected(treasuryAddr, _token, feeAmount);\n    }\n\n    function registerAllowedSigner(address _signer) external {\n        if (_signer == address(0)) {\n            delete (allowedSigner[msg.sender]);\n        } else {\n            allowedSigner[msg.sender] = _signer;\n        }\n    }\n\n    // This is needed to receive ETH\n    receive() external payable {}\n\n    //=========================== helper functions ==========================\n\n    function getQuoteHash(Quote calldata _quote) public pure returns (bytes32) {\n        return\n            keccak256(\n                abi.encodePacked(\n                    _quote.srcChainId,\n                    _quote.srcToken,\n                    _quote.srcAmount,\n                    _quote.srcReleaseAmount,\n                    _quote.dstChainId,\n                    _quote.dstToken,\n                    _quote.dstAmount,\n                    _quote.deadline,\n                    _quote.nonce,\n                    _quote.sender,\n                    _quote.receiver,\n                    _quote.refundTo,\n                    _quote.liquidityProvider\n                )\n            );\n    }\n\n    function getRfqFee(uint64 _chainId, uint256 _amount) public view returns (uint256) {\n        uint32 feePerc = feePercOverride[_chainId];\n        if (feePerc == 0) {\n            feePerc = feePercGlobal;\n        }\n        return (_amount * feePerc) / 1e6;\n    }\n\n    function getMsgFee(bytes calldata _message) public view returns (uint256) {\n        return IMessageBus(messageBus).calcFee(_message);\n    }\n\n    function getSignerOfQuoteHash(bytes32 _quoteHash, bytes calldata _sig) public view returns (address) {\n        bytes32 msgHash = keccak256(abi.encodePacked(block.chainid, address(this), \"AllowedTransfer\", _quoteHash))\n            .toEthSignedMessageHash();\n        return msgHash.recover(_sig);\n    }\n\n    function verifySigOfQuoteHash(\n        address _liquidityProvider,\n        bytes32 _quoteHash,\n        bytes calldata _sig\n    ) public view {\n        address signer = getSignerOfQuoteHash(_quoteHash, _sig);\n        require(\n            signer == _liquidityProvider ||\n                (allowedSigner[_liquidityProvider] != address(0) && signer == allowedSigner[_liquidityProvider]),\n            \"Rfq: not allowed signer\"\n        );\n    }\n\n    function _receiveMessage(\n        bytes calldata _execMsgCallData,\n        bytes32 _quoteHash,\n        MessageType _msgType\n    ) private {\n        bytes32 expectedMsg = keccak256(abi.encodePacked(_quoteHash, _msgType));\n        if (!unconsumedMsg[expectedMsg]) {\n            (bool success, ) = messageBus.call(_execMsgCallData);\n            require(success, \"execute msg failed\");\n        }\n        require(unconsumedMsg[expectedMsg], \"Rfq: invalid msg\");\n        delete unconsumedMsg[expectedMsg];\n    }\n\n    function _transferNativeToken(address _receiver, uint256 _amount) private {\n        require(nativeWrap != address(0), \"Rfq: native wrap not set\");\n        (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n        require(sent, \"Rfq: failed to transfer native token\");\n    }\n\n    function _withdrawNativeToken(address _receiver, uint256 _amount) private {\n        require(nativeWrap != address(0), \"Rfq: native wrap not set\");\n        IWETH(nativeWrap).withdraw(_amount);\n        (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n        require(sent, \"Rfq: failed to withdraw native token\");\n    }\n\n    //=========================== admin operations ==========================\n\n    function setRemoteRfqContracts(uint64[] calldata _chainIds, address[] calldata _remoteRfqContracts)\n        external\n        onlyOwner\n    {\n        require(_chainIds.length == _remoteRfqContracts.length, \"Rfq: length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            remoteRfqContracts[_chainIds[i]] = _remoteRfqContracts[i];\n        }\n        emit RfqContractsUpdated(_chainIds, _remoteRfqContracts);\n    }\n\n    function setFeePerc(uint64[] calldata _chainIds, uint32[] calldata _feePercs) external onlyGovernor {\n        require(_chainIds.length == _feePercs.length, \"Rfq: length mismatch\");\n        for (uint256 i = 0; i < _chainIds.length; i++) {\n            require(_feePercs[i] < 1e6, \"Rfq: fee percentage too large\");\n            if (_chainIds[i] == 0) {\n                feePercGlobal = _feePercs[i];\n            } else {\n                feePercOverride[_chainIds[i]] = _feePercs[i];\n            }\n        }\n        emit FeePercUpdated(_chainIds, _feePercs);\n    }\n\n    function setTreasuryAddr(address _treasuryAddr) external onlyOwner {\n        treasuryAddr = _treasuryAddr;\n        emit TreasuryAddrUpdated(_treasuryAddr);\n    }\n\n    function setNativeWrap(address _nativeWrap) external onlyOwner {\n        nativeWrap = _nativeWrap;\n    }\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyOwner {\n        nativeTokenTransferGas = _gasUsed;\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/adapter/MessageReceiverAdapter.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\nimport \"../../safeguard/MessageAppPauser.sol\";\nimport \"../../safeguard/DelayedMessage.sol\";\nimport \"../../../libraries/Utils.sol\";\n\ncontract MessageReceiverAdapter is MessageApp, MessageAppPauser, DelayedMessage {\n    event ExternalCall(address srcContract, uint64 srcChainId, address dstContract, bytes callData);\n    event AllowedSenderUpdated(address dstContract, uint64 srcChainId, address srcContract, bool allowed);\n\n    // dstContract => srcChainId => srcContract => allowed or not\n    mapping(address => mapping(uint64 => mapping(address => bool))) public allowedSender;\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    // Called by MessageBus on destination chain to receive cross-chain messages.\n    // The message is abi.encode of (dst_contract_address, dst_contract_calldata).\n    // If a delayed period is configured, the message would be put in a delayed message queue,\n    // otherwise, the external call to the dst contract will be executed immediately\n    function executeMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus whenNotMsgPaused returns (ExecutionStatus) {\n        (address dstContract, bytes memory callData) = abi.decode(_message, (address, bytes));\n        require(allowedSender[dstContract][_srcChainId][_srcContract], \"not allowed sender\");\n        if (delayPeriod > 0) {\n            _addDelayedMessage(_srcContract, _srcChainId, _message);\n        } else {\n            _externalCall(_srcContract, _srcChainId, dstContract, callData);\n        }\n        return ExecutionStatus.Success;\n    }\n\n    // execute external call to the dst contract after the message delay period is passed.\n    function executeDelayedMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        uint32 _nonce\n    ) external payable whenNotPaused {\n        _executeDelayedMessage(_srcContract, _srcChainId, _message, _nonce);\n        (address dstContract, bytes memory callData) = abi.decode(_message, (address, bytes));\n        _externalCall(_srcContract, _srcChainId, dstContract, callData);\n    }\n\n    function _externalCall(\n        address _srcContract,\n        uint64 _srcChainId,\n        address _dstContract,\n        bytes memory _callData\n    ) internal {\n        (bool ok, bytes memory returnData) = _dstContract.call{value: msg.value}(_callData);\n        if (!ok) {\n            revert(Utils.getRevertMsg(returnData));\n        }\n        emit ExternalCall(_srcContract, _srcChainId, _dstContract, _callData);\n    }\n\n    function setAllowedSender(\n        address _dstContract,\n        uint64 _srcChainId,\n        address[] calldata _srcContracts,\n        bool[] calldata _alloweds\n    ) external onlyOwner {\n        require(_srcContracts.length == _alloweds.length, \"length mismatch\");\n        for (uint256 i = 0; i < _srcContracts.length; i++) {\n            allowedSender[_dstContract][_srcChainId][_srcContracts[i]] = _alloweds[i];\n            emit AllowedSenderUpdated(_dstContract, _srcChainId, _srcContracts[i], _alloweds[i]);\n        }\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/BatchTransfer.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../framework/MessageApp.sol\";\n\n/** @title Sample app to test message passing flow, not for production use */\ncontract BatchTransfer is MessageApp {\n    using SafeERC20 for IERC20;\n\n    struct TransferRequest {\n        uint64 nonce;\n        address[] accounts;\n        uint256[] amounts;\n        address sender;\n    }\n\n    enum TransferStatus {\n        Null,\n        Success,\n        Fail\n    }\n\n    struct TransferReceipt {\n        uint64 nonce;\n        TransferStatus status;\n    }\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    // ============== functions and states on source chain ==============\n\n    uint64 nonce;\n\n    struct BatchTransferStatus {\n        bytes32 h; // hash(receiver, dstChainId)\n        TransferStatus status;\n    }\n    mapping(uint64 => BatchTransferStatus) public status; // nonce -> BatchTransferStatus\n\n    modifier onlyEOA() {\n        require(msg.sender == tx.origin, \"Not EOA\");\n        _;\n    }\n\n    // called by sender on source chain to send tokens to a list of\n    // <_accounts, _amounts> on the destination chain\n    function batchTransfer(\n        address _dstContract, // BatchTransfer contract address at the dst chain\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint32 _maxSlippage,\n        MsgDataTypes.BridgeSendType _bridgeSendType,\n        address[] calldata _accounts,\n        uint256[] calldata _amounts\n    ) external payable onlyEOA {\n        uint256 totalAmt;\n        for (uint256 i = 0; i < _amounts.length; i++) {\n            totalAmt += _amounts[i];\n        }\n        // commented out the slippage check below to trigger failure case for handleFailedMessageWithTransfer testing\n        // uint256 minRecv = _amount - (_amount * _maxSlippage) / 1e6;\n        // require(minRecv > totalAmt, \"invalid maxSlippage\");\n        nonce += 1;\n        status[nonce] = BatchTransferStatus({\n            h: keccak256(abi.encodePacked(_dstContract, _dstChainId)),\n            status: TransferStatus.Null\n        });\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        bytes memory message = abi.encode(\n            TransferRequest({nonce: nonce, accounts: _accounts, amounts: _amounts, sender: msg.sender})\n        );\n        // send token and message to the destination chain\n        sendMessageWithTransfer(\n            _dstContract,\n            _token,\n            _amount,\n            _dstChainId,\n            nonce,\n            _maxSlippage,\n            message,\n            _bridgeSendType,\n            msg.value\n        );\n    }\n\n    // called by MessageBus on source chain to handle message with token transfer failures (e.g., due to bad slippage).\n    // the associated token transfer is guaranteed to have already been refunded\n    function executeMessageWithTransferRefund(\n        address _token,\n        uint256 _amount,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        TransferRequest memory transfer = abi.decode((_message), (TransferRequest));\n        IERC20(_token).safeTransfer(transfer.sender, _amount);\n        return ExecutionStatus.Success;\n    }\n\n    // called by MessageBus on source chain to receive receipts\n    function executeMessage(\n        address _sender,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        TransferReceipt memory receipt = abi.decode((_message), (TransferReceipt));\n        require(status[receipt.nonce].h == keccak256(abi.encodePacked(_sender, _srcChainId)), \"invalid message\");\n        status[receipt.nonce].status = receipt.status;\n        return ExecutionStatus.Success;\n    }\n\n    // ============== functions on destination chain ==============\n\n    // called by MessageBus on destination chain to handle batchTransfer message by\n    // distributing tokens to receivers and sending receipt.\n    // the lump sum token transfer associated with the message is guaranteed to have already been received.\n    function executeMessageWithTransfer(\n        address _srcContract,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        TransferRequest memory transfer = abi.decode((_message), (TransferRequest));\n        uint256 totalAmt;\n        for (uint256 i = 0; i < transfer.accounts.length; i++) {\n            IERC20(_token).safeTransfer(transfer.accounts[i], transfer.amounts[i]);\n            totalAmt += transfer.amounts[i];\n        }\n        uint256 remainder = _amount - totalAmt;\n        if (_amount > totalAmt) {\n            // transfer the remainder of the money to sender as fee for executing this transfer\n            IERC20(_token).safeTransfer(transfer.sender, remainder);\n        }\n        bytes memory message = abi.encode(TransferReceipt({nonce: transfer.nonce, status: TransferStatus.Success}));\n        // send receipt back to the source chain contract\n        sendMessage(_srcContract, _srcChainId, message, msg.value);\n        return ExecutionStatus.Success;\n    }\n\n    // called by MessageBus if handleMessageWithTransfer above got reverted\n    function executeMessageWithTransferFallback(\n        address _srcContract,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        TransferRequest memory transfer = abi.decode((_message), (TransferRequest));\n        IERC20(_token).safeTransfer(transfer.sender, _amount);\n        bytes memory message = abi.encode(TransferReceipt({nonce: transfer.nonce, status: TransferStatus.Fail}));\n        // send receipt back to the source chain contract\n        sendMessage(_srcContract, _srcChainId, message, msg.value);\n        return ExecutionStatus.Success;\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/MsgExampleBasic.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\n// A HelloWorld example for basic cross-chain message passing\ncontract MsgExampleBasic is MessageApp {\n    event MessageReceived(address srcContract, uint64 srcChainId, address sender, bytes message);\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    // called by user on source chain to send cross-chain messages\n    function sendMessage(\n        address _dstContract,\n        uint64 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        bytes memory message = abi.encode(msg.sender, _message);\n        sendMessage(_dstContract, _dstChainId, message, msg.value);\n    }\n\n    // called by MessageBus on destination chain to receive cross-chain messages\n    function executeMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address sender, bytes memory message) = abi.decode((_message), (address, bytes));\n        emit MessageReceived(_srcContract, _srcChainId, sender, message);\n        return ExecutionStatus.Success;\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/MsgExampleBasicTransfer.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../../framework/MessageApp.sol\";\n\n// A HelloWorld example for basic cross-chain message passing with associate cross-chain token transfer\ncontract MsgExampleBasicTransfer is MessageApp {\n    using SafeERC20 for IERC20;\n\n    event MessageWithTransferReceived(address sender, address token, uint256 amount, uint64 srcChainId, bytes note);\n    event MessageWithTransferRefunded(address sender, address token, uint256 amount, bytes note);\n\n    // account, token -> balance\n    mapping(address => mapping(address => uint256)) public balances;\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    // called by user on source chain to send token with note to destination chain\n    function sendTokenWithNote(\n        address _dstContract,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage,\n        bytes calldata _note,\n        MsgDataTypes.BridgeSendType _bridgeSendType\n    ) external payable {\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        bytes memory message = abi.encode(msg.sender, _note);\n        sendMessageWithTransfer(\n            _dstContract,\n            _token,\n            _amount,\n            _dstChainId,\n            _nonce,\n            _maxSlippage,\n            message,\n            _bridgeSendType,\n            msg.value\n        );\n    }\n\n    // called by MessageBus on destination chain to receive message, record and emit info.\n    // the associated token transfer is guaranteed to have already been received\n    function executeMessageWithTransfer(\n        address, // srcContract\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address sender, bytes memory note) = abi.decode((_message), (address, bytes));\n        balances[sender][_token] += _amount;\n        emit MessageWithTransferReceived(sender, _token, _amount, _srcChainId, note);\n        return ExecutionStatus.Success;\n    }\n\n    // called by MessageBus on source chain to handle message with failed token transfer\n    // the associated token transfer is guaranteed to have already been refunded\n    function executeMessageWithTransferRefund(\n        address _token,\n        uint256 _amount,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address sender, bytes memory note) = abi.decode((_message), (address, bytes));\n        IERC20(_token).safeTransfer(sender, _amount);\n        emit MessageWithTransferRefunded(sender, _token, _amount, note);\n        return ExecutionStatus.Success;\n    }\n\n    // called by user on destination chain to withdraw tokens\n    function withdraw(address _token, uint256 _amount) external {\n        balances[msg.sender][_token] -= _amount;\n        IERC20(_token).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/MsgExampleInOrder.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\n// a simple example to enforce in-order message delivery\ncontract MsgExampleInOrder is MessageApp {\n    event MessageReceived(address srcContract, uint64 srcChainId, address sender, uint64 seq, bytes message);\n\n    // map at source chain. (dstChainId, dstContract) -> seq\n    mapping(uint64 => mapping(address => uint64)) public sendSeq;\n\n    // map at destination chain (srcChainId, srcContract) -> seq\n    mapping(uint64 => mapping(address => uint64)) public recvSeq;\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    // called by user on source chain to send cross-chain message\n    function sendMessage(\n        address _dstContract,\n        uint64 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        uint64 seq = sendSeq[_dstChainId][_dstContract];\n        bytes memory message = abi.encode(msg.sender, seq, _message);\n        sendMessage(_dstContract, _dstChainId, message, msg.value);\n        sendSeq[_dstChainId][_dstContract] += 1;\n    }\n\n    // called by MessageBus on destination chain to receive message\n    function executeMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address sender, uint64 seq, bytes memory message) = abi.decode((_message), (address, uint64, bytes));\n        uint64 expectedSeq = recvSeq[_srcChainId][_srcContract];\n        if (seq != expectedSeq) {\n            // sequence number not expected, let executor retry.\n            // Note: cannot revert here, because once a message execute tx is reverted, it cannot be retried later.\n            return ExecutionStatus.Retry;\n        }\n        emit MessageReceived(_srcContract, _srcChainId, sender, seq, message);\n        recvSeq[_srcChainId][_srcContract] += 1;\n        return ExecutionStatus.Success;\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/MsgTest.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../../framework/MessageApp.sol\";\nimport \"../../../safeguard/Ownable.sol\";\n\n/** @title Application to test message with transfer refund flow */\ncontract MsgTest is MessageApp, Ownable {\n    using SafeERC20 for IERC20;\n    uint64 nonce;\n\n    event MessageReceivedWithTransfer(\n        address token,\n        uint256 amount,\n        address sender,\n        uint64 srcChainId,\n        address receiver,\n        bytes message\n    );\n    event Refunded(address receiver, address token, uint256 amount, bytes message);\n    event MessageReceived(address sender, uint64 srcChainId, uint64 nonce, bytes message);\n    event Message2Received(bytes sender, uint64 srcChainId, uint64 nonce, bytes message);\n\n    constructor(address _messageBus) MessageApp(_messageBus) {}\n\n    function sendMessageWithTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint32 _maxSlippage,\n        bytes calldata _message,\n        MsgDataTypes.BridgeSendType _bridgeSendType\n    ) external payable {\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        bytes memory message = abi.encode(msg.sender, _message);\n        sendMessageWithTransfer(\n            _receiver,\n            _token,\n            _amount,\n            _dstChainId,\n            nonce,\n            _maxSlippage,\n            message,\n            _bridgeSendType,\n            msg.value\n        );\n        nonce++;\n    }\n\n    function executeMessageWithTransfer(\n        address _sender,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address receiver, bytes memory message) = abi.decode((_message), (address, bytes));\n        IERC20(_token).safeTransfer(receiver, _amount);\n        emit MessageReceivedWithTransfer(_token, _amount, _sender, _srcChainId, receiver, message);\n        return ExecutionStatus.Success;\n    }\n\n    function executeMessageWithTransferRefund(\n        address _token,\n        uint256 _amount,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (address receiver, bytes memory message) = abi.decode((_message), (address, bytes));\n        IERC20(_token).safeTransfer(receiver, _amount);\n        emit Refunded(receiver, _token, _amount, message);\n        return ExecutionStatus.Success;\n    }\n\n    function sendMessage(\n        address _receiver,\n        uint64 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        bytes memory message = abi.encode(nonce, _message);\n        nonce++;\n        sendMessage(_receiver, _dstChainId, message, msg.value);\n    }\n\n    function sendMessage(\n        bytes calldata _receiver,\n        uint64 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        bytes memory message = abi.encode(nonce, _message);\n        nonce++;\n        sendMessage(_receiver, _dstChainId, message, msg.value);\n    }\n\n    function sendMessages(\n        address _receiver,\n        uint64 _dstChainId,\n        bytes[] calldata _messages,\n        uint256[] calldata _fees\n    ) external payable {\n        for (uint256 i = 0; i < _messages.length; i++) {\n            bytes memory message = abi.encode(nonce, _messages[i]);\n            nonce++;\n            sendMessage(_receiver, _dstChainId, message, _fees[i]);\n        }\n    }\n\n    function sendMessageWithNonce(\n        address _receiver,\n        uint64 _dstChainId,\n        bytes calldata _message,\n        uint64 _nonce\n    ) external payable {\n        bytes memory message = abi.encode(_nonce, _message);\n        sendMessage(_receiver, _dstChainId, message, msg.value);\n    }\n\n    function executeMessage(\n        address _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (uint64 n, bytes memory message) = abi.decode((_message), (uint64, bytes));\n        require(n != 100000000000001, \"invalid nonce\"); // test revert with reason\n        if (n == 100000000000002) {\n            // test revert without reason\n            revert();\n        } else if (n == 100000000000003) {\n            return ExecutionStatus.Retry;\n        }\n        // test execution revert\n        require(n != 100000000000004, _abortReason(\"invalid nonce\"));\n        emit MessageReceived(_sender, _srcChainId, n, message);\n        return ExecutionStatus.Success;\n    }\n\n    function executeMessage(\n        bytes calldata _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        (uint64 n, bytes memory message) = abi.decode((_message), (uint64, bytes));\n        emit Message2Received(_sender, _srcChainId, n, message);\n        return ExecutionStatus.Success;\n    }\n\n    function drainToken(address _token, uint256 _amount) external onlyOwner {\n        IERC20(_token).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/TransferSwap.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"../../framework/MessageApp.sol\";\nimport \"../../../safeguard/Ownable.sol\";\nimport \"../../../interfaces/IWETH.sol\";\nimport \"../../../interfaces/IUniswapV2.sol\";\n\n/**\n * @title Demo application contract that facilitates swapping on a chain, transferring to another chain,\n * and swapping another time on the destination chain before sending the result tokens to a user\n */\ncontract TransferSwap is MessageApp, Ownable {\n    using SafeERC20 for IERC20;\n\n    modifier onlyEOA() {\n        require(msg.sender == tx.origin, \"Not EOA\");\n        _;\n    }\n\n    struct SwapInfo {\n        // if this array has only one element, it means no need to swap\n        address[] path;\n        // the following fields are only needed if path.length > 1\n        address dex; // the DEX to use for the swap\n        uint256 deadline; // deadline for the swap\n        uint256 minRecvAmt; // minimum receive amount for the swap\n    }\n\n    struct SwapRequest {\n        SwapInfo swap;\n        // the receiving party (the user) of the final output token\n        address receiver;\n        // this field is best to be per-user per-transaction unique so that\n        // a nonce that is specified by the calling party (the user),\n        uint64 nonce;\n        // indicates whether the output token coming out of the swap on destination\n        // chain should be unwrapped before sending to the user\n        bool nativeOut;\n    }\n\n    enum SwapStatus {\n        Null,\n        Succeeded,\n        Failed,\n        Fallback\n    }\n\n    // emitted when requested dstChainId == srcChainId, no bridging\n    event DirectSwap(\n        bytes32 id,\n        uint64 srcChainId,\n        uint256 amountIn,\n        address tokenIn,\n        uint256 amountOut,\n        address tokenOut\n    );\n    event SwapRequestSent(bytes32 id, uint64 dstChainId, uint256 srcAmount, address srcToken, address dstToken);\n    event SwapRequestDone(bytes32 id, uint256 dstAmount, SwapStatus status);\n\n    mapping(address => uint256) public minSwapAmounts;\n    mapping(address => bool) supportedDex;\n\n    // erc20 wrap of gas token of this chain, eg. WETH\n    address public nativeWrap;\n    uint256 public nativeTokenTransferGas = 50000;\n\n    constructor(\n        address _messageBus,\n        address _supportedDex,\n        address _nativeWrap\n    ) MessageApp(_messageBus) {\n        supportedDex[_supportedDex] = true;\n        nativeWrap = _nativeWrap;\n    }\n\n    function transferWithSwapNative(\n        address _receiver,\n        uint256 _amountIn,\n        uint64 _dstChainId,\n        SwapInfo calldata _srcSwap,\n        SwapInfo calldata _dstSwap,\n        uint32 _maxBridgeSlippage,\n        uint64 _nonce,\n        bool _nativeOut\n    ) external payable onlyEOA {\n        require(msg.value >= _amountIn, \"Amount insufficient\");\n        require(_srcSwap.path[0] == nativeWrap, \"token mismatch\");\n        IWETH(nativeWrap).deposit{value: _amountIn}();\n        _transferWithSwap(\n            _receiver,\n            _amountIn,\n            _dstChainId,\n            _srcSwap,\n            _dstSwap,\n            _maxBridgeSlippage,\n            _nonce,\n            _nativeOut,\n            msg.value - _amountIn\n        );\n    }\n\n    function transferWithSwap(\n        address _receiver,\n        uint256 _amountIn,\n        uint64 _dstChainId,\n        SwapInfo calldata _srcSwap,\n        SwapInfo calldata _dstSwap,\n        uint32 _maxBridgeSlippage,\n        uint64 _nonce\n    ) external payable onlyEOA {\n        IERC20(_srcSwap.path[0]).safeTransferFrom(msg.sender, address(this), _amountIn);\n        _transferWithSwap(\n            _receiver,\n            _amountIn,\n            _dstChainId,\n            _srcSwap,\n            _dstSwap,\n            _maxBridgeSlippage,\n            _nonce,\n            false,\n            msg.value\n        );\n    }\n\n    /**\n     * @notice Sends a cross-chain transfer via the liquidity pool-based bridge and sends a message specifying a wanted swap action on the\n               destination chain via the message bus\n     * @param _receiver the app contract that implements the MessageReceiver abstract contract\n     *        NOTE not to be confused with the receiver field in SwapInfo which is an EOA address of a user\n     * @param _amountIn the input amount that the user wants to swap and/or bridge\n     * @param _dstChainId destination chain ID\n     * @param _srcSwap a struct containing swap related requirements\n     * @param _dstSwap a struct containing swap related requirements\n     * @param _maxBridgeSlippage the max acceptable slippage at bridge, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the\n     *        transfer can be refunded.\n     * @param _fee the fee to pay to MessageBus.\n     */\n    function _transferWithSwap(\n        address _receiver,\n        uint256 _amountIn,\n        uint64 _dstChainId,\n        SwapInfo memory _srcSwap,\n        SwapInfo memory _dstSwap,\n        uint32 _maxBridgeSlippage,\n        uint64 _nonce,\n        bool _nativeOut,\n        uint256 _fee\n    ) private {\n        require(_srcSwap.path.length > 0, \"empty src swap path\");\n        address srcTokenOut = _srcSwap.path[_srcSwap.path.length - 1];\n\n        require(_amountIn > minSwapAmounts[_srcSwap.path[0]], \"amount must be greater than min swap amount\");\n        uint64 chainId = uint64(block.chainid);\n        require(_srcSwap.path.length > 1 || _dstChainId != chainId, \"noop is not allowed\"); // revert early to save gas\n\n        uint256 srcAmtOut = _amountIn;\n\n        // swap source token for intermediate token on the source DEX\n        if (_srcSwap.path.length > 1) {\n            bool ok = true;\n            (ok, srcAmtOut) = _trySwap(_srcSwap, _amountIn);\n            if (!ok) revert(\"src swap failed\");\n        }\n\n        if (_dstChainId == chainId) {\n            _directSend(_receiver, _amountIn, chainId, _srcSwap, _nonce, srcTokenOut, srcAmtOut);\n        } else {\n            _crossChainTransferWithSwap(\n                _receiver,\n                _amountIn,\n                chainId,\n                _dstChainId,\n                _srcSwap,\n                _dstSwap,\n                _maxBridgeSlippage,\n                _nonce,\n                _nativeOut,\n                _fee,\n                srcTokenOut,\n                srcAmtOut\n            );\n        }\n    }\n\n    function _directSend(\n        address _receiver,\n        uint256 _amountIn,\n        uint64 _chainId,\n        SwapInfo memory _srcSwap,\n        uint64 _nonce,\n        address srcTokenOut,\n        uint256 srcAmtOut\n    ) private {\n        // no need to bridge, directly send the tokens to user\n        IERC20(srcTokenOut).safeTransfer(_receiver, srcAmtOut);\n        // use uint64 for chainid to be consistent with other components in the system\n        bytes32 id = keccak256(abi.encode(msg.sender, _chainId, _receiver, _nonce, _srcSwap));\n        emit DirectSwap(id, _chainId, _amountIn, _srcSwap.path[0], srcAmtOut, srcTokenOut);\n    }\n\n    function _crossChainTransferWithSwap(\n        address _receiver,\n        uint256 _amountIn,\n        uint64 _chainId,\n        uint64 _dstChainId,\n        SwapInfo memory _srcSwap,\n        SwapInfo memory _dstSwap,\n        uint32 _maxBridgeSlippage,\n        uint64 _nonce,\n        bool _nativeOut,\n        uint256 _fee,\n        address srcTokenOut,\n        uint256 srcAmtOut\n    ) private {\n        require(_dstSwap.path.length > 0, \"empty dst swap path\");\n        bytes memory message = abi.encode(\n            SwapRequest({swap: _dstSwap, receiver: msg.sender, nonce: _nonce, nativeOut: _nativeOut})\n        );\n        bytes32 id = _computeSwapRequestId(msg.sender, _chainId, _dstChainId, message);\n        // bridge the intermediate token to destination chain along with the message\n        // NOTE In production, it's better use a per-user per-transaction nonce so that it's less likely transferId collision\n        // would happen at Bridge contract. Currently this nonce is a timestamp supplied by frontend\n        sendMessageWithTransfer(\n            _receiver,\n            srcTokenOut,\n            srcAmtOut,\n            _dstChainId,\n            _nonce,\n            _maxBridgeSlippage,\n            message,\n            MsgDataTypes.BridgeSendType.Liquidity,\n            _fee\n        );\n        emit SwapRequestSent(id, _dstChainId, _amountIn, _srcSwap.path[0], _dstSwap.path[_dstSwap.path.length - 1]);\n    }\n\n    /**\n     * @notice called by MessageBus when the tokens are checked to be arrived at this contract's address.\n               sends the amount received to the receiver. swaps beforehand if swap behavior is defined in message\n     * NOTE: if the swap fails, it sends the tokens received directly to the receiver as fallback behavior\n     * @param _token the address of the token sent through the bridge\n     * @param _amount the amount of tokens received at this contract through the cross-chain bridge\n     * @param _srcChainId source chain ID\n     * @param _message SwapRequest message that defines the swap behavior on this destination chain\n     */\n    function executeMessageWithTransfer(\n        address, // _sender\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        SwapRequest memory m = abi.decode((_message), (SwapRequest));\n        require(_token == m.swap.path[0], \"bridged token must be the same as the first token in destination swap path\");\n        bytes32 id = _computeSwapRequestId(m.receiver, _srcChainId, uint64(block.chainid), _message);\n        uint256 dstAmount;\n        SwapStatus status = SwapStatus.Succeeded;\n\n        if (m.swap.path.length > 1) {\n            bool ok = true;\n            (ok, dstAmount) = _trySwap(m.swap, _amount);\n            if (ok) {\n                _sendToken(m.swap.path[m.swap.path.length - 1], dstAmount, m.receiver, m.nativeOut);\n                status = SwapStatus.Succeeded;\n            } else {\n                // handle swap failure, send the received token directly to receiver\n                _sendToken(_token, _amount, m.receiver, false);\n                dstAmount = _amount;\n                status = SwapStatus.Fallback;\n            }\n        } else {\n            // no need to swap, directly send the bridged token to user\n            _sendToken(m.swap.path[0], _amount, m.receiver, m.nativeOut);\n            dstAmount = _amount;\n            status = SwapStatus.Succeeded;\n        }\n        emit SwapRequestDone(id, dstAmount, status);\n        // always return success since swap failure is already handled in-place\n        return ExecutionStatus.Success;\n    }\n\n    /**\n     * @notice called by MessageBus when the executeMessageWithTransfer call fails. does nothing but emitting a \"fail\" event\n     * @param _srcChainId source chain ID\n     * @param _message SwapRequest message that defines the swap behavior on this destination chain\n     */\n    function executeMessageWithTransferFallback(\n        address, // _sender\n        address, // _token\n        uint256, // _amount\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        SwapRequest memory m = abi.decode((_message), (SwapRequest));\n        bytes32 id = _computeSwapRequestId(m.receiver, _srcChainId, uint64(block.chainid), _message);\n        emit SwapRequestDone(id, 0, SwapStatus.Failed);\n        // always return fail to mark this transfer as failed since if this function is called then there nothing more\n        // we can do in this app as the swap failures are already handled in executeMessageWithTransfer\n        return ExecutionStatus.Fail;\n    }\n\n    function _trySwap(SwapInfo memory _swap, uint256 _amount) private returns (bool ok, uint256 amountOut) {\n        uint256 zero;\n        if (!supportedDex[_swap.dex]) {\n            return (false, zero);\n        }\n        IERC20(_swap.path[0]).safeIncreaseAllowance(_swap.dex, _amount);\n        try\n            IUniswapV2(_swap.dex).swapExactTokensForTokens(\n                _amount,\n                _swap.minRecvAmt,\n                _swap.path,\n                address(this),\n                _swap.deadline\n            )\n        returns (uint256[] memory amounts) {\n            return (true, amounts[amounts.length - 1]);\n        } catch {\n            return (false, zero);\n        }\n    }\n\n    function _sendToken(\n        address _token,\n        uint256 _amount,\n        address _receiver,\n        bool _nativeOut\n    ) private {\n        if (_nativeOut) {\n            require(_token == nativeWrap, \"token mismatch\");\n            IWETH(nativeWrap).withdraw(_amount);\n            (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n            require(sent, \"failed to send native\");\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n\n    function _computeSwapRequestId(\n        address _sender,\n        uint64 _srcChainId,\n        uint64 _dstChainId,\n        bytes memory _message\n    ) private pure returns (bytes32) {\n        return keccak256(abi.encodePacked(_sender, _srcChainId, _dstChainId, _message));\n    }\n\n    function setMinSwapAmount(address _token, uint256 _minSwapAmount) external onlyOwner {\n        minSwapAmounts[_token] = _minSwapAmount;\n    }\n\n    function setSupportedDex(address _dex, bool _enabled) external onlyOwner {\n        supportedDex[_dex] = _enabled;\n    }\n\n    function setNativeWrap(address _nativeWrap) external onlyOwner {\n        nativeWrap = _nativeWrap;\n    }\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyOwner {\n        nativeTokenTransferGas = _gasUsed;\n    }\n\n    function setMessageBus(address _messageBus) public onlyOwner {\n        messageBus = _messageBus;\n    }\n\n    // This is needed to receive ETH when calling `IWETH.withdraw`\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/message/apps/examples/TransferSwapSendBack.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../../framework/MessageApp.sol\";\n\ninterface ISwapToken {\n    // function sellBase(address to) external returns (uint256);\n    // uniswap v2\n    function swapExactTokensForTokens(\n        uint256,\n        uint256,\n        address[] calldata,\n        address,\n        uint256\n    ) external returns (uint256[] memory);\n}\n\ncontract CrossChainSwap is MessageApp {\n    using SafeERC20 for IERC20;\n\n    address public dex; // needed on swap chain\n\n    struct SwapInfo {\n        address wantToken; // token user want to receive on dest chain\n        address user;\n        bool sendBack; // if true, send wantToken back to start chain\n        uint32 cbrMaxSlippage; // _maxSlippage for cbridge send\n    }\n\n    constructor(address _messageBus, address dex_) MessageApp(_messageBus) {\n        dex = dex_;\n    }\n\n    // ========== on start chain ==========\n\n    uint64 nonce; // required by IBridge.send\n\n    // this func could be called by a router contract\n    function startCrossChainSwap(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        SwapInfo calldata swapInfo // wantToken on destChain and actual user address as receiver when send back\n    ) external payable {\n        nonce += 1;\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        bytes memory message = abi.encode(swapInfo);\n        sendMessageWithTransfer(\n            _receiver,\n            _token,\n            _amount,\n            _dstChainId,\n            nonce,\n            swapInfo.cbrMaxSlippage,\n            message,\n            MsgDataTypes.BridgeSendType.Liquidity,\n            msg.value\n        );\n    }\n\n    // ========== on swap chain ==========\n    // do dex, send received asset to src chain via bridge\n    function executeMessageWithTransfer(\n        address, // _sender\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes memory _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        SwapInfo memory swapInfo = abi.decode((_message), (SwapInfo));\n        IERC20(_token).approve(dex, _amount);\n        address[] memory path = new address[](2);\n        path[0] = _token;\n        path[1] = swapInfo.wantToken;\n        if (swapInfo.sendBack) {\n            nonce += 1;\n            uint256[] memory swapReturn = ISwapToken(dex).swapExactTokensForTokens(\n                _amount,\n                0,\n                path,\n                address(this),\n                type(uint256).max\n            );\n            // send received token back to start chain. swapReturn[1] is amount of wantToken\n            sendTokenTransfer(\n                swapInfo.user,\n                swapInfo.wantToken,\n                swapReturn[1],\n                _srcChainId,\n                nonce,\n                swapInfo.cbrMaxSlippage,\n                MsgDataTypes.BridgeSendType.Liquidity\n            );\n        } else {\n            // swap to wantToken and send to user\n            ISwapToken(dex).swapExactTokensForTokens(_amount, 0, path, swapInfo.user, type(uint256).max);\n        }\n        // bytes memory notice; // send back to src chain to handleMessage\n        // sendMessage(_sender, _srcChainId, notice);\n        return ExecutionStatus.Success;\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/nft-bridge/MCNNFT.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\";\nimport \"../../../safeguard/Pauser.sol\";\n\ninterface INFTBridge {\n    function sendMsg(\n        uint64 _dstChid,\n        address _sender,\n        address _receiver,\n        uint256 _id,\n        string calldata _uri\n    ) external payable;\n\n    function sendMsg(\n        uint64 _dstChid,\n        address _sender,\n        bytes calldata _receiver,\n        uint256 _id,\n        string calldata _uri\n    ) external payable;\n\n    function totalFee(\n        uint64 _dstChid,\n        address _nft,\n        uint256 _id\n    ) external view returns (uint256);\n}\n\n// Multi-Chain Native NFT, same contract on all chains. User interacts with this directly.\ncontract MCNNFT is ERC721URIStorage, Pauser {\n    event NFTBridgeUpdated(address);\n    address public nftBridge;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address _nftBridge\n    ) ERC721(name_, symbol_) {\n        nftBridge = _nftBridge;\n    }\n\n    modifier onlyNftBridge() {\n        require(msg.sender == nftBridge, \"caller is not bridge\");\n        _;\n    }\n\n    function bridgeMint(\n        address to,\n        uint256 id,\n        string memory uri\n    ) external onlyNftBridge {\n        _mint(to, id);\n        _setTokenURI(id, uri);\n    }\n\n    // calls nft bridge to get total fee for crossChain msg.Value\n    function totalFee(uint64 _dstChid, uint256 _id) external view returns (uint256) {\n        return INFTBridge(nftBridge).totalFee(_dstChid, address(this), _id);\n    }\n\n    // called by user, burn token on this chain and mint same id/uri on dest chain\n    function crossChain(\n        uint64 _dstChid,\n        uint256 _id,\n        address _receiver\n    ) external payable whenNotPaused {\n        require(msg.sender == ownerOf(_id), \"not token owner\");\n        string memory _uri = tokenURI(_id);\n        _burn(_id);\n        INFTBridge(nftBridge).sendMsg{value: msg.value}(_dstChid, msg.sender, _receiver, _id, _uri);\n    }\n\n    // support chains using bytes for address\n    function crossChain(\n        uint64 _dstChid,\n        uint256 _id,\n        bytes calldata _receiver\n    ) external payable whenNotPaused {\n        require(msg.sender == ownerOf(_id), \"not token owner\");\n        string memory _uri = tokenURI(_id);\n        _burn(_id);\n        INFTBridge(nftBridge).sendMsg{value: msg.value}(_dstChid, msg.sender, _receiver, _id, _uri);\n    }\n\n    // ===== only Owner\n    function mint(\n        address to,\n        uint256 id,\n        string memory uri\n    ) external onlyOwner {\n        _mint(to, id);\n        _setTokenURI(id, uri);\n    }\n\n    function setNFTBridge(address _newBridge) public onlyOwner {\n        nftBridge = _newBridge;\n        emit NFTBridgeUpdated(_newBridge);\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/nft-bridge/NFTBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"../../framework/MessageReceiverApp.sol\";\nimport \"../../interfaces/IMessageBus.sol\";\nimport \"../../../safeguard/Pauser.sol\";\n\n// interface for NFT contract, ERC721 and metadata, only funcs needed by NFTBridge\ninterface INFT {\n    function tokenURI(uint256 tokenId) external view returns (string memory);\n\n    function ownerOf(uint256 tokenId) external view returns (address owner);\n\n    // we do not support NFT that charges transfer fees\n    function transferFrom(\n        address from,\n        address to,\n        uint256 tokenId\n    ) external;\n\n    // impl by NFToken contract, mint an NFT with id and uri to user or burn\n    function bridgeMint(\n        address to,\n        uint256 id,\n        string memory uri\n    ) external;\n\n    function burn(uint256 id) external;\n}\n\n/** @title NFT Bridge */\ncontract NFTBridge is MessageReceiverApp, Pauser {\n    /// per dest chain id executor fee in this chain's gas token\n    mapping(uint64 => uint256) public destTxFee;\n    /// per dest chain id NFTBridge address\n    mapping(uint64 => address) public destBridge;\n    /// first key is NFT address on this chain, 2nd key is dest chain id, value is address on dest chain\n    mapping(address => mapping(uint64 => address)) public destNFTAddr;\n\n    /// only set to true if NFT addr on this chain is the orig, so we will use deposit/withdraw instead of burn/mint.\n    /// not applicable for mcn nft (always burn/mint)\n    mapping(address => bool) public origNFT;\n\n    /// only for non-evm chains and address can't fit 20bytes\n    mapping(uint64 => bytes) public destBridge2;\n    mapping(address => mapping(uint64 => bytes)) public destNFTAddr2;\n\n    struct NFTMsg {\n        address user; // receiver of minted or withdrawn NFT\n        address nft; // NFT contract on mint/withdraw chain\n        uint256 id; // token ID\n        string uri; // tokenURI from source NFT\n    }\n    // for non-evm dst chain, address type is bytes\n    struct NFTMsg2 {\n        bytes user; // receiver of minted or withdrawn NFT\n        bytes nft; // NFT contract on mint/withdraw chain\n        uint256 id; // token ID\n        string uri; // tokenURI from source NFT\n    }\n    // emit in deposit or burn\n    event Sent(address sender, address srcNft, uint256 id, uint64 dstChid, address receiver, address dstNft);\n    // bytes type for receiver and dstNft\n    event Sent2(address sender, address srcNft, uint256 id, uint64 dstChid, bytes receiver, bytes dstNft);\n    // emit for mint or withdraw message\n    event Received(address receiver, address nft, uint256 id, uint64 srcChid);\n\n    // emit when params change\n    event SetDestNFT(address srcNft, uint64 dstChid, address dstNft);\n    event SetTxFee(uint64 chid, uint256 fee);\n    event SetDestBridge(uint64 dstChid, address dstNftBridge);\n    event FeeClaimed(uint256 amount);\n    event SetOrigNFT(address nft, bool isOrig);\n    // emit if executeMessage calls nft transfer or bridgeMint returns error\n    event ExtCallErr(bytes returnData);\n\n    event SetDestNFT2(address srcNft, uint64 dstChid, bytes dstNft);\n    event SetDestBridge2(uint64 dstChid, bytes dstNftBridge);\n\n    constructor(address _msgBus) {\n        messageBus = _msgBus;\n    }\n\n    // only to be called by Proxy via delegatecall and will modify Proxy state\n    // initOwner will fail if owner is already set, so only delegateCall will work\n    function init(address _msgBus) external {\n        initOwner();\n        messageBus = _msgBus;\n    }\n\n    /**\n     * @notice totalFee returns gas token value to be set in user tx, includes both msg fee and executor fee for dest chain\n     * @dev we assume if dst chain address are bytes, user and nft are same length, otherwise we need to add receiver to args\n     * @param _dstChid dest chain ID\n     * @param _nft address of source NFT contract\n     * @param _id token ID to bridge (need to get accurate tokenURI length)\n     * @return total fee needed for user tx\n     */\n    function totalFee(\n        uint64 _dstChid,\n        address _nft,\n        uint256 _id\n    ) external view returns (uint256) {\n        string memory _uri = INFT(_nft).tokenURI(_id);\n        bytes memory message;\n        // try non-evm first\n        bytes memory dstNft = destNFTAddr2[_nft][_dstChid];\n        if (dstNft.length > 0) {\n            message = abi.encode(NFTMsg2(dstNft, dstNft, _id, _uri));\n        } else {\n            // evm chains or not configured, assume to be evm, 20 bytes address\n            message = abi.encode(NFTMsg(_nft, _nft, _id, _uri));\n        }\n        return IMessageBus(messageBus).calcFee(message) + destTxFee[_dstChid];\n    }\n\n    // ===== called by user\n    /**\n     * @notice locks or burn user's NFT in this contract and send message to mint (or withdraw) on dest chain\n     * @param _nft address of source NFT contract\n     * @param _id nft token ID to bridge\n     * @param _dstChid dest chain ID\n     * @param _receiver receiver address on dest chain\n     */\n    function sendTo(\n        address _nft,\n        uint256 _id,\n        uint64 _dstChid,\n        address _receiver\n    ) external payable whenNotPaused {\n        require(msg.sender == INFT(_nft).ownerOf(_id), \"not token owner\");\n        // must save _uri before burn\n        string memory _uri = INFT(_nft).tokenURI(_id);\n        lockOrBurn(_nft, _id);\n        (address _dstBridge, address _dstNft) = checkAddr(_nft, _dstChid);\n        msgBus(_dstBridge, _dstChid, abi.encode(NFTMsg(_receiver, _dstNft, _id, _uri)));\n        emit Sent(msg.sender, _nft, _id, _dstChid, _receiver, _dstNft);\n    }\n\n    /**\n     * @notice locks or burn user's NFT in this contract and send message to mint (or withdraw) on dest chain\n     * @param _nft address of source NFT contract\n     * @param _id nft token ID to bridge\n     * @param _dstChid dest chain ID\n     * @param _receiver receiver address on dest chain, arbitrary bytes\n     */\n    function sendTo(\n        address _nft,\n        uint256 _id,\n        uint64 _dstChid,\n        bytes calldata _receiver\n    ) external payable whenNotPaused {\n        require(msg.sender == INFT(_nft).ownerOf(_id), \"not token owner\");\n        // must save _uri before burn\n        string memory _uri = INFT(_nft).tokenURI(_id);\n        lockOrBurn(_nft, _id);\n        (bytes memory _dstBridge, bytes memory _dstNft) = checkAddr2(_nft, _dstChid);\n        msgBus(_dstBridge, _dstChid, abi.encode(NFTMsg2(_receiver, _dstNft, _id, _uri)));\n        emit Sent2(msg.sender, _nft, _id, _dstChid, _receiver, _dstNft);\n    }\n\n    // ===== called by MCN NFT after NFT is burnt\n    function sendMsg(\n        uint64 _dstChid,\n        address _sender,\n        address _receiver,\n        uint256 _id,\n        string calldata _uri\n    ) external payable whenNotPaused {\n        address _nft = msg.sender;\n        (address _dstBridge, address _dstNft) = checkAddr(_nft, _dstChid);\n        msgBus(_dstBridge, _dstChid, abi.encode(NFTMsg(_receiver, _dstNft, _id, _uri)));\n        emit Sent(_sender, _nft, _id, _dstChid, _receiver, _dstNft);\n    }\n\n    // for non-evm chains and address can't fit 20bytes or non-hex\n    function sendMsg(\n        uint64 _dstChid,\n        address _sender,\n        bytes calldata _receiver,\n        uint256 _id,\n        string calldata _uri\n    ) external payable whenNotPaused {\n        address _nft = msg.sender;\n        (bytes memory _dstBridge, bytes memory _dstNft) = checkAddr2(_nft, _dstChid);\n        msgBus(_dstBridge, _dstChid, abi.encode(NFTMsg2(_receiver, _dstNft, _id, _uri)));\n        emit Sent2(_sender, _nft, _id, _dstChid, _receiver, _dstNft);\n    }\n\n    // ===== called by msgbus\n    function executeMessage(\n        address sender,\n        uint64 srcChid,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        // Must check sender to ensure msg is from another nft bridge\n        // but we allow retry later in case it's a temporary config error\n        // risk is invalid sender will be retried but this can be easily filtered\n        // in executor or require manual trigger for retry\n        if (paused() || sender != destBridge[srcChid]) {\n            return ExecutionStatus.Retry;\n        }\n        return xferOrMint(_message, srcChid);\n    }\n\n    function executeMessage(\n        bytes calldata sender,\n        uint64 srcChid,\n        bytes calldata _message,\n        address // executor\n    ) external payable override onlyMessageBus returns (ExecutionStatus) {\n        if (paused() || keccak256(sender) != keccak256(destBridge2[srcChid])) {\n            return ExecutionStatus.Retry;\n        }\n        return xferOrMint(_message, srcChid);\n    }\n\n    // ===== internal utils\n    // lockOrBurn on sender side\n    function lockOrBurn(address _nft, uint256 _id) internal {\n        if (origNFT[_nft] == true) {\n            // deposit\n            INFT(_nft).transferFrom(msg.sender, address(this), _id);\n            require(INFT(_nft).ownerOf(_id) == address(this), \"transfer NFT failed\");\n        } else {\n            // burn\n            INFT(_nft).burn(_id);\n        }\n    }\n\n    // xferOrMint on receiver side, transfer or mint NFT to receiver\n    function xferOrMint(bytes calldata _message, uint64 srcChid) internal returns (ExecutionStatus) {\n        // withdraw original locked nft back to user, or mint new nft depending on if this is the orig chain of nft\n        NFTMsg memory nftMsg = abi.decode((_message), (NFTMsg));\n        // if we are on nft orig chain, use transfer, otherwise, use mint\n        // we must never return fail because burnt nft will be lost forever\n        if (origNFT[nftMsg.nft] == true) {\n            try INFT(nftMsg.nft).transferFrom(address(this), nftMsg.user, nftMsg.id) {\n                // do nothing here to move on to emit Received event and return success\n            } catch (bytes memory returnData) {\n                emit ExtCallErr(returnData);\n                return ExecutionStatus.Retry;\n            }\n        } else {\n            try INFT(nftMsg.nft).bridgeMint(nftMsg.user, nftMsg.id, nftMsg.uri) {\n                // do nothing here to move on to emit Received event and return success\n            } catch (bytes memory returnData) {\n                emit ExtCallErr(returnData);\n                return ExecutionStatus.Retry;\n            }\n        }\n        emit Received(nftMsg.user, nftMsg.nft, nftMsg.id, srcChid);\n        return ExecutionStatus.Success;\n    }\n\n    // check _nft and destChid are valid, return dstBridge and dstNft\n    function checkAddr(address _nft, uint64 _dstChid) internal view returns (address dstBridge, address dstNft) {\n        dstBridge = destBridge[_dstChid];\n        require(dstBridge != address(0), \"dest NFT Bridge not found\");\n        dstNft = destNFTAddr[_nft][_dstChid];\n        require(dstNft != address(0), \"dest NFT not found\");\n    }\n\n    function checkAddr2(address _nft, uint64 _dstChid)\n        internal\n        view\n        returns (bytes memory dstBridge, bytes memory dstNft)\n    {\n        dstBridge = destBridge2[_dstChid];\n        require(dstBridge.length != 0, \"dest NFT Bridge not found\");\n        dstNft = destNFTAddr2[_nft][_dstChid];\n        require(dstNft.length != 0, \"dest NFT not found\");\n    }\n\n    // check fee and call msgbus sendMessage\n    function msgBus(\n        address _dstBridge,\n        uint64 _dstChid,\n        bytes memory message\n    ) internal {\n        uint256 fee = IMessageBus(messageBus).calcFee(message);\n        require(msg.value >= fee + destTxFee[_dstChid], \"insufficient fee\");\n        IMessageBus(messageBus).sendMessage{value: fee}(_dstBridge, _dstChid, message);\n    }\n\n    function msgBus(\n        bytes memory _dstBridge,\n        uint64 _dstChid,\n        bytes memory message\n    ) internal {\n        uint256 fee = IMessageBus(messageBus).calcFee(message);\n        require(msg.value >= fee + destTxFee[_dstChid], \"insufficient fee\");\n        IMessageBus(messageBus).sendMessage{value: fee}(_dstBridge, _dstChid, message);\n    }\n\n    // only owner\n    // set per NFT, per chain id, address\n    function setDestNFT(\n        address srcNft,\n        uint64 dstChid,\n        address dstNft\n    ) external onlyOwner {\n        destNFTAddr[srcNft][dstChid] = dstNft;\n        emit SetDestNFT(srcNft, dstChid, dstNft);\n    }\n\n    // add to destNFTAddr2\n    function setDestNFT(\n        address srcNft,\n        uint64 dstChid,\n        bytes calldata dstNft\n    ) external onlyOwner {\n        destNFTAddr2[srcNft][dstChid] = dstNft;\n        emit SetDestNFT2(srcNft, dstChid, dstNft);\n    }\n\n    // set all dest chains\n    function setDestNFTs(\n        address srcNft,\n        uint64[] calldata dstChid,\n        address[] calldata dstNft\n    ) external onlyOwner {\n        require(dstChid.length == dstNft.length, \"length mismatch\");\n        for (uint256 i = 0; i < dstChid.length; i++) {\n            destNFTAddr[srcNft][dstChid[i]] = dstNft[i];\n        }\n    }\n\n    // set destTxFee\n    function setTxFee(uint64 chid, uint256 fee) external onlyOwner {\n        destTxFee[chid] = fee;\n        emit SetTxFee(chid, fee);\n    }\n\n    // set per chain id, nft bridge address\n    function setDestBridge(uint64 dstChid, address dstNftBridge) external onlyOwner {\n        destBridge[dstChid] = dstNftBridge;\n        emit SetDestBridge(dstChid, dstNftBridge);\n    }\n\n    function setDestBridge(uint64 dstChid, bytes calldata dstNftBridge) external onlyOwner {\n        destBridge2[dstChid] = dstNftBridge;\n        emit SetDestBridge2(dstChid, dstNftBridge);\n    }\n\n    // batch set nft bridge addresses for multiple chainids\n    function setDestBridges(uint64[] calldata dstChid, address[] calldata dstNftBridge) external onlyOwner {\n        for (uint256 i = 0; i < dstChid.length; i++) {\n            destBridge[dstChid[i]] = dstNftBridge[i];\n        }\n    }\n\n    // only called on NFT's orig chain, not applicable for mcn nft\n    function setOrigNFT(address _nft) external onlyOwner {\n        origNFT[_nft] = true;\n        emit SetOrigNFT(_nft, true);\n    }\n\n    // remove origNFT entry\n    function delOrigNFT(address _nft) external onlyOwner {\n        delete origNFT[_nft];\n        emit SetOrigNFT(_nft, false);\n    }\n\n    // send all gas token this contract has to owner\n    function claimFee() external onlyOwner {\n        uint256 amount = address(this).balance;\n        payable(msg.sender).transfer(amount);\n        emit FeeClaimed(amount);\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/nft-bridge/OrigNFT.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\";\nimport \"../../../safeguard/Ownable.sol\";\n\ncontract OrigNFT is ERC721URIStorage, Ownable {\n    constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) {}\n\n    function mint(\n        address to,\n        uint256 id,\n        string memory uri\n    ) external onlyOwner {\n        _mint(to, id);\n        _setTokenURI(id, uri);\n    }\n}\n"
  },
  {
    "path": "contracts/message/apps/nft-bridge/PegNFT.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol\";\n\ncontract PegNFT is ERC721URIStorage {\n    address public immutable nftBridge;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address _nftBridge\n    ) ERC721(name_, symbol_) {\n        nftBridge = _nftBridge;\n    }\n\n    modifier onlyNftBridge() {\n        require(msg.sender == nftBridge, \"caller is not bridge\");\n        _;\n    }\n\n    function bridgeMint(\n        address to,\n        uint256 id,\n        string memory uri\n    ) external onlyNftBridge {\n        _mint(to, id);\n        _setTokenURI(id, uri);\n    }\n\n    function burn(uint256 id) external onlyNftBridge {\n        _burn(id);\n    }\n}\n"
  },
  {
    "path": "contracts/message/framework/MessageApp.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"./MessageSenderApp.sol\";\nimport \"./MessageReceiverApp.sol\";\n\nabstract contract MessageApp is MessageSenderApp, MessageReceiverApp {\n    constructor(address _messageBus) {\n        messageBus = _messageBus;\n    }\n}\n"
  },
  {
    "path": "contracts/message/framework/MessageBusAddress.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nabstract contract MessageBusAddress {\n    address public messageBus;\n}\n"
  },
  {
    "path": "contracts/message/framework/MessageReceiverApp.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../interfaces/IMessageReceiverApp.sol\";\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"./MessageBusAddress.sol\";\n\nabstract contract MessageReceiverApp is IMessageReceiverApp, MessageBusAddress {\n    modifier onlyMessageBus() {\n        require(msg.sender == messageBus, \"caller is not message bus\");\n        _;\n    }\n\n    // Add abort prefix in the reason string for require or revert.\n    // This will abort (revert) the message execution without markig it as failed state,\n    // making it possible to retry later.\n    function _abortReason(string memory reason) internal pure returns (string memory) {\n        return MsgDataTypes.abortReason(reason);\n    }\n\n    /**\n     * @notice Called by MessageBus to execute a message\n     * @param _sender The address of the source app contract\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessage(\n        address _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {}\n\n    // execute message from non-evm chain with bytes for sender address,\n    // otherwise same as above.\n    function executeMessage(\n        bytes calldata _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {}\n\n    /**\n     * @notice Called by MessageBus to execute a message with an associated token transfer.\n     * The contract is guaranteed to have received the right amount of tokens before this function is called.\n     * @param _sender The address of the source app contract\n     * @param _token The address of the token that comes out of the bridge\n     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransfer(\n        address _sender,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {}\n\n    /**\n     * @notice Only called by MessageBus if\n     *         1. executeMessageWithTransfer reverts, or\n     *         2. executeMessageWithTransfer returns ExecutionStatus.Fail\n     * The contract is guaranteed to have received the right amount of tokens before this function is called.\n     * @param _sender The address of the source app contract\n     * @param _token The address of the token that comes out of the bridge\n     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransferFallback(\n        address _sender,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {}\n\n    /**\n     * @notice Called by MessageBus to process refund of the original transfer from this contract.\n     * The contract is guaranteed to have received the refund before this function is called.\n     * @param _token The token address of the original transfer\n     * @param _amount The amount of the original transfer\n     * @param _message The same message associated with the original transfer\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransferRefund(\n        address _token,\n        uint256 _amount,\n        bytes calldata _message,\n        address _executor\n    ) external payable virtual override onlyMessageBus returns (ExecutionStatus) {}\n}\n"
  },
  {
    "path": "contracts/message/framework/MessageSenderApp.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"../libraries/MessageSenderLib.sol\";\nimport \"../messagebus/MessageBus.sol\";\nimport \"./MessageBusAddress.sol\";\n\nabstract contract MessageSenderApp is MessageBusAddress {\n    using SafeERC20 for IERC20;\n\n    // ============== Utility functions called by apps ==============\n\n    /**\n     * @notice Sends a message to a contract on another chain.\n     * Sender needs to make sure the uniqueness of the message Id, which is computed as\n     * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).\n     * If messages with the same Id are sent, only one of them will succeed at dst chain.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     * @param _fee The fee amount to pay to MessageBus.\n     */\n    function sendMessage(\n        address _receiver,\n        uint64 _dstChainId,\n        bytes memory _message,\n        uint256 _fee\n    ) internal {\n        MessageSenderLib.sendMessage(_receiver, _dstChainId, _message, messageBus, _fee);\n    }\n\n    // Send message to non-evm chain with bytes for receiver address,\n    // otherwise same as above.\n    function sendMessage(\n        bytes calldata _receiver,\n        uint64 _dstChainId,\n        bytes memory _message,\n        uint256 _fee\n    ) internal {\n        MessageSenderLib.sendMessage(_receiver, _dstChainId, _message, messageBus, _fee);\n    }\n\n    /**\n     * @notice Sends a message associated with a transfer to a contract on another chain.\n     * @param _receiver The address of the destination app contract.\n     * @param _token The address of the token to be sent.\n     * @param _amount The amount of tokens to be sent.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {MsgDataTypes.BridgeSendType.Liquidity}.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     *        If message is empty, only the token transfer will be sent\n     * @param _bridgeSendType One of the {BridgeSendType} enum.\n     * @param _fee The fee amount to pay to MessageBus.\n     * @return The transfer ID.\n     */\n    function sendMessageWithTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage,\n        bytes memory _message,\n        MsgDataTypes.BridgeSendType _bridgeSendType,\n        uint256 _fee\n    ) internal returns (bytes32) {\n        return\n            MessageSenderLib.sendMessageWithTransfer(\n                _receiver,\n                _token,\n                _amount,\n                _dstChainId,\n                _nonce,\n                _maxSlippage,\n                _message,\n                _bridgeSendType,\n                messageBus,\n                _fee\n            );\n    }\n\n    /**\n     * @notice Sends a token transfer via a bridge.\n     * @dev sendMessageWithTransfer with empty message\n     * @param _receiver The address of the destination app contract.\n     * @param _token The address of the token to be sent.\n     * @param _amount The amount of tokens to be sent.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {MsgDataTypes.BridgeSendType.Liquidity}.\n     * @param _bridgeSendType One of the {BridgeSendType} enum.\n     */\n    function sendTokenTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage,\n        MsgDataTypes.BridgeSendType _bridgeSendType\n    ) internal returns (bytes32) {\n        return\n            MessageSenderLib.sendMessageWithTransfer(\n                _receiver,\n                _token,\n                _amount,\n                _dstChainId,\n                _nonce,\n                _maxSlippage,\n                \"\", // empty message, which will not trigger sendMessage\n                _bridgeSendType,\n                messageBus,\n                0\n            );\n    }\n}\n"
  },
  {
    "path": "contracts/message/interfaces/IMessageBus.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../libraries/MsgDataTypes.sol\";\n\ninterface IMessageBus {\n    /**\n     * @notice Send a message to a contract on another chain.\n     * Sender needs to make sure the uniqueness of the message Id, which is computed as\n     * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).\n     * If messages with the same Id are sent, only one of them will succeed at dst chain..\n     * A fee is charged in the native gas token.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     */\n    function sendMessage(\n        address _receiver,\n        uint256 _dstChainId,\n        bytes calldata _message\n    ) external payable;\n\n    // same as above, except that receiver is an non-evm chain address,\n    function sendMessage(\n        bytes calldata _receiver,\n        uint256 _dstChainId,\n        bytes calldata _message\n    ) external payable;\n\n    /**\n     * @notice Send a message associated with a token transfer to a contract on another chain.\n     * If messages with the same srcTransferId are sent, only one of them will succeed at dst chain..\n     * A fee is charged in the native token.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _srcBridge The bridge contract to send the transfer with.\n     * @param _srcTransferId The transfer ID.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     */\n    function sendMessageWithTransfer(\n        address _receiver,\n        uint256 _dstChainId,\n        address _srcBridge,\n        bytes32 _srcTransferId,\n        bytes calldata _message\n    ) external payable;\n\n    /**\n     * @notice Execute a message not associated with a transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessage(\n        bytes calldata _message,\n        MsgDataTypes.RouteInfo calldata _route,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external payable;\n\n    /**\n     * @notice Execute a message with a successful transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _transfer The transfer info.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessageWithTransfer(\n        bytes calldata _message,\n        MsgDataTypes.TransferInfo calldata _transfer,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external payable;\n\n    /**\n     * @notice Execute a message with a refunded transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _transfer The transfer info.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessageWithTransferRefund(\n        bytes calldata _message, // the same message associated with the original transfer\n        MsgDataTypes.TransferInfo calldata _transfer,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external payable;\n\n    /**\n     * @notice Withdraws message fee in the form of native gas token.\n     * @param _account The address receiving the fee.\n     * @param _cumulativeFee The cumulative fee credited to the account. Tracked by SGN.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be\n     * signed-off by +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdrawFee(\n        address _account,\n        uint256 _cumulativeFee,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external;\n\n    /**\n     * @notice Calculates the required fee for the message.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     @ @return The required fee.\n     */\n    function calcFee(bytes calldata _message) external view returns (uint256);\n\n    function liquidityBridge() external view returns (address);\n\n    function pegBridge() external view returns (address);\n\n    function pegBridgeV2() external view returns (address);\n\n    function pegVault() external view returns (address);\n\n    function pegVaultV2() external view returns (address);\n}\n"
  },
  {
    "path": "contracts/message/interfaces/IMessageReceiverApp.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\ninterface IMessageReceiverApp {\n    enum ExecutionStatus {\n        Fail, // execution failed, finalized\n        Success, // execution succeeded, finalized\n        Retry // execution rejected, can retry later\n    }\n\n    /**\n     * @notice Called by MessageBus to execute a message\n     * @param _sender The address of the source app contract\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessage(\n        address _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable returns (ExecutionStatus);\n\n    // same as above, except that sender is an non-evm chain address,\n    // otherwise same as above.\n    function executeMessage(\n        bytes calldata _sender,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable returns (ExecutionStatus);\n\n    /**\n     * @notice Called by MessageBus to execute a message with an associated token transfer.\n     * The contract is guaranteed to have received the right amount of tokens before this function is called.\n     * @param _sender The address of the source app contract\n     * @param _token The address of the token that comes out of the bridge\n     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransfer(\n        address _sender,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable returns (ExecutionStatus);\n\n    /**\n     * @notice Only called by MessageBus if\n     *         1. executeMessageWithTransfer reverts, or\n     *         2. executeMessageWithTransfer returns ExecutionStatus.Fail\n     * The contract is guaranteed to have received the right amount of tokens before this function is called.\n     * @param _sender The address of the source app contract\n     * @param _token The address of the token that comes out of the bridge\n     * @param _amount The amount of tokens received at this contract through the cross-chain bridge.\n     * @param _srcChainId The source chain ID where the transfer is originated from\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransferFallback(\n        address _sender,\n        address _token,\n        uint256 _amount,\n        uint64 _srcChainId,\n        bytes calldata _message,\n        address _executor\n    ) external payable returns (ExecutionStatus);\n\n    /**\n     * @notice Called by MessageBus to process refund of the original transfer from this contract.\n     * The contract is guaranteed to have received the refund before this function is called.\n     * @param _token The token address of the original transfer\n     * @param _amount The amount of the original transfer\n     * @param _message The same message associated with the original transfer\n     * @param _executor Address who called the MessageBus execution function\n     */\n    function executeMessageWithTransferRefund(\n        address _token,\n        uint256 _amount,\n        bytes calldata _message,\n        address _executor\n    ) external payable returns (ExecutionStatus);\n}\n"
  },
  {
    "path": "contracts/message/libraries/MessageSenderLib.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"../../interfaces/IBridge.sol\";\nimport \"../../interfaces/IOriginalTokenVault.sol\";\nimport \"../../interfaces/IOriginalTokenVaultV2.sol\";\nimport \"../../interfaces/IPeggedTokenBridge.sol\";\nimport \"../../interfaces/IPeggedTokenBridgeV2.sol\";\nimport \"../interfaces/IMessageBus.sol\";\nimport \"./MsgDataTypes.sol\";\n\nlibrary MessageSenderLib {\n    using SafeERC20 for IERC20;\n\n    // ============== Internal library functions called by apps ==============\n\n    /**\n     * @notice Sends a message to an app on another chain via MessageBus without an associated transfer.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     * @param _messageBus The address of the MessageBus on this chain.\n     * @param _fee The fee amount to pay to MessageBus.\n     */\n    function sendMessage(\n        address _receiver,\n        uint64 _dstChainId,\n        bytes memory _message,\n        address _messageBus,\n        uint256 _fee\n    ) internal {\n        IMessageBus(_messageBus).sendMessage{value: _fee}(_receiver, _dstChainId, _message);\n    }\n\n    // Send message to non-evm chain with bytes for receiver address,\n    // otherwise same as above.\n    function sendMessage(\n        bytes calldata _receiver,\n        uint64 _dstChainId,\n        bytes memory _message,\n        address _messageBus,\n        uint256 _fee\n    ) internal {\n        IMessageBus(_messageBus).sendMessage{value: _fee}(_receiver, _dstChainId, _message);\n    }\n\n    /**\n     * @notice Sends a message to an app on another chain via MessageBus with an associated transfer.\n     * @param _receiver The address of the destination app contract.\n     * @param _token The address of the token to be sent.\n     * @param _amount The amount of tokens to be sent.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     * Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the\n     * transfer can be refunded. Only applicable to the {MsgDataTypes.BridgeSendType.Liquidity}.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     * @param _bridgeSendType One of the {MsgDataTypes.BridgeSendType} enum.\n     * @param _messageBus The address of the MessageBus on this chain.\n     * @param _fee The fee amount to pay to MessageBus.\n     * @return The transfer ID.\n     */\n    function sendMessageWithTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage,\n        bytes memory _message,\n        MsgDataTypes.BridgeSendType _bridgeSendType,\n        address _messageBus,\n        uint256 _fee\n    ) internal returns (bytes32) {\n        (bytes32 transferId, address bridge) = sendTokenTransfer(\n            _receiver,\n            _token,\n            _amount,\n            _dstChainId,\n            _nonce,\n            _maxSlippage,\n            _bridgeSendType,\n            _messageBus\n        );\n        if (_message.length > 0) {\n            IMessageBus(_messageBus).sendMessageWithTransfer{value: _fee}(\n                _receiver,\n                _dstChainId,\n                bridge,\n                transferId,\n                _message\n            );\n        }\n        return transferId;\n    }\n\n    /**\n     * @notice Sends a token transfer via a bridge.\n     * @param _receiver The address of the destination app contract.\n     * @param _token The address of the token to be sent.\n     * @param _amount The amount of tokens to be sent.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     * Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least (100% - max slippage percentage) * amount or the\n     * transfer can be refunded.\n     * @param _bridgeSendType One of the {MsgDataTypes.BridgeSendType} enum.\n     */\n    function sendTokenTransfer(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage,\n        MsgDataTypes.BridgeSendType _bridgeSendType,\n        address _messageBus\n    ) internal returns (bytes32 transferId, address bridge) {\n        if (_bridgeSendType == MsgDataTypes.BridgeSendType.Liquidity) {\n            bridge = IMessageBus(_messageBus).liquidityBridge();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            IBridge(bridge).send(_receiver, _token, _amount, _dstChainId, _nonce, _maxSlippage);\n            transferId = computeLiqBridgeTransferId(_receiver, _token, _amount, _dstChainId, _nonce);\n        } else if (_bridgeSendType == MsgDataTypes.BridgeSendType.PegDeposit) {\n            bridge = IMessageBus(_messageBus).pegVault();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            IOriginalTokenVault(bridge).deposit(_token, _amount, _dstChainId, _receiver, _nonce);\n            transferId = computePegV1DepositId(_receiver, _token, _amount, _dstChainId, _nonce);\n        } else if (_bridgeSendType == MsgDataTypes.BridgeSendType.PegBurn) {\n            bridge = IMessageBus(_messageBus).pegBridge();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            IPeggedTokenBridge(bridge).burn(_token, _amount, _receiver, _nonce);\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(bridge, 0);\n            transferId = computePegV1BurnId(_receiver, _token, _amount, _nonce);\n        } else if (_bridgeSendType == MsgDataTypes.BridgeSendType.PegV2Deposit) {\n            bridge = IMessageBus(_messageBus).pegVaultV2();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            transferId = IOriginalTokenVaultV2(bridge).deposit(_token, _amount, _dstChainId, _receiver, _nonce);\n        } else if (_bridgeSendType == MsgDataTypes.BridgeSendType.PegV2Burn) {\n            bridge = IMessageBus(_messageBus).pegBridgeV2();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            transferId = IPeggedTokenBridgeV2(bridge).burn(_token, _amount, _dstChainId, _receiver, _nonce);\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(bridge, 0);\n        } else if (_bridgeSendType == MsgDataTypes.BridgeSendType.PegV2BurnFrom) {\n            bridge = IMessageBus(_messageBus).pegBridgeV2();\n            IERC20(_token).safeIncreaseAllowance(bridge, _amount);\n            transferId = IPeggedTokenBridgeV2(bridge).burnFrom(_token, _amount, _dstChainId, _receiver, _nonce);\n            // handle cases where certain tokens do not spend allowance for role-based burn\n            IERC20(_token).safeApprove(bridge, 0);\n        } else {\n            revert(\"bridge type not supported\");\n        }\n    }\n\n    function computeLiqBridgeTransferId(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce\n    ) internal view returns (bytes32) {\n        return\n            keccak256(\n                abi.encodePacked(address(this), _receiver, _token, _amount, _dstChainId, _nonce, uint64(block.chainid))\n            );\n    }\n\n    function computePegV1DepositId(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce\n    ) internal view returns (bytes32) {\n        return\n            keccak256(\n                abi.encodePacked(address(this), _token, _amount, _dstChainId, _receiver, _nonce, uint64(block.chainid))\n            );\n    }\n\n    function computePegV1BurnId(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _nonce\n    ) internal view returns (bytes32) {\n        return keccak256(abi.encodePacked(address(this), _token, _amount, _receiver, _nonce, uint64(block.chainid)));\n    }\n}\n"
  },
  {
    "path": "contracts/message/libraries/MsgDataTypes.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nlibrary MsgDataTypes {\n    string constant ABORT_PREFIX = \"MSG::ABORT:\";\n\n    // Add abort prefix in the reason string for require or revert.\n    // This will abort (revert) the message execution without markig it as failed state,\n    // making it possible to retry later.\n    function abortReason(string memory reason) internal pure returns (string memory) {\n        return string.concat(MsgDataTypes.ABORT_PREFIX, reason);\n    }\n\n    // bridge operation type at the sender side (src chain)\n    enum BridgeSendType {\n        Null,\n        Liquidity,\n        PegDeposit,\n        PegBurn,\n        PegV2Deposit,\n        PegV2Burn,\n        PegV2BurnFrom\n    }\n\n    // bridge operation type at the receiver side (dst chain)\n    enum TransferType {\n        Null,\n        LqRelay, // relay through liquidity bridge\n        LqWithdraw, // withdraw from liquidity bridge\n        PegMint, // mint through pegged token bridge\n        PegWithdraw, // withdraw from original token vault\n        PegV2Mint, // mint through pegged token bridge v2\n        PegV2Withdraw // withdraw from original token vault v2\n    }\n\n    enum MsgType {\n        MessageWithTransfer,\n        MessageOnly\n    }\n\n    enum TxStatus {\n        Null,\n        Success,\n        Fail,\n        Fallback,\n        Pending // transient state within a transaction\n    }\n\n    struct TransferInfo {\n        TransferType t;\n        address sender;\n        address receiver;\n        address token;\n        uint256 amount;\n        uint64 wdseq; // only needed for LqWithdraw (refund)\n        uint64 srcChainId;\n        bytes32 refId;\n        bytes32 srcTxHash; // src chain msg tx hash\n    }\n\n    struct RouteInfo {\n        address sender;\n        address receiver;\n        uint64 srcChainId;\n        bytes32 srcTxHash; // src chain msg tx hash\n    }\n\n    // used for msg from non-evm chains with longer-bytes address\n    struct RouteInfo2 {\n        bytes sender;\n        address receiver;\n        uint64 srcChainId;\n        bytes32 srcTxHash;\n    }\n\n    // combination of RouteInfo and RouteInfo2 for easier processing\n    struct Route {\n        address sender; // from RouteInfo\n        bytes senderBytes; // from RouteInfo2\n        address receiver;\n        uint64 srcChainId;\n        bytes32 srcTxHash;\n    }\n\n    struct MsgWithTransferExecutionParams {\n        bytes message;\n        TransferInfo transfer;\n        bytes[] sigs;\n        address[] signers;\n        uint256[] powers;\n    }\n\n    struct BridgeTransferParams {\n        bytes request;\n        bytes[] sigs;\n        address[] signers;\n        uint256[] powers;\n    }\n}\n"
  },
  {
    "path": "contracts/message/messagebus/MessageBus.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./MessageBusSender.sol\";\nimport \"./MessageBusReceiver.sol\";\n\ncontract MessageBus is MessageBusSender, MessageBusReceiver {\n    constructor(\n        ISigsVerifier _sigsVerifier,\n        address _liquidityBridge,\n        address _pegBridge,\n        address _pegVault,\n        address _pegBridgeV2,\n        address _pegVaultV2\n    )\n        MessageBusSender(_sigsVerifier)\n        MessageBusReceiver(_liquidityBridge, _pegBridge, _pegVault, _pegBridgeV2, _pegVaultV2)\n    {}\n\n    // this is only to be called by Proxy via delegateCall as initOwner will require _owner is 0.\n    // so calling init on this contract directly will guarantee to fail\n    function init(\n        address _liquidityBridge,\n        address _pegBridge,\n        address _pegVault,\n        address _pegBridgeV2,\n        address _pegVaultV2\n    ) external {\n        // MUST manually call ownable init and must only call once\n        initOwner();\n        // we don't need sender init as _sigsVerifier is immutable so already in the deployed code\n        initReceiver(_liquidityBridge, _pegBridge, _pegVault, _pegBridgeV2, _pegVaultV2);\n    }\n}\n"
  },
  {
    "path": "contracts/message/messagebus/MessageBusReceiver.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"../interfaces/IMessageReceiverApp.sol\";\nimport \"../../interfaces/IBridge.sol\";\nimport \"../../interfaces/IOriginalTokenVault.sol\";\nimport \"../../interfaces/IOriginalTokenVaultV2.sol\";\nimport \"../../interfaces/IPeggedTokenBridge.sol\";\nimport \"../../interfaces/IPeggedTokenBridgeV2.sol\";\nimport \"../../interfaces/IDelayedTransfer.sol\";\nimport \"../../safeguard/Ownable.sol\";\nimport \"../../libraries/Utils.sol\";\n\ncontract MessageBusReceiver is Ownable {\n    mapping(bytes32 => MsgDataTypes.TxStatus) public executedMessages;\n\n    address public liquidityBridge; // liquidity bridge address\n    address public pegBridge; // peg bridge address\n    address public pegVault; // peg original vault address\n    address public pegBridgeV2; // peg bridge address\n    address public pegVaultV2; // peg original vault address\n\n    // minimum amount of gas needed by this contract before it tries to\n    // deliver a message to the target contract.\n    uint256 public preExecuteMessageGasUsage;\n\n    event Executed(\n        MsgDataTypes.MsgType msgType,\n        bytes32 msgId,\n        MsgDataTypes.TxStatus status,\n        address indexed receiver,\n        uint64 srcChainId,\n        bytes32 srcTxHash\n    );\n    event NeedRetry(MsgDataTypes.MsgType msgType, bytes32 msgId, uint64 srcChainId, bytes32 srcTxHash);\n    event CallReverted(string reason); // help debug\n\n    event LiquidityBridgeUpdated(address liquidityBridge);\n    event PegBridgeUpdated(address pegBridge);\n    event PegVaultUpdated(address pegVault);\n    event PegBridgeV2Updated(address pegBridgeV2);\n    event PegVaultV2Updated(address pegVaultV2);\n\n    constructor(\n        address _liquidityBridge,\n        address _pegBridge,\n        address _pegVault,\n        address _pegBridgeV2,\n        address _pegVaultV2\n    ) {\n        liquidityBridge = _liquidityBridge;\n        pegBridge = _pegBridge;\n        pegVault = _pegVault;\n        pegBridgeV2 = _pegBridgeV2;\n        pegVaultV2 = _pegVaultV2;\n    }\n\n    function initReceiver(\n        address _liquidityBridge,\n        address _pegBridge,\n        address _pegVault,\n        address _pegBridgeV2,\n        address _pegVaultV2\n    ) internal {\n        require(liquidityBridge == address(0), \"liquidityBridge already set\");\n        liquidityBridge = _liquidityBridge;\n        pegBridge = _pegBridge;\n        pegVault = _pegVault;\n        pegBridgeV2 = _pegBridgeV2;\n        pegVaultV2 = _pegVaultV2;\n    }\n\n    // ============== functions called by executor ==============\n\n    /**\n     * @notice Execute a message with a successful transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _transfer The transfer info.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessageWithTransfer(\n        bytes calldata _message,\n        MsgDataTypes.TransferInfo calldata _transfer,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) public payable {\n        // For message with token transfer, message Id is computed through transfer info\n        // in order to guarantee that each transfer can only be used once.\n        bytes32 messageId = verifyTransfer(_transfer);\n        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, \"transfer already executed\");\n        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\n\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"MessageWithTransfer\"));\n        IBridge(liquidityBridge).verifySigs(\n            abi.encodePacked(domain, messageId, _message, _transfer.srcTxHash),\n            _sigs,\n            _signers,\n            _powers\n        );\n        MsgDataTypes.TxStatus status;\n        IMessageReceiverApp.ExecutionStatus est = executeMessageWithTransfer(_transfer, _message);\n        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\n            status = MsgDataTypes.TxStatus.Success;\n        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\n            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\n            emit NeedRetry(\n                MsgDataTypes.MsgType.MessageWithTransfer,\n                messageId,\n                _transfer.srcChainId,\n                _transfer.srcTxHash\n            );\n            return;\n        } else {\n            est = executeMessageWithTransferFallback(_transfer, _message);\n            if (est == IMessageReceiverApp.ExecutionStatus.Success) {\n                status = MsgDataTypes.TxStatus.Fallback;\n            } else {\n                status = MsgDataTypes.TxStatus.Fail;\n            }\n        }\n        executedMessages[messageId] = status;\n        emitMessageWithTransferExecutedEvent(messageId, status, _transfer);\n    }\n\n    /**\n     * @notice Execute a message with a refunded transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _transfer The transfer info.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessageWithTransferRefund(\n        bytes calldata _message, // the same message associated with the original transfer\n        MsgDataTypes.TransferInfo calldata _transfer,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) public payable {\n        // similar to executeMessageWithTransfer\n        bytes32 messageId = verifyTransfer(_transfer);\n        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, \"transfer already executed\");\n        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\n\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"MessageWithTransferRefund\"));\n        IBridge(liquidityBridge).verifySigs(\n            abi.encodePacked(domain, messageId, _message, _transfer.srcTxHash),\n            _sigs,\n            _signers,\n            _powers\n        );\n        MsgDataTypes.TxStatus status;\n        IMessageReceiverApp.ExecutionStatus est = executeMessageWithTransferRefund(_transfer, _message);\n        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\n            status = MsgDataTypes.TxStatus.Success;\n        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\n            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\n            emit NeedRetry(\n                MsgDataTypes.MsgType.MessageWithTransfer,\n                messageId,\n                _transfer.srcChainId,\n                _transfer.srcTxHash\n            );\n            return;\n        } else {\n            status = MsgDataTypes.TxStatus.Fail;\n        }\n        executedMessages[messageId] = status;\n        emitMessageWithTransferExecutedEvent(messageId, status, _transfer);\n    }\n\n    /**\n     * @notice Execute a message not associated with a transfer.\n     * @param _message Arbitrary message bytes originated from and encoded by the source app contract\n     * @param _route The info about the sender and the receiver.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function executeMessage(\n        bytes calldata _message,\n        MsgDataTypes.RouteInfo calldata _route,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external payable {\n        MsgDataTypes.Route memory route = getRouteInfo(_route);\n        executeMessage(_message, route, _sigs, _signers, _powers, \"Message\");\n    }\n\n    // execute message from non-evm chain with bytes for sender address,\n    // otherwise same as above.\n    function executeMessage(\n        bytes calldata _message,\n        MsgDataTypes.RouteInfo2 calldata _route,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external payable {\n        MsgDataTypes.Route memory route = getRouteInfo(_route);\n        executeMessage(_message, route, _sigs, _signers, _powers, \"Message2\");\n    }\n\n    function executeMessage(\n        bytes calldata _message,\n        MsgDataTypes.Route memory _route,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers,\n        string memory domainName\n    ) private {\n        // For message without associated token transfer, message Id is computed through message info,\n        // in order to guarantee that each message can only be applied once\n        bytes32 messageId = computeMessageOnlyId(_route, _message);\n        require(executedMessages[messageId] == MsgDataTypes.TxStatus.Null, \"message already executed\");\n        executedMessages[messageId] = MsgDataTypes.TxStatus.Pending;\n\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), domainName));\n        IBridge(liquidityBridge).verifySigs(abi.encodePacked(domain, messageId), _sigs, _signers, _powers);\n        MsgDataTypes.TxStatus status;\n        IMessageReceiverApp.ExecutionStatus est = executeMessage(_route, _message);\n        if (est == IMessageReceiverApp.ExecutionStatus.Success) {\n            status = MsgDataTypes.TxStatus.Success;\n        } else if (est == IMessageReceiverApp.ExecutionStatus.Retry) {\n            executedMessages[messageId] = MsgDataTypes.TxStatus.Null;\n            emit NeedRetry(MsgDataTypes.MsgType.MessageOnly, messageId, _route.srcChainId, _route.srcTxHash);\n            return;\n        } else {\n            status = MsgDataTypes.TxStatus.Fail;\n        }\n        executedMessages[messageId] = status;\n        emitMessageOnlyExecutedEvent(messageId, status, _route);\n    }\n\n    // ================= utils (to avoid stack too deep) =================\n\n    function emitMessageWithTransferExecutedEvent(\n        bytes32 _messageId,\n        MsgDataTypes.TxStatus _status,\n        MsgDataTypes.TransferInfo calldata _transfer\n    ) private {\n        emit Executed(\n            MsgDataTypes.MsgType.MessageWithTransfer,\n            _messageId,\n            _status,\n            _transfer.receiver,\n            _transfer.srcChainId,\n            _transfer.srcTxHash\n        );\n    }\n\n    function emitMessageOnlyExecutedEvent(\n        bytes32 _messageId,\n        MsgDataTypes.TxStatus _status,\n        MsgDataTypes.Route memory _route\n    ) private {\n        emit Executed(\n            MsgDataTypes.MsgType.MessageOnly,\n            _messageId,\n            _status,\n            _route.receiver,\n            _route.srcChainId,\n            _route.srcTxHash\n        );\n    }\n\n    function executeMessageWithTransfer(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\n        private\n        returns (IMessageReceiverApp.ExecutionStatus)\n    {\n        uint256 gasLeftBeforeExecution = gasleft();\n        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\n            abi.encodeWithSelector(\n                IMessageReceiverApp.executeMessageWithTransfer.selector,\n                _transfer.sender,\n                _transfer.token,\n                _transfer.amount,\n                _transfer.srcChainId,\n                _message,\n                msg.sender\n            )\n        );\n        if (ok) {\n            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\n        }\n        handleExecutionRevert(gasLeftBeforeExecution, res);\n        return IMessageReceiverApp.ExecutionStatus.Fail;\n    }\n\n    function executeMessageWithTransferFallback(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\n        private\n        returns (IMessageReceiverApp.ExecutionStatus)\n    {\n        uint256 gasLeftBeforeExecution = gasleft();\n        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\n            abi.encodeWithSelector(\n                IMessageReceiverApp.executeMessageWithTransferFallback.selector,\n                _transfer.sender,\n                _transfer.token,\n                _transfer.amount,\n                _transfer.srcChainId,\n                _message,\n                msg.sender\n            )\n        );\n        if (ok) {\n            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\n        }\n        handleExecutionRevert(gasLeftBeforeExecution, res);\n        return IMessageReceiverApp.ExecutionStatus.Fail;\n    }\n\n    function executeMessageWithTransferRefund(MsgDataTypes.TransferInfo calldata _transfer, bytes calldata _message)\n        private\n        returns (IMessageReceiverApp.ExecutionStatus)\n    {\n        uint256 gasLeftBeforeExecution = gasleft();\n        (bool ok, bytes memory res) = address(_transfer.receiver).call{value: msg.value}(\n            abi.encodeWithSelector(\n                IMessageReceiverApp.executeMessageWithTransferRefund.selector,\n                _transfer.token,\n                _transfer.amount,\n                _message,\n                msg.sender\n            )\n        );\n        if (ok) {\n            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\n        }\n        handleExecutionRevert(gasLeftBeforeExecution, res);\n        return IMessageReceiverApp.ExecutionStatus.Fail;\n    }\n\n    function verifyTransfer(MsgDataTypes.TransferInfo calldata _transfer) private view returns (bytes32) {\n        bytes32 transferId;\n        address bridgeAddr;\n        MsgDataTypes.TransferType t = _transfer.t;\n        if (t == MsgDataTypes.TransferType.LqRelay) {\n            bridgeAddr = liquidityBridge;\n            transferId = keccak256(\n                abi.encodePacked(\n                    _transfer.sender,\n                    _transfer.receiver,\n                    _transfer.token,\n                    _transfer.amount,\n                    _transfer.srcChainId,\n                    uint64(block.chainid),\n                    _transfer.refId\n                )\n            );\n            require(IBridge(bridgeAddr).transfers(transferId) == true, \"relay not exist\");\n        } else if (t == MsgDataTypes.TransferType.LqWithdraw) {\n            bridgeAddr = liquidityBridge;\n            transferId = keccak256(\n                abi.encodePacked(\n                    uint64(block.chainid),\n                    _transfer.wdseq,\n                    _transfer.receiver,\n                    _transfer.token,\n                    _transfer.amount\n                )\n            );\n            require(IBridge(bridgeAddr).withdraws(transferId) == true, \"withdraw not exist\");\n        } else {\n            if (t == MsgDataTypes.TransferType.PegMint || t == MsgDataTypes.TransferType.PegWithdraw) {\n                bridgeAddr = (t == MsgDataTypes.TransferType.PegMint) ? pegBridge : pegVault;\n                transferId = keccak256(\n                    abi.encodePacked(\n                        _transfer.receiver,\n                        _transfer.token,\n                        _transfer.amount,\n                        _transfer.sender,\n                        _transfer.srcChainId,\n                        _transfer.refId\n                    )\n                );\n            } else {\n                bridgeAddr = (t == MsgDataTypes.TransferType.PegV2Mint) ? pegBridgeV2 : pegVaultV2;\n                transferId = keccak256(\n                    abi.encodePacked(\n                        _transfer.receiver,\n                        _transfer.token,\n                        _transfer.amount,\n                        _transfer.sender,\n                        _transfer.srcChainId,\n                        _transfer.refId,\n                        bridgeAddr\n                    )\n                );\n            }\n            // function is same for peg, peg2, vault, vault2\n            require(IPeggedTokenBridge(bridgeAddr).records(transferId) == true, \"record not exist\");\n        }\n        require(IDelayedTransfer(bridgeAddr).delayedTransfers(transferId).timestamp == 0, \"transfer delayed\");\n        return keccak256(abi.encodePacked(MsgDataTypes.MsgType.MessageWithTransfer, bridgeAddr, transferId));\n    }\n\n    function computeMessageOnlyId(MsgDataTypes.Route memory _route, bytes calldata _message)\n        private\n        view\n        returns (bytes32)\n    {\n        bytes memory sender = _route.senderBytes;\n        if (sender.length == 0) {\n            sender = abi.encodePacked(_route.sender);\n        }\n        return\n            keccak256(\n                abi.encodePacked(\n                    MsgDataTypes.MsgType.MessageOnly,\n                    sender,\n                    _route.receiver,\n                    _route.srcChainId,\n                    _route.srcTxHash,\n                    uint64(block.chainid),\n                    _message\n                )\n            );\n    }\n\n    function executeMessage(MsgDataTypes.Route memory _route, bytes calldata _message)\n        private\n        returns (IMessageReceiverApp.ExecutionStatus)\n    {\n        uint256 gasLeftBeforeExecution = gasleft();\n        bool ok;\n        bytes memory res;\n        if (_route.senderBytes.length == 0) {\n            (ok, res) = address(_route.receiver).call{value: msg.value}(\n                abi.encodeWithSelector(\n                    bytes4(keccak256(bytes(\"executeMessage(address,uint64,bytes,address)\"))),\n                    _route.sender,\n                    _route.srcChainId,\n                    _message,\n                    msg.sender\n                )\n            );\n        } else {\n            (ok, res) = address(_route.receiver).call{value: msg.value}(\n                abi.encodeWithSelector(\n                    bytes4(keccak256(bytes(\"executeMessage(bytes,uint64,bytes,address)\"))),\n                    _route.senderBytes,\n                    _route.srcChainId,\n                    _message,\n                    msg.sender\n                )\n            );\n        }\n        if (ok) {\n            return abi.decode((res), (IMessageReceiverApp.ExecutionStatus));\n        }\n        handleExecutionRevert(gasLeftBeforeExecution, res);\n        return IMessageReceiverApp.ExecutionStatus.Fail;\n    }\n\n    function handleExecutionRevert(uint256 _gasLeftBeforeExecution, bytes memory _returnData) private {\n        uint256 gasLeftAfterExecution = gasleft();\n        uint256 maxTargetGasLimit = block.gaslimit - preExecuteMessageGasUsage;\n        if (_gasLeftBeforeExecution < maxTargetGasLimit && gasLeftAfterExecution <= _gasLeftBeforeExecution / 64) {\n            // if this happens, the executor must have not provided sufficient gas limit,\n            // then the tx should revert instead of recording a non-retryable failure status\n            // https://github.com/wolflo/evm-opcodes/blob/main/gas.md#aa-f-gas-to-send-with-call-operations\n            assembly {\n                invalid()\n            }\n        }\n        string memory revertMsg = Utils.getRevertMsg(_returnData);\n        // revert the execution if the revert message has the ABORT prefix\n        checkAbortPrefix(revertMsg);\n        // otherwiase, emit revert message, return and mark the execution as failed (non-retryable)\n        emit CallReverted(revertMsg);\n    }\n\n    function checkAbortPrefix(string memory _revertMsg) private pure {\n        bytes memory prefixBytes = bytes(MsgDataTypes.ABORT_PREFIX);\n        bytes memory msgBytes = bytes(_revertMsg);\n        if (msgBytes.length >= prefixBytes.length) {\n            for (uint256 i = 0; i < prefixBytes.length; i++) {\n                if (msgBytes[i] != prefixBytes[i]) {\n                    return; // prefix not match, return\n                }\n            }\n            revert(_revertMsg); // prefix match, revert\n        }\n    }\n\n    function getRouteInfo(MsgDataTypes.RouteInfo calldata _route) private pure returns (MsgDataTypes.Route memory) {\n        return MsgDataTypes.Route(_route.sender, \"\", _route.receiver, _route.srcChainId, _route.srcTxHash);\n    }\n\n    function getRouteInfo(MsgDataTypes.RouteInfo2 calldata _route) private pure returns (MsgDataTypes.Route memory) {\n        return MsgDataTypes.Route(address(0), _route.sender, _route.receiver, _route.srcChainId, _route.srcTxHash);\n    }\n\n    // ================= helper functions =====================\n\n    /**\n     * @notice combine bridge transfer and msg execution calls into a single tx\n     * @dev caller needs to get the required input params from SGN\n     * @param _tp params to call bridge transfer\n     * @param _mp params to execute message\n     */\n    function transferAndExecuteMsg(\n        MsgDataTypes.BridgeTransferParams calldata _tp,\n        MsgDataTypes.MsgWithTransferExecutionParams calldata _mp\n    ) external {\n        _bridgeTransfer(_mp.transfer.t, _tp);\n        executeMessageWithTransfer(_mp.message, _mp.transfer, _mp.sigs, _mp.signers, _mp.powers);\n    }\n\n    /**\n     * @notice combine bridge refund and msg execution calls into a single tx\n     * @dev caller needs to get the required input params from SGN\n     * @param _tp params to call bridge transfer for refund\n     * @param _mp params to execute message for refund\n     */\n    function refundAndExecuteMsg(\n        MsgDataTypes.BridgeTransferParams calldata _tp,\n        MsgDataTypes.MsgWithTransferExecutionParams calldata _mp\n    ) external {\n        _bridgeTransfer(_mp.transfer.t, _tp);\n        executeMessageWithTransferRefund(_mp.message, _mp.transfer, _mp.sigs, _mp.signers, _mp.powers);\n    }\n\n    function _bridgeTransfer(MsgDataTypes.TransferType t, MsgDataTypes.BridgeTransferParams calldata _params) private {\n        if (t == MsgDataTypes.TransferType.LqRelay) {\n            IBridge(liquidityBridge).relay(_params.request, _params.sigs, _params.signers, _params.powers);\n        } else if (t == MsgDataTypes.TransferType.LqWithdraw) {\n            IBridge(liquidityBridge).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\n        } else if (t == MsgDataTypes.TransferType.PegMint) {\n            IPeggedTokenBridge(pegBridge).mint(_params.request, _params.sigs, _params.signers, _params.powers);\n        } else if (t == MsgDataTypes.TransferType.PegV2Mint) {\n            IPeggedTokenBridgeV2(pegBridgeV2).mint(_params.request, _params.sigs, _params.signers, _params.powers);\n        } else if (t == MsgDataTypes.TransferType.PegWithdraw) {\n            IOriginalTokenVault(pegVault).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\n        } else if (t == MsgDataTypes.TransferType.PegV2Withdraw) {\n            IOriginalTokenVaultV2(pegVaultV2).withdraw(_params.request, _params.sigs, _params.signers, _params.powers);\n        }\n    }\n\n    // ================= contract config =================\n\n    function setLiquidityBridge(address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        liquidityBridge = _addr;\n        emit LiquidityBridgeUpdated(liquidityBridge);\n    }\n\n    function setPegBridge(address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        pegBridge = _addr;\n        emit PegBridgeUpdated(pegBridge);\n    }\n\n    function setPegVault(address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        pegVault = _addr;\n        emit PegVaultUpdated(pegVault);\n    }\n\n    function setPegBridgeV2(address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        pegBridgeV2 = _addr;\n        emit PegBridgeV2Updated(pegBridgeV2);\n    }\n\n    function setPegVaultV2(address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        pegVaultV2 = _addr;\n        emit PegVaultV2Updated(pegVaultV2);\n    }\n\n    function setPreExecuteMessageGasUsage(uint256 _usage) public onlyOwner {\n        preExecuteMessageGasUsage = _usage;\n    }\n}\n"
  },
  {
    "path": "contracts/message/messagebus/MessageBusSender.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../safeguard/Ownable.sol\";\nimport \"../../interfaces/ISigsVerifier.sol\";\n\ncontract MessageBusSender is Ownable {\n    ISigsVerifier public immutable sigsVerifier;\n\n    uint256 public feeBase;\n    uint256 public feePerByte;\n    mapping(address => uint256) public withdrawnFees;\n\n    event Message(address indexed sender, address receiver, uint256 dstChainId, bytes message, uint256 fee);\n    // message to non-evm chain with >20 bytes addr\n    event Message2(address indexed sender, bytes receiver, uint256 dstChainId, bytes message, uint256 fee);\n\n    event MessageWithTransfer(\n        address indexed sender,\n        address receiver,\n        uint256 dstChainId,\n        address bridge,\n        bytes32 srcTransferId,\n        bytes message,\n        uint256 fee\n    );\n\n    event FeeWithdrawn(address receiver, uint256 amount);\n\n    event FeeBaseUpdated(uint256 feeBase);\n    event FeePerByteUpdated(uint256 feePerByte);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Sends a message to a contract on another chain.\n     * Sender needs to make sure the uniqueness of the message Id, which is computed as\n     * hash(type.MessageOnly, sender, receiver, srcChainId, srcTxHash, dstChainId, message).\n     * If messages with the same Id are sent, only one of them will succeed at dst chain.\n     * A fee is charged in the native gas token.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     */\n    function sendMessage(\n        address _receiver,\n        uint256 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        _sendMessage(_dstChainId, _message);\n        emit Message(msg.sender, _receiver, _dstChainId, _message, msg.value);\n    }\n\n    // Send message to non-evm chain with bytes for receiver address,\n    // otherwise same as above.\n    function sendMessage(\n        bytes calldata _receiver,\n        uint256 _dstChainId,\n        bytes calldata _message\n    ) external payable {\n        _sendMessage(_dstChainId, _message);\n        emit Message2(msg.sender, _receiver, _dstChainId, _message, msg.value);\n    }\n\n    function _sendMessage(uint256 _dstChainId, bytes calldata _message) private {\n        require(_dstChainId != block.chainid, \"Invalid chainId\");\n        uint256 minFee = calcFee(_message);\n        require(msg.value >= minFee, \"Insufficient fee\");\n    }\n\n    /**\n     * @notice Sends a message associated with a transfer to a contract on another chain.\n     * If messages with the same srcTransferId are sent, only one of them will succeed.\n     * A fee is charged in the native token.\n     * @param _receiver The address of the destination app contract.\n     * @param _dstChainId The destination chain ID.\n     * @param _srcBridge The bridge contract to send the transfer with.\n     * @param _srcTransferId The transfer ID.\n     * @param _dstChainId The destination chain ID.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     */\n    function sendMessageWithTransfer(\n        address _receiver,\n        uint256 _dstChainId,\n        address _srcBridge,\n        bytes32 _srcTransferId,\n        bytes calldata _message\n    ) external payable {\n        require(_dstChainId != block.chainid, \"Invalid chainId\");\n        uint256 minFee = calcFee(_message);\n        require(msg.value >= minFee, \"Insufficient fee\");\n        // SGN needs to verify\n        // 1. msg.sender matches sender of the src transfer\n        // 2. dstChainId matches dstChainId of the src transfer\n        // 3. bridge is either liquidity bridge, peg src vault, or peg dst bridge\n        emit MessageWithTransfer(msg.sender, _receiver, _dstChainId, _srcBridge, _srcTransferId, _message, msg.value);\n    }\n\n    /**\n     * @notice Withdraws message fee in the form of native gas token.\n     * @param _account The address receiving the fee.\n     * @param _cumulativeFee The cumulative fee credited to the account. Tracked by SGN.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A withdrawal must be\n     * signed-off by +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdrawFee(\n        address _account,\n        uint256 _cumulativeFee,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"withdrawFee\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _account, _cumulativeFee), _sigs, _signers, _powers);\n        uint256 amount = _cumulativeFee - withdrawnFees[_account];\n        require(amount > 0, \"No new amount to withdraw\");\n        withdrawnFees[_account] = _cumulativeFee;\n        (bool sent, ) = _account.call{value: amount, gas: 50000}(\"\");\n        require(sent, \"failed to withdraw fee\");\n        emit FeeWithdrawn(_account, amount);\n    }\n\n    /**\n     * @notice Calculates the required fee for the message.\n     * @param _message Arbitrary message bytes to be decoded by the destination app contract.\n     @ @return The required fee.\n     */\n    function calcFee(bytes calldata _message) public view returns (uint256) {\n        return feeBase + _message.length * feePerByte;\n    }\n\n    // -------------------- Admin --------------------\n\n    function setFeePerByte(uint256 _fee) external onlyOwner {\n        feePerByte = _fee;\n        emit FeePerByteUpdated(feePerByte);\n    }\n\n    function setFeeBase(uint256 _fee) external onlyOwner {\n        feeBase = _fee;\n        emit FeeBaseUpdated(feeBase);\n    }\n}\n"
  },
  {
    "path": "contracts/message/safeguard/DelayedMessage.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../../safeguard/Ownable.sol\";\n\nabstract contract DelayedMessage is Ownable {\n    // universal unique id (not msgId) => delay start time\n    mapping(bytes32 => uint256) public delayedMessages;\n    uint256 public delayPeriod; // in seconds\n    uint32 public nonce;\n\n    event DelayedMessageAdded(bytes32 id, address srcContract, uint64 srcChainId, bytes message, uint32 nonce);\n    event DelayedMessageExecuted(bytes32 id);\n\n    event DelayPeriodUpdated(uint256 period);\n\n    function _addDelayedMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes calldata _message\n    ) internal {\n        bytes32 id = keccak256(abi.encodePacked(_srcContract, _srcChainId, _message, uint64(block.chainid), nonce));\n        delayedMessages[id] = uint256(block.timestamp);\n        emit DelayedMessageAdded(id, _srcContract, _srcChainId, _message, nonce);\n        nonce += 1;\n    }\n\n    // caller needs to do the actual message execution\n    function _executeDelayedMessage(\n        address _srcContract,\n        uint64 _srcChainId,\n        bytes memory _message,\n        uint32 _nonce\n    ) internal {\n        bytes32 id = keccak256(abi.encodePacked(_srcContract, _srcChainId, _message, uint64(block.chainid), _nonce));\n        require(delayedMessages[id] > 0, \"delayed message not exist\");\n        require(block.timestamp > delayedMessages[id] + delayPeriod, \"delayed message still locked\");\n        delete delayedMessages[id];\n        emit DelayedMessageExecuted(id);\n    }\n\n    function setDelayPeriod(uint256 _period) external onlyOwner {\n        delayPeriod = _period;\n        emit DelayPeriodUpdated(_period);\n    }\n}\n"
  },
  {
    "path": "contracts/message/safeguard/MessageAppPauser.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.0;\n\nimport \"../libraries/MsgDataTypes.sol\";\nimport \"../../safeguard/Pauser.sol\";\n\nabstract contract MessageAppPauser is Pauser {\n    /**\n     * @dev Modifier to make the message execution function callable only when\n     * the contract is not paused.\n     *\n     * Added the ABORT_PREFIX (\"MSG::ABORT:\") in front of the revert message to\n     * work with the Celer IM MessageBus contract, so that the message execution\n     * can be retried later when the contract is unpaused.\n     */\n    modifier whenNotMsgPaused() {\n        require(!paused(), MsgDataTypes.abortReason(\"Pausable: paused\"));\n        _;\n    }\n}\n"
  },
  {
    "path": "contracts/miscs/Faucet.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract Faucet is Ownable {\n    using SafeERC20 for IERC20;\n\n    uint256 public minDripBlkInterval;\n    mapping(address => uint256) public lastDripBlk;\n\n    /**\n     * @dev Sends 0.01% of each token to the caller.\n     * @param tokens The tokens to drip.\n     */\n    function drip(address[] calldata tokens) public {\n        require(block.number - lastDripBlk[msg.sender] >= minDripBlkInterval, \"too frequent\");\n        for (uint256 i = 0; i < tokens.length; i++) {\n            IERC20 token = IERC20(tokens[i]);\n            uint256 balance = token.balanceOf(address(this));\n            require(balance > 0, \"Faucet is empty\");\n            token.safeTransfer(msg.sender, balance / 10000); // 0.01%\n        }\n        lastDripBlk[msg.sender] = block.number;\n    }\n\n    /**\n     * @dev Owner set minDripBlkInterval\n     *\n     * @param _interval minDripBlkInterval value\n     */\n    function setMinDripBlkInterval(uint256 _interval) external onlyOwner {\n        minDripBlkInterval = _interval;\n    }\n\n    /**\n     * @dev Owner drains one type of tokens\n     *\n     * @param _asset drained asset address\n     * @param _amount drained asset amount\n     */\n    function drainToken(address _asset, uint256 _amount) external onlyOwner {\n        IERC20(_asset).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/miscs/MintableERC20.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\";\n\n/**\n * @title A mintable {ERC20} token.\n */\ncontract MintableERC20 is ERC20Burnable, Ownable {\n    uint8 private _decimals;\n\n    /**\n     * @dev Constructor that gives msg.sender an initial supply of tokens.\n     */\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_,\n        uint256 initialSupply_\n    ) ERC20(name_, symbol_) {\n        _decimals = decimals_;\n        _mint(msg.sender, initialSupply_);\n    }\n\n    /**\n     * @dev Creates `amount` new tokens for `to`.\n     */\n    function mint(address to, uint256 amount) public onlyOwner {\n        _mint(to, amount);\n    }\n\n    function decimals() public view override returns (uint8) {\n        return _decimals;\n    }\n}\n"
  },
  {
    "path": "contracts/miscs/oasys/L1StandardERC20.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol\";\nimport \"@openzeppelin/contracts/access/AccessControlEnumerable.sol\";\nimport \"@openzeppelin/contracts/utils/Context.sol\";\n\n/**\n * @dev {ERC20} token, including:\n *\n *  - ability for holders to burn (destroy) their tokens\n *  - a minter role that allows for token minting (creation)\n *  - a pauser role that allows to stop all token transfers\n *\n * This contract uses {AccessControl} to lock permissioned functions using the\n * different roles - head to its documentation for details.\n *\n * The account that deploys the contract will be granted the minter and pauser\n * roles, as well as the default admin role, which will let it grant both minter\n * and pauser roles to other accounts.\n */\ncontract L1StandardERC20 is Context, AccessControlEnumerable, ERC20Burnable, ERC20Pausable {\n    bytes32 public constant MINTER_ROLE = keccak256(\"MINTER_ROLE\");\n    bytes32 public constant PAUSER_ROLE = keccak256(\"PAUSER_ROLE\");\n\n    /**\n     * @dev Grants `DEFAULT_ADMIN_ROLE`, `MINTER_ROLE` and `PAUSER_ROLE` to the\n     * account that deploys the contract.\n     *\n     * See {ERC20-constructor}.\n     */\n    constructor(\n        address owner,\n        string memory name,\n        string memory symbol\n    ) ERC20(name, symbol) {\n        _setupRole(DEFAULT_ADMIN_ROLE, owner);\n\n        _setupRole(MINTER_ROLE, owner);\n        _setupRole(PAUSER_ROLE, owner);\n    }\n\n    /**\n     * @dev Creates `amount` new tokens for `to`.\n     *\n     * See {ERC20-_mint}.\n     *\n     * Requirements:\n     *\n     * - the caller must have the `MINTER_ROLE`.\n     */\n    function mint(address to, uint256 amount) public virtual {\n        require(hasRole(MINTER_ROLE, _msgSender()), \"ERC20PresetMinterPauser: must have minter role to mint\");\n        _mint(to, amount);\n    }\n\n    /**\n     * @dev Pauses all token transfers.\n     *\n     * See {ERC20Pausable} and {Pausable-_pause}.\n     *\n     * Requirements:\n     *\n     * - the caller must have the `PAUSER_ROLE`.\n     */\n    function pause() public virtual {\n        require(hasRole(PAUSER_ROLE, _msgSender()), \"ERC20PresetMinterPauser: must have pauser role to pause\");\n        _pause();\n    }\n\n    /**\n     * @dev Unpauses all token transfers.\n     *\n     * See {ERC20Pausable} and {Pausable-_unpause}.\n     *\n     * Requirements:\n     *\n     * - the caller must have the `PAUSER_ROLE`.\n     */\n    function unpause() public virtual {\n        require(hasRole(PAUSER_ROLE, _msgSender()), \"ERC20PresetMinterPauser: must have pauser role to unpause\");\n        _unpause();\n    }\n\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual override(ERC20, ERC20Pausable) {\n        super._beforeTokenTransfer(from, to, amount);\n    }\n}\n"
  },
  {
    "path": "contracts/miscs/oasys/L1StandardERC20Factory.sol",
    "content": "// SPDX-License-Identifier: MIT\npragma solidity ^0.8.9;\n\nimport {L1StandardERC20} from \"./L1StandardERC20.sol\";\n\n/**\n * @title L1StandardERC20Factory\n * @dev L1StandardERC20Factory deploys the Oasys Standard ERC20 contract.\n */\ncontract L1StandardERC20Factory {\n    /**********\n     * Events *\n     **********/\n\n    event ERC20Created(string indexed _symbol, address indexed _address);\n\n    /********************\n     * Public Functions *\n     ********************/\n\n    /**\n     * Deploys the Oasys Standard ERC20.\n     * @param _name Name of the ERC20.\n     * @param _symbol Symbol of the ERC20.\n     */\n    function createStandardERC20(string memory _name, string memory _symbol) external {\n        L1StandardERC20 erc20 = new L1StandardERC20(msg.sender, _name, _symbol);\n        emit ERC20Created(_symbol, address(erc20));\n    }\n}\n"
  },
  {
    "path": "contracts/miscs/oasys/README.md",
    "content": "# Oasys specific contracts\n\nUsed for generating typechain for oasys_token_factory.ts to deploy new tokens on Oasys chain\n"
  },
  {
    "path": "contracts/miscs/proxy-admin/Ownable.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/utils/Context.sol\";\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor (address initialOwner) {\n        _transferOwnership(initialOwner);\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        _transferOwnership(address(0));\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _transferOwnership(newOwner);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Internal function without access restriction.\n     */\n    function _transferOwnership(address newOwner) internal virtual {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}"
  },
  {
    "path": "contracts/miscs/proxy-admin/ProxyAdmin.sol",
    "content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol\";\nimport \"./Ownable.sol\";\n\n/**\n * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an\n * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.\n */\ncontract ProxyAdmin is Ownable {\n\n    constructor (address initialOwner) Ownable(initialOwner) {}\n\n    /**\n     * @dev Returns the current implementation of `proxy`.\n     *\n     * Requirements:\n     *\n     * - This contract must be the admin of `proxy`.\n     */\n    function getProxyImplementation(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n        // We need to manually run the static call since the getter cannot be flagged as view\n        // bytes4(keccak256(\"implementation()\")) == 0x5c60da1b\n        (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"5c60da1b\");\n        require(success);\n        return abi.decode(returndata, (address));\n    }\n\n    /**\n     * @dev Returns the current admin of `proxy`.\n     *\n     * Requirements:\n     *\n     * - This contract must be the admin of `proxy`.\n     */\n    function getProxyAdmin(TransparentUpgradeableProxy proxy) public view virtual returns (address) {\n        // We need to manually run the static call since the getter cannot be flagged as view\n        // bytes4(keccak256(\"admin()\")) == 0xf851a440\n        (bool success, bytes memory returndata) = address(proxy).staticcall(hex\"f851a440\");\n        require(success);\n        return abi.decode(returndata, (address));\n    }\n\n    /**\n     * @dev Changes the admin of `proxy` to `newAdmin`.\n     *\n     * Requirements:\n     *\n     * - This contract must be the current admin of `proxy`.\n     */\n    function changeProxyAdmin(TransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {\n        proxy.changeAdmin(newAdmin);\n    }\n\n    /**\n     * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}.\n     *\n     * Requirements:\n     *\n     * - This contract must be the admin of `proxy`.\n     */\n    function upgrade(TransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {\n        proxy.upgradeTo(implementation);\n    }\n\n    /**\n     * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See\n     * {TransparentUpgradeableProxy-upgradeToAndCall}.\n     *\n     * Requirements:\n     *\n     * - This contract must be the admin of `proxy`.\n     */\n    function upgradeAndCall(\n        TransparentUpgradeableProxy proxy,\n        address implementation,\n        bytes memory data\n    ) public payable virtual onlyOwner {\n        proxy.upgradeToAndCall{value: msg.value}(implementation, data);\n    }\n}"
  },
  {
    "path": "contracts/pegged-bridge/OriginalTokenVault.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../interfaces/IWETH.sol\";\nimport \"../libraries/PbPegged.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\n\n/**\n * @title the vault to deposit and withdraw original tokens\n * @dev Work together with PeggedTokenBridge contracts deployed at remote chains\n */\ncontract OriginalTokenVault is ReentrancyGuard, Pauser, VolumeControl, DelayedTransfer {\n    using SafeERC20 for IERC20;\n\n    ISigsVerifier public immutable sigsVerifier;\n\n    mapping(bytes32 => bool) public records;\n\n    mapping(address => uint256) public minDeposit;\n    mapping(address => uint256) public maxDeposit;\n\n    address public nativeWrap;\n    uint256 public nativeTokenTransferGas = 50000;\n\n    event Deposited(\n        bytes32 depositId,\n        address depositor,\n        address token,\n        uint256 amount,\n        uint64 mintChainId,\n        address mintAccount\n    );\n    event Withdrawn(\n        bytes32 withdrawId,\n        address receiver,\n        address token,\n        uint256 amount,\n        // ref_chain_id defines the reference chain ID, taking values of:\n        // 1. The common case of burn-withdraw: the chain ID on which the corresponding burn happened;\n        // 2. Pegbridge fee claim: zero / not applicable;\n        // 3. Refund for wrong deposit: this chain ID on which the deposit happened\n        uint64 refChainId,\n        // ref_id defines a unique reference ID, taking values of:\n        // 1. The common case of burn-withdraw: the burn ID on the remote chain;\n        // 2. Pegbridge fee claim: a per-account nonce;\n        // 3. Refund for wrong deposit: the deposit ID on this chain\n        bytes32 refId,\n        address burnAccount\n    );\n    event MinDepositUpdated(address token, uint256 amount);\n    event MaxDepositUpdated(address token, uint256 amount);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Lock original tokens to trigger cross-chain mint of pegged tokens at a remote chain's PeggedTokenBridge.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The original token address.\n     * @param _amount The amount to deposit.\n     * @param _mintChainId The destination chain ID to mint tokens.\n     * @param _mintAccount The destination account to receive the minted pegged tokens.\n     * @param _nonce A number input to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external nonReentrant whenNotPaused {\n        bytes32 depId = _deposit(_token, _amount, _mintChainId, _mintAccount, _nonce);\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit Deposited(depId, msg.sender, _token, _amount, _mintChainId, _mintAccount);\n    }\n\n    /**\n     * @notice Lock native token as original token to trigger cross-chain mint of pegged tokens at a remote chain's\n     * PeggedTokenBridge.\n     * @param _amount The amount to deposit.\n     * @param _mintChainId The destination chain ID to mint tokens.\n     * @param _mintAccount The destination account to receive the minted pegged tokens.\n     * @param _nonce A number input to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function depositNative(\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external payable nonReentrant whenNotPaused {\n        require(msg.value == _amount, \"Amount mismatch\");\n        require(nativeWrap != address(0), \"Native wrap not set\");\n        bytes32 depId = _deposit(nativeWrap, _amount, _mintChainId, _mintAccount, _nonce);\n        IWETH(nativeWrap).deposit{value: _amount}();\n        emit Deposited(depId, msg.sender, nativeWrap, _amount, _mintChainId, _mintAccount);\n    }\n\n    function _deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) private returns (bytes32) {\n        require(_amount > minDeposit[_token], \"amount too small\");\n        require(maxDeposit[_token] == 0 || _amount <= maxDeposit[_token], \"amount too large\");\n        bytes32 depId = keccak256(\n            // len = 20 + 20 + 32 + 8 + 20 + 8 + 8 = 116\n            abi.encodePacked(msg.sender, _token, _amount, _mintChainId, _mintAccount, _nonce, uint64(block.chainid))\n        );\n        require(records[depId] == false, \"record exists\");\n        records[depId] = true;\n        return depId;\n    }\n\n    /**\n     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\n     * @param _request The serialized Withdraw protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Withdraw\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _request), _sigs, _signers, _powers);\n        PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);\n        bytes32 wdId = keccak256(\n            // len = 20 + 20 + 32 + 20 + 8 + 32 = 132\n            abi.encodePacked(\n                request.receiver,\n                request.token,\n                request.amount,\n                request.burnAccount,\n                request.refChainId,\n                request.refId\n            )\n        );\n        require(records[wdId] == false, \"record exists\");\n        records[wdId] = true;\n        _updateVolume(request.token, request.amount);\n        uint256 delayThreshold = delayThresholds[request.token];\n        if (delayThreshold > 0 && request.amount > delayThreshold) {\n            _addDelayedTransfer(wdId, request.receiver, request.token, request.amount);\n        } else {\n            _sendToken(request.receiver, request.token, request.amount);\n        }\n        emit Withdrawn(\n            wdId,\n            request.receiver,\n            request.token,\n            request.amount,\n            request.refChainId,\n            request.refId,\n            request.burnAccount\n        );\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        _sendToken(transfer.receiver, transfer.token, transfer.amount);\n    }\n\n    function setMinDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minDeposit[_tokens[i]] = _amounts[i];\n            emit MinDepositUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxDeposit[_tokens[i]] = _amounts[i];\n            emit MaxDepositUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setWrap(address _weth) external onlyOwner {\n        nativeWrap = _weth;\n    }\n\n    function _sendToken(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) private {\n        if (_token == nativeWrap) {\n            // withdraw then transfer native to receiver\n            IWETH(nativeWrap).withdraw(_amount);\n            (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n            require(sent, \"failed to send native token\");\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyGovernor {\n        nativeTokenTransferGas = _gasUsed;\n    }\n\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/OriginalTokenVaultV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../interfaces/IWETH.sol\";\nimport \"../libraries/PbPegged.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\n\n/**\n * @title the vault to deposit and withdraw original tokens\n * @dev Work together with PeggedTokenBridge contracts deployed at remote chains\n */\ncontract OriginalTokenVaultV2 is ReentrancyGuard, Pauser, VolumeControl, DelayedTransfer {\n    using SafeERC20 for IERC20;\n\n    ISigsVerifier public immutable sigsVerifier;\n\n    mapping(bytes32 => bool) public records;\n\n    mapping(address => uint256) public minDeposit;\n    mapping(address => uint256) public maxDeposit;\n\n    address public nativeWrap;\n    uint256 public nativeTokenTransferGas = 50000;\n\n    event Deposited(\n        bytes32 depositId,\n        address depositor,\n        address token,\n        uint256 amount,\n        uint64 mintChainId,\n        address mintAccount,\n        uint64 nonce\n    );\n    event Withdrawn(\n        bytes32 withdrawId,\n        address receiver,\n        address token,\n        uint256 amount,\n        // ref_chain_id defines the reference chain ID, taking values of:\n        // 1. The common case of burn-withdraw: the chain ID on which the corresponding burn happened;\n        // 2. Pegbridge fee claim: zero / not applicable;\n        // 3. Refund for wrong deposit: this chain ID on which the deposit happened\n        uint64 refChainId,\n        // ref_id defines a unique reference ID, taking values of:\n        // 1. The common case of burn-withdraw: the burn ID on the remote chain;\n        // 2. Pegbridge fee claim: a per-account nonce;\n        // 3. Refund for wrong deposit: the deposit ID on this chain\n        bytes32 refId,\n        address burnAccount\n    );\n    event MinDepositUpdated(address token, uint256 amount);\n    event MaxDepositUpdated(address token, uint256 amount);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Lock original tokens to trigger cross-chain mint of pegged tokens at a remote chain's PeggedTokenBridge.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The original token address.\n     * @param _amount The amount to deposit.\n     * @param _mintChainId The destination chain ID to mint tokens.\n     * @param _mintAccount The destination account to receive the minted pegged tokens.\n     * @param _nonce A number input to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external nonReentrant whenNotPaused returns (bytes32) {\n        bytes32 depId = _deposit(_token, _amount, _mintChainId, _mintAccount, _nonce);\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        emit Deposited(depId, msg.sender, _token, _amount, _mintChainId, _mintAccount, _nonce);\n        return depId;\n    }\n\n    /**\n     * @notice Lock native token as original token to trigger cross-chain mint of pegged tokens at a remote chain's\n     * PeggedTokenBridge.\n     * @param _amount The amount to deposit.\n     * @param _mintChainId The destination chain ID to mint tokens.\n     * @param _mintAccount The destination account to receive the minted pegged tokens.\n     * @param _nonce A number input to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function depositNative(\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) external payable nonReentrant whenNotPaused returns (bytes32) {\n        require(msg.value == _amount, \"Amount mismatch\");\n        require(nativeWrap != address(0), \"Native wrap not set\");\n        bytes32 depId = _deposit(nativeWrap, _amount, _mintChainId, _mintAccount, _nonce);\n        IWETH(nativeWrap).deposit{value: _amount}();\n        emit Deposited(depId, msg.sender, nativeWrap, _amount, _mintChainId, _mintAccount, _nonce);\n        return depId;\n    }\n\n    function _deposit(\n        address _token,\n        uint256 _amount,\n        uint64 _mintChainId,\n        address _mintAccount,\n        uint64 _nonce\n    ) private returns (bytes32) {\n        require(_amount > minDeposit[_token], \"amount too small\");\n        require(maxDeposit[_token] == 0 || _amount <= maxDeposit[_token], \"amount too large\");\n        bytes32 depId = keccak256(\n            // len = 20 + 20 + 32 + 8 + 20 + 8 + 8 + 20 = 136\n            abi.encodePacked(\n                msg.sender,\n                _token,\n                _amount,\n                _mintChainId,\n                _mintAccount,\n                _nonce,\n                uint64(block.chainid),\n                address(this)\n            )\n        );\n        require(records[depId] == false, \"record exists\");\n        records[depId] = true;\n        return depId;\n    }\n\n    /**\n     * @notice Withdraw locked original tokens triggered by a burn at a remote chain's PeggedTokenBridge.\n     * @param _request The serialized Withdraw protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the bridge's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function withdraw(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Withdraw\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _request), _sigs, _signers, _powers);\n        PbPegged.Withdraw memory request = PbPegged.decWithdraw(_request);\n        bytes32 wdId = keccak256(\n            // len = 20 + 20 + 32 + 20 + 8 + 32 + 20 = 152\n            abi.encodePacked(\n                request.receiver,\n                request.token,\n                request.amount,\n                request.burnAccount,\n                request.refChainId,\n                request.refId,\n                address(this)\n            )\n        );\n        require(records[wdId] == false, \"record exists\");\n        records[wdId] = true;\n        _updateVolume(request.token, request.amount);\n        uint256 delayThreshold = delayThresholds[request.token];\n        if (delayThreshold > 0 && request.amount > delayThreshold) {\n            _addDelayedTransfer(wdId, request.receiver, request.token, request.amount);\n        } else {\n            _sendToken(request.receiver, request.token, request.amount);\n        }\n        emit Withdrawn(\n            wdId,\n            request.receiver,\n            request.token,\n            request.amount,\n            request.refChainId,\n            request.refId,\n            request.burnAccount\n        );\n        return wdId;\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        _sendToken(transfer.receiver, transfer.token, transfer.amount);\n    }\n\n    function setMinDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minDeposit[_tokens[i]] = _amounts[i];\n            emit MinDepositUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxDeposit[_tokens[i]] = _amounts[i];\n            emit MaxDepositUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setWrap(address _weth) external onlyOwner {\n        nativeWrap = _weth;\n    }\n\n    function _sendToken(\n        address _receiver,\n        address _token,\n        uint256 _amount\n    ) private {\n        if (_token == nativeWrap) {\n            // withdraw then transfer native to receiver\n            IWETH(nativeWrap).withdraw(_amount);\n            (bool sent, ) = _receiver.call{value: _amount, gas: nativeTokenTransferGas}(\"\");\n            require(sent, \"failed to send native token\");\n        } else {\n            IERC20(_token).safeTransfer(_receiver, _amount);\n        }\n    }\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external onlyGovernor {\n        nativeTokenTransferGas = _gasUsed;\n    }\n\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/PeggedBrc20Bridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/IPeggedToken.sol\";\nimport \"../interfaces/IPeggedTokenBurnFrom.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\n\n/**\n * @title The bridge contract to mint and burn pegged brc20 tokens\n */\ncontract PeggedBrc20Bridge is Pauser, VolumeControl, DelayedTransfer {\n    address public minter;\n\n    mapping(bytes32 => bool) public records;\n    mapping(address => uint256) public supplies;\n\n    mapping(address => uint256) public minBurn;\n    mapping(address => uint256) public maxBurn;\n\n    event Mint(\n        bytes32 mintId,\n        address token,\n        address account,\n        uint256 amount,\n        // ref_chain_id defines the reference chain ID, taking values of:\n        // 1. The common case: the chain ID on which the remote corresponding deposit or burn happened;\n        // 2. Refund for wrong burn: this chain ID on which the burn happened\n        uint64 refChainId,\n        // ref_id defines a unique reference ID, taking values of:\n        // 1. The common case of deposit/burn-mint: the deposit or burn ID on the remote chain;\n        // 2. Refund for wrong burn: the burn ID on this chain\n        bytes32 refId,\n        bytes depositor\n    );\n    event Burn(\n        bytes32 burnId,\n        address token,\n        address account,\n        uint256 amount,\n        uint64 toChainId,\n        bytes toAccount,\n        uint64 nonce\n    );\n    event MinBurnUpdated(address token, uint256 amount);\n    event MaxBurnUpdated(address token, uint256 amount);\n    event SupplyUpdated(address token, uint256 supply);\n    event MinterUpdated(address origMinter, address newMinter);\n\n    constructor(address _minter) {\n        minter = _minter;\n    }\n\n    /**\n     * @notice Mint tokens triggered by deposit on BTC chain\n     * @param _receiver The receiver address.\n     * @param _token The pegged token.\n     * @param _amount The amount.\n     * @param _depositor The depositor BTC address.\n     * @param _refChainId The BTC chain ID.\n     * @param _refId Reserved reference ID.\n     */\n    function mint(\n        address _receiver,\n        address _token,\n        uint256 _amount,\n        bytes calldata _depositor,\n        uint64 _refChainId,\n        bytes32 _refId\n    ) external whenNotPaused returns (bytes32) {\n        require(msg.sender == minter, \"not minter\");\n\n        bytes32 mintId = keccak256(\n            abi.encodePacked(_receiver, _token, _amount, _depositor, _refChainId, _refId, address(this))\n        );\n        require(records[mintId] == false, \"record exists\");\n        records[mintId] = true;\n        _updateVolume(_token, _amount);\n        uint256 delayThreshold = delayThresholds[_token];\n        if (delayThreshold > 0 && _amount > delayThreshold) {\n            _addDelayedTransfer(mintId, _receiver, _token, _amount);\n        } else {\n            IPeggedToken(_token).mint(_receiver, _amount);\n        }\n        supplies[_token] += _amount;\n        emit Mint(mintId, _token, _receiver, _amount, _refChainId, _refId, _depositor);\n        return mintId;\n    }\n\n    /**\n     * @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens on the BTC chain.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The pegged token address.\n     * @param _amount The amount to burn.\n     * @param _toChainId If zero, withdraw from BTC chain; otherwise, the remote chain to mint tokens.\n     * @param _toAccount The account to receive tokens on the remote chain\n     * @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function burn(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        bytes calldata _toAccount,\n        uint64 _nonce\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 burnId = _burn(_token, _amount, _toChainId, _toAccount, _nonce);\n        IPeggedToken(_token).burn(msg.sender, _amount);\n        return burnId;\n    }\n\n    // same with `burn` above, use openzeppelin ERC20Burnable interface\n    function burnFrom(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        bytes calldata _toAccount,\n        uint64 _nonce\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 burnId = _burn(_token, _amount, _toChainId, _toAccount, _nonce);\n        IPeggedTokenBurnFrom(_token).burnFrom(msg.sender, _amount);\n        return burnId;\n    }\n\n    function _burn(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        bytes calldata _toAccount,\n        uint64 _nonce\n    ) internal returns (bytes32) {\n        require(_amount > minBurn[_token], \"amount too small\");\n        require(maxBurn[_token] == 0 || _amount <= maxBurn[_token], \"amount too large\");\n        supplies[_token] -= _amount;\n        bytes32 burnId = keccak256(\n            abi.encodePacked(\n                msg.sender,\n                _token,\n                _amount,\n                _toChainId,\n                _toAccount,\n                _nonce,\n                uint64(block.chainid),\n                address(this)\n            )\n        );\n        require(records[burnId] == false, \"record exists\");\n        records[burnId] = true;\n        emit Burn(burnId, _token, msg.sender, _amount, _toChainId, _toAccount, _nonce);\n        return burnId;\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        IPeggedToken(transfer.token).mint(transfer.receiver, transfer.amount);\n    }\n\n    function setMinBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minBurn[_tokens[i]] = _amounts[i];\n            emit MinBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxBurn[_tokens[i]] = _amounts[i];\n            emit MaxBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setSupply(address _token, uint256 _supply) external onlyOwner {\n        supplies[_token] = _supply;\n        emit SupplyUpdated(_token, _supply);\n    }\n\n    function increaseSupply(address _token, uint256 _delta) external onlyOwner {\n        supplies[_token] += _delta;\n        emit SupplyUpdated(_token, supplies[_token]);\n    }\n\n    function decreaseSupply(address _token, uint256 _delta) external onlyOwner {\n        supplies[_token] -= _delta;\n        emit SupplyUpdated(_token, supplies[_token]);\n    }\n\n    function setMinter(address _minter) external onlyOwner {\n        address origMinter = minter;\n        minter = _minter;\n        emit MinterUpdated(origMinter, _minter);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/PeggedTokenBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../interfaces/IPeggedToken.sol\";\nimport \"../libraries/PbPegged.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\n\n/**\n * @title The bridge contract to mint and burn pegged tokens\n * @dev Work together with OriginalTokenVault deployed at remote chains.\n */\ncontract PeggedTokenBridge is Pauser, VolumeControl, DelayedTransfer {\n    ISigsVerifier public immutable sigsVerifier;\n\n    mapping(bytes32 => bool) public records;\n\n    mapping(address => uint256) public minBurn;\n    mapping(address => uint256) public maxBurn;\n\n    event Mint(\n        bytes32 mintId,\n        address token,\n        address account,\n        uint256 amount,\n        // ref_chain_id defines the reference chain ID, taking values of:\n        // 1. The common case: the chain ID on which the remote corresponding deposit or burn happened;\n        // 2. Refund for wrong burn: this chain ID on which the burn happened\n        uint64 refChainId,\n        // ref_id defines a unique reference ID, taking values of:\n        // 1. The common case of deposit/burn-mint: the deposit or burn ID on the remote chain;\n        // 2. Refund for wrong burn: the burn ID on this chain\n        bytes32 refId,\n        address depositor\n    );\n    event Burn(bytes32 burnId, address token, address account, uint256 amount, address withdrawAccount);\n    event MinBurnUpdated(address token, uint256 amount);\n    event MaxBurnUpdated(address token, uint256 amount);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\n     * @param _request The serialized Mint protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function mint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Mint\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _request), _sigs, _signers, _powers);\n        PbPegged.Mint memory request = PbPegged.decMint(_request);\n        bytes32 mintId = keccak256(\n            // len = 20 + 20 + 32 + 20 + 8 + 32 = 132\n            abi.encodePacked(\n                request.account,\n                request.token,\n                request.amount,\n                request.depositor,\n                request.refChainId,\n                request.refId\n            )\n        );\n        require(records[mintId] == false, \"record exists\");\n        records[mintId] = true;\n        _updateVolume(request.token, request.amount);\n        uint256 delayThreshold = delayThresholds[request.token];\n        if (delayThreshold > 0 && request.amount > delayThreshold) {\n            _addDelayedTransfer(mintId, request.account, request.token, request.amount);\n        } else {\n            IPeggedToken(request.token).mint(request.account, request.amount);\n        }\n        emit Mint(\n            mintId,\n            request.token,\n            request.account,\n            request.amount,\n            request.refChainId,\n            request.refId,\n            request.depositor\n        );\n    }\n\n    /**\n     * @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens at a remote chain's\n     * OriginalTokenVault.\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The pegged token address.\n     * @param _amount The amount to burn.\n     * @param _withdrawAccount The account to receive the original tokens withdrawn on the remote chain.\n     * @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function burn(\n        address _token,\n        uint256 _amount,\n        address _withdrawAccount,\n        uint64 _nonce\n    ) external whenNotPaused {\n        require(_amount > minBurn[_token], \"amount too small\");\n        require(maxBurn[_token] == 0 || _amount <= maxBurn[_token], \"amount too large\");\n        bytes32 burnId = keccak256(\n            // len = 20 + 20 + 32 + 20 + 8 + 8 = 108\n            abi.encodePacked(msg.sender, _token, _amount, _withdrawAccount, _nonce, uint64(block.chainid))\n        );\n        require(records[burnId] == false, \"record exists\");\n        records[burnId] = true;\n        IPeggedToken(_token).burn(msg.sender, _amount);\n        emit Burn(burnId, _token, msg.sender, _amount, _withdrawAccount);\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        IPeggedToken(transfer.token).mint(transfer.receiver, transfer.amount);\n    }\n\n    function setMinBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minBurn[_tokens[i]] = _amounts[i];\n            emit MinBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxBurn[_tokens[i]] = _amounts[i];\n            emit MaxBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/PeggedTokenBridgeV2.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../interfaces/IPeggedToken.sol\";\nimport \"../interfaces/IPeggedTokenBurnFrom.sol\";\nimport \"../libraries/PbPegged.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/VolumeControl.sol\";\nimport \"../safeguard/DelayedTransfer.sol\";\n\n/**\n * @title The bridge contract to mint and burn pegged tokens\n * @dev Work together with OriginalTokenVault deployed at remote chains.\n */\ncontract PeggedTokenBridgeV2 is Pauser, VolumeControl, DelayedTransfer {\n    ISigsVerifier public immutable sigsVerifier;\n\n    mapping(bytes32 => bool) public records;\n    mapping(address => uint256) public supplies;\n\n    mapping(address => uint256) public minBurn;\n    mapping(address => uint256) public maxBurn;\n\n    event Mint(\n        bytes32 mintId,\n        address token,\n        address account,\n        uint256 amount,\n        // ref_chain_id defines the reference chain ID, taking values of:\n        // 1. The common case: the chain ID on which the remote corresponding deposit or burn happened;\n        // 2. Refund for wrong burn: this chain ID on which the burn happened\n        uint64 refChainId,\n        // ref_id defines a unique reference ID, taking values of:\n        // 1. The common case of deposit/burn-mint: the deposit or burn ID on the remote chain;\n        // 2. Refund for wrong burn: the burn ID on this chain\n        bytes32 refId,\n        address depositor\n    );\n    event Burn(\n        bytes32 burnId,\n        address token,\n        address account,\n        uint256 amount,\n        uint64 toChainId,\n        address toAccount,\n        uint64 nonce\n    );\n    event MinBurnUpdated(address token, uint256 amount);\n    event MaxBurnUpdated(address token, uint256 amount);\n    event SupplyUpdated(address token, uint256 supply);\n\n    constructor(ISigsVerifier _sigsVerifier) {\n        sigsVerifier = _sigsVerifier;\n    }\n\n    /**\n     * @notice Mint tokens triggered by deposit at a remote chain's OriginalTokenVault.\n     * @param _request The serialized Mint protobuf.\n     * @param _sigs The list of signatures sorted by signing addresses in ascending order. A relay must be signed-off by\n     * +2/3 of the sigsVerifier's current signing power to be delivered.\n     * @param _signers The sorted list of signers.\n     * @param _powers The signing powers of the signers.\n     */\n    function mint(\n        bytes calldata _request,\n        bytes[] calldata _sigs,\n        address[] calldata _signers,\n        uint256[] calldata _powers\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Mint\"));\n        sigsVerifier.verifySigs(abi.encodePacked(domain, _request), _sigs, _signers, _powers);\n        PbPegged.Mint memory request = PbPegged.decMint(_request);\n        bytes32 mintId = keccak256(\n            // len = 20 + 20 + 32 + 20 + 8 + 32 + 20 = 152\n            abi.encodePacked(\n                request.account,\n                request.token,\n                request.amount,\n                request.depositor,\n                request.refChainId,\n                request.refId,\n                address(this)\n            )\n        );\n        require(records[mintId] == false, \"record exists\");\n        records[mintId] = true;\n        _updateVolume(request.token, request.amount);\n        uint256 delayThreshold = delayThresholds[request.token];\n        if (delayThreshold > 0 && request.amount > delayThreshold) {\n            _addDelayedTransfer(mintId, request.account, request.token, request.amount);\n        } else {\n            IPeggedToken(request.token).mint(request.account, request.amount);\n        }\n        supplies[request.token] += request.amount;\n        emit Mint(\n            mintId,\n            request.token,\n            request.account,\n            request.amount,\n            request.refChainId,\n            request.refId,\n            request.depositor\n        );\n        return mintId;\n    }\n\n    /**\n     * @notice Burn pegged tokens to trigger a cross-chain withdrawal of the original tokens at a remote chain's\n     * OriginalTokenVault, or mint at another remote chain\n     * NOTE: This function DOES NOT SUPPORT fee-on-transfer / rebasing tokens.\n     * @param _token The pegged token address.\n     * @param _amount The amount to burn.\n     * @param _toChainId If zero, withdraw from original vault; otherwise, the remote chain to mint tokens.\n     * @param _toAccount The account to receive tokens on the remote chain\n     * @param _nonce A number to guarantee unique depositId. Can be timestamp in practice.\n     */\n    function burn(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 burnId = _burn(_token, _amount, _toChainId, _toAccount, _nonce);\n        IPeggedToken(_token).burn(msg.sender, _amount);\n        return burnId;\n    }\n\n    // same with `burn` above, use openzeppelin ERC20Burnable interface\n    function burnFrom(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) external whenNotPaused returns (bytes32) {\n        bytes32 burnId = _burn(_token, _amount, _toChainId, _toAccount, _nonce);\n        IPeggedTokenBurnFrom(_token).burnFrom(msg.sender, _amount);\n        return burnId;\n    }\n\n    function _burn(\n        address _token,\n        uint256 _amount,\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) internal returns (bytes32) {\n        require(_amount > minBurn[_token], \"amount too small\");\n        require(maxBurn[_token] == 0 || _amount <= maxBurn[_token], \"amount too large\");\n        supplies[_token] -= _amount;\n        bytes32 burnId = keccak256(\n            // len = 20 + 20 + 32 + 8 + 20 + 8 + 8 + 20 = 136\n            abi.encodePacked(\n                msg.sender,\n                _token,\n                _amount,\n                _toChainId,\n                _toAccount,\n                _nonce,\n                uint64(block.chainid),\n                address(this)\n            )\n        );\n        require(records[burnId] == false, \"record exists\");\n        records[burnId] = true;\n        emit Burn(burnId, _token, msg.sender, _amount, _toChainId, _toAccount, _nonce);\n        return burnId;\n    }\n\n    function executeDelayedTransfer(bytes32 id) external whenNotPaused {\n        delayedTransfer memory transfer = _executeDelayedTransfer(id);\n        IPeggedToken(transfer.token).mint(transfer.receiver, transfer.amount);\n    }\n\n    function setMinBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            minBurn[_tokens[i]] = _amounts[i];\n            emit MinBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setMaxBurn(address[] calldata _tokens, uint256[] calldata _amounts) external onlyGovernor {\n        require(_tokens.length == _amounts.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            maxBurn[_tokens[i]] = _amounts[i];\n            emit MaxBurnUpdated(_tokens[i], _amounts[i]);\n        }\n    }\n\n    function setSupply(address _token, uint256 _supply) external onlyOwner {\n        supplies[_token] = _supply;\n        emit SupplyUpdated(_token, _supply);\n    }\n\n    function increaseSupply(address _token, uint256 _delta) external onlyOwner {\n        supplies[_token] += _delta;\n        emit SupplyUpdated(_token, supplies[_token]);\n    }\n\n    function decreaseSupply(address _token, uint256 _delta) external onlyOwner {\n        supplies[_token] -= _delta;\n        emit SupplyUpdated(_token, supplies[_token]);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/README.md",
    "content": "# Pegged Token Bridge\n\nGoal: Token T exists on chain A but not on chain B, and we would like to support a 1:1 pegged token T' on chain B.\n\nApproach: Deploy a PeggedToken ([example](./tokens/MultiBridgeToken.sol)) on chain B with zero initial supply, and config SGN (through gov) to mark it as 1:1 pegged to the chain A’s original token. Anyone can lock original token T on chain A’s OriginalTokenVault contract to trigger mint of pegged token T’ on chain B through the PeggedTokenBridge contract accordingly.\n\n## Basic workflows\n\n### Deposit original token on chain A and mint pegged token on chain B\n\n1. User calls [deposit](./OriginalTokenVault.sol#L72) on chain A to lock original tokens in chain A’s vault contract.\n2. SGN generates the [Mint proto msg](../libraries/proto/pegged.proto#L14) cosigned by validators, and call [mint](./PeggedTokenBridge.sol#L55) function on chain B.\n\n### Burn pegged token on chain B and withdraw original token on chain A\n\n1. User calls [burn](./PeggedTokenBridge.sol#L104) on chain B to burn the pegged token.\n2. SGN generates the [Withdraw proto msg](../libraries/proto/pegged.proto#L34) cosigned by validators, and call [withdraw](./OriginalTokenVault.sol#L131) function on chain A.\n\n### Burn pegged token on chain B (PeggedTokenBridgeV2) and mint pegged token on chain C\n\n1. User calls [burn](./PeggedTokenBridgeV2.sol#L116) on chain B to burn the pegged token, specifying chain C's chainId as `toChainId`.\n2. SGN generates the [Mint proto msg](../libraries/proto/pegged.proto#L14) cosigned by validators, and call [mint](./PeggedTokenBridge.sol#L55) function on chain C.\n\n## Safeguard monitoring\n\nAnyone can verify the correctness of the pegged bridge behavior by tracking the contract events ([Deposit](./OriginalTokenVault.sol#L31), [Withdrawn](./OriginalTokenVault.sol#L39), [Mint](./PeggedTokenBridge.sol#L24), [Burn](./PeggedTokenBridge.sol#L39)), and verifying the `refChainId` and `refId` fields of `Mint` and `Withdrawn` events according to the code comments ([example](./OriginalTokenVault.sol#L44-L53)).\n\nFor example, if we catch a `Withdrawn` event on chain A with `refChainId` of chain B and a `refId`, then we should be able to find a `Burn` event on chain B with `burnId` equals to `refId`, and then compare values of other fields of these two events.\n"
  },
  {
    "path": "contracts/pegged-bridge/customized/PeggedNativeTokenBridge.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../PeggedTokenBridgeV2.sol\";\n\ninterface INativeVault {\n    function burn() external payable;\n}\n\ncontract PeggedNativeTokenBridge is PeggedTokenBridgeV2 {\n    // native vault address is treated as the pegged natvie token address\n    address public nativeVault;\n\n    constructor(ISigsVerifier _sigsVerifier) PeggedTokenBridgeV2(_sigsVerifier) {}\n\n    function burnNative(\n        uint64 _toChainId,\n        address _toAccount,\n        uint64 _nonce\n    ) external payable whenNotPaused returns (bytes32) {\n        require(msg.value > 0, \"zero msg value\");\n        bytes32 burnId = _burn(nativeVault, msg.value, _toChainId, _toAccount, _nonce);\n        INativeVault(nativeVault).burn{value: msg.value}();\n        return burnId;\n    }\n\n    function setNativeVault(address _natvieVault) external onlyOwner {\n        nativeVault = _natvieVault;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/ERC20Permit/MintSwapCanonicalTokenPermit.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport \"../MintSwapCanonicalToken.sol\";\n\n/**\n * @title MintSwapCanonicalToke with ERC20Permit\n */\ncontract MintSwapCanonicalTokenPermit is ERC20Permit, MintSwapCanonicalToken {\n    uint8 private immutable _decimals;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MintSwapCanonicalToken(name_, symbol_, decimals_) ERC20Permit(name_) {\n        _decimals = decimals_;\n    }\n\n    function decimals() public view override(ERC20, MultiBridgeToken) returns (uint8) {\n        return _decimals;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/ERC20Permit/MultiBridgeTokenPermit.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport \"../MultiBridgeToken.sol\";\n\n/**\n * @title Example Multi-Bridge Pegged ERC20Permit token\n */\ncontract MultiBridgeTokenPermit is ERC20Permit, MultiBridgeToken {\n    uint8 private immutable _decimals;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MultiBridgeToken(name_, symbol_, decimals_) ERC20Permit(name_) {\n        _decimals = decimals_;\n    }\n\n    function decimals() public view override(ERC20, MultiBridgeToken) returns (uint8) {\n        return _decimals;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/ERC20Permit/SingleBridgeTokenPermit.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol\";\nimport \"../SingleBridgeToken.sol\";\n\n/**\n * @title Example Pegged ERC20Permit token\n */\ncontract SingleBridgeTokenPermit is ERC20Permit, SingleBridgeToken {\n    uint8 private immutable _decimals;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_,\n        address bridge_\n    ) SingleBridgeToken(name_, symbol_, decimals_, bridge_) ERC20Permit(name_) {\n        _decimals = decimals_;\n    }\n\n    function decimals() public view override(ERC20, SingleBridgeToken) returns (uint8) {\n        return _decimals;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/IntermediaryBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ninterface IERC20MintableBurnable is IERC20 {\n    function mint(address receiver, uint256 amount) external;\n\n    function burn(uint256 amount) external;\n}\n\n/**\n * @title Per bridge intermediary token that delegates to a canonical token.\n * Useful for canonical tokens that don't support the burn / burnFrom function signature required by\n * PeggedTokenBridge.\n */\ncontract IntermediaryBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    address public bridge;\n    address public immutable canonical; // canonical token that support swap\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        canonical = canonical_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount); // totalSupply == bridge liquidity\n        IERC20MintableBurnable(canonical).mint(_to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        _burn(address(this), _amount);\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        IERC20MintableBurnable(canonical).burn(_amount);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/IntermediaryOriginalToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/**\n * @title Intermediary token that automatically transfers the canonical token when interacting with approved bridges.\n */\ncontract IntermediaryOriginalToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    mapping(address => bool) public bridges;\n    address public immutable canonical; // canonical token\n\n    event BridgeUpdated(address bridge, bool enable);\n\n    modifier onlyBridge() {\n        require(bridges[msg.sender], \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address[] memory bridges_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        for (uint256 i = 0; i < bridges_.length; i++) {\n            bridges[bridges_[i]] = true;\n        }\n        canonical = canonical_;\n    }\n\n    function transfer(address _to, uint256 _amount) public virtual override onlyBridge returns (bool) {\n        _burn(msg.sender, _amount);\n        IERC20(canonical).safeTransfer(_to, _amount);\n        return true;\n    }\n\n    function transferFrom(\n        address _from,\n        address _to,\n        uint256 _amount\n    ) public virtual override onlyBridge returns (bool) {\n        _mint(_to, _amount);\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        return true;\n    }\n\n    function updateBridge(address _bridge, bool _enable) external onlyOwner {\n        bridges[_bridge] = _enable;\n        emit BridgeUpdated(_bridge, _enable);\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/MintSwapCanonicalToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./MultiBridgeToken.sol\";\n\n/**\n * @title Canonical token that supports multi-bridge minter and multi-token swap\n */\ncontract MintSwapCanonicalToken is MultiBridgeToken {\n    using SafeERC20 for IERC20;\n\n    // bridge token address -> minted amount and cap for each bridge\n    mapping(address => Supply) public swapSupplies;\n\n    event TokenSwapCapUpdated(address token, uint256 cap);\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MultiBridgeToken(name_, symbol_, decimals_) {}\n\n    /**\n     * @notice msg.sender has bridge token and wants to get canonical token.\n     * @param _bridgeToken The intermediary token address for a particular bridge.\n     * @param _amount The amount.\n     */\n    function swapBridgeForCanonical(address _bridgeToken, uint256 _amount) external returns (uint256) {\n        Supply storage supply = swapSupplies[_bridgeToken];\n        require(supply.cap > 0, \"invalid bridge token\");\n        require(supply.total + _amount <= supply.cap, \"exceed swap cap\");\n\n        supply.total += _amount;\n        _mint(msg.sender, _amount);\n\n        // move bridge token from msg.sender to canonical token _amount\n        IERC20(_bridgeToken).safeTransferFrom(msg.sender, address(this), _amount);\n        return _amount;\n    }\n\n    /**\n     * @notice msg.sender has canonical token and wants to get bridge token (eg. for cross chain burn).\n     * @param _bridgeToken The intermediary token address for a particular bridge.\n     * @param _amount The amount.\n     */\n    function swapCanonicalForBridge(address _bridgeToken, uint256 _amount) external returns (uint256) {\n        Supply storage supply = swapSupplies[_bridgeToken];\n        require(supply.cap > 0, \"invalid bridge token\");\n\n        supply.total -= _amount;\n        _burn(msg.sender, _amount);\n\n        IERC20(_bridgeToken).safeTransfer(msg.sender, _amount);\n        return _amount;\n    }\n\n    /**\n     * @dev Update existing bridge token swap cap or add a new bridge token with swap cap.\n     * Setting cap to 0 will disable the bridge token.\n     * @param _bridgeToken The intermediary token address for a particular bridge.\n     * @param _swapCap The new swap cap.\n     */\n    function setBridgeTokenSwapCap(address _bridgeToken, uint256 _swapCap) external onlyOwner {\n        swapSupplies[_bridgeToken].cap = _swapCap;\n        emit TokenSwapCapUpdated(_bridgeToken, _swapCap);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/MintSwapCanonicalTokenUpgradable.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"./MintSwapCanonicalToken.sol\";\n\n/**\n * @title Upgradable canonical token that supports multi-bridge minter and multi-token swap\n */\n\n// First deploy this contract, constructor will set name, symbol and owner in contract state, but these are NOT used.\n// decimal isn't saved in state because it's immutable in MultiBridgeToken and will be set in the code binary.\n// Then deploy proxy contract with this contract as impl, proxy constructor will delegatecall this.init which sets name, symbol and owner in proxy contract state.\n// why we need to shadow name and symbol: ERC20 only allows set them in constructor which isn't available after deploy so proxy state can't be updated.\ncontract MintSwapCanonicalTokenUpgradable is MintSwapCanonicalToken {\n    string private _name;\n    string private _symbol;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MintSwapCanonicalToken(name_, symbol_, decimals_) {}\n\n    // only to be called by Proxy via delegatecall and will modify Proxy state\n    // this func has no access control because initOwner only allows delegateCall\n    function init(string memory name_, string memory symbol_) external {\n        initOwner(); // this will fail if Ownable._owner is already set\n        _name = name_;\n        _symbol = symbol_;\n    }\n\n    // override name, symbol and owner getters\n    function name() public view virtual override returns (string memory) {\n        return _name;\n    }\n\n    function symbol() public view virtual override returns (string memory) {\n        return _symbol;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/MultiBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"../../safeguard/Ownable.sol\";\n\n/**\n * @title Example Multi-Bridge Pegged ERC20 token\n */\ncontract MultiBridgeToken is ERC20, Ownable {\n    struct Supply {\n        uint256 cap;\n        uint256 total;\n    }\n    mapping(address => Supply) public bridges; // bridge address -> supply\n\n    uint8 private immutable _decimals;\n\n    event BridgeSupplyCapUpdated(address bridge, uint256 supplyCap);\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) ERC20(name_, symbol_) {\n        _decimals = decimals_;\n    }\n\n    /**\n     * @notice Mints tokens to an address. Increases total amount minted by the calling bridge.\n     * @param _to The address to mint tokens to.\n     * @param _amount The amount to mint.\n     */\n    function mint(address _to, uint256 _amount) external returns (bool) {\n        Supply storage b = bridges[msg.sender];\n        require(b.cap > 0, \"invalid caller\");\n        b.total += _amount;\n        require(b.total <= b.cap, \"exceeds bridge supply cap\");\n        _mint(_to, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Burns tokens for msg.sender.\n     * @param _amount The amount to burn.\n     */\n    function burn(uint256 _amount) external returns (bool) {\n        _burn(msg.sender, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Burns tokens from an address. Decreases total amount minted if called by a bridge.\n     * Alternative to {burnFrom} for compatibility with some bridge implementations.\n     * See {_burnFrom}.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function burn(address _from, uint256 _amount) external returns (bool) {\n        return _burnFrom(_from, _amount);\n    }\n\n    /**\n     * @notice Burns tokens from an address. Decreases total amount minted if called by a bridge.\n     * See {_burnFrom}.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function burnFrom(address _from, uint256 _amount) external returns (bool) {\n        return _burnFrom(_from, _amount);\n    }\n\n    /**\n     * @dev Burns tokens from an address, deducting from the caller's allowance.\n     *      Decreases total amount minted if called by a bridge.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function _burnFrom(address _from, uint256 _amount) internal returns (bool) {\n        Supply storage b = bridges[msg.sender];\n        if (b.cap > 0 || b.total > 0) {\n            // set cap to 1 would effectively disable a deprecated bridge's ability to burn\n            require(b.total >= _amount, \"exceeds bridge minted amount\");\n            unchecked {\n                b.total -= _amount;\n            }\n        }\n        _spendAllowance(_from, msg.sender, _amount);\n        _burn(_from, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Returns the decimals of the token.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return _decimals;\n    }\n\n    /**\n     * @notice Updates the supply cap for a bridge.\n     * @param _bridge The bridge address.\n     * @param _cap The new supply cap.\n     */\n    function updateBridgeSupplyCap(address _bridge, uint256 _cap) external onlyOwner {\n        // cap == 0 means revoking bridge role\n        bridges[_bridge].cap = _cap;\n        emit BridgeSupplyCapUpdated(_bridge, _cap);\n    }\n\n    /**\n     * @notice Returns the owner address. Required by BEP20.\n     */\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/SingleBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n/**\n * @title Example Pegged ERC20 token\n */\ncontract SingleBridgeToken is ERC20, Ownable {\n    address public bridge;\n\n    uint8 private immutable _decimals;\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_,\n        address bridge_\n    ) ERC20(name_, symbol_) {\n        _decimals = decimals_;\n        bridge = bridge_;\n    }\n\n    /**\n     * @notice Mints tokens to an address.\n     * @param _to The address to mint tokens to.\n     * @param _amount The amount to mint.\n     */\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(_to, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Burns tokens for msg.sender.\n     * @param _amount The amount to burn.\n     */\n    function burn(uint256 _amount) external returns (bool) {\n        _burn(msg.sender, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Burns tokens from an address.\n     * Alternative to {burnFrom} for compatibility with some bridge implementations.\n     * See {_burnFrom}.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function burn(address _from, uint256 _amount) external returns (bool) {\n        return _burnFrom(_from, _amount);\n    }\n\n    /**\n     * @notice Burns tokens from an address.\n     * See {_burnFrom}.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function burnFrom(address _from, uint256 _amount) external returns (bool) {\n        return _burnFrom(_from, _amount);\n    }\n\n    /**\n     * @dev Burns tokens from an address, deducting from the caller's allowance.\n     * @param _from The address to burn tokens from.\n     * @param _amount The amount to burn.\n     */\n    function _burnFrom(address _from, uint256 _amount) internal returns (bool) {\n        _spendAllowance(_from, msg.sender, _amount);\n        _burn(_from, _amount);\n        return true;\n    }\n\n    /**\n     * @notice Returns the decimals of the token.\n     */\n    function decimals() public view virtual override returns (uint8) {\n        return _decimals;\n    }\n\n    /**\n     * @notice Updates the bridge address.\n     * @param _bridge The bridge address.\n     */\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    /**\n     * @notice Returns the owner address. Required by BEP20.\n     */\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/SwapBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ninterface ISwapCanoToken {\n    function swapBridgeForCanonical(address, uint256) external returns (uint256);\n\n    function swapCanonicalForBridge(address, uint256) external returns (uint256);\n}\n\n/**\n * @title Per bridge intermediary token that supports swapping with a canonical token.\n */\ncontract SwapBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    address public bridge;\n    address public immutable canonical; // canonical token that support swap\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        canonical = canonical_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount); // add amount to myself so swapBridgeForCanonical can transfer amount\n        uint256 got = ISwapCanoToken(canonical).swapBridgeForCanonical(address(this), _amount);\n        // now this has canonical token, next step is to transfer to user\n        IERC20(canonical).safeTransfer(_to, got);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        uint256 got = ISwapCanoToken(canonical).swapCanonicalForBridge(address(this), _amount);\n        _burn(address(this), got);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    // approve canonical token so swapBridgeForCanonical can work. or we approve before call it in mint w/ added gas\n    function approveCanonical() external onlyOwner {\n        _approve(address(this), canonical, type(uint256).max);\n    }\n\n    function revokeCanonical() external onlyOwner {\n        _approve(address(this), canonical, 0);\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/WrappedBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\n// Use pegged model to support no-slippage liquidity pool\ncontract WrappedBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    // The PeggedTokenBridge\n    address public bridge;\n    // The canonical\n    address public immutable canonical;\n\n    mapping(address => uint256) public liquidity;\n\n    event BridgeUpdated(address bridge);\n    event LiquidityAdded(address provider, uint256 amount);\n    event LiquidityRemoved(address provider, uint256 amount);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        canonical = canonical_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount);\n        IERC20(canonical).safeTransfer(_to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        _burn(address(this), _amount);\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        return true;\n    }\n\n    function addLiquidity(uint256 _amount) external {\n        liquidity[msg.sender] += _amount;\n        IERC20(canonical).safeTransferFrom(msg.sender, address(this), _amount);\n        emit LiquidityAdded(msg.sender, _amount);\n    }\n\n    function removeLiquidity(uint256 _amount) external {\n        liquidity[msg.sender] -= _amount;\n        IERC20(canonical).safeTransfer(msg.sender, _amount);\n        emit LiquidityRemoved(msg.sender, _amount);\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/CircleBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\nimport \"../../../libraries/BridgeTransferLib.sol\";\n\n// Interface for the canonical Circle token\ninterface IFiatToken is IERC20 {\n    function mint(address to, uint256 amount) external;\n\n    // Burns OWN tokens\n    function burn(uint256 amount) external;\n}\n\n/**\n * @title Intermediary token that delegates to a canonical Circle token. With the functionality to\n * migrate minting / burning permissions to Circle in the future.\n */\ncontract CircleBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    address public bridge;\n    address public immutable canonical; // canonical Circle token\n\n    bool public migrated;\n    uint64 public immutable origChainId;\n    address public origChainWithdrawAddress;\n\n    event BridgeUpdated(address bridge);\n    event OrigChainWithdrawAddressUpdated(address origChainWithdrawAddress);\n    event Migrated(bytes32 transferId, address origChainWithdrawAddress, uint256 totalSupply);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address canonical_,\n        uint64 origChainId_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        canonical = canonical_;\n        origChainId = origChainId_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        require(!migrated, \"already migrated\");\n\n        _mint(address(this), _amount); // totalSupply == bridge liquidity\n        IFiatToken(canonical).mint(_to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        _burn(address(this), _amount);\n\n        if (!migrated) {\n            IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n            IFiatToken(canonical).burn(_amount);\n        }\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n\n    function setOrigChainWithdrawAddress(address _origChainWithdrawAddress) external onlyOwner {\n        origChainWithdrawAddress = _origChainWithdrawAddress;\n        emit OrigChainWithdrawAddressUpdated(origChainWithdrawAddress);\n    }\n\n    function migrate() external onlyOwner {\n        require(!migrated, \"already migrated\");\n\n        migrated = true;\n\n        uint256 supply = totalSupply();\n        bytes32 transferId = BridgeTransferLib.sendTransfer(\n            origChainWithdrawAddress,\n            address(this),\n            supply,\n            origChainId,\n            uint64(block.timestamp),\n            0, // _maxSlippage\n            BridgeTransferLib.BridgeSendType.PegV2Burn,\n            bridge\n        );\n        emit Migrated(transferId, origChainWithdrawAddress, supply);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/FraxBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ninterface IFraxCanoToken {\n    function exchangeOldForCanonical(address, uint256) external returns (uint256);\n\n    function exchangeCanonicalForOld(address, uint256) external returns (uint256);\n}\n\n/**\n * @title Intermediary bridge token that supports swapping with the canonical Frax token.\n */\ncontract FraxBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    // The PeggedTokenBridge\n    address public bridge;\n    // The canonical Frax token that supports swapping\n    address public immutable canonical;\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        canonical = canonical_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount); // add amount to myself so exchangeOldForCanonical can transfer amount\n        _approve(address(this), canonical, _amount);\n        uint256 got = IFraxCanoToken(canonical).exchangeOldForCanonical(address(this), _amount);\n        // now this has canonical token, next step is to transfer to user\n        IERC20(canonical).safeTransfer(_to, got);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        uint256 got = IFraxCanoToken(canonical).exchangeCanonicalForOld(address(this), _amount);\n        _burn(address(this), got);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/MaiBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ninterface IMaiBridgeHub {\n    // send bridge token, get asset\n    function swapIn(address, uint256) external;\n\n    // send asset, get bridge token back\n    function swapOut(address, uint256) external;\n\n    // asset address\n    function asset() external view returns (address);\n}\n\n/**\n * @title Intermediary bridge token that supports swapping with the Mai hub.\n * NOTE: Mai hub is NOT the canonical token itself. The asset is set in the hub constructor.\n */\ncontract MaiBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    // The PeggedTokenBridge\n    address public bridge;\n    // Mai hub for swapping\n    address public immutable maihub;\n    // The canonical Mai token\n    address public immutable asset;\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address maihub_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        maihub = maihub_;\n        asset = IMaiBridgeHub(maihub_).asset();\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount); // add amount to myself so swapIn can transfer amount to hub\n        _approve(address(this), maihub, _amount);\n        IMaiBridgeHub(maihub).swapIn(address(this), _amount);\n        // now this has canonical token, next step is to transfer to user\n        IERC20(asset).safeTransfer(_to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        IERC20(asset).safeTransferFrom(_from, address(this), _amount);\n        IERC20(asset).safeIncreaseAllowance(address(maihub), _amount);\n        IMaiBridgeHub(maihub).swapOut(address(this), _amount);\n        _burn(address(this), _amount);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(asset).decimals();\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/OntologyBridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\ninterface IOntologyBridgeTokenWrapper {\n    function swapBridgeForCanonical(\n        address bridgeToken,\n        address _to,\n        uint256 _amount\n    ) external returns (uint256);\n\n    function swapCanonicalForBridge(\n        address bridgeToken,\n        address _to,\n        uint256 _amount\n    ) external payable returns (uint256);\n}\n\n/**\n * @title Intermediary bridge token that supports swapping with the Ontology bridge token wrapper.\n * NOTE: The bridge wrapper is NOT the canonical token itself.\n */\ncontract OntologyBridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    // The PeggedTokenBridge\n    address public bridge;\n    // Bridge token wrapper for swapping\n    address public immutable wrapper;\n    // The canonical token\n    address public immutable canonical;\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address bridge_,\n        address wrapper_,\n        address canonical_\n    ) ERC20(name_, symbol_) {\n        bridge = bridge_;\n        wrapper = wrapper_;\n        canonical = canonical_;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount);\n        _approve(address(this), wrapper, _amount);\n        // NOTE: swapBridgeForCanonical automatically transfers canonical token to _to.\n        IOntologyBridgeTokenWrapper(wrapper).swapBridgeForCanonical(address(this), _to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        IERC20(canonical).safeTransferFrom(_from, address(this), _amount);\n        IERC20(canonical).safeIncreaseAllowance(address(wrapper), _amount);\n        // NOTE: swapCanonicalForBridge automatically transfers bridge token to _from.\n        uint256 got = IOntologyBridgeTokenWrapper(wrapper).swapCanonicalForBridge(address(this), _from, _amount);\n        _burn(_from, got);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonical).decimals();\n    }\n\n    // to make compatible with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/xc20/XC20BridgeHub.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./interfaces/IXC20BridgeHub.sol\";\n\n/**\n * @title A hub for managing swapping between canonical XC20 tokens and intermediary bridge tokens.\n */\ncontract XC20BridgeHub is Ownable, IXC20BridgeHub, Pausable {\n    using SafeERC20 for IERC20;\n\n    struct TokenPair {\n        address bridgeToken;\n        address canonicalToken;\n        bool paused;\n        uint256 limit; // Max amount of bridge token allowed in the hub.\n    }\n    address[] public bridgeTokens;\n    // bridge token address => TokenPair\n    mapping(address => TokenPair) public tokenPairMap;\n\n    event TokenPairAdded(address indexed bridgeToken, address indexed canonicalToken, uint256 limit);\n    event TokenPairRemoved(address indexed bridgeToken, address indexed canonicalToken);\n    event TokenPairPaused(address indexed bridgeToken, address indexed canonicalToken);\n    event TokenPairUnpaused(address indexed bridgeToken, address indexed canonicalToken);\n    event TokenPairLimitSet(address indexed bridgeToken, address indexed canonicalToken, uint256 limit);\n    event BridgeSwappedForCanonical(\n        address indexed bridgeToken,\n        address indexed canonicalToken,\n        uint256 bridgeTokenAmount,\n        uint256 refund,\n        uint256 canonicalTokenAmount\n    );\n    event CanonicalSwappedForBridge(\n        address indexed bridgeToken,\n        address indexed canonicalToken,\n        uint256 canonicalTokenAmount,\n        uint256 refund,\n        uint256 bridgeTokenAmount\n    );\n\n    /**\n     * @dev Pauses a token pair.\n     * @param _bridgeToken The bridge token of the pair.\n     */\n    function pauseTokenPair(address _bridgeToken) external onlyOwner {\n        TokenPair storage pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        require(!pair.paused, \"XC20BridgeHub: token pair already paused\");\n        pair.paused = true;\n        emit TokenPairPaused(_bridgeToken, pair.canonicalToken);\n    }\n\n    /**\n     * @dev Unpauses a token pair.\n     * @param _bridgeToken The bridge token of the pair.\n     */\n    function unpauseTokenPair(address _bridgeToken) external onlyOwner {\n        TokenPair storage pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        require(pair.paused, \"XC20BridgeHub: token pair already unpaused\");\n        pair.paused = false;\n        emit TokenPairUnpaused(_bridgeToken, pair.canonicalToken);\n    }\n\n    /**\n     * @dev Sets a token pair limit.\n     * @param _bridgeToken The bridge token of the pair.\n     * @param _limit The max amount of bridge token allowed in the hub.\n     */\n    function setTokenPairLimit(address _bridgeToken, uint256 _limit) external onlyOwner {\n        TokenPair storage pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        pair.limit = _limit;\n        emit TokenPairLimitSet(_bridgeToken, pair.canonicalToken, _limit);\n    }\n\n    /**\n     * @dev Adds a token pair.\n     * @param _bridgeToken The bridge token of the pair.\n     * @param _canonicalToken The canonical token of the pair.\n     * @param _limit The max amount of bridge token allowed in the hub.\n     */\n    function addTokenPair(\n        address _bridgeToken,\n        address _canonicalToken,\n        uint256 _limit\n    ) external onlyOwner {\n        require(_bridgeToken != address(0), \"XC20BridgeHub: bridge token is zero address\");\n        require(tokenPairMap[_bridgeToken].bridgeToken == address(0), \"XC20BridgeHub: bridge token exists\");\n        require(\n            IERC20Metadata(_bridgeToken).decimals() == IERC20Metadata(_canonicalToken).decimals(),\n            \"XC20BridgeHub: decimals mismatch\"\n        );\n\n        TokenPair memory pair = TokenPair(address(_bridgeToken), address(_canonicalToken), false, _limit);\n        bridgeTokens.push(_bridgeToken);\n        tokenPairMap[_bridgeToken] = pair;\n        emit TokenPairAdded(_bridgeToken, _canonicalToken, _limit);\n    }\n\n    /**\n     * @dev Removes a token pair.\n     * @param _bridgeToken The bridge token of the pair.\n     */\n    function removeTokenPair(address _bridgeToken) external onlyOwner {\n        TokenPair memory pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        delete tokenPairMap[_bridgeToken];\n        uint256 index = bridgeTokens.length;\n        for (uint256 i = 0; i < bridgeTokens.length; i++) {\n            if (bridgeTokens[i] == _bridgeToken) {\n                index = i;\n                break;\n            }\n        }\n        if (index < bridgeTokens.length) {\n            delete bridgeTokens[index];\n        }\n        emit TokenPairRemoved(_bridgeToken, pair.canonicalToken);\n    }\n\n    /**\n     * @dev Returns all token pairs.\n     */\n    function getAllTokenPairs() external view returns (TokenPair[] memory) {\n        TokenPair[] memory pairs = new TokenPair[](bridgeTokens.length);\n        for (uint256 i = 0; i < pairs.length; i++) {\n            pairs[i] = tokenPairMap[bridgeTokens[i]];\n        }\n        return pairs;\n    }\n\n    /**\n     * @dev Swaps intermediary bridge token for canonical XC-20 token.\n     * @param _bridgeToken The intermediary bridge token\n     * @param _amount The amount to swap\n     * @return The canonical token amount\n     */\n    function swapBridgeForCanonical(address _bridgeToken, uint256 _amount)\n        external\n        override\n        whenNotPaused\n        returns (uint256)\n    {\n        TokenPair memory pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        require(!pair.paused, \"XC20BridgeHub: token pair paused\");\n        IERC20 bridgeErc20 = IERC20(_bridgeToken);\n        require(\n            pair.limit > 0 && (bridgeErc20.balanceOf(address(this))) + _amount <= pair.limit,\n            \"XC20BridgeHub: exceeds bridge limit\"\n        );\n\n        address canonicalToken = pair.canonicalToken;\n        uint256 delta = transferIn(_bridgeToken, _amount);\n        (uint256 canonicalTokenAmount, uint256 refund) = calcTransferAmountWithDecimals(\n            delta,\n            IERC20Metadata(_bridgeToken).decimals(),\n            IERC20Metadata(canonicalToken).decimals()\n        );\n        if (refund > 0) {\n            IERC20(_bridgeToken).safeTransfer(msg.sender, refund);\n        }\n        if (canonicalTokenAmount > 0) {\n            IERC20(pair.canonicalToken).safeTransfer(msg.sender, _amount);\n        }\n        emit BridgeSwappedForCanonical(_bridgeToken, canonicalToken, _amount, refund, canonicalTokenAmount);\n        return canonicalTokenAmount;\n    }\n\n    /**\n     * @dev Swaps canonical XC-20 token for intermediary bridge token.\n     * @param _bridgeToken The intermediary bridge token\n     * @param _amount The amount to swap\n     * @return The bridge token amount\n     */\n    function swapCanonicalForBridge(address _bridgeToken, uint256 _amount)\n        external\n        override\n        whenNotPaused\n        returns (uint256)\n    {\n        TokenPair memory pair = tokenPairMap[_bridgeToken];\n        require(pair.bridgeToken != address(0), \"XC20BridgeHub: non-existent bridge token\");\n        require(!pair.paused, \"XC20BridgeHub: token pair paused\");\n\n        address canonicalToken = pair.canonicalToken;\n        uint256 delta = transferIn(canonicalToken, _amount);\n        (uint256 bridgeTokenAmount, uint256 refund) = calcTransferAmountWithDecimals(\n            delta,\n            IERC20Metadata(canonicalToken).decimals(),\n            IERC20Metadata(_bridgeToken).decimals()\n        );\n        if (refund > 0) {\n            IERC20(canonicalToken).safeTransfer(msg.sender, refund);\n        }\n        if (bridgeTokenAmount > 0) {\n            IERC20(_bridgeToken).safeTransfer(msg.sender, _amount);\n        }\n        emit CanonicalSwappedForBridge(_bridgeToken, canonicalToken, _amount, refund, bridgeTokenAmount);\n        return bridgeTokenAmount;\n    }\n\n    /**\n     * @dev Sets the paused status of the hub.\n     * @param _paused Whether the hub should be paused\n     */\n    function setPaused(bool _paused) external onlyOwner {\n        if (_paused) {\n            _pause();\n        } else {\n            _unpause();\n        }\n    }\n\n    /**\n     * @dev Transfers the amount of tokens into the hub.\n     * @param _token The token address\n     * @param _amount The transfer amount\n     * @return The balance change\n     */\n    function transferIn(address _token, uint256 _amount) internal returns (uint256) {\n        uint256 balanceBefore = IERC20(_token).balanceOf(address(this));\n        IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n        uint256 balanceAfter = IERC20(_token).balanceOf(address(this));\n        return balanceAfter - balanceBefore;\n    }\n\n    /**\n     * @dev Calculates the transfer amount and if applicable, refund amount, taking into account the\n     * difference between token decimals.\n     * @param _amount The original amount\n     * @param _aDecimals The decimals of token A\n     * @param _bDecimals The decimals of token B\n     */\n    function calcTransferAmountWithDecimals(\n        uint256 _amount,\n        uint256 _aDecimals,\n        uint256 _bDecimals\n    ) internal pure returns (uint256 newAmount, uint256 refund) {\n        if (_aDecimals > _bDecimals) {\n            newAmount = _amount / (10**(_aDecimals - _bDecimals));\n            refund = _amount - newAmount * (10**(_aDecimals - _bDecimals));\n        } else if (_aDecimals < _bDecimals) {\n            newAmount = _amount * (10**(_bDecimals - _aDecimals));\n        } else {\n            newAmount = _amount;\n        }\n    }\n\n    // This account has to hold some amount of native currency in order to be eligible\n    // to receive canonical x20 assets per Astar rule\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/xc20/XC20BridgeToken.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"./interfaces/IXC20BridgeHub.sol\";\n\n/**\n * @title Intermediary bridge token that supports swapping with the XC-20 bridge hub.\n * NOTE: XC-20 bridge hub is NOT the canonical token itself.\n */\ncontract XC20BridgeToken is ERC20, Ownable {\n    using SafeERC20 for IERC20;\n\n    // The pegged token bridge\n    address public bridge;\n    // XC20 bridge hub for swapping\n    address public immutable bridgeHub;\n    // The canonical token\n    address public immutable canonicalToken;\n\n    event BridgeUpdated(address bridge);\n\n    modifier onlyBridge() {\n        require(msg.sender == bridge, \"XC20BridgeToken: caller is not bridge\");\n        _;\n    }\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        address _bridge,\n        address _bridgeHub,\n        address _canonicalToken\n    ) ERC20(name_, symbol_) {\n        bridge = _bridge;\n        bridgeHub = _bridgeHub;\n        canonicalToken = _canonicalToken;\n    }\n\n    function mint(address _to, uint256 _amount) external onlyBridge returns (bool) {\n        _mint(address(this), _amount); // Mint to this contract to be transferred to the hub\n        _approve(address(this), bridgeHub, _amount);\n        IXC20BridgeHub(bridgeHub).swapBridgeForCanonical(address(this), _amount);\n        // Now this has canonical token, next step is to transfer to user.\n        IERC20(canonicalToken).safeTransfer(_to, _amount);\n        return true;\n    }\n\n    function burn(address _from, uint256 _amount) external onlyBridge returns (bool) {\n        IERC20(canonicalToken).safeTransferFrom(_from, address(this), _amount);\n        IERC20(canonicalToken).safeIncreaseAllowance(address(bridgeHub), _amount);\n        IXC20BridgeHub(bridgeHub).swapCanonicalForBridge(address(this), _amount);\n        _burn(address(this), _amount);\n        return true;\n    }\n\n    function updateBridge(address _bridge) external onlyOwner {\n        bridge = _bridge;\n        emit BridgeUpdated(bridge);\n    }\n\n    function decimals() public view virtual override returns (uint8) {\n        return ERC20(canonicalToken).decimals();\n    }\n\n    // For compatibility with BEP20\n    function getOwner() external view returns (address) {\n        return owner();\n    }\n\n    // This account has to hold some amount of native currency in order to be eligible\n    // to receive canonical x20 assets per Astar rule\n    receive() external payable {}\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/customized/xc20/interfaces/IXC20BridgeHub.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\ninterface IXC20BridgeHub {\n    /**\n     * @dev Swaps intermediary bridge token for canonical XC-20 token.\n     * @param bridgeToken The intermediary bridge token\n     * @param amount The amount to swap\n     * @return The canonical token amount\n     */\n    function swapBridgeForCanonical(address bridgeToken, uint256 amount) external returns (uint256);\n\n    /**\n     * @dev Swaps canonical XC-20 token for intermediary bridge token.\n     * @param bridgeToken The intermediary bridge token\n     * @param amount The amount to swap\n     * @return The bridge token amount\n     */\n    function swapCanonicalForBridge(address bridgeToken, uint256 amount) external returns (uint256);\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/freezable/Freezable.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nabstract contract Freezable {\n    event Frozen(address account);\n    event Unfrozen(address account);\n    mapping(address => bool) internal freezes;\n\n    function isFrozen(address _account) public view virtual returns (bool) {\n        return freezes[_account];\n    }\n\n    modifier whenAccountNotFrozen(address _account) {\n        require(!isFrozen(_account), \"Freezable: frozen\");\n        _;\n    }\n\n    modifier whenAccountFrozen(address _account) {\n        require(isFrozen(_account), \"Freezable: not frozen\");\n        _;\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/freezable/MintSwapCanonicalTokenFreezable.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Freezable.sol\";\nimport \"../MintSwapCanonicalToken.sol\";\n\n/**\n * @title Canonical token that supports multi-bridge minter and multi-token swap. Support freezable erc20 transfer\n */\ncontract MintSwapCanonicalTokenFreezable is MintSwapCanonicalToken, Freezable {\n    string private _name;\n    string private _symbol;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MintSwapCanonicalToken(name_, symbol_, decimals_) {}\n\n    // freezable related\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual override {\n        super._beforeTokenTransfer(from, to, amount);\n        require(!isFrozen(from), \"ERC20Freezable: from account is frozen\");\n        require(!isFrozen(to), \"ERC20Freezable: to account is frozen\");\n    }\n\n    function freeze(address _account) public onlyOwner {\n        freezes[_account] = true;\n        emit Frozen(_account);\n    }\n\n    function unfreeze(address _account) public onlyOwner {\n        freezes[_account] = false;\n        emit Unfrozen(_account);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/freezable/MintSwapCanonicalTokenUpgradableFreezable.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Freezable.sol\";\nimport \"../MintSwapCanonicalTokenUpgradable.sol\";\n\n/**\n * @title Upgradable canonical token that supports multi-bridge minter and multi-token swap. Support freezable erc20 transfer\n */\ncontract MintSwapCanonicalTokenUpgradableFreezable is MintSwapCanonicalTokenUpgradable, Freezable {\n    string private _name;\n    string private _symbol;\n\n    constructor(\n        string memory name_,\n        string memory symbol_,\n        uint8 decimals_\n    ) MintSwapCanonicalTokenUpgradable(name_, symbol_, decimals_) {}\n\n    // freezable related\n    function _beforeTokenTransfer(\n        address from,\n        address to,\n        uint256 amount\n    ) internal virtual override {\n        super._beforeTokenTransfer(from, to, amount);\n        require(!isFrozen(from), \"ERC20Freezable: from account is frozen\");\n        require(!isFrozen(to), \"ERC20Freezable: to account is frozen\");\n    }\n\n    function freeze(address _account) public onlyOwner {\n        freezes[_account] = true;\n        emit Frozen(_account);\n    }\n\n    function unfreeze(address _account) public onlyOwner {\n        freezes[_account] = false;\n        emit Unfrozen(_account);\n    }\n}\n"
  },
  {
    "path": "contracts/pegged-bridge/tokens/owners/RestrictedMultiBridgeTokenOwner.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../../../safeguard/Ownable.sol\";\n\ninterface IMultiBridgeToken {\n    function updateBridgeSupplyCap(address _bridge, uint256 _cap) external;\n}\n\n// restrict multi-bridge token to effectively only have one bridge (minter)\ncontract RestrictedMultiBridgeTokenOwner is Ownable {\n    address public immutable token;\n    address public bridge;\n\n    constructor(address _token, address _bridge) {\n        token = _token;\n        bridge = _bridge;\n    }\n\n    function updateBridgeSupplyCap(uint256 _cap) external onlyOwner {\n        IMultiBridgeToken(token).updateBridgeSupplyCap(bridge, _cap);\n    }\n\n    function changeBridge(address _bridge, uint256 _cap) external onlyOwner {\n        // set previous bridge cap to 1 to disable mint but still allow burn\n        // till its total supply becomes zero\n        IMultiBridgeToken(token).updateBridgeSupplyCap(bridge, 1);\n        // set new bridge and cap\n        IMultiBridgeToken(token).updateBridgeSupplyCap(_bridge, _cap);\n        bridge = _bridge;\n    }\n\n    function revokeBridge(address _bridge) external onlyOwner {\n        // set previous bridge cap to 0 to disable both mint and burn\n        IMultiBridgeToken(token).updateBridgeSupplyCap(_bridge, 0);\n    }\n}\n"
  },
  {
    "path": "contracts/proxy/TransferAgent.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/security/ReentrancyGuard.sol\";\n\nimport \"../libraries/BridgeTransferLib.sol\";\nimport \"../safeguard/Ownable.sol\";\n\n/**\n * @title Transfer agent. Designed to support arbitrary length receiver address for transfer. Supports the liquidity pool-based {Bridge}, the {OriginalTokenVault} for pegged\n * deposit and the {PeggedTokenBridge} for pegged burn.\n */\ncontract TransferAgent is ReentrancyGuard, Ownable {\n    using SafeERC20 for IERC20;\n\n    struct Extension {\n        uint8 Type;\n        bytes Value;\n    }\n\n    mapping(BridgeTransferLib.BridgeSendType => address) public bridges;\n\n    event Supplement(\n        BridgeTransferLib.BridgeSendType bridgeSendType,\n        bytes32 transferId,\n        address sender,\n        bytes receiver,\n        Extension[] extensions\n    );\n    event BridgeUpdated(BridgeTransferLib.BridgeSendType bridgeSendType, address bridgeAddr);\n\n    /**\n     * @notice Send a cross-chain transfer of ERC20 token either via liquidity pool-based bridge or in form of mint/burn.\n     * @param _receiver The address of the receiver.\n     * @param _token The address of the token.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {BridgeSendType.Liquidity}.\n     * @param _bridgeSendType The type of bridge used by this transfer. One of the {BridgeSendType} enum.\n     * @param _extensions A list of extension to be processed by agent, is designed to be used for extending\n     *        present transfer. Contact Celer team to learn about already supported type of extension.\n     */\n    function transfer(\n        bytes calldata _receiver,\n        address _token,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000\n        BridgeTransferLib.BridgeSendType _bridgeSendType,\n        Extension[] calldata _extensions\n    ) external nonReentrant returns (bytes32) {\n        bytes32 transferId;\n        {\n            address _bridgeAddr = bridges[_bridgeSendType];\n            require(_bridgeAddr != address(0), \"unknown bridge type\");\n            IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount);\n            transferId = BridgeTransferLib.sendTransfer(\n                address(0),\n                _token,\n                _amount,\n                _dstChainId,\n                _nonce,\n                _maxSlippage,\n                _bridgeSendType,\n                _bridgeAddr\n            );\n        }\n        emit Supplement(_bridgeSendType, transferId, msg.sender, _receiver, _extensions);\n        return transferId;\n    }\n\n    /**\n     * @notice Send a cross-chain transfer of native token either via liquidity pool-based bridge or in form of mint/burn.\n     * @param _receiver The address of the receiver.\n     * @param _amount The amount of the transfer.\n     * @param _dstChainId The destination chain ID.\n     * @param _nonce A number input to guarantee uniqueness of transferId. Can be timestamp in practice.\n     * @param _maxSlippage The max slippage accepted, given as percentage in point (pip). Eg. 5000 means 0.5%.\n     *        Must be greater than minimalMaxSlippage. Receiver is guaranteed to receive at least\n     *        (100% - max slippage percentage) * amount or the transfer can be refunded.\n     *        Only applicable to the {BridgeSendType.Liquidity}.\n     * @param _bridgeSendType The type of bridge used by this transfer. One of the {BridgeSendType} enum.\n     * @param _extensions A list of extension to be processed by agent, is designed to be used for extending\n     *        present transfer. Contact Celer team to learn about already supported type of extension.\n     */\n    function transferNative(\n        bytes calldata _receiver,\n        uint256 _amount,\n        uint64 _dstChainId,\n        uint64 _nonce,\n        uint32 _maxSlippage, // slippage * 1M, eg. 0.5% -> 5000\n        BridgeTransferLib.BridgeSendType _bridgeSendType,\n        Extension[] calldata _extensions\n    ) external payable nonReentrant returns (bytes32) {\n        bytes32 transferId;\n        {\n            address _bridgeAddr = bridges[_bridgeSendType];\n            require(_bridgeAddr != address(0), \"unknown bridge type\");\n            require(msg.value == _amount, \"amount mismatch\");\n            transferId = BridgeTransferLib.sendNativeTransfer(\n                address(0),\n                _amount,\n                _dstChainId,\n                _nonce,\n                _maxSlippage,\n                _bridgeSendType,\n                _bridgeAddr\n            );\n        }\n        emit Supplement(_bridgeSendType, transferId, msg.sender, _receiver, _extensions);\n        return transferId;\n    }\n\n    // ----------------------Admin operation-----------------------\n\n    function setBridgeAddress(BridgeTransferLib.BridgeSendType _bridgeSendType, address _addr) public onlyOwner {\n        require(_addr != address(0), \"invalid address\");\n        bridges[_bridgeSendType] = _addr;\n        emit BridgeUpdated(_bridgeSendType, _addr);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/DelayedTransfer.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Governor.sol\";\n\nabstract contract DelayedTransfer is Governor {\n    struct delayedTransfer {\n        address receiver;\n        address token;\n        uint256 amount;\n        uint256 timestamp;\n    }\n    mapping(bytes32 => delayedTransfer) public delayedTransfers;\n    mapping(address => uint256) public delayThresholds;\n    uint256 public delayPeriod; // in seconds\n\n    event DelayedTransferAdded(bytes32 id);\n    event DelayedTransferExecuted(bytes32 id, address receiver, address token, uint256 amount);\n\n    event DelayPeriodUpdated(uint256 period);\n    event DelayThresholdUpdated(address token, uint256 threshold);\n\n    function setDelayThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) external onlyGovernor {\n        require(_tokens.length == _thresholds.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            delayThresholds[_tokens[i]] = _thresholds[i];\n            emit DelayThresholdUpdated(_tokens[i], _thresholds[i]);\n        }\n    }\n\n    function setDelayPeriod(uint256 _period) external onlyGovernor {\n        delayPeriod = _period;\n        emit DelayPeriodUpdated(_period);\n    }\n\n    function _addDelayedTransfer(\n        bytes32 id,\n        address receiver,\n        address token,\n        uint256 amount\n    ) internal {\n        require(delayedTransfers[id].timestamp == 0, \"delayed transfer already exists\");\n        delayedTransfers[id] = delayedTransfer({\n            receiver: receiver,\n            token: token,\n            amount: amount,\n            timestamp: block.timestamp\n        });\n        emit DelayedTransferAdded(id);\n    }\n\n    // caller needs to do the actual token transfer\n    function _executeDelayedTransfer(bytes32 id) internal returns (delayedTransfer memory) {\n        delayedTransfer memory transfer = delayedTransfers[id];\n        require(transfer.timestamp > 0, \"delayed transfer not exist\");\n        require(block.timestamp > transfer.timestamp + delayPeriod, \"delayed transfer still locked\");\n        delete delayedTransfers[id];\n        emit DelayedTransferExecuted(id, transfer.receiver, transfer.token, transfer.amount);\n        return transfer;\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/Governor.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Ownable.sol\";\n\nabstract contract Governor is Ownable {\n    mapping(address => bool) public governors;\n\n    event GovernorAdded(address account);\n    event GovernorRemoved(address account);\n\n    modifier onlyGovernor() {\n        require(isGovernor(msg.sender), \"Caller is not governor\");\n        _;\n    }\n\n    constructor() {\n        _addGovernor(msg.sender);\n    }\n\n    function isGovernor(address _account) public view returns (bool) {\n        return governors[_account];\n    }\n\n    function addGovernor(address _account) public onlyOwner {\n        _addGovernor(_account);\n    }\n\n    function removeGovernor(address _account) public onlyOwner {\n        _removeGovernor(_account);\n    }\n\n    function renounceGovernor() public {\n        _removeGovernor(msg.sender);\n    }\n\n    function _addGovernor(address _account) private {\n        require(!isGovernor(_account), \"Account is already governor\");\n        governors[_account] = true;\n        emit GovernorAdded(_account);\n    }\n\n    function _removeGovernor(address _account) private {\n        require(isGovernor(_account), \"Account is not governor\");\n        governors[_account] = false;\n        emit GovernorRemoved(_account);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/Ownable.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n *\n * This adds a normal func that setOwner if _owner is address(0). So we can't allow\n * renounceOwnership. So we can support Proxy based upgradable contract\n */\nabstract contract Ownable {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor() {\n        _setOwner(msg.sender);\n    }\n\n    /**\n     * @dev Only to be called by inherit contracts, in their init func called by Proxy\n     * we require _owner == address(0), which is only possible when it's a delegateCall\n     * because constructor sets _owner in contract state.\n     */\n    function initOwner() internal {\n        require(_owner == address(0), \"owner already set\");\n        _setOwner(msg.sender);\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner() == msg.sender, \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        _setOwner(newOwner);\n    }\n\n    function _setOwner(address newOwner) private {\n        address oldOwner = _owner;\n        _owner = newOwner;\n        emit OwnershipTransferred(oldOwner, newOwner);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/Pauser.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/security/Pausable.sol\";\nimport \"./Ownable.sol\";\n\nabstract contract Pauser is Ownable, Pausable {\n    mapping(address => bool) public pausers;\n\n    event PauserAdded(address account);\n    event PauserRemoved(address account);\n\n    constructor() {\n        _addPauser(msg.sender);\n    }\n\n    modifier onlyPauser() {\n        require(isPauser(msg.sender), \"Caller is not pauser\");\n        _;\n    }\n\n    function pause() public onlyPauser {\n        _pause();\n    }\n\n    function unpause() public onlyPauser {\n        _unpause();\n    }\n\n    function isPauser(address account) public view returns (bool) {\n        return pausers[account];\n    }\n\n    function addPauser(address account) public onlyOwner {\n        _addPauser(account);\n    }\n\n    function removePauser(address account) public onlyOwner {\n        _removePauser(account);\n    }\n\n    function renouncePauser() public {\n        _removePauser(msg.sender);\n    }\n\n    function _addPauser(address account) private {\n        require(!isPauser(account), \"Account is already pauser\");\n        pausers[account] = true;\n        emit PauserAdded(account);\n    }\n\n    function _removePauser(address account) private {\n        require(isPauser(account), \"Account is not pauser\");\n        pausers[account] = false;\n        emit PauserRemoved(account);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/VolumeControl.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Governor.sol\";\n\nabstract contract VolumeControl is Governor {\n    uint256 public epochLength; // seconds\n    mapping(address => uint256) public epochVolumes; // key is token\n    mapping(address => uint256) public epochVolumeCaps; // key is token\n    mapping(address => uint256) public lastOpTimestamps; // key is token\n\n    event EpochLengthUpdated(uint256 length);\n    event EpochVolumeUpdated(address token, uint256 cap);\n\n    function setEpochLength(uint256 _length) external onlyGovernor {\n        epochLength = _length;\n        emit EpochLengthUpdated(_length);\n    }\n\n    function setEpochVolumeCaps(address[] calldata _tokens, uint256[] calldata _caps) external onlyGovernor {\n        require(_tokens.length == _caps.length, \"length mismatch\");\n        for (uint256 i = 0; i < _tokens.length; i++) {\n            epochVolumeCaps[_tokens[i]] = _caps[i];\n            emit EpochVolumeUpdated(_tokens[i], _caps[i]);\n        }\n    }\n\n    function _updateVolume(address _token, uint256 _amount) internal {\n        if (epochLength == 0) {\n            return;\n        }\n        uint256 cap = epochVolumeCaps[_token];\n        if (cap == 0) {\n            return;\n        }\n        uint256 volume = epochVolumes[_token];\n        uint256 timestamp = block.timestamp;\n        uint256 epochStartTime = (timestamp / epochLength) * epochLength;\n        if (lastOpTimestamps[_token] < epochStartTime) {\n            volume = _amount;\n        } else {\n            volume += _amount;\n        }\n        require(volume <= cap, \"volume exceeds cap\");\n        epochVolumes[_token] = volume;\n        lastOpTimestamps[_token] = timestamp;\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/Whitelist.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Ownable.sol\";\n\nabstract contract Whitelist is Ownable {\n    mapping(address => bool) public whitelist;\n    bool public whitelistEnabled;\n\n    event WhitelistedAdded(address account);\n    event WhitelistedRemoved(address account);\n\n    modifier onlyWhitelisted() {\n        if (whitelistEnabled) {\n            require(isWhitelisted(msg.sender), \"Caller is not whitelisted\");\n        }\n        _;\n    }\n\n    /**\n     * @notice Set whitelistEnabled\n     */\n    function setWhitelistEnabled(bool _whitelistEnabled) external onlyOwner {\n        whitelistEnabled = _whitelistEnabled;\n    }\n\n    /**\n     * @notice Add an account to whitelist\n     */\n    function addWhitelisted(address account) external onlyOwner {\n        require(!isWhitelisted(account), \"Already whitelisted\");\n        whitelist[account] = true;\n        emit WhitelistedAdded(account);\n    }\n\n    /**\n     * @notice Remove an account from whitelist\n     */\n    function removeWhitelisted(address account) external onlyOwner {\n        require(isWhitelisted(account), \"Not whitelisted\");\n        whitelist[account] = false;\n        emit WhitelistedRemoved(account);\n    }\n\n    /**\n     * @return is account whitelisted\n     */\n    function isWhitelisted(address account) public view returns (bool) {\n        return whitelist[account];\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/sentinel/Guard.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"../Ownable.sol\";\n\nabstract contract Guard is Ownable {\n    enum GuardState {\n        None,\n        Guarded,\n        Relaxed\n    }\n\n    bool public relaxed;\n    uint256 public numRelaxedGuards;\n    uint256 public relaxThreshold;\n    address[] public guards;\n    mapping(address => GuardState) public guardStates; // guard address -> guard state\n\n    event GuardUpdated(address account, GuardState state);\n    event RelaxStatusUpdated(bool relaxed);\n    event RelaxThresholdUpdated(uint256 threshold, uint256 total);\n\n    function _initGuards(address[] memory _guards) internal {\n        require(guards.length == 0, \"guards already initiated\");\n        for (uint256 i = 0; i < _guards.length; i++) {\n            _addGuard(_guards[i]);\n        }\n        _setRelaxThreshold(guards.length);\n    }\n\n    // change GuardState of msg.sender from relaxed to guarded\n    function guard() external {\n        require(guardStates[msg.sender] == GuardState.Relaxed, \"invalid caller\");\n        guardStates[msg.sender] = GuardState.Guarded;\n        numRelaxedGuards--;\n        _updateRelaxed();\n        emit GuardUpdated(msg.sender, GuardState.Guarded);\n    }\n\n    // change GuardState of msg.sender from guarded to relaxed\n    function relax() external {\n        require(guardStates[msg.sender] == GuardState.Guarded, \"invalid caller\");\n        guardStates[msg.sender] = GuardState.Relaxed;\n        numRelaxedGuards++;\n        _updateRelaxed();\n        emit GuardUpdated(msg.sender, GuardState.Relaxed);\n    }\n\n    function updateGuards(\n        address[] calldata _add,\n        address[] calldata _remove,\n        uint256 _newRelaxThreshold\n    ) external onlyOwner {\n        for (uint256 i = 0; i < _remove.length; i++) {\n            _removeGuard(_remove[i]);\n        }\n        for (uint256 i = 0; i < _add.length; i++) {\n            _addGuard(_add[i]);\n        }\n        _setRelaxThreshold(_newRelaxThreshold);\n    }\n\n    function _addGuard(address _account) private {\n        require(guardStates[_account] == GuardState.None, \"account is already guard\");\n        guards.push(_account);\n        guardStates[_account] = GuardState.Guarded;\n        emit GuardUpdated(_account, GuardState.Guarded);\n    }\n\n    function _removeGuard(address _account) private {\n        GuardState state = guardStates[_account];\n        require(state != GuardState.None, \"account is not guard\");\n        if (state == GuardState.Relaxed) {\n            numRelaxedGuards--;\n        }\n        uint256 lastIndex = guards.length - 1;\n        for (uint256 i = 0; i < guards.length; i++) {\n            if (guards[i] == _account) {\n                if (i < lastIndex) {\n                    guards[i] = guards[lastIndex];\n                }\n                guards.pop();\n                guardStates[_account] = GuardState.None;\n                emit GuardUpdated(_account, GuardState.None);\n                return;\n            }\n        }\n        revert(\"guard not found\"); // this should never happen\n    }\n\n    function setRelaxThreshold(uint256 _threshold) external onlyOwner {\n        _setRelaxThreshold(_threshold);\n    }\n\n    function _setRelaxThreshold(uint256 _threshold) private {\n        require(_threshold <= guards.length, \"invalid threshold\");\n        relaxThreshold = _threshold;\n        _updateRelaxed();\n        emit RelaxThresholdUpdated(_threshold, guards.length);\n    }\n\n    function _updateRelaxed() private {\n        bool _relaxed = numRelaxedGuards >= relaxThreshold;\n        if (relaxed != _relaxed) {\n            relaxed = _relaxed;\n            emit RelaxStatusUpdated(relaxed);\n        }\n    }\n\n    function numGuards() public view returns (uint256) {\n        return guards.length;\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/sentinel/GuardedGovernor.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Guard.sol\";\n\ninterface IBridge {\n    // delayed transfer\n    function setDelayPeriod(uint256 _period) external;\n\n    function delayPeriod() external view returns (uint256);\n\n    function setDelayThresholds(address[] calldata _tokens, uint256[] calldata _thresholds) external;\n\n    function delayThresholds(address _token) external view returns (uint256);\n\n    // volume control\n    function setEpochLength(uint256 _length) external;\n\n    function epochLength() external view returns (uint256);\n\n    function setEpochVolumeCaps(address[] calldata _tokens, uint256[] calldata _caps) external;\n\n    function epochVolumeCaps(address _token) external view returns (uint256);\n\n    // pool bridge\n    function setMinAdd(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function minAdd(address _token) external view returns (uint256);\n\n    function setMinSend(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function minSend(address _token) external view returns (uint256);\n\n    function setMaxSend(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function maxSend(address _token) external view returns (uint256);\n\n    function setNativeTokenTransferGas(uint256 _gasUsed) external;\n\n    function setMinimalMaxSlippage(uint32 _minimalMaxSlippage) external;\n\n    // peg bridge\n    function setMinDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function minDeposit(address _token) external view returns (uint256);\n\n    function setMaxDeposit(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function maxDeposit(address _token) external view returns (uint256);\n\n    function setMinBurn(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function minBurn(address _token) external view returns (uint256);\n\n    function setMaxBurn(address[] calldata _tokens, uint256[] calldata _amounts) external;\n\n    function maxBurn(address _token) external view returns (uint256);\n}\n\nabstract contract GuardedGovernor is Guard {\n    uint64 public numGovernors;\n    mapping(address => bool) public governors;\n\n    event GovernorUpdated(address account, bool added);\n\n    function _initGovernors(address[] memory _governors) internal {\n        require(numGovernors == 0, \"governors already initiated\");\n        for (uint256 i = 0; i < _governors.length; i++) {\n            _addGovernor(_governors[i]);\n        }\n    }\n\n    modifier onlyGovernor() {\n        require(isGovernor(msg.sender), \"Caller is not governor\");\n        _;\n    }\n\n    // delayed transfer\n\n    function setDelayPeriod(address _target, uint256 _period) external onlyGovernor {\n        if (!relaxed) {\n            uint256 current = IBridge(_target).delayPeriod();\n            require(_period > current, \"not in relax mode, can only increase period\");\n        }\n        IBridge(_target).setDelayPeriod(_period);\n    }\n\n    function setDelayThresholds(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _thresholds\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).delayThresholds(_tokens[i]);\n                require(_thresholds[i] > current, \"not in relax mode, can only increase threshold\");\n            }\n        }\n        IBridge(_target).setDelayThresholds(_tokens, _thresholds);\n    }\n\n    // volume control\n\n    function setEpochLength(address _target, uint256 _length) external onlyGovernor {\n        if (!relaxed) {\n            uint256 current = IBridge(_target).epochLength();\n            require(_length > current, \"not in relax mode, can only increase length\");\n        }\n        IBridge(_target).setEpochLength(_length);\n    }\n\n    function setEpochVolumeCaps(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _caps\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).epochVolumeCaps(_tokens[i]);\n                require(_caps[i] < current, \"not in relax mode, can only reduce cap\");\n            }\n        }\n        IBridge(_target).setEpochVolumeCaps(_tokens, _caps);\n    }\n\n    // pool bridge\n\n    function setMinAdd(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).minAdd(_tokens[i]);\n                require(_amounts[i] > current, \"not in relax mode, can only increase minAdd\");\n            }\n        }\n        IBridge(_target).setMinAdd(_tokens, _amounts);\n    }\n\n    function setMinSend(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).minSend(_tokens[i]);\n                require(_amounts[i] > current, \"not in relax mode, can only increase minSend\");\n            }\n        }\n        IBridge(_target).setMinSend(_tokens, _amounts);\n    }\n\n    function setMaxSend(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).maxSend(_tokens[i]);\n                require(_amounts[i] < current, \"not in relax mode, can only reduce maxSend\");\n            }\n        }\n        IBridge(_target).setMaxSend(_tokens, _amounts);\n    }\n\n    function setNativeTokenTransferGas(address _target, uint256 _gasUsed) external onlyGovernor {\n        IBridge(_target).setNativeTokenTransferGas(_gasUsed);\n    }\n\n    function setMinimalMaxSlippage(address _target, uint32 _minimalMaxSlippage) external onlyGovernor {\n        IBridge(_target).setMinimalMaxSlippage(_minimalMaxSlippage);\n    }\n\n    // peg bridge\n\n    function setMinDeposit(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).minDeposit(_tokens[i]);\n                require(_amounts[i] > current, \"not in relax mode, can only increase minDeposit\");\n            }\n        }\n        IBridge(_target).setMinDeposit(_tokens, _amounts);\n    }\n\n    function setMaxDeposit(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).maxDeposit(_tokens[i]);\n                require(_amounts[i] < current, \"not in relax mode, can only reduce maxDeposit\");\n            }\n        }\n        IBridge(_target).setMaxDeposit(_tokens, _amounts);\n    }\n\n    function setMinBurn(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).minBurn(_tokens[i]);\n                require(_amounts[i] > current, \"not in relax mode, can only increase minBurn\");\n            }\n        }\n        IBridge(_target).setMinBurn(_tokens, _amounts);\n    }\n\n    function setMaxBurn(\n        address _target,\n        address[] calldata _tokens,\n        uint256[] calldata _amounts\n    ) external onlyGovernor {\n        if (!relaxed) {\n            for (uint256 i = 0; i < _tokens.length; i++) {\n                uint256 current = IBridge(_target).maxBurn(_tokens[i]);\n                require(_amounts[i] < current, \"not in relax mode, can only reduce maxBurn\");\n            }\n        }\n        IBridge(_target).setMaxBurn(_tokens, _amounts);\n    }\n\n    function isGovernor(address _account) public view returns (bool) {\n        return governors[_account];\n    }\n\n    function addGovernors(address[] calldata _accounts) external onlyOwner {\n        for (uint256 i = 0; i < _accounts.length; i++) {\n            _addGovernor(_accounts[i]);\n        }\n    }\n\n    function _addGovernor(address _account) internal {\n        require(!isGovernor(_account), \"Account is already governor\");\n        governors[_account] = true;\n        numGovernors++;\n        emit GovernorUpdated(_account, true);\n    }\n\n    function removeGovernors(address[] calldata _accounts) external onlyOwner {\n        for (uint256 i = 0; i < _accounts.length; i++) {\n            _removeGovernor(_accounts[i]);\n        }\n    }\n\n    function _removeGovernor(address _account) private {\n        require(isGovernor(_account), \"Account is not governor\");\n        governors[_account] = false;\n        numGovernors--;\n        emit GovernorUpdated(_account, false);\n    }\n\n    function renounceGovernor() external {\n        _removeGovernor(msg.sender);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/sentinel/GuardedPauser.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./Guard.sol\";\nimport \"../../libraries/Utils.sol\";\n\ninterface IPauser {\n    function pause() external;\n\n    function unpause() external;\n}\n\nabstract contract GuardedPauser is Guard {\n    enum PauserRole {\n        None,\n        Full,\n        PauseOnly\n    }\n\n    uint64 public numPausers;\n    mapping(address => PauserRole) public pausers;\n\n    event PauserUpdated(address account, PauserRole role);\n    event Failed(address target, string reason);\n\n    function _initPausers(address[] memory _pausers) internal {\n        require(numPausers == 0, \"pausers already initiated\");\n        for (uint256 i = 0; i < _pausers.length; i++) {\n            _addPauser(_pausers[i], PauserRole.Full);\n        }\n    }\n\n    function pause(address _target) public {\n        require(pausers[msg.sender] != PauserRole.None, \"invalid caller\");\n        IPauser(_target).pause();\n    }\n\n    function pause(address[] calldata _targets) public {\n        require(pausers[msg.sender] != PauserRole.None, \"invalid caller\");\n        require(_targets.length > 0, \"empty target list\");\n        bool hasSuccess;\n        for (uint256 i = 0; i < _targets.length; i++) {\n            (bool ok, bytes memory res) = address(_targets[i]).call(abi.encodeWithSelector(IPauser.pause.selector));\n            if (ok) {\n                hasSuccess = true;\n            } else {\n                emit Failed(_targets[i], Utils.getRevertMsg(res));\n            }\n        }\n        require(hasSuccess, \"pause failed for all targets\");\n    }\n\n    function unpause(address _target) public {\n        require(pausers[msg.sender] == PauserRole.Full, \"invalid caller\");\n        require(relaxed, \"not in relaxed mode\");\n        IPauser(_target).unpause();\n    }\n\n    function unpause(address[] calldata _targets) public {\n        require(pausers[msg.sender] == PauserRole.Full, \"invalid caller\");\n        require(relaxed, \"not in relaxed mode\");\n        require(_targets.length > 0, \"empty target list\");\n        bool hasSuccess;\n        for (uint256 i = 0; i < _targets.length; i++) {\n            (bool ok, bytes memory res) = address(_targets[i]).call(abi.encodeWithSelector(IPauser.unpause.selector));\n            if (ok) {\n                hasSuccess = true;\n            } else {\n                emit Failed(_targets[i], Utils.getRevertMsg(res));\n            }\n        }\n        require(hasSuccess, \"unpause failed for all targets\");\n    }\n\n    function addPausers(address[] calldata _accounts, PauserRole[] calldata _roles) external onlyOwner {\n        for (uint256 i = 0; i < _accounts.length; i++) {\n            _addPauser(_accounts[i], _roles[i]);\n        }\n    }\n\n    function _addPauser(address _account, PauserRole _role) private {\n        require(pausers[_account] == PauserRole.None, \"account is already pauser\");\n        require(_role == PauserRole.Full || _role == PauserRole.PauseOnly, \"invalid role\");\n        pausers[_account] = _role;\n        numPausers++;\n        emit PauserUpdated(_account, _role);\n    }\n\n    function removePausers(address[] calldata _accounts) external onlyOwner {\n        for (uint256 i = 0; i < _accounts.length; i++) {\n            _removePauser(_accounts[i]);\n        }\n    }\n\n    function _removePauser(address _account) private {\n        require(pausers[_account] != PauserRole.None, \"account is not pauser\");\n        pausers[_account] = PauserRole.None;\n        numPausers--;\n        emit PauserUpdated(_account, PauserRole.None);\n    }\n\n    function setPausers(address[] calldata _accounts, PauserRole[] calldata _roles) external onlyOwner {\n        for (uint256 i = 0; i < _accounts.length; i++) {\n            _setPauser(_accounts[i], _roles[i]);\n        }\n    }\n\n    function _setPauser(address _account, PauserRole _role) private {\n        require(pausers[_account] != PauserRole.None, \"account is not pauser\");\n        require(_role == PauserRole.Full || _role == PauserRole.PauseOnly, \"invalid role\");\n        pausers[_account] = _role;\n        emit PauserUpdated(_account, _role);\n    }\n}\n"
  },
  {
    "path": "contracts/safeguard/sentinel/Sentinel.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"./GuardedPauser.sol\";\nimport \"./GuardedGovernor.sol\";\n\ncontract Sentinel is GuardedPauser, GuardedGovernor {\n    // NOTE: Comment out for zksync\n    constructor(address[] memory _guards, address[] memory _pausers, address[] memory _governors) {\n        _initGuards(_guards);\n        _initPausers(_pausers);\n        _initGovernors(_governors);\n    }\n\n    // This is to support upgradable deployment.\n    // Only to be called by Proxy via delegateCall as initOwner will require _owner is 0,\n    // so calling init on this contract directly will guarantee to fail\n    function init(address[] memory _guards, address[] memory _pausers, address[] memory _governors) external {\n        initOwner();\n        _initGuards(_guards);\n        _initPausers(_pausers);\n        _initGovernors(_governors);\n    }\n}\n"
  },
  {
    "path": "contracts/staking/DataTypes.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nlibrary DataTypes {\n    uint256 constant CELR_DECIMAL = 1e18;\n    uint256 constant MAX_INT = 2**256 - 1;\n    uint256 constant COMMISSION_RATE_BASE = 10000; // 1 commissionRate means 0.01%\n    uint256 constant MAX_UNDELEGATION_ENTRIES = 10;\n    uint256 constant SLASH_FACTOR_DECIMAL = 1e6;\n\n    enum ValidatorStatus {\n        Null,\n        Unbonded,\n        Unbonding,\n        Bonded\n    }\n\n    enum ParamName {\n        ProposalDeposit,\n        VotingPeriod,\n        UnbondingPeriod,\n        MaxBondedValidators,\n        MinValidatorTokens,\n        MinSelfDelegation,\n        AdvanceNoticePeriod,\n        ValidatorBondInterval,\n        MaxSlashFactor\n    }\n\n    struct Undelegation {\n        uint256 shares;\n        uint256 creationBlock;\n    }\n\n    struct Undelegations {\n        mapping(uint256 => Undelegation) queue;\n        uint32 head;\n        uint32 tail;\n    }\n\n    struct Delegator {\n        uint256 shares;\n        Undelegations undelegations;\n    }\n\n    struct Validator {\n        ValidatorStatus status;\n        address signer;\n        uint256 tokens; // sum of all tokens delegated to this validator\n        uint256 shares; // sum of all delegation shares\n        uint256 undelegationTokens; // tokens being undelegated\n        uint256 undelegationShares; // shares of tokens being undelegated\n        mapping(address => Delegator) delegators;\n        uint256 minSelfDelegation;\n        uint64 bondBlock; // cannot become bonded before this block\n        uint64 unbondBlock; // cannot become unbonded before this block\n        uint64 commissionRate; // equal to real commission rate * COMMISSION_RATE_BASE\n    }\n\n    // used for external view output\n    struct ValidatorTokens {\n        address valAddr;\n        uint256 tokens;\n    }\n\n    // used for external view output\n    struct ValidatorInfo {\n        address valAddr;\n        ValidatorStatus status;\n        address signer;\n        uint256 tokens;\n        uint256 shares;\n        uint256 minSelfDelegation;\n        uint64 commissionRate;\n    }\n\n    // used for external view output\n    struct DelegatorInfo {\n        address valAddr;\n        uint256 tokens;\n        uint256 shares;\n        Undelegation[] undelegations;\n        uint256 undelegationTokens;\n        uint256 withdrawableUndelegationTokens;\n    }\n}\n"
  },
  {
    "path": "contracts/staking/Govern.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimport \"./Staking.sol\";\n\n/**\n * @title Governance module for Staking contract\n */\ncontract Govern {\n    using SafeERC20 for IERC20;\n\n    Staking public immutable staking;\n    IERC20 public immutable celerToken;\n\n    enum ProposalStatus {\n        Uninitiated,\n        Voting,\n        Closed\n    }\n\n    enum VoteOption {\n        Null,\n        Yes,\n        Abstain,\n        No\n    }\n\n    struct ParamProposal {\n        address proposer;\n        uint256 deposit;\n        uint256 voteDeadline;\n        dt.ParamName name;\n        uint256 newValue;\n        ProposalStatus status;\n        mapping(address => VoteOption) votes;\n    }\n\n    mapping(uint256 => ParamProposal) public paramProposals;\n    uint256 public nextParamProposalId;\n\n    uint256 public forfeiture;\n    address public immutable collector;\n\n    event CreateParamProposal(\n        uint256 proposalId,\n        address proposer,\n        uint256 deposit,\n        uint256 voteDeadline,\n        dt.ParamName name,\n        uint256 newValue\n    );\n    event VoteParam(uint256 proposalId, address voter, VoteOption vote);\n    event ConfirmParamProposal(uint256 proposalId, bool passed, dt.ParamName name, uint256 newValue);\n\n    constructor(\n        Staking _staking,\n        address _celerTokenAddress,\n        address _collector\n    ) {\n        staking = _staking;\n        celerToken = IERC20(_celerTokenAddress);\n        collector = _collector;\n    }\n\n    /**\n     * @notice Get the vote type of a voter on a parameter proposal\n     * @param _proposalId the proposal id\n     * @param _voter the voter address\n     * @return the vote type of the given voter on the given parameter proposal\n     */\n    function getParamProposalVote(uint256 _proposalId, address _voter) public view returns (VoteOption) {\n        return paramProposals[_proposalId].votes[_voter];\n    }\n\n    /**\n     * @notice Create a parameter proposal\n     * @param _name the key of this parameter\n     * @param _value the new proposed value of this parameter\n     */\n    function createParamProposal(dt.ParamName _name, uint256 _value) external {\n        ParamProposal storage p = paramProposals[nextParamProposalId];\n        nextParamProposalId = nextParamProposalId + 1;\n        address msgSender = msg.sender;\n        uint256 deposit = staking.getParamValue(dt.ParamName.ProposalDeposit);\n\n        p.proposer = msgSender;\n        p.deposit = deposit;\n        p.voteDeadline = block.number + staking.getParamValue(dt.ParamName.VotingPeriod);\n        p.name = _name;\n        p.newValue = _value;\n        p.status = ProposalStatus.Voting;\n\n        celerToken.safeTransferFrom(msgSender, address(this), deposit);\n\n        emit CreateParamProposal(nextParamProposalId - 1, msgSender, deposit, p.voteDeadline, _name, _value);\n    }\n\n    /**\n     * @notice Vote for a parameter proposal with a specific type of vote\n     * @param _proposalId the id of the parameter proposal\n     * @param _vote the type of vote\n     */\n    function voteParam(uint256 _proposalId, VoteOption _vote) external {\n        address valAddr = msg.sender;\n        require(staking.getValidatorStatus(valAddr) == dt.ValidatorStatus.Bonded, \"Voter is not a bonded validator\");\n        ParamProposal storage p = paramProposals[_proposalId];\n        require(p.status == ProposalStatus.Voting, \"Invalid proposal status\");\n        require(block.number < p.voteDeadline, \"Vote deadline passed\");\n        require(p.votes[valAddr] == VoteOption.Null, \"Voter has voted\");\n        require(_vote != VoteOption.Null, \"Invalid vote\");\n\n        p.votes[valAddr] = _vote;\n\n        emit VoteParam(_proposalId, valAddr, _vote);\n    }\n\n    /**\n     * @notice Confirm a parameter proposal\n     * @param _proposalId the id of the parameter proposal\n     */\n    function confirmParamProposal(uint256 _proposalId) external {\n        uint256 yesVotes;\n        uint256 bondedTokens;\n        dt.ValidatorTokens[] memory validators = staking.getBondedValidatorsTokens();\n        for (uint32 i = 0; i < validators.length; i++) {\n            if (getParamProposalVote(_proposalId, validators[i].valAddr) == VoteOption.Yes) {\n                yesVotes += validators[i].tokens;\n            }\n            bondedTokens += validators[i].tokens;\n        }\n        bool passed = (yesVotes >= (bondedTokens * 2) / 3 + 1);\n\n        ParamProposal storage p = paramProposals[_proposalId];\n        require(p.status == ProposalStatus.Voting, \"Invalid proposal status\");\n        require(block.number >= p.voteDeadline, \"Vote deadline not reached\");\n\n        p.status = ProposalStatus.Closed;\n        if (passed) {\n            staking.setParamValue(p.name, p.newValue);\n            celerToken.safeTransfer(p.proposer, p.deposit);\n        } else {\n            forfeiture += p.deposit;\n        }\n\n        emit ConfirmParamProposal(_proposalId, passed, p.name, p.newValue);\n    }\n\n    function collectForfeiture() external {\n        require(forfeiture > 0, \"Nothing to collect\");\n        celerToken.safeTransfer(collector, forfeiture);\n        forfeiture = 0;\n    }\n}\n"
  },
  {
    "path": "contracts/staking/SGN.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimport \"../libraries/PbSgn.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"./Staking.sol\";\n\n/**\n * @title contract of SGN chain\n */\ncontract SGN is Pauser {\n    using SafeERC20 for IERC20;\n\n    Staking public immutable staking;\n    bytes32[] public deposits;\n    // account -> (token -> amount)\n    mapping(address => mapping(address => uint256)) public withdrawnAmts;\n    mapping(address => bytes) public sgnAddrs;\n\n    /* Events */\n    event SgnAddrUpdate(address indexed valAddr, bytes oldAddr, bytes newAddr);\n    event Deposit(uint256 depositId, address account, address token, uint256 amount);\n    event Withdraw(address account, address token, uint256 amount);\n\n    /**\n     * @notice SGN constructor\n     * @dev Need to deploy Staking contract first before deploying SGN contract\n     * @param _staking address of Staking Contract\n     */\n    constructor(Staking _staking) {\n        staking = _staking;\n    }\n\n    /**\n     * @notice Update sgn address\n     * @param _sgnAddr the new address in the layer 2 SGN\n     */\n    function updateSgnAddr(bytes calldata _sgnAddr) external {\n        address valAddr = msg.sender;\n        if (staking.signerVals(msg.sender) != address(0)) {\n            valAddr = staking.signerVals(msg.sender);\n        }\n\n        dt.ValidatorStatus status = staking.getValidatorStatus(valAddr);\n        require(status == dt.ValidatorStatus.Unbonded, \"Not unbonded validator\");\n\n        bytes memory oldAddr = sgnAddrs[valAddr];\n        sgnAddrs[valAddr] = _sgnAddr;\n\n        staking.validatorNotice(valAddr, \"sgn-addr\", _sgnAddr);\n        emit SgnAddrUpdate(valAddr, oldAddr, _sgnAddr);\n    }\n\n    /**a\n     * @notice Deposit to SGN\n     * @param _amount subscription fee paid along this function call in CELR tokens\n     */\n    function deposit(address _token, uint256 _amount) external whenNotPaused {\n        address msgSender = msg.sender;\n        deposits.push(keccak256(abi.encodePacked(msgSender, _token, _amount)));\n        IERC20(_token).safeTransferFrom(msgSender, address(this), _amount);\n        uint64 depositId = uint64(deposits.length - 1);\n        emit Deposit(depositId, msgSender, _token, _amount);\n    }\n\n    /**\n     * @notice Withdraw token\n     * @dev Here we use cumulative amount to make withdrawal process idempotent\n     * @param _withdrawalRequest withdrawal request bytes coded in protobuf\n     * @param _sigs list of validator signatures\n     */\n    function withdraw(bytes calldata _withdrawalRequest, bytes[] calldata _sigs) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Withdrawal\"));\n        staking.verifySignatures(abi.encodePacked(domain, _withdrawalRequest), _sigs);\n        PbSgn.Withdrawal memory withdrawal = PbSgn.decWithdrawal(_withdrawalRequest);\n\n        uint256 amount = withdrawal.cumulativeAmount - withdrawnAmts[withdrawal.account][withdrawal.token];\n        require(amount > 0, \"No new amount to withdraw\");\n        withdrawnAmts[withdrawal.account][withdrawal.token] = withdrawal.cumulativeAmount;\n\n        IERC20(withdrawal.token).safeTransfer(withdrawal.account, amount);\n        emit Withdraw(withdrawal.account, withdrawal.token, amount);\n    }\n\n    /**\n     * @notice Owner drains one type of tokens when the contract is paused\n     * @dev emergency use only\n     * @param _amount drained token amount\n     */\n    function drainToken(address _token, uint256 _amount) external whenPaused onlyOwner {\n        IERC20(_token).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/staking/Staking.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@openzeppelin/contracts/utils/cryptography/ECDSA.sol\";\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimport \"../interfaces/ISigsVerifier.sol\";\nimport \"../libraries/PbStaking.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"../safeguard/Whitelist.sol\";\n\n/**\n * @title A Staking contract shared by all external sidechains and apps\n */\ncontract Staking is ISigsVerifier, Pauser, Whitelist {\n    using SafeERC20 for IERC20;\n    using ECDSA for bytes32;\n\n    IERC20 public immutable CELER_TOKEN;\n\n    uint256 public bondedTokens;\n    uint256 public nextBondBlock;\n    address[] public valAddrs;\n    address[] public bondedValAddrs;\n    mapping(address => dt.Validator) public validators; // key is valAddr\n    mapping(address => address) public signerVals; // signerAddr -> valAddr\n    mapping(uint256 => bool) public slashNonces;\n\n    mapping(dt.ParamName => uint256) public params;\n    address public govContract;\n    address public rewardContract;\n    uint256 public forfeiture;\n\n    /* Events */\n    event ValidatorNotice(address indexed valAddr, string key, bytes data, address from);\n    event ValidatorStatusUpdate(address indexed valAddr, dt.ValidatorStatus indexed status);\n    event DelegationUpdate(\n        address indexed valAddr,\n        address indexed delAddr,\n        uint256 valTokens,\n        uint256 delShares,\n        int256 tokenDiff\n    );\n    event Undelegated(address indexed valAddr, address indexed delAddr, uint256 amount);\n    event Slash(address indexed valAddr, uint64 nonce, uint256 slashAmt);\n    event SlashAmtCollected(address indexed recipient, uint256 amount);\n\n    /**\n     * @notice Staking constructor\n     * @param _celerTokenAddress address of Celer Token Contract\n     * @param _proposalDeposit required deposit amount for a governance proposal\n     * @param _votingPeriod voting timeout for a governance proposal\n     * @param _unbondingPeriod the locking time for funds locked before withdrawn\n     * @param _maxBondedValidators the maximum number of bonded validators\n     * @param _minValidatorTokens the global minimum token amount requirement for bonded validator\n     * @param _minSelfDelegation minimal amount of self-delegated tokens\n     * @param _advanceNoticePeriod the wait time after the announcement and prior to the effective date of an update\n     * @param _validatorBondInterval min interval between bondValidator\n     * @param _maxSlashFactor maximal slashing factor (1e6 = 100%)\n     */\n    constructor(\n        address _celerTokenAddress,\n        uint256 _proposalDeposit,\n        uint256 _votingPeriod,\n        uint256 _unbondingPeriod,\n        uint256 _maxBondedValidators,\n        uint256 _minValidatorTokens,\n        uint256 _minSelfDelegation,\n        uint256 _advanceNoticePeriod,\n        uint256 _validatorBondInterval,\n        uint256 _maxSlashFactor\n    ) {\n        CELER_TOKEN = IERC20(_celerTokenAddress);\n\n        params[dt.ParamName.ProposalDeposit] = _proposalDeposit;\n        params[dt.ParamName.VotingPeriod] = _votingPeriod;\n        params[dt.ParamName.UnbondingPeriod] = _unbondingPeriod;\n        params[dt.ParamName.MaxBondedValidators] = _maxBondedValidators;\n        params[dt.ParamName.MinValidatorTokens] = _minValidatorTokens;\n        params[dt.ParamName.MinSelfDelegation] = _minSelfDelegation;\n        params[dt.ParamName.AdvanceNoticePeriod] = _advanceNoticePeriod;\n        params[dt.ParamName.ValidatorBondInterval] = _validatorBondInterval;\n        params[dt.ParamName.MaxSlashFactor] = _maxSlashFactor;\n    }\n\n    receive() external payable {}\n\n    /*********************************\n     * External and Public Functions *\n     *********************************/\n\n    /**\n     * @notice Initialize a validator candidate\n     * @param _signer signer address\n     * @param _minSelfDelegation minimal amount of tokens staked by the validator itself\n     * @param _commissionRate the self-declaimed commission rate\n     */\n    function initializeValidator(\n        address _signer,\n        uint256 _minSelfDelegation,\n        uint64 _commissionRate\n    ) external whenNotPaused onlyWhitelisted {\n        address valAddr = msg.sender;\n        dt.Validator storage validator = validators[valAddr];\n        require(validator.status == dt.ValidatorStatus.Null, \"Validator is initialized\");\n        require(validators[_signer].status == dt.ValidatorStatus.Null, \"Signer is other validator\");\n        require(signerVals[valAddr] == address(0), \"Validator is other signer\");\n        require(signerVals[_signer] == address(0), \"Signer already used\");\n        require(_commissionRate <= dt.COMMISSION_RATE_BASE, \"Invalid commission rate\");\n        require(_minSelfDelegation >= params[dt.ParamName.MinSelfDelegation], \"Insufficient min self delegation\");\n        validator.signer = _signer;\n        validator.status = dt.ValidatorStatus.Unbonded;\n        validator.minSelfDelegation = _minSelfDelegation;\n        validator.commissionRate = _commissionRate;\n        valAddrs.push(valAddr);\n        signerVals[_signer] = valAddr;\n\n        delegate(valAddr, _minSelfDelegation);\n        emit ValidatorNotice(valAddr, \"init\", abi.encode(_signer, _minSelfDelegation, _commissionRate), address(0));\n    }\n\n    /**\n     * @notice Update validator signer address\n     * @param _signer signer address\n     */\n    function updateValidatorSigner(address _signer) external {\n        address valAddr = msg.sender;\n        dt.Validator storage validator = validators[valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator not initialized\");\n        require(signerVals[_signer] == address(0), \"Signer already used\");\n        if (_signer != valAddr) {\n            require(validators[_signer].status == dt.ValidatorStatus.Null, \"Signer is other validator\");\n        }\n\n        delete signerVals[validator.signer];\n        validator.signer = _signer;\n        signerVals[_signer] = valAddr;\n\n        emit ValidatorNotice(valAddr, \"signer\", abi.encode(_signer), address(0));\n    }\n\n    /**\n     * @notice Candidate claims to become a bonded validator\n     * @dev caller can be either validator owner or signer\n     */\n    function bondValidator() external {\n        address valAddr = msg.sender;\n        if (signerVals[msg.sender] != address(0)) {\n            valAddr = signerVals[msg.sender];\n        }\n        dt.Validator storage validator = validators[valAddr];\n        require(\n            validator.status == dt.ValidatorStatus.Unbonded || validator.status == dt.ValidatorStatus.Unbonding,\n            \"Invalid validator status\"\n        );\n        require(block.number >= validator.bondBlock, \"Bond block not reached\");\n        require(block.number >= nextBondBlock, \"Too frequent validator bond\");\n        nextBondBlock = block.number + params[dt.ParamName.ValidatorBondInterval];\n        require(hasMinRequiredTokens(valAddr, true), \"Not have min tokens\");\n\n        uint256 maxBondedValidators = params[dt.ParamName.MaxBondedValidators];\n        // if the number of validators has not reached the max_validator_num,\n        // add validator directly\n        if (bondedValAddrs.length < maxBondedValidators) {\n            _bondValidator(valAddr);\n            _decentralizationCheck(validator.tokens);\n            return;\n        }\n        // if the number of validators has already reached the max_validator_num,\n        // add validator only if its tokens is more than the current least bonded validator tokens\n        uint256 minTokens = dt.MAX_INT;\n        uint256 minTokensIndex;\n        for (uint256 i = 0; i < maxBondedValidators; i++) {\n            if (validators[bondedValAddrs[i]].tokens < minTokens) {\n                minTokensIndex = i;\n                minTokens = validators[bondedValAddrs[i]].tokens;\n                if (minTokens == 0) {\n                    break;\n                }\n            }\n        }\n        require(validator.tokens > minTokens, \"Insufficient tokens\");\n        _replaceBondedValidator(valAddr, minTokensIndex);\n        _decentralizationCheck(validator.tokens);\n    }\n\n    /**\n     * @notice Confirm validator status from Unbonding to Unbonded\n     * @param _valAddr the address of the validator\n     */\n    function confirmUnbondedValidator(address _valAddr) external {\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status == dt.ValidatorStatus.Unbonding, \"Validator not unbonding\");\n        require(block.number >= validator.unbondBlock, \"Unbond block not reached\");\n\n        validator.status = dt.ValidatorStatus.Unbonded;\n        delete validator.unbondBlock;\n        emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Unbonded);\n    }\n\n    /**\n     * @notice Delegate CELR tokens to a validator\n     * @dev Minimal amount per delegate operation is 1 CELR\n     * @param _valAddr validator to delegate\n     * @param _tokens the amount of delegated CELR tokens\n     */\n    function delegate(address _valAddr, uint256 _tokens) public whenNotPaused {\n        address delAddr = msg.sender;\n        require(_tokens >= dt.CELR_DECIMAL, \"Minimal amount is 1 CELR\");\n\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        uint256 shares = _tokenToShare(_tokens, validator.tokens, validator.shares);\n\n        dt.Delegator storage delegator = validator.delegators[delAddr];\n        delegator.shares += shares;\n        validator.shares += shares;\n        validator.tokens += _tokens;\n        if (validator.status == dt.ValidatorStatus.Bonded) {\n            bondedTokens += _tokens;\n            _decentralizationCheck(validator.tokens);\n        }\n        CELER_TOKEN.safeTransferFrom(delAddr, address(this), _tokens);\n        emit DelegationUpdate(_valAddr, delAddr, validator.tokens, delegator.shares, int256(_tokens));\n    }\n\n    /**\n     * @notice Undelegate shares from a validator\n     * @dev Tokens are delegated by the msgSender to the validator\n     * @param _valAddr the address of the validator\n     * @param _shares undelegate shares\n     */\n    function undelegateShares(address _valAddr, uint256 _shares) external {\n        require(_shares >= dt.CELR_DECIMAL, \"Minimal amount is 1 share\");\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        uint256 tokens = _shareToToken(_shares, validator.tokens, validator.shares);\n        _undelegate(validator, _valAddr, tokens, _shares);\n    }\n\n    /**\n     * @notice Undelegate shares from a validator\n     * @dev Tokens are delegated by the msgSender to the validator\n     * @param _valAddr the address of the validator\n     * @param _tokens undelegate tokens\n     */\n    function undelegateTokens(address _valAddr, uint256 _tokens) external {\n        require(_tokens >= dt.CELR_DECIMAL, \"Minimal amount is 1 CELR\");\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        uint256 shares = _tokenToShare(_tokens, validator.tokens, validator.shares);\n        _undelegate(validator, _valAddr, _tokens, shares);\n    }\n\n    /**\n     * @notice Complete pending undelegations from a validator\n     * @param _valAddr the address of the validator\n     */\n    function completeUndelegate(address _valAddr) external {\n        address delAddr = msg.sender;\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        dt.Delegator storage delegator = validator.delegators[delAddr];\n\n        uint256 unbondingPeriod = params[dt.ParamName.UnbondingPeriod];\n        bool isUnbonded = validator.status == dt.ValidatorStatus.Unbonded;\n        // for all pending undelegations\n        uint32 i;\n        uint256 undelegationShares;\n        for (i = delegator.undelegations.head; i < delegator.undelegations.tail; i++) {\n            if (isUnbonded || delegator.undelegations.queue[i].creationBlock + unbondingPeriod <= block.number) {\n                // complete undelegation when the validator becomes unbonded or\n                // the unbondingPeriod for the pending undelegation is up.\n                undelegationShares += delegator.undelegations.queue[i].shares;\n                delete delegator.undelegations.queue[i];\n                continue;\n            }\n            break;\n        }\n        delegator.undelegations.head = i;\n\n        require(undelegationShares > 0, \"No undelegation ready to be completed\");\n        uint256 tokens = _shareToToken(undelegationShares, validator.undelegationTokens, validator.undelegationShares);\n        validator.undelegationShares -= undelegationShares;\n        validator.undelegationTokens -= tokens;\n        CELER_TOKEN.safeTransfer(delAddr, tokens);\n        emit Undelegated(_valAddr, delAddr, tokens);\n    }\n\n    /**\n     * @notice Update commission rate\n     * @param _newRate new commission rate\n     */\n    function updateCommissionRate(uint64 _newRate) external {\n        address valAddr = msg.sender;\n        dt.Validator storage validator = validators[valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        require(_newRate <= dt.COMMISSION_RATE_BASE, \"Invalid new rate\");\n        validator.commissionRate = _newRate;\n        emit ValidatorNotice(valAddr, \"commission\", abi.encode(_newRate), address(0));\n    }\n\n    /**\n     * @notice Update minimal self delegation value\n     * @param _minSelfDelegation minimal amount of tokens staked by the validator itself\n     */\n    function updateMinSelfDelegation(uint256 _minSelfDelegation) external {\n        address valAddr = msg.sender;\n        dt.Validator storage validator = validators[valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        require(_minSelfDelegation >= params[dt.ParamName.MinSelfDelegation], \"Insufficient min self delegation\");\n        if (_minSelfDelegation < validator.minSelfDelegation) {\n            require(validator.status != dt.ValidatorStatus.Bonded, \"Validator is bonded\");\n            validator.bondBlock = uint64(block.number + params[dt.ParamName.AdvanceNoticePeriod]);\n        }\n        validator.minSelfDelegation = _minSelfDelegation;\n        emit ValidatorNotice(valAddr, \"min-self-delegation\", abi.encode(_minSelfDelegation), address(0));\n    }\n\n    /**\n     * @notice Slash a validator and its delegators\n     * @param _slashRequest slash request bytes coded in protobuf\n     * @param _sigs list of validator signatures\n     */\n    function slash(bytes calldata _slashRequest, bytes[] calldata _sigs) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"Slash\"));\n        verifySignatures(abi.encodePacked(domain, _slashRequest), _sigs);\n\n        PbStaking.Slash memory request = PbStaking.decSlash(_slashRequest);\n        require(block.timestamp < request.expireTime, \"Slash expired\");\n        require(request.slashFactor <= dt.SLASH_FACTOR_DECIMAL, \"Invalid slash factor\");\n        require(request.slashFactor <= params[dt.ParamName.MaxSlashFactor], \"Exceed max slash factor\");\n        require(!slashNonces[request.nonce], \"Used slash nonce\");\n        slashNonces[request.nonce] = true;\n\n        address valAddr = request.validator;\n        dt.Validator storage validator = validators[valAddr];\n        require(\n            validator.status == dt.ValidatorStatus.Bonded || validator.status == dt.ValidatorStatus.Unbonding,\n            \"Invalid validator status\"\n        );\n\n        // slash delegated tokens\n        uint256 slashAmt = (validator.tokens * request.slashFactor) / dt.SLASH_FACTOR_DECIMAL;\n        validator.tokens -= slashAmt;\n        if (validator.status == dt.ValidatorStatus.Bonded) {\n            bondedTokens -= slashAmt;\n            if (request.jailPeriod > 0 || !hasMinRequiredTokens(valAddr, true)) {\n                _unbondValidator(valAddr);\n            }\n        }\n        if (validator.status == dt.ValidatorStatus.Unbonding && request.jailPeriod > 0) {\n            validator.bondBlock = uint64(block.number + request.jailPeriod);\n        }\n        emit DelegationUpdate(valAddr, address(0), validator.tokens, 0, -int256(slashAmt));\n\n        // slash pending undelegations\n        uint256 slashUndelegation = (validator.undelegationTokens * request.slashFactor) / dt.SLASH_FACTOR_DECIMAL;\n        validator.undelegationTokens -= slashUndelegation;\n        slashAmt += slashUndelegation;\n\n        uint256 collectAmt;\n        for (uint256 i = 0; i < request.collectors.length; i++) {\n            PbStaking.AcctAmtPair memory collector = request.collectors[i];\n            if (collectAmt + collector.amount > slashAmt) {\n                collector.amount = slashAmt - collectAmt;\n            }\n            if (collector.amount > 0) {\n                collectAmt += collector.amount;\n                if (collector.account == address(0)) {\n                    CELER_TOKEN.safeTransfer(msg.sender, collector.amount);\n                    emit SlashAmtCollected(msg.sender, collector.amount);\n                } else {\n                    CELER_TOKEN.safeTransfer(collector.account, collector.amount);\n                    emit SlashAmtCollected(collector.account, collector.amount);\n                }\n            }\n        }\n        forfeiture += slashAmt - collectAmt;\n        emit Slash(valAddr, request.nonce, slashAmt);\n    }\n\n    function collectForfeiture() external {\n        require(forfeiture > 0, \"Nothing to collect\");\n        CELER_TOKEN.safeTransfer(rewardContract, forfeiture);\n        forfeiture = 0;\n    }\n\n    /**\n     * @notice Validator notice event, could be triggered by anyone\n     */\n    function validatorNotice(\n        address _valAddr,\n        string calldata _key,\n        bytes calldata _data\n    ) external {\n        dt.Validator storage validator = validators[_valAddr];\n        require(validator.status != dt.ValidatorStatus.Null, \"Validator is not initialized\");\n        emit ValidatorNotice(_valAddr, _key, _data, msg.sender);\n    }\n\n    function setParamValue(dt.ParamName _name, uint256 _value) external {\n        require(msg.sender == govContract, \"Caller is not gov contract\");\n        if (_name == dt.ParamName.MaxBondedValidators) {\n            require(bondedValAddrs.length <= _value, \"invalid value\");\n        }\n        params[_name] = _value;\n    }\n\n    function setGovContract(address _addr) external onlyOwner {\n        govContract = _addr;\n    }\n\n    function setRewardContract(address _addr) external onlyOwner {\n        rewardContract = _addr;\n    }\n\n    /**\n     * @notice Set max slash factor\n     */\n    function setMaxSlashFactor(uint256 _maxSlashFactor) external onlyOwner {\n        params[dt.ParamName.MaxSlashFactor] = _maxSlashFactor;\n    }\n\n    /**\n     * @notice Owner drains tokens when the contract is paused\n     * @dev emergency use only\n     * @param _amount drained token amount\n     */\n    function drainToken(uint256 _amount) external whenPaused onlyOwner {\n        CELER_TOKEN.safeTransfer(msg.sender, _amount);\n    }\n\n    /**************************\n     *  Public View Functions *\n     **************************/\n\n    /**\n     * @notice Validate if a message is signed by quorum tokens\n     * @param _msg signed message\n     * @param _sigs list of validator signatures\n     */\n    function verifySignatures(bytes memory _msg, bytes[] memory _sigs) public view returns (bool) {\n        bytes32 hash = keccak256(_msg).toEthSignedMessageHash();\n        uint256 signedTokens;\n        address prev = address(0);\n        uint256 quorum = getQuorumTokens();\n        for (uint256 i = 0; i < _sigs.length; i++) {\n            address signer = hash.recover(_sigs[i]);\n            require(signer > prev, \"Signers not in ascending order\");\n            prev = signer;\n            dt.Validator storage validator = validators[signerVals[signer]];\n            if (validator.status != dt.ValidatorStatus.Bonded) {\n                continue;\n            }\n            signedTokens += validator.tokens;\n            if (signedTokens >= quorum) {\n                return true;\n            }\n        }\n        revert(\"Quorum not reached\");\n    }\n\n    /**\n     * @notice Verifies that a message is signed by a quorum among the validators.\n     * @param _msg signed message\n     * @param _sigs the list of signatures\n     */\n    function verifySigs(\n        bytes memory _msg,\n        bytes[] calldata _sigs,\n        address[] calldata,\n        uint256[] calldata\n    ) public view override {\n        require(verifySignatures(_msg, _sigs), \"Failed to verify sigs\");\n    }\n\n    /**\n     * @notice Get quorum amount of tokens\n     * @return the quorum amount\n     */\n    function getQuorumTokens() public view returns (uint256) {\n        return (bondedTokens * 2) / 3 + 1;\n    }\n\n    /**\n     * @notice Get validator info\n     * @param _valAddr the address of the validator\n     * @return Validator token amount\n     */\n    function getValidatorTokens(address _valAddr) public view returns (uint256) {\n        return validators[_valAddr].tokens;\n    }\n\n    /**\n     * @notice Get validator info\n     * @param _valAddr the address of the validator\n     * @return Validator status\n     */\n    function getValidatorStatus(address _valAddr) public view returns (dt.ValidatorStatus) {\n        return validators[_valAddr].status;\n    }\n\n    /**\n     * @notice Check the given address is a validator or not\n     * @param _addr the address to check\n     * @return the given address is a validator or not\n     */\n    function isBondedValidator(address _addr) public view returns (bool) {\n        return validators[_addr].status == dt.ValidatorStatus.Bonded;\n    }\n\n    /**\n     * @notice Get the number of validators\n     * @return the number of validators\n     */\n    function getValidatorNum() public view returns (uint256) {\n        return valAddrs.length;\n    }\n\n    /**\n     * @notice Get the number of bonded validators\n     * @return the number of bonded validators\n     */\n    function getBondedValidatorNum() public view returns (uint256) {\n        return bondedValAddrs.length;\n    }\n\n    /**\n     * @return addresses and token amounts of bonded validators\n     */\n    function getBondedValidatorsTokens() public view returns (dt.ValidatorTokens[] memory) {\n        dt.ValidatorTokens[] memory infos = new dt.ValidatorTokens[](bondedValAddrs.length);\n        for (uint256 i = 0; i < bondedValAddrs.length; i++) {\n            address valAddr = bondedValAddrs[i];\n            infos[i] = dt.ValidatorTokens(valAddr, validators[valAddr].tokens);\n        }\n        return infos;\n    }\n\n    /**\n     * @notice Check if min token requirements are met\n     * @param _valAddr the address of the validator\n     * @param _checkSelfDelegation check self delegation\n     */\n    function hasMinRequiredTokens(address _valAddr, bool _checkSelfDelegation) public view returns (bool) {\n        dt.Validator storage v = validators[_valAddr];\n        uint256 valTokens = v.tokens;\n        if (valTokens < params[dt.ParamName.MinValidatorTokens]) {\n            return false;\n        }\n        if (_checkSelfDelegation) {\n            uint256 selfDelegation = _shareToToken(v.delegators[_valAddr].shares, valTokens, v.shares);\n            if (selfDelegation < v.minSelfDelegation) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * @notice Get the delegator info of a specific validator\n     * @param _valAddr the address of the validator\n     * @param _delAddr the address of the delegator\n     * @return DelegatorInfo from the given validator\n     */\n    function getDelegatorInfo(address _valAddr, address _delAddr) public view returns (dt.DelegatorInfo memory) {\n        dt.Validator storage validator = validators[_valAddr];\n        dt.Delegator storage d = validator.delegators[_delAddr];\n        uint256 tokens = _shareToToken(d.shares, validator.tokens, validator.shares);\n\n        uint256 undelegationShares;\n        uint256 withdrawableUndelegationShares;\n        uint256 unbondingPeriod = params[dt.ParamName.UnbondingPeriod];\n        bool isUnbonded = validator.status == dt.ValidatorStatus.Unbonded;\n        uint256 len = d.undelegations.tail - d.undelegations.head;\n        dt.Undelegation[] memory undelegations = new dt.Undelegation[](len);\n        for (uint256 i = 0; i < len; i++) {\n            undelegations[i] = d.undelegations.queue[i + d.undelegations.head];\n            undelegationShares += undelegations[i].shares;\n            if (isUnbonded || undelegations[i].creationBlock + unbondingPeriod <= block.number) {\n                withdrawableUndelegationShares += undelegations[i].shares;\n            }\n        }\n        uint256 undelegationTokens = _shareToToken(\n            undelegationShares,\n            validator.undelegationTokens,\n            validator.undelegationShares\n        );\n        uint256 withdrawableUndelegationTokens = _shareToToken(\n            withdrawableUndelegationShares,\n            validator.undelegationTokens,\n            validator.undelegationShares\n        );\n\n        return\n            dt.DelegatorInfo(\n                _valAddr,\n                tokens,\n                d.shares,\n                undelegations,\n                undelegationTokens,\n                withdrawableUndelegationTokens\n            );\n    }\n\n    /**\n     * @notice Get the value of a specific uint parameter\n     * @param _name the key of this parameter\n     * @return the value of this parameter\n     */\n    function getParamValue(dt.ParamName _name) public view returns (uint256) {\n        return params[_name];\n    }\n\n    /*********************\n     * Private Functions *\n     *********************/\n\n    function _undelegate(\n        dt.Validator storage validator,\n        address _valAddr,\n        uint256 _tokens,\n        uint256 _shares\n    ) private {\n        address delAddr = msg.sender;\n        dt.Delegator storage delegator = validator.delegators[delAddr];\n        delegator.shares -= _shares;\n        validator.shares -= _shares;\n        validator.tokens -= _tokens;\n        if (validator.tokens != validator.shares && delegator.shares <= 2) {\n            // Remove residual share caused by rounding error when total shares and tokens are not equal\n            validator.shares -= delegator.shares;\n            delegator.shares = 0;\n        }\n        require(delegator.shares == 0 || delegator.shares >= dt.CELR_DECIMAL, \"not enough remaining shares\");\n\n        if (validator.status == dt.ValidatorStatus.Unbonded) {\n            CELER_TOKEN.safeTransfer(delAddr, _tokens);\n            emit Undelegated(_valAddr, delAddr, _tokens);\n            return;\n        } else if (validator.status == dt.ValidatorStatus.Bonded) {\n            bondedTokens -= _tokens;\n            if (!hasMinRequiredTokens(_valAddr, delAddr == _valAddr)) {\n                _unbondValidator(_valAddr);\n            }\n        }\n        require(\n            delegator.undelegations.tail - delegator.undelegations.head < dt.MAX_UNDELEGATION_ENTRIES,\n            \"Exceed max undelegation entries\"\n        );\n\n        uint256 undelegationShares = _tokenToShare(_tokens, validator.undelegationTokens, validator.undelegationShares);\n        validator.undelegationShares += undelegationShares;\n        validator.undelegationTokens += _tokens;\n        dt.Undelegation storage undelegation = delegator.undelegations.queue[delegator.undelegations.tail];\n        undelegation.shares = undelegationShares;\n        undelegation.creationBlock = block.number;\n        delegator.undelegations.tail++;\n\n        emit DelegationUpdate(_valAddr, delAddr, validator.tokens, delegator.shares, -int256(_tokens));\n    }\n\n    /**\n     * @notice Set validator to bonded\n     * @param _valAddr the address of the validator\n     */\n    function _setBondedValidator(address _valAddr) private {\n        dt.Validator storage validator = validators[_valAddr];\n        validator.status = dt.ValidatorStatus.Bonded;\n        delete validator.unbondBlock;\n        bondedTokens += validator.tokens;\n        emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Bonded);\n    }\n\n    /**\n     * @notice Set validator to unbonding\n     * @param _valAddr the address of the validator\n     */\n    function _setUnbondingValidator(address _valAddr) private {\n        dt.Validator storage validator = validators[_valAddr];\n        validator.status = dt.ValidatorStatus.Unbonding;\n        validator.unbondBlock = uint64(block.number + params[dt.ParamName.UnbondingPeriod]);\n        bondedTokens -= validator.tokens;\n        emit ValidatorStatusUpdate(_valAddr, dt.ValidatorStatus.Unbonding);\n    }\n\n    /**\n     * @notice Bond a validator\n     * @param _valAddr the address of the validator\n     */\n    function _bondValidator(address _valAddr) private {\n        bondedValAddrs.push(_valAddr);\n        _setBondedValidator(_valAddr);\n    }\n\n    /**\n     * @notice Replace a bonded validator\n     * @param _valAddr the address of the new validator\n     * @param _index the index of the validator to be replaced\n     */\n    function _replaceBondedValidator(address _valAddr, uint256 _index) private {\n        _setUnbondingValidator(bondedValAddrs[_index]);\n        bondedValAddrs[_index] = _valAddr;\n        _setBondedValidator(_valAddr);\n    }\n\n    /**\n     * @notice Unbond a validator\n     * @param _valAddr validator to be removed\n     */\n    function _unbondValidator(address _valAddr) private {\n        uint256 lastIndex = bondedValAddrs.length - 1;\n        for (uint256 i = 0; i < bondedValAddrs.length; i++) {\n            if (bondedValAddrs[i] == _valAddr) {\n                if (i < lastIndex) {\n                    bondedValAddrs[i] = bondedValAddrs[lastIndex];\n                }\n                bondedValAddrs.pop();\n                _setUnbondingValidator(_valAddr);\n                return;\n            }\n        }\n        revert(\"Not bonded validator\");\n    }\n\n    /**\n     * @notice Check if one validator has too much power\n     * @param _valTokens token amounts of the validator\n     */\n    function _decentralizationCheck(uint256 _valTokens) private view {\n        uint256 bondedValNum = bondedValAddrs.length;\n        if (bondedValNum == 2 || bondedValNum == 3) {\n            require(_valTokens < getQuorumTokens(), \"Single validator should not have quorum tokens\");\n        } else if (bondedValNum > 3) {\n            require(_valTokens < bondedTokens / 3, \"Single validator should not have 1/3 tokens\");\n        }\n    }\n\n    /**\n     * @notice Convert token to share\n     */\n    function _tokenToShare(\n        uint256 tokens,\n        uint256 totalTokens,\n        uint256 totalShares\n    ) private pure returns (uint256) {\n        if (totalTokens == 0) {\n            return tokens;\n        }\n        return (tokens * totalShares) / totalTokens;\n    }\n\n    /**\n     * @notice Convert share to token\n     */\n    function _shareToToken(\n        uint256 shares,\n        uint256 totalTokens,\n        uint256 totalShares\n    ) private pure returns (uint256) {\n        if (totalShares == 0) {\n            return shares;\n        }\n        return (shares * totalTokens) / totalShares;\n    }\n}\n"
  },
  {
    "path": "contracts/staking/StakingReward.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimport \"../safeguard/Pauser.sol\";\nimport \"./Staking.sol\";\n\n/**\n * @title A contract to hold and distribute CELR staking rewards.\n */\ncontract StakingReward is Pauser {\n    using SafeERC20 for IERC20;\n\n    Staking public immutable staking;\n\n    // recipient => CELR reward amount\n    mapping(address => uint256) public claimedRewardAmounts;\n\n    event StakingRewardClaimed(address indexed recipient, uint256 reward);\n    event StakingRewardContributed(address indexed contributor, uint256 contribution);\n\n    constructor(Staking _staking) {\n        staking = _staking;\n    }\n\n    /**\n     * @notice Claim reward\n     * @dev Here we use cumulative reward to make claim process idempotent\n     * @param _rewardRequest reward request bytes coded in protobuf\n     * @param _sigs list of validator signatures\n     */\n    function claimReward(bytes calldata _rewardRequest, bytes[] calldata _sigs) external whenNotPaused {\n        bytes32 domain = keccak256(abi.encodePacked(block.chainid, address(this), \"StakingReward\"));\n        staking.verifySignatures(abi.encodePacked(domain, _rewardRequest), _sigs);\n        PbStaking.StakingReward memory reward = PbStaking.decStakingReward(_rewardRequest);\n\n        uint256 cumulativeRewardAmount = reward.cumulativeRewardAmount;\n        uint256 newReward = cumulativeRewardAmount - claimedRewardAmounts[reward.recipient];\n        require(newReward > 0, \"No new reward\");\n        claimedRewardAmounts[reward.recipient] = cumulativeRewardAmount;\n        staking.CELER_TOKEN().safeTransfer(reward.recipient, newReward);\n        emit StakingRewardClaimed(reward.recipient, newReward);\n    }\n\n    /**\n     * @notice Contribute CELR tokens to the reward pool\n     * @param _amount the amount of CELR token to contribute\n     */\n    function contributeToRewardPool(uint256 _amount) external whenNotPaused {\n        address contributor = msg.sender;\n        IERC20(staking.CELER_TOKEN()).safeTransferFrom(contributor, address(this), _amount);\n\n        emit StakingRewardContributed(contributor, _amount);\n    }\n\n    /**\n     * @notice Owner drains CELR tokens when the contract is paused\n     * @dev emergency use only\n     * @param _amount drained CELR token amount\n     */\n    function drainToken(uint256 _amount) external whenPaused onlyOwner {\n        IERC20(staking.CELER_TOKEN()).safeTransfer(msg.sender, _amount);\n    }\n}\n"
  },
  {
    "path": "contracts/staking/Viewer.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport {DataTypes as dt} from \"./DataTypes.sol\";\nimport \"./Staking.sol\";\n\n/**\n * @title Viewer of the staking contract\n * @notice Using a separate viewer contract to reduce staking contract size\n */\ncontract Viewer {\n    Staking public immutable staking;\n\n    constructor(Staking _staking) {\n        staking = _staking;\n    }\n\n    function getValidatorInfos() public view returns (dt.ValidatorInfo[] memory) {\n        uint256 valNum = staking.getValidatorNum();\n        dt.ValidatorInfo[] memory infos = new dt.ValidatorInfo[](valNum);\n        for (uint32 i = 0; i < valNum; i++) {\n            infos[i] = getValidatorInfo(staking.valAddrs(i));\n        }\n        return infos;\n    }\n\n    function getBondedValidatorInfos() public view returns (dt.ValidatorInfo[] memory) {\n        uint256 bondedValNum = staking.getBondedValidatorNum();\n        dt.ValidatorInfo[] memory infos = new dt.ValidatorInfo[](bondedValNum);\n        for (uint32 i = 0; i < bondedValNum; i++) {\n            infos[i] = getValidatorInfo(staking.bondedValAddrs(i));\n        }\n        return infos;\n    }\n\n    function getValidatorInfo(address _valAddr) public view returns (dt.ValidatorInfo memory) {\n        (\n            dt.ValidatorStatus status,\n            address signer,\n            uint256 tokens,\n            uint256 shares,\n            ,\n            ,\n            uint256 minSelfDelegation,\n            ,\n            ,\n            uint64 commissionRate\n        ) = staking.validators(_valAddr);\n        return\n            dt.ValidatorInfo({\n                valAddr: _valAddr,\n                status: status,\n                signer: signer,\n                tokens: tokens,\n                shares: shares,\n                minSelfDelegation: minSelfDelegation,\n                commissionRate: commissionRate\n            });\n    }\n\n    function getDelegatorInfos(address _delAddr) public view returns (dt.DelegatorInfo[] memory) {\n        uint256 valNum = staking.getValidatorNum();\n        dt.DelegatorInfo[] memory infos = new dt.DelegatorInfo[](valNum);\n        uint32 num = 0;\n        for (uint32 i = 0; i < valNum; i++) {\n            address valAddr = staking.valAddrs(i);\n            infos[i] = staking.getDelegatorInfo(valAddr, _delAddr);\n            if (infos[i].shares != 0 || infos[i].undelegationTokens != 0) {\n                num++;\n            }\n        }\n        dt.DelegatorInfo[] memory res = new dt.DelegatorInfo[](num);\n        uint32 j = 0;\n        for (uint32 i = 0; i < valNum; i++) {\n            if (infos[i].shares != 0 || infos[i].undelegationTokens != 0) {\n                res[j] = infos[i];\n                j++;\n            }\n        }\n        return res;\n    }\n\n    function getDelegatorTokens(address _delAddr) public view returns (uint256, uint256) {\n        dt.DelegatorInfo[] memory infos = getDelegatorInfos(_delAddr);\n        uint256 tokens;\n        uint256 undelegationTokens;\n        for (uint32 i = 0; i < infos.length; i++) {\n            tokens += infos[i].tokens;\n            undelegationTokens += infos[i].undelegationTokens;\n        }\n        return (tokens, undelegationTokens);\n    }\n\n    /**\n     * @notice Get the minimum staking pool of all bonded validators\n     * @return the minimum staking pool of all bonded validators\n     */\n    function getMinValidatorTokens() public view returns (uint256) {\n        uint256 bondedValNum = staking.getBondedValidatorNum();\n        if (bondedValNum < staking.params(dt.ParamName.MaxBondedValidators)) {\n            return 0;\n        }\n        uint256 minTokens = dt.MAX_INT;\n        for (uint256 i = 0; i < bondedValNum; i++) {\n            uint256 tokens = staking.getValidatorTokens(staking.bondedValAddrs(i));\n            if (tokens < minTokens) {\n                minTokens = tokens;\n                if (minTokens == 0) {\n                    return 0;\n                }\n            }\n        }\n        return minTokens;\n    }\n\n    function shouldBondValidator(address _valAddr) public view returns (bool) {\n        (dt.ValidatorStatus status, , uint256 tokens, , , , , uint64 bondBlock, , ) = staking.validators(_valAddr);\n        if (status == dt.ValidatorStatus.Null || status == dt.ValidatorStatus.Bonded) {\n            return false;\n        }\n        if (block.number < bondBlock) {\n            return false;\n        }\n        if (!staking.hasMinRequiredTokens(_valAddr, true)) {\n            return false;\n        }\n        if (tokens <= getMinValidatorTokens()) {\n            return false;\n        }\n        uint256 nextBondBlock = staking.nextBondBlock();\n        if (block.number < nextBondBlock) {\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "contracts/test-helpers/DummySwap.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\ncontract DummySwap {\n    using SafeERC20 for IERC20;\n\n    uint256 fakeSlippage; // 100% = 100 * 1e4\n    uint256 hundredPercent = 100 * 1e4;\n\n    constructor(uint256 _fakeSlippage) {\n        fakeSlippage = _fakeSlippage;\n    }\n\n    function swapExactTokensForTokens(\n        uint256 amountIn,\n        uint256 amountOutMin,\n        address[] calldata path,\n        address to,\n        uint256 deadline\n    ) external returns (uint256[] memory amounts) {\n        require(deadline != 0 && deadline > block.timestamp, \"deadline exceeded\");\n        require(path.length > 1, \"path must have more than 1 token in it\");\n        IERC20(path[0]).transferFrom(msg.sender, address(this), amountIn);\n        // fake simulate slippage\n        uint256 amountAfterSlippage = (amountIn * (hundredPercent - fakeSlippage)) / hundredPercent;\n        require(amountAfterSlippage > amountOutMin, \"bad slippage\");\n\n        IERC20(path[path.length - 1]).safeTransfer(to, amountAfterSlippage);\n        uint256[] memory ret = new uint256[](2);\n        ret[0] = amountIn;\n        ret[1] = amountAfterSlippage;\n        return ret;\n    }\n\n    function setFakeSlippage(uint256 _fakeSlippage) public {\n        fakeSlippage = _fakeSlippage;\n    }\n}\n"
  },
  {
    "path": "contracts/test-helpers/TestERC20.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/**\n * @title A test ERC20 token.\n */\ncontract TestERC20 is ERC20 {\n    uint256 public constant INITIAL_SUPPLY = 1e28;\n\n    /**\n     * @dev Constructor that gives msg.sender all of the existing tokens.\n     */\n    constructor() ERC20(\"TestERC20\", \"TERC20\") {\n        _mint(msg.sender, INITIAL_SUPPLY);\n    }\n}\n"
  },
  {
    "path": "contracts/test-helpers/WETH.sol",
    "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity >=0.8.9;\nimport \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\ncontract WETH is ERC20 {\n    constructor() ERC20(\"WETH\", \"WETH\") {}\n\n    function deposit() external payable {\n        _mint(msg.sender, msg.value);\n    }\n\n    function withdraw(uint256 _amount) external {\n        _burn(msg.sender, _amount);\n        (bool sent, ) = msg.sender.call{value: _amount, gas: 50000}(\"\");\n        require(sent, \"failed to send\");\n    }\n\n    receive() external payable {}\n}\n"
  },
  {
    "path": "deploy/circle-usdc/000_circle_bridge_proxy.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('CircleBridgeProxy', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.CIRCLE_BRIDGE,\n      process.env.CIRCLE_BRIDGE_FEE_COLLECTOR\n    ]\n  });\n};\n\ndeployFunc.tags = ['CircleBridgeProxy'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/circle-usdc/000_circle_bridge_proxy_v2.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('CircleBridgeProxyV2', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.CIRCLE_BRIDGE,\n      process.env.CIRCLE_BRIDGE_FEE_COLLECTOR\n    ]\n  });\n};\n\ndeployFunc.tags = ['CircleBridgeProxyV2'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/core/000_sgn_staking.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('Staking', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.CELR,\n      process.env.PROPOSAL_DEPOSIT,\n      process.env.VOTING_PERIOD,\n      process.env.UNBONDING_PERIOD,\n      process.env.MAX_VALIDATOR_NUM,\n      process.env.MIN_VALIDATOR_TOKENS,\n      process.env.MIN_SELF_DELEGATION,\n      process.env.ADVANCE_NOTICE_PERIOD,\n      process.env.VALIDATOR_BOND_INTERVAL,\n      process.env.MAX_SLASH_FACTOR\n    ]\n  });\n  const staking = await deployments.get('Staking');\n  await deploy('SGN', {\n    from: deployer,\n    log: true,\n    args: [staking.address]\n  });\n  await deploy('StakingReward', {\n    from: deployer,\n    log: true,\n    args: [staking.address]\n  });\n  const stakingReward = await deployments.get('StakingReward');\n  await deploy('Govern', {\n    from: deployer,\n    log: true,\n    args: [staking.address, process.env.CELR, stakingReward.address]\n  });\n  await deploy('Viewer', {\n    from: deployer,\n    log: true,\n    args: [staking.address]\n  });\n};\n\ndeployFunc.tags = ['SGNStaking'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/governed-owner/000_governed_owner.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const ownerProxyArgs = [process.env.GOVERNANCE_INITIALIZER];\n  const governedOwnerProxy = await deploy('GovernedOwnerProxy', {\n    from: deployer,\n    log: true,\n    args: ownerProxyArgs\n  });\n  await hre.run('verify:verify', { address: governedOwnerProxy.address, constructorArguments: ownerProxyArgs });\n\n  const voters = (process.env.GOVERNANCE_VOTERS as string).split(',');\n  const powers = (process.env.GOVERNANCE_POWERS as string).split(',');\n  const governanceArgs = [\n    voters,\n    powers,\n    [governedOwnerProxy.address],\n    process.env.GOVERNANCE_ACTIVE_PERIOD,\n    process.env.GOVERNANCE_QUORUM_THRESHOLD,\n    process.env.GOVERNANCE_FAST_PASS_THRESHOLD\n  ];\n  const simpleGovernance = await deploy('SimpleGovernance', {\n    from: deployer,\n    log: true,\n    args: governanceArgs\n  });\n  await hre.run('verify:verify', { address: simpleGovernance.address, constructorArguments: governanceArgs });\n};\n\ndeployFunc.tags = ['GovernedOwner'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/governed-owner/customized/000_message_bus_owner.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const voters = (process.env.GOVERNANCE_VOTERS as string).split(',');\n  const powers = (process.env.GOVERNANCE_POWERS as string).split(',');\n  const messageBusOwnerArgs = [\n    voters,\n    powers,\n    process.env.GOVERNANCE_ACTIVE_PERIOD,\n    process.env.GOVERNANCE_QUORUM_THRESHOLD\n  ];\n  const messageBusOwner = await deploy('MessageBusOwner', {\n    from: deployer,\n    log: true,\n    args: messageBusOwnerArgs\n  });\n  await hre.run('verify:verify', { address: messageBusOwner.address, constructorArguments: messageBusOwnerArgs });\n};\n\ndeployFunc.tags = ['MessageBusOwner'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/liquidity-bridge/000_bridge.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const bridge = await deploy('Bridge', {\n    from: deployer,\n    log: true\n  });\n  await hre.run('verify:verify', { address: bridge.address });\n};\n\ndeployFunc.tags = ['Bridge'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/liquidity-bridge/001_farming_rewards.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [process.env.FARMING_REWARDS_SIGS_VERIFIER];\n\n  const farmingRewards = await deploy('FarmingRewards', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: farmingRewards.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['FarmingRewards'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/000_message_bus_init.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { AbiCoder } from 'ethers';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nimport { MessageBus__factory } from '../../typechain';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const initArgs: [string, string, string, string, string] = [\n    process.env.MESSAGE_BUS_LIQUIDITY_BRIDGE as string,\n    process.env.MESSAGE_BUS_PEG_BRIDGE as string,\n    process.env.MESSAGE_BUS_PEG_VAULT as string,\n    process.env.MESSAGE_BUS_PEG_BRIDGE_V2 as string,\n    process.env.MESSAGE_BUS_PEG_VAULT_V2 as string\n  ];\n  const constructorArgs = [process.env.MESSAGE_BUS_SIGS_VERIFIER].concat(initArgs);\n\n  await deploy('MessageBus', {\n    from: deployer,\n    log: true,\n    args: constructorArgs,\n    proxy: {\n      proxyContract: 'OptimizedTransparentProxy',\n      execute: {\n        // only called when proxy is deployed, it'll call MessageBus contract.init\n        // with proper args\n        init: {\n          methodName: 'init',\n          args: initArgs\n        }\n      }\n    }\n  });\n  const messageBusInterface = MessageBus__factory.createInterface();\n  const encodedInitData = messageBusInterface.encodeFunctionData('init', initArgs);\n  console.log('Encoded init data', encodedInitData);\n  const proxyAdmin = await deployments.get('DefaultProxyAdmin');\n  console.log('DefaultProxyAdmin', proxyAdmin.address);\n  const proxy = await deployments.get('MessageBus_Proxy');\n  console.log('MessageBus_Proxy', proxy.address);\n  const messageBus = await deployments.get('MessageBus_Implementation');\n  await hre.run('verify:verify', { address: messageBus.address, constructorArguments: constructorArgs });\n  // Have to manually verify because hardhat-deploy compiles proxy with 0.8.10\n  // const proxyArgs = [messageBus.address, proxyAdmin.address].concat(encodedInitData);\n  // await hre.run('verify:verify', { address: proxy.address, constructorArguments: proxyArgs });\n  console.log(\n    'Encoded proxy constructor args',\n    AbiCoder.defaultAbiCoder().encode(\n      ['address', 'address', 'bytes'],\n      [messageBus.address, proxyAdmin.address, encodedInitData]\n    )\n  );\n};\n\ndeployFunc.tags = ['MessageBusInit'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/001_message_bus.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const constructorArgs = [\n    process.env.MESSAGE_BUS_SIGS_VERIFIER,\n    process.env.MESSAGE_BUS_LIQUIDITY_BRIDGE as string,\n    process.env.MESSAGE_BUS_PEG_BRIDGE as string,\n    process.env.MESSAGE_BUS_PEG_VAULT as string,\n    process.env.MESSAGE_BUS_PEG_BRIDGE_V2 as string,\n    process.env.MESSAGE_BUS_PEG_VAULT_V2 as string\n  ];\n\n  const result = await deploy('MessageBus', {\n    from: deployer,\n    log: true,\n    args: constructorArgs\n  });\n  await hre.run('verify:verify', { address: result.address, constructorArguments: constructorArgs });\n};\n\ndeployFunc.tags = ['MessageBus'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/000_transfer_swap.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('TransferSwap', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.CC_SWAP_MSG_BUS,\n      process.env.CC_SWAP_DEX,\n      process.env.CC_SWAP_BRIDGE,\n      process.env.CC_SWAP_BRIDGE_TOKEN,\n      process.env.CC_SWAP_NATIVE_WRAP\n    ]\n  });\n};\n\ndeployFunc.tags = ['TransferSwap'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/001_nft_bridge.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('NFTBridge', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MSG_BUS_ADDR\n    ],\n    proxy: {\n      proxyContract: \"OptimizedTransparentProxy\",\n      execute: {\n        // only called when proxy is deployed, it'll call NFTBridge.init\n        // to set owner and msgbus in proxy contract state\n        init: {\n          methodName: 'init',\n          args: [\n            process.env.MSG_BUS_ADDR\n          ]\n        }\n      }\n    }\n  });\n};\n\ndeployFunc.tags = ['NFTBridge'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/002_peg_nft.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('PegNFT', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.NFT_NAME,\n      process.env.NFT_SYM,\n      process.env.NFT_BRIDGE_ADDR\n    ]\n  });\n};\n\ndeployFunc.tags = ['PegNFT'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/003_orig_nft.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('OrigNFT', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.NFT_NAME,\n      process.env.NFT_SYM\n    ]\n  });\n};\n\ndeployFunc.tags = ['OrigNFT'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/004_mcn_nft.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MCNNFT', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.NFT_NAME,\n      process.env.NFT_SYM,\n      process.env.NFT_BRIDGE_ADDR\n    ]\n  });\n};\n\ndeployFunc.tags = ['MCNNFT'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/005_msg_test.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MsgTest', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MESSAGE_BUS_ADDR,\n    ]\n  });\n};\n\ndeployFunc.tags = ['MsgTest'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/006_rfq.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n    const { deployments, getNamedAccounts } = hre;\n    const { deploy } = deployments;\n    const { deployer } = await getNamedAccounts();\n\n    await deploy('RFQ', {\n        from: deployer,\n        log: true,\n        args: [process.env.MESSAGE_BUS_ADDR]\n    });\n};\n\ndeployFunc.tags = ['RFQ'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/message/apps/007_adapter.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n    const { deployments, getNamedAccounts } = hre;\n    const { deploy } = deployments;\n    const { deployer } = await getNamedAccounts();\n\n    await deploy('MessageReceiverAdapter', {\n        from: deployer,\n        log: true,\n        args: [process.env.MESSAGE_BUS_ADDR]\n    });\n};\n\ndeployFunc.tags = ['MessageReceiverAdapter'];\ndeployFunc.dependencies = [];\nexport default deployFunc;"
  },
  {
    "path": "deploy/miscs/000_test_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.TEST_TOKEN_NAME,\n    process.env.TEST_TOKEN_SYMBOL,\n    process.env.TEST_TOKEN_DECIMALS,\n    process.env.TEST_TOKEN_SUPPLY\n  ];\n\n  const testToken = await deploy('MintableERC20', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: testToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['Test' + (process.env.TEST_TOKEN_SYMBOL || 'Token')];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/001_faucet.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('Faucet', {\n    from: deployer,\n    log: true\n  });\n};\n\ndeployFunc.tags = ['Faucet'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/002_dummy_swap.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('DummySwap', {\n    from: deployer,\n    log: true,\n    args: [50000] // 5% fake slippage\n  });\n};\n\ndeployFunc.tags = ['DummySwap'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/003_withdraw_inbox.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const withdrawInbox = await deploy('WithdrawInbox', {\n    from: deployer,\n    log: true\n  });\n  await hre.run('verify:verify', { address: withdrawInbox.address });\n};\n\ndeployFunc.tags = ['WithdrawInbox'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/004_contract_as_lp.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('ContractAsLP', {\n    from: deployer,\n    log: true,\n    args: [process.env.BRIDGE, process.env.WITHDRAW_INBOX]\n  });\n};\n\ndeployFunc.tags = ['ContractAsLP'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/005_contract_as_sender.ts",
    "content": "import { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('ContractAsSender', {\n    from: deployer,\n    log: true\n  });\n};\n\ndeployFunc.tags = ['ContractAsSender'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/006_transfer_agent.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n    const { deployments, getNamedAccounts } = hre;\n    const { deploy } = deployments;\n    const { deployer } = await getNamedAccounts();\n\n    await deploy('TransferAgent', {\n        from: deployer,\n        log: true\n    });\n};\n\ndeployFunc.tags = ['TransferAgent'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/miscs/007_default_proxy_admin.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n  const args = [process.env.PROXY_ADMIN_OWNER];\n  const proxyAdmin = await deploy('DefaultProxyAdmin', {\n    contract: 'ProxyAdmin',\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: proxyAdmin.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['DefaultProxyAdmin'];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/000_original_token_vault.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('OriginalTokenVault', {\n    from: deployer,\n    log: true,\n    args: [process.env.ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER]\n  });\n};\n\ndeployFunc.tags = ['OriginalTokenVault'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/001_pegged_token_bridge.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('PeggedTokenBridge', {\n    from: deployer,\n    log: true,\n    args: [process.env.PEGGED_TOKEN_BRIDGE_SIGS_VERIFIER]\n  });\n};\n\ndeployFunc.tags = ['PeggedTokenBridge'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/002_original_token_vault_v2.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [process.env.ORIGINAL_TOKEN_VAULT_SIGS_VERIFIER];\n\n  const originalTokenVaultV2 = await deploy('OriginalTokenVaultV2', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: originalTokenVaultV2.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['OriginalTokenVaultV2'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/003_pegged_token_bridge_v2.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [process.env.PEGGED_TOKEN_BRIDGE_SIGS_VERIFIER];\n\n  const peggedTokenBridgeV2 = await deploy('PeggedTokenBridgeV2', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: peggedTokenBridgeV2.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['PeggedTokenBridgeV2'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/004_pegged_brc20_bridge.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [process.env.PEGGED_BRC20_BRIDGE_MINTER];\n\n  const peggedBrc20Bridge = await deploy('PeggedBrc20Bridge', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: peggedBrc20Bridge.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['PeggedBrc20Bridge'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/customized/000_pegged_native_token_bridge.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [process.env.PEGGED_TOKEN_BRIDGE_SIGS_VERIFIER];\n\n  const peggedTokenBridgeV2 = await deploy('PeggedNativeTokenBridge', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: peggedTokenBridgeV2.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['PeggedNativeTokenBridge'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/000_single_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('SingleBridgeToken', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.SINGLE_BRIDGE_TOKEN_NAME,\n      process.env.SINGLE_BRIDGE_TOKEN_SYMBOL,\n      process.env.SINGLE_BRIDGE_TOKEN_DECIMALS,\n      process.env.SINGLE_BRIDGE_TOKEN_BRIDGE\n    ]\n  });\n};\n\ndeployFunc.tags = ['SingleBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/001_single_bridge_token_permit.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('SingleBridgeTokenPermit', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.SINGLE_BRIDGE_TOKEN_PERMIT_NAME,\n      process.env.SINGLE_BRIDGE_TOKEN_PERMIT_SYMBOL,\n      process.env.SINGLE_BRIDGE_TOKEN_PERMIT_DECIMALS,\n      process.env.SINGLE_BRIDGE_TOKEN_PERMIT_BRIDGE\n    ]\n  });\n};\n\ndeployFunc.tags = ['SingleBridgeTokenPermit'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/002_multi_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n  const args = [\n    process.env.MULTI_BRIDGE_TOKEN_NAME,\n    process.env.MULTI_BRIDGE_TOKEN_SYMBOL,\n    process.env.MULTI_BRIDGE_TOKEN_DECIMALS\n  ];\n  const multiBridgeToken = await deploy('MultiBridgeToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: multiBridgeToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['MultiBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/003_multi_bridge_token_permit.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MultiBridgeTokenPermit', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MULTI_BRIDGE_TOKEN_PERMIT_NAME,\n      process.env.MULTI_BRIDGE_TOKEN_PERMIT_SYMBOL,\n      process.env.MULTI_BRIDGE_TOKEN_PERMIT_DECIMALS\n    ]\n  });\n};\n\ndeployFunc.tags = ['MultiBridgeTokenPermit'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/004_mint_swap_canonical_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n    process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL,\n    process.env.MINT_SWAP_CANONICAL_TOKEN_DECIMALS\n  ];\n  const mintSwapCanonicalToken = await deploy('MintSwapCanonicalToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: mintSwapCanonicalToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['MintSwapCanonicalToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/005_mint_swap_canonical_token_permit.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MintSwapCanonicalTokenPermit', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MINT_SWAP_CANONICAL_TOKEN_PERMIT_NAME,\n      process.env.MULTI_BRIDGE_TOKEN_PERMIT_SYMBOL,\n      process.env.MULTI_BRIDGE_TOKEN_PERMIT_DECIMALS\n    ]\n  });\n};\n\ndeployFunc.tags = ['MintSwapCanonicalTokenPermit'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/006_mint_swap_canonical_token_upgradable.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MintSwapCanonicalTokenUpgradable', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_DECIMALS\n    ],\n    proxy: {\n      proxyContract: \"OptimizedTransparentProxy\",\n      execute: {\n        // only called when proxy is deployed, it'll call Token contract.init\n        // with proper args\n        init: {\n          methodName: 'init',\n          args: [\n            process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n            process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL]\n        }\n      }\n    }\n  });\n};\n\ndeployFunc.tags = ['MintSwapCanonicalTokenUpgradable'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/007_wrapped_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.WRAPPED_BRIDGE_TOKEN_NAME,\n    process.env.WRAPPED_BRIDGE_TOKEN_SYMBOL,\n    process.env.WRAPPED_BRIDGE_TOKEN_BRIDGE,\n    process.env.WRAPPED_BRIDGE_TOKEN_CANONICAL\n  ];\n  const wrappedBridgeToken = await deploy('WrappedBridgeToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: wrappedBridgeToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['WrappedBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/008_intermediary_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.INTERMEDIARY_BRIDGE_TOKEN_NAME,\n    process.env.INTERMEDIARY_BRIDGE_TOKEN_SYMBOL,\n    process.env.INTERMEDIARY_BRIDGE_TOKEN_BRIDGE,\n    process.env.INTERMEDIARY_BRIDGE_TOKEN_CANONICAL\n  ];\n  const intermediaryBridgeToken = await deploy('IntermediaryBridgeToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: intermediaryBridgeToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['IntermediaryBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/009_intermediary_original_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const bridges = (process.env.INTERMEDIARY_ORIGINAL_TOKEN_BRIDGES as string).split(',');\n  const args = [\n    process.env.INTERMEDIARY_ORIGINAL_TOKEN_NAME,\n    process.env.INTERMEDIARY_ORIGINAL_TOKEN_SYMBOL,\n    bridges,\n    process.env.INTERMEDIARY_ORIGINAL_TOKEN_CANONICAL\n  ];\n  const intermediaryOriginalToken = await deploy('IntermediaryOriginalToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: intermediaryOriginalToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['IntermediaryOriginalToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/010_circle_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.CIRCLE_BRIDGE_TOKEN_NAME,\n    process.env.CIRCLE_BRIDGE_TOKEN_SYMBOL,\n    process.env.CIRCLE_BRIDGE_TOKEN_BRIDGE,\n    process.env.CIRCLE_BRIDGE_TOKEN_CANONICAL,\n    process.env.CIRCLE_BRIDGE_TOKEN_ORIG_CHAIN_ID\n  ];\n  const circleBridgeToken = await deploy('CircleBridgeToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  const txHash = circleBridgeToken.transactionHash!;\n  const tx = await hre.ethers.provider.getTransaction(txHash);\n  console.log('deploy tx data');\n  console.log(tx.data);\n  await hre.run('verify:verify', { address: circleBridgeToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['CircleBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/customized/000_frax_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args = [\n    process.env.FRAX_BRIDGE_TOKEN_NAME,\n    process.env.FRAX_BRIDGE_TOKEN_SYMBOL,\n    process.env.FRAX_BRIDGE_TOKEN_BRIDGE,\n    process.env.FRAX_BRIDGE_TOKEN_CANONICAL\n  ];\n\n  const fraxBridgeToken = await deploy('FraxBridgeToken', {\n    from: deployer,\n    log: true,\n    args: args\n  });\n  await hre.run('verify:verify', { address: fraxBridgeToken.address, constructorArguments: args });\n};\n\ndeployFunc.tags = ['FraxBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/customized/001_mai_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MaiBridgeToken', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MAI_BRIDGE_TOKEN_NAME,\n      process.env.MAI_BRIDGE_TOKEN_SYMBOL,\n      process.env.MAI_BRIDGE_TOKEN_BRIDGE,\n      process.env.MAI_BRIDGE_TOKEN_HUB\n    ]\n  });\n};\n\ndeployFunc.tags = ['MaiBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/customized/002_ontology_bridge_token.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('OntologyBridgeToken', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.ONTOLOGY_BRIDGE_TOKEN_NAME,\n      process.env.ONTOLOGY_BRIDGE_TOKEN_SYMBOL,\n      process.env.ONTOLOGY_BRIDGE_TOKEN_BRIDGE,\n      process.env.ONTOLOGY_BRIDGE_TOKEN_WRAPPER,\n      process.env.ONTOLOGY_BRIDGE_TOKEN_CANONICAL\n    ]\n  });\n};\n\ndeployFunc.tags = ['OntologyBridgeToken'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/freezable/000_mint_swap_canonical_token_upgradable_freezable.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MintSwapCanonicalTokenUpgradableFreezable', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_DECIMALS\n    ],\n    proxy: {\n      proxyContract: 'OptimizedTransparentProxy',\n      execute: {\n        // only called when proxy is deployed, it'll call Token contract.init\n        // with proper args\n        init: {\n          methodName: 'init',\n          args: [process.env.MINT_SWAP_CANONICAL_TOKEN_NAME, process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL]\n        }\n      }\n    }\n  });\n};\n\ndeployFunc.tags = ['MintSwapCanonicalTokenUpgradableFreezable'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/freezable/001_mint_swap_canonical_token_freezable.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('MintSwapCanonicalTokenFreezable', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.MINT_SWAP_CANONICAL_TOKEN_NAME,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_SYMBOL,\n      process.env.MINT_SWAP_CANONICAL_TOKEN_DECIMALS\n    ]\n  });\n};\n\ndeployFunc.tags = ['MintSwapCanonicalTokenFreezable'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/pegged-bridge/tokens/owners/000_restricted_multi_bridge_token_owner.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  await deploy('RestrictedMultiBridgeTokenOwner', {\n    from: deployer,\n    log: true,\n    args: [\n      process.env.RESTRICTED_MULTI_BRIDGE_TOKEN_OWNER_TOKEN,\n      process.env.RESTRICTED_MULTI_BRIDGE_TOKEN_OWNER_BRIDGE\n    ]\n  });\n};\n\ndeployFunc.tags = ['RestrictedMultiBridgeTokenOwner'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/sentinel/000_sentinel.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { AbiCoder } from 'ethers';\nimport { DeployFunction } from 'hardhat-deploy/types';\nimport { HardhatRuntimeEnvironment } from 'hardhat/types';\n\nimport { Sentinel__factory } from '../../typechain';\n\ndotenv.config();\n\nconst deployFunc: DeployFunction = async (hre: HardhatRuntimeEnvironment) => {\n  const { deployments, getNamedAccounts } = hre;\n  const { deploy } = deployments;\n  const { deployer } = await getNamedAccounts();\n\n  const args: [string[], string[], string[]] = [\n    (process.env.SENTINEL_GUARDS as string).split(','),\n    (process.env.SENTINEL_PAUSERS as string).split(','),\n    (process.env.SENTINEL_GOVERNORS as string).split(',')\n  ];\n  await deploy('Sentinel', {\n    from: deployer,\n    log: true,\n    args: args,\n    proxy: {\n      proxyContract: 'OptimizedTransparentProxy',\n      owner: process.env.PROXY_ADMIN_OWNER,\n      viaAdminContract: { name: 'DefaultProxyAdmin', artifact: 'ProxyAdmin' }, // TODO: Check\n      execute: {\n        init: {\n          methodName: 'init',\n          args: args\n        }\n      }\n    }\n  });\n  const sentinelInterface = Sentinel__factory.createInterface();\n  const encodedInitData = sentinelInterface.encodeFunctionData('init', args);\n  console.log('Encoded init data', encodedInitData);\n  const proxyAdmin = await deployments.get('DefaultProxyAdmin');\n  console.log('DefaultProxyAdmin', proxyAdmin.address);\n  const proxy = await deployments.get('Sentinel_Proxy');\n  console.log('Sentinel_Proxy', proxy.address);\n  const sentinel = await deployments.get('Sentinel_Implementation');\n  await hre.run('verify:verify', { address: sentinel.address, constructorArguments: args });\n  // Have to manually verify because hardhat-deploy compiles proxy with 0.8.10\n  // const proxyArgs = [sentinel.address, proxyAdmin.address].concat(encodedInitData);\n  // await hre.run('verify:verify', { address: proxy.address, constructorArguments: proxyArgs });\n  console.log(\n    'Encoded proxy constructor args',\n    AbiCoder.defaultAbiCoder().encode(\n      ['address', 'address', 'bytes'],\n      [sentinel.address, proxyAdmin.address, encodedInitData]\n    )\n  );\n};\n\ndeployFunc.tags = ['SentinelUpgradable'];\ndeployFunc.dependencies = [];\nexport default deployFunc;\n"
  },
  {
    "path": "deploy/sentinel/001_sentinel_zksync.ts",
    "content": "// Uncomment for zksync\n// import * as dotenv from 'dotenv';\n// import * as hre from 'hardhat';\n// import { Wallet } from 'zksync-web3';\n\n// import { Deployer } from '@matterlabs/hardhat-zksync-deploy';\n\n// dotenv.config();\n\n// async function deploy() {\n//   const contractName = 'Sentinel';\n//   console.log('Deploying ' + contractName + '...');\n\n//   const zkWallet = new Wallet(process.env.ZKSYNC_ERA_PRIVATE_KEY as string);\n//   const deployer = new Deployer(hre, zkWallet);\n\n//   const contract = await deployer.loadArtifact(contractName);\n//   const args: [string[], string[], string[]] = [\n//     (process.env.SENTINEL_GUARDS as string).split(','),\n//     (process.env.SENTINEL_PAUSERS as string).split(','),\n//     (process.env.SENTINEL_GOVERNORS as string).split(',')\n//   ];\n//   const sentinelProxy = await hre.zkUpgrades.deployProxy(deployer.zkWallet, contract, args, {\n//     initializer: 'init',\n//     unsafeAllow: ['constructor']\n//   });\n\n//   await sentinelProxy.deployed();\n//   console.log(contractName + ' deployed to:', sentinelProxy.address);\n// }\n\n// deploy().catch((error) => {\n//   console.error(error);\n//   process.exitCode = 1;\n// });\n"
  },
  {
    "path": "hardhat.config.ts",
    "content": "import '@nomicfoundation/hardhat-ethers';\nimport '@nomicfoundation/hardhat-toolbox';\nimport '@nomicfoundation/hardhat-verify';\nimport '@typechain/hardhat';\nimport 'hardhat-contract-sizer';\nimport 'hardhat-deploy';\nimport 'hardhat-gas-reporter';\nimport 'hardhat-signer-kms';\nimport '@oasisprotocol/sapphire-hardhat';\nimport '@matterlabs/hardhat-zksync-deploy';\nimport '@matterlabs/hardhat-zksync-solc';\n// Imports the verify plugin before the upgradable plugin\nimport '@matterlabs/hardhat-zksync-verify';\nimport '@matterlabs/hardhat-zksync-upgradable';\n\nimport * as dotenv from 'dotenv';\nimport { HardhatUserConfig, HttpNetworkUserConfig, NetworkUserConfig } from 'hardhat/types';\n\ndotenv.config();\n\nconst DEFAULT_ENDPOINT = 'http://localhost:8545';\nconst DEFAULT_PRIVATE_KEY =\n  process.env.DEFAULT_PRIVATE_KEY || 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';\n\nconst kmsKeyId = process.env.KMS_KEY_ID || '';\n\n// Testnets\nconst kovanEndpoint = process.env.KOVAN_ENDPOINT || DEFAULT_ENDPOINT;\nconst kovanPrivateKey = process.env.KOVAN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst ropstenEndpoint = process.env.ROPSTEN_ENDPOINT || DEFAULT_ENDPOINT;\nconst ropstenPrivateKey = process.env.ROPSTEN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst goerliEndpoint = process.env.GOERLI_ENDPOINT || DEFAULT_ENDPOINT;\nconst goerliPrivateKey = process.env.GOERLI_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst bscTestEndpoint = process.env.BSC_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst bscTestPrivateKey = process.env.BSC_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst optimismTestEndpoint = process.env.OPTIMISM_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst optimismTestPrivateKey = process.env.OPTIMISM_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst fantomTestEndpoint = process.env.FANTOM_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst fantomTestPrivateKey = process.env.FANTOM_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst avalancheTestEndpoint = process.env.AVALANCHE_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst avalancheTestPrivateKey = process.env.AVALANCHE_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst celoAlfajoresTestEndpoint = process.env.CELO_ALFAJORES_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst celoAlfajoresTestPrivateKey = process.env.CELO_ALFAJORES_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasisEmeraldTestEndpoint = process.env.OASIS_EMERALD_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasisEmeraldTestPrivateKey = process.env.OASIS_EMERALD_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasisSapphireTestEndpoint = process.env.OASIS_SAPPHIRE_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasisSapphireTestPrivateKey = process.env.OASIS_SAPPHIRE_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst moonbaseAlphaTestEndpoint = process.env.MOONBASE_ALPHA_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst moonbaseAlphaTestPrivateKey = process.env.MOONBASE_ALPHA_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst reiTestEndpoint = process.env.REI_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst reiTestPrivateKey = process.env.REI_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst nervosGodwokenTestEndpoint = process.env.NERVOS_GODWOKEN_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst nervosGodwokenTestPrivateKey = process.env.NERVOS_GODWOKEN_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst kavaTestEndpoint = process.env.KAVA_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst kavaTestPrivateKey = process.env.KAVA_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst darwiniaPangolinTestEndpoint = process.env.DARWINIA_PANGOLIN_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst darwiniaPangolinTestPrivateKey = process.env.DARWINIA_PANGOLIN_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst platonTestEndpoint = process.env.PLATON_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst platonTestPrivateKey = process.env.PLATON_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst polygonTestEndpoint = process.env.POLYGON_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst polygonTestPrivateKey = process.env.POLYGON_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst sxTestEndpoint = process.env.SX_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst sxTestPrivateKey = process.env.SX_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst swimmerTestEndpoint = process.env.SWIMMER_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst swimmerTestPrivateKey = process.env.SWIMMER_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst dexalotTestEndpoint = process.env.DEXALOT_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst dexalotTestPrivateKey = process.env.DEXALOT_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst nervosTestnetEndpoint = process.env.NERVOS_TESTNET_ENDPOINT || DEFAULT_ENDPOINT;\nconst nervosTestnetPrivateKey = process.env.NERVOS_TESTNET_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst shibuyaTestnetEndpoint = process.env.SHIBUYA_TESTNET_ENDPOINT || DEFAULT_ENDPOINT;\nconst shibuyaTestnetPrivateKey = process.env.SHIBUYA_TESTNET_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst cubeDevnetEndpoint = process.env.CUBE_DEVNET_ENDPOINT || DEFAULT_ENDPOINT;\nconst cubeDevnetPrivateKey = process.env.CUBE_DEVNET_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasysTestEndpoint = process.env.OASYS_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasysTestPrivateKey = process.env.OASYS_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst antimatterB2TestEndpoint = process.env.ANTIMATTER_B2_TEST_ENDPOINT || DEFAULT_ENDPOINT;\nconst antimatterB2TestPrivateKey = process.env.ANTIMATTER_B2_TEST_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst holeskyEndpoint = process.env.HOLESKY_ENDPOINT || DEFAULT_ENDPOINT;\nconst holeskyPrivateKey = process.env.HOLESKY_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\n// Mainnets\nconst ethMainnetEndpoint = process.env.ETH_MAINNET_ENDPOINT || DEFAULT_ENDPOINT;\nconst ethMainnetPrivateKey = process.env.ETH_MAINNET_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst bscEndpoint = process.env.BSC_ENDPOINT || DEFAULT_ENDPOINT;\nconst bscPrivateKey = process.env.BSC_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst arbitrumOneEndpoint = process.env.ARBITRUM_ONE_ENDPOINT || DEFAULT_ENDPOINT;\nconst arbitrumOnePrivateKey = process.env.ARBITRUM_ONE_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst arbitrumNovaEndpoint = process.env.ARBITRUM_NOVA_ENDPOINT || DEFAULT_ENDPOINT;\nconst arbitrumNovaPrivateKey = process.env.ARBITRUM_NOVA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst polygonEndpoint = process.env.POLYGON_ENDPOINT || DEFAULT_ENDPOINT;\nconst polygonPrivateKey = process.env.POLYGON_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst fantomEndpoint = process.env.FANTOM_ENDPOINT || DEFAULT_ENDPOINT;\nconst fantomPrivateKey = process.env.FANTOM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst avalancheEndpoint = process.env.AVALANCHE_ENDPOINT || DEFAULT_ENDPOINT;\nconst avalanchePrivateKey = process.env.AVALANCHE_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst optimismEndpoint = process.env.OPTIMISM_ENDPOINT || DEFAULT_ENDPOINT;\nconst optimismPrivateKey = process.env.OPTIMISM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst bobaEndpoint = process.env.BOBA_ENDPOINT || DEFAULT_ENDPOINT;\nconst bobaPrivateKey = process.env.BOBA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst harmonyEndpoint = process.env.HARMONY_ENDPOINT || DEFAULT_ENDPOINT;\nconst harmonyPrivateKey = process.env.HARMONY_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst moonbeamEndpoint = process.env.MOONBEAM_ENDPOINT || DEFAULT_ENDPOINT;\nconst moonbeamPrivateKey = process.env.MOONBEAM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst moonriverEndpoint = process.env.MOONRIVER_ENDPOINT || DEFAULT_ENDPOINT;\nconst moonriverPrivateKey = process.env.MOONRIVER_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst celoEndpoint = process.env.CELO_ENDPOINT || DEFAULT_ENDPOINT;\nconst celoPrivateKey = process.env.CELO_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasisEmeraldEndpoint = process.env.OASIS_EMERALD_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasisEmeraldPrivateKey = process.env.OASIS_EMERALD_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasisSapphireEndpoint = process.env.OASIS_SAPPHIRE_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasisSapphirePrivateKey = process.env.OASIS_SAPPHIRE_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst metisEndpoint = process.env.METIS_ENDPOINT || DEFAULT_ENDPOINT;\nconst metisPrivateKey = process.env.METIS_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst auroraEndpoint = process.env.AURORA_ENDPOINT || DEFAULT_ENDPOINT;\nconst auroraPrivateKey = process.env.AURORA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst xdaiEndpoint = process.env.XDAI_ENDPOINT || DEFAULT_ENDPOINT;\nconst xdaiPrivateKey = process.env.XDAI_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oecEndpoint = process.env.OEC_ENDPOINT || DEFAULT_ENDPOINT;\nconst oecPrivateKey = process.env.OEC_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst hecoEndpoint = process.env.HECO_ENDPOINT || DEFAULT_ENDPOINT;\nconst hecoPrivateKey = process.env.HECO_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst astarEndpoint = process.env.ASTAR_ENDPOINT || DEFAULT_ENDPOINT;\nconst astarPrivateKey = process.env.ASTAR_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst shidenEndpoint = process.env.SHIDEN_ENDPOINT || DEFAULT_ENDPOINT;\nconst shidenPrivateKey = process.env.SHIDEN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst syscoinEndpoint = process.env.SYSCOIN_ENDPOINT || DEFAULT_ENDPOINT;\nconst syscoinPrivateKey = process.env.SYSCOIN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst milkomedaC1Endpoint = process.env.MILKOMEDA_C1_ENDPOINT || DEFAULT_ENDPOINT;\nconst milkomedaC1PrivateKey = process.env.MILKOMEDA_C1_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst milkomedaA1Endpoint = process.env.MILKOMEDA_A1_ENDPOINT || DEFAULT_ENDPOINT;\nconst milkomedaA1PrivateKey = process.env.MILKOMEDA_A1_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst evmosEndpoint = process.env.EVMOS_ENDPOINT || DEFAULT_ENDPOINT;\nconst evmosPrivateKey = process.env.EVMOS_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst cloverEndpoint = process.env.CLOVER_ENDPOINT || DEFAULT_ENDPOINT;\nconst cloverPrivateKey = process.env.CLOVER_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst reiEndpoint = process.env.REI_ENDPOINT || DEFAULT_ENDPOINT;\nconst reiPrivateKey = process.env.REI_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst confluxEndpoint = process.env.CONFLUX_ENDPOINT || DEFAULT_ENDPOINT;\nconst confluxPrivateKey = process.env.CONFLUX_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst darwiniaCrabEndpoint = process.env.DARWINIA_CRAB_ENDPOINT || DEFAULT_ENDPOINT;\nconst darwiniaCrabPrivateKey = process.env.DARWINIA_CRAB_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst platonEndpoint = process.env.PLATON_ENDPOINT || DEFAULT_ENDPOINT;\nconst platonPrivateKey = process.env.PLATON_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst ontologyEndpoint = process.env.ONTOLOGY_ENDPOINT || DEFAULT_ENDPOINT;\nconst ontologyPrivateKey = process.env.ONTOLOGY_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst swimmerEndpoint = process.env.SWIMMER_ENDPOINT || DEFAULT_ENDPOINT;\nconst swimmerPrivateKey = process.env.SWIMMER_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst sxNetworkEndpoint = process.env.SX_NETWORK_ENDPOINT || DEFAULT_ENDPOINT;\nconst sxNetworkPrivateKey = process.env.SX_NETWORK_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst apeEndpoint = process.env.APE_ENDPOINT || DEFAULT_ENDPOINT;\nconst apePrivateKey = process.env.APE_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst kavaEndpoint = process.env.KAVA_ENDPOINT || DEFAULT_ENDPOINT;\nconst kavaPrivateKey = process.env.KAVA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst fncyEndpoint = process.env.FNCY_ENDPOINT || DEFAULT_ENDPOINT;\nconst fncyPrivateKey = process.env.FNCY_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst nervosGodwokenEndpoint = process.env.NERVOS_GODWOKEN_ENDPOINT || DEFAULT_ENDPOINT;\nconst nervosGodwokenPrivateKey = process.env.NERVOS_GODWOKEN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst klaytnEndpoint = process.env.KLAYTN_ENDPOINT || DEFAULT_ENDPOINT;\nconst klaytnPrivateKey = process.env.KLAYTN_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst oasysEndpoint = process.env.OASYS_ENDPOINT || DEFAULT_ENDPOINT;\nconst oasysPrivateKey = process.env.OASYS_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst spsEndpoint = process.env.SPS_ENDPOINT || DEFAULT_ENDPOINT;\nconst spsPrivateKey = process.env.SPS_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst fvmEndpoint = process.env.FVM_ENDPOINT || DEFAULT_ENDPOINT;\nconst fvmPrivateKey = process.env.FVM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst cantoEndpoint = process.env.CANTO_ENDPOINT || DEFAULT_ENDPOINT;\nconst cantoPrivateKey = process.env.CANTO_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst polygonZkevmEndpoint = process.env.POLYGON_ZKEVM_ENDPOINT || DEFAULT_ENDPOINT;\nconst polygonZkevmPrivateKey = process.env.POLYGON_ZKEVM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst antimatterB2Endpoint = process.env.ANTIMATTER_B2_ENDPOINT || DEFAULT_ENDPOINT;\nconst antimatterB2PrivateKey = process.env.ANTIMATTER_B2_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst lineaEndpoint = process.env.LINEA_ENDPOINT || DEFAULT_ENDPOINT;\nconst lineaPrivateKey = process.env.LINEA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst baseEndpoint = process.env.BASE_ENDPOINT || DEFAULT_ENDPOINT;\nconst basePrivateKey = process.env.BASE_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst telosEndpoint = process.env.TELOS_ENDPOINT || DEFAULT_ENDPOINT;\nconst telosPrivateKey = process.env.TELOS_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst scrollEndpoint = process.env.SCROLL_ENDPOINT || DEFAULT_ENDPOINT;\nconst scrollPrivateKey = process.env.SCROLL_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst zksyncEraEndpoint = process.env.ZKSYNC_ERA_ENDPOINT || DEFAULT_ENDPOINT;\nconst zksyncEraPrivateKey = process.env.ZKSYNC_ERA_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\nconst flowEvmEndpoint = process.env.FLOW_EVM_ENDPOINT || DEFAULT_ENDPOINT;\nconst flowEvmPrivateKey = process.env.FLOW_EVM_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;\n\n// use kmsKeyId if it's not empty, otherwise use privateKey\nfunction getNetworkConfig(url: string, kmsKeyId: string, privateKey: string, gasPrice?: number): NetworkUserConfig {\n  const network: NetworkUserConfig = !kmsKeyId\n    ? {\n      url: url,\n      accounts: [`0x${privateKey}`]\n    }\n    : {\n      url: url,\n      kmsKeyId: kmsKeyId\n    };\n  if (gasPrice) {\n    network.gasPrice = gasPrice;\n  }\n\n  return network;\n}\n\nconst zksyncEraNetwork = getNetworkConfig(zksyncEraEndpoint, kmsKeyId, zksyncEraPrivateKey);\nzksyncEraNetwork.zksync = true;\n(zksyncEraNetwork as HttpNetworkUserConfig).ethNetwork = 'ethMainnet';\nzksyncEraNetwork.verifyURL = 'https://zksync2-mainnet-explorer.zksync.io/contract_verification';\n\nconst config: HardhatUserConfig = {\n  defaultNetwork: 'hardhat',\n  networks: {\n    // Testnets\n    hardhat: {},\n    localhost: { timeout: 600000 },\n    kovan: {\n      url: kovanEndpoint,\n      accounts: [`0x${kovanPrivateKey}`],\n      ethNetwork: ''\n    },\n    ropsten: {\n      url: ropstenEndpoint,\n      accounts: [`0x${ropstenPrivateKey}`]\n    },\n    holesky: {\n      url: holeskyEndpoint,\n      accounts: [`0x${holeskyPrivateKey}`]\n    },\n    goerli: {\n      url: goerliEndpoint,\n      accounts: [`0x${goerliPrivateKey}`]\n    },\n    bscTest: {\n      url: bscTestEndpoint,\n      accounts: [`0x${bscTestPrivateKey}`]\n    },\n    fantomTest: {\n      url: fantomTestEndpoint,\n      accounts: [`0x${fantomTestPrivateKey}`]\n    },\n    optimismTest: {\n      url: optimismTestEndpoint,\n      accounts: [`0x${optimismTestPrivateKey}`]\n    },\n    avalancheTest: {\n      url: avalancheTestEndpoint,\n      accounts: [`0x${avalancheTestPrivateKey}`]\n    },\n    celoAlfajoresTest: {\n      url: celoAlfajoresTestEndpoint,\n      accounts: [`0x${celoAlfajoresTestPrivateKey}`]\n    },\n    oasisEmeraldTest: {\n      url: oasisEmeraldTestEndpoint,\n      accounts: [`0x${oasisEmeraldTestPrivateKey}`]\n    },\n    oasisSapphireTest: {\n      url: oasisSapphireTestEndpoint,\n      accounts: [`0x${oasisSapphireTestPrivateKey}`]\n    },\n    moonbaseAlphaTest: {\n      url: moonbaseAlphaTestEndpoint,\n      accounts: [`0x${moonbaseAlphaTestPrivateKey}`]\n    },\n    reiTest: {\n      url: reiTestEndpoint,\n      accounts: [`0x${reiTestPrivateKey}`]\n    },\n    nervosGodwokenTest: {\n      url: nervosGodwokenTestEndpoint,\n      accounts: [`0x${nervosGodwokenTestPrivateKey}`]\n    },\n    kavaTest: {\n      url: kavaTestEndpoint,\n      accounts: [`0x${kavaTestPrivateKey}`]\n    },\n    darwiniaPangolinTest: {\n      url: darwiniaPangolinTestEndpoint,\n      accounts: [`0x${darwiniaPangolinTestPrivateKey}`]\n    },\n    platonTest: {\n      url: platonTestEndpoint,\n      accounts: [`0x${platonTestPrivateKey}`]\n    },\n    polygonTest: {\n      url: polygonTestEndpoint,\n      accounts: [`0x${polygonTestPrivateKey}`]\n    },\n    sxTest: {\n      url: sxTestEndpoint,\n      accounts: [`0x${sxTestPrivateKey}`]\n    },\n    swimmerTest: {\n      url: swimmerTestEndpoint,\n      accounts: [`0x${swimmerTestPrivateKey}`]\n    },\n    dexalotTest: {\n      url: dexalotTestEndpoint,\n      accounts: [`0x${dexalotTestPrivateKey}`]\n    },\n    nervosTestnet: {\n      url: nervosTestnetEndpoint,\n      accounts: [`0x${nervosTestnetPrivateKey}`]\n    },\n    shibuyaTestnet: {\n      url: shibuyaTestnetEndpoint,\n      accounts: [`0x${shibuyaTestnetPrivateKey}`]\n    },\n    cubeDevnet: {\n      url: cubeDevnetEndpoint,\n      accounts: [`0x${cubeDevnetPrivateKey}`]\n    },\n    oasysTest: {\n      url: oasysTestEndpoint,\n      accounts: [`0x${oasysTestPrivateKey}`]\n    },\n    antimatterB2Test: {\n      url: antimatterB2TestEndpoint,\n      accounts: [`0x${antimatterB2TestPrivateKey}`]\n    },\n    // Mainnets\n    ethMainnet: getNetworkConfig(ethMainnetEndpoint, kmsKeyId, ethMainnetPrivateKey),\n    bsc: getNetworkConfig(bscEndpoint, kmsKeyId, bscPrivateKey, 5000000000),\n    arbitrumOne: getNetworkConfig(arbitrumOneEndpoint, kmsKeyId, arbitrumOnePrivateKey),\n    arbitrumNova: getNetworkConfig(arbitrumNovaEndpoint, kmsKeyId, arbitrumNovaPrivateKey),\n    polygon: getNetworkConfig(polygonEndpoint, kmsKeyId, polygonPrivateKey, 50000000000),\n    fantom: getNetworkConfig(fantomEndpoint, kmsKeyId, fantomPrivateKey),\n    avalanche: getNetworkConfig(avalancheEndpoint, kmsKeyId, avalanchePrivateKey),\n    optimism: getNetworkConfig(optimismEndpoint, kmsKeyId, optimismPrivateKey),\n    boba: getNetworkConfig(bobaEndpoint, kmsKeyId, bobaPrivateKey),\n    harmony: getNetworkConfig(harmonyEndpoint, kmsKeyId, harmonyPrivateKey),\n    moonbeam: getNetworkConfig(moonbeamEndpoint, kmsKeyId, moonbeamPrivateKey),\n    moonriver: getNetworkConfig(moonriverEndpoint, kmsKeyId, moonriverPrivateKey),\n    celo: getNetworkConfig(celoEndpoint, kmsKeyId, celoPrivateKey),\n    oasisEmerald: getNetworkConfig(oasisEmeraldEndpoint, kmsKeyId, oasisEmeraldPrivateKey),\n    oasisSapphire: getNetworkConfig(oasisSapphireEndpoint, kmsKeyId, oasisSapphirePrivateKey),\n    metis: getNetworkConfig(metisEndpoint, kmsKeyId, metisPrivateKey),\n    aurora: getNetworkConfig(auroraEndpoint, kmsKeyId, auroraPrivateKey),\n    xdai: getNetworkConfig(xdaiEndpoint, kmsKeyId, xdaiPrivateKey),\n    oec: getNetworkConfig(oecEndpoint, kmsKeyId, oecPrivateKey),\n    heco: getNetworkConfig(hecoEndpoint, kmsKeyId, hecoPrivateKey),\n    astar: getNetworkConfig(astarEndpoint, kmsKeyId, astarPrivateKey),\n    shiden: getNetworkConfig(shidenEndpoint, kmsKeyId, shidenPrivateKey),\n    syscoin: getNetworkConfig(syscoinEndpoint, kmsKeyId, syscoinPrivateKey),\n    milkomedaC1: getNetworkConfig(milkomedaC1Endpoint, kmsKeyId, milkomedaC1PrivateKey),\n    milkomedaA1: getNetworkConfig(milkomedaA1Endpoint, kmsKeyId, milkomedaA1PrivateKey),\n    evmos: getNetworkConfig(evmosEndpoint, kmsKeyId, evmosPrivateKey),\n    clover: getNetworkConfig(cloverEndpoint, kmsKeyId, cloverPrivateKey),\n    rei: getNetworkConfig(reiEndpoint, kmsKeyId, reiPrivateKey),\n    conflux: getNetworkConfig(confluxEndpoint, kmsKeyId, confluxPrivateKey),\n    darwiniaCrab: getNetworkConfig(darwiniaCrabEndpoint, kmsKeyId, darwiniaCrabPrivateKey),\n    platon: getNetworkConfig(platonEndpoint, kmsKeyId, platonPrivateKey),\n    ontology: getNetworkConfig(ontologyEndpoint, kmsKeyId, ontologyPrivateKey),\n    swimmer: getNetworkConfig(swimmerEndpoint, kmsKeyId, swimmerPrivateKey),\n    sxNetwork: getNetworkConfig(sxNetworkEndpoint, kmsKeyId, sxNetworkPrivateKey),\n    ape: getNetworkConfig(apeEndpoint, kmsKeyId, apePrivateKey),\n    kava: getNetworkConfig(kavaEndpoint, kmsKeyId, kavaPrivateKey),\n    fncy: getNetworkConfig(fncyEndpoint, kmsKeyId, fncyPrivateKey),\n    nervosGodwoken: getNetworkConfig(nervosGodwokenEndpoint, kmsKeyId, nervosGodwokenPrivateKey),\n    klaytn: getNetworkConfig(klaytnEndpoint, kmsKeyId, klaytnPrivateKey, 250000000000),\n    oasys: getNetworkConfig(oasysEndpoint, kmsKeyId, oasysPrivateKey),\n    sps: getNetworkConfig(spsEndpoint, kmsKeyId, spsPrivateKey),\n    fvm: getNetworkConfig(fvmEndpoint, kmsKeyId, fvmPrivateKey),\n    canto: getNetworkConfig(cantoEndpoint, kmsKeyId, cantoPrivateKey),\n    polygonZkevm: getNetworkConfig(polygonZkevmEndpoint, kmsKeyId, polygonZkevmPrivateKey),\n    antimatterB2: getNetworkConfig(antimatterB2Endpoint, kmsKeyId, antimatterB2PrivateKey),\n    linea: getNetworkConfig(lineaEndpoint, kmsKeyId, lineaPrivateKey),\n    base: getNetworkConfig(baseEndpoint, kmsKeyId, basePrivateKey),\n    telos: getNetworkConfig(telosEndpoint, kmsKeyId, telosPrivateKey),\n    scroll: getNetworkConfig(scrollEndpoint, kmsKeyId, scrollPrivateKey),\n    zksyncEra: zksyncEraNetwork,\n    flowEvm: getNetworkConfig(flowEvmEndpoint, kmsKeyId, flowEvmPrivateKey),\n  },\n  namedAccounts: {\n    deployer: {\n      default: 0\n    }\n  },\n  solidity: {\n    version: '0.8.17',\n    settings: {\n      optimizer: {\n        enabled: true,\n        runs: 800\n      }\n    }\n  },\n  contractSizer: {\n    alphaSort: true,\n    runOnCompile: false,\n    disambiguatePaths: false\n  },\n  gasReporter: {\n    enabled: process.env.REPORT_GAS === 'true' ? true : false,\n    noColors: true,\n    outputFile: 'reports/gas_usage/summary.txt'\n  },\n  typechain: {\n    outDir: 'typechain',\n    target: 'ethers-v6'\n  },\n  etherscan: {\n    apiKey: {\n      // Testnets\n      goerli: process.env.ETHERSCAN_API_KEY || '',\n      holesky: process.env.ETHERSCAN_API_KEY || '',\n      avalancheFujiTestnet: process.env.SNOWTRACE_API_KEY || '',\n      bscTestnet: process.env.BSCSCAN_API_KEY || '',\n      arbitrumTestnet: process.env.ARBISCAN_API_KEY || '',\n      ftmTestnet: process.env.FTMSCAN_API_KEY || '',\n      polygonMumbai: process.env.POLYGONSCAN_API_KEY || '',\n      // Mainnets\n      mainnet: process.env.ETHERSCAN_API_KEY || '',\n      avalanche: process.env.SNOWTRACE_API_KEY || '',\n      bsc: process.env.BSCSCAN_API_KEY || '',\n      arbitrumOne: process.env.ARBISCAN_API_KEY || '',\n      optimisticEthereum: process.env.OPTIMISTIC_ETHERSCAN_API_KEY || '',\n      opera: process.env.FTMSCAN_API_KEY || '',\n      polygon: process.env.POLYGONSCAN_API_KEY || '',\n      aurora: process.env.AURORASCAN_API_KEY || '',\n      moonriver: process.env.MOONRIVER_MOONSCAN_API_KEY || '',\n      moonbeam: process.env.MOONBEAM_MOONSCAN_API_KEY || '',\n      heco: process.env.HECOSCAN_API_KEY || '',\n      arbitrumNova: process.env.ARBISCAN_NOVA_API_KEY || '',\n      linea: process.env.LINEA_API_KEY || '',\n      base: process.env.BASE_API_KEY || ''\n    },\n    customChains: [\n      {\n        network: 'arbitrumNova',\n        chainId: 42170,\n        urls: {\n          apiURL: process.env.ARBITRUM_NOVA_ENDPOINT || '',\n          browserURL: process.env.ARBITRUM_NOVA_EXPLORER || ''\n        }\n      },\n      {\n        network: 'linea',\n        chainId: 59144,\n        urls: {\n          apiURL: process.env.LINEA_API_ENDPOINT || '',\n          browserURL: process.env.LINEA_EXPLORER || ''\n        }\n      },\n      {\n        network: 'base',\n        chainId: 8453,\n        urls: {\n          apiURL: process.env.BASE_API_ENDPOINT || '',\n          browserURL: process.env.BASE_EXPLORER || ''\n        }\n      }\n    ]\n  },\n  zksolc: {\n    version: 'latest', // optional.\n    settings: {\n      //compilerPath: 'zksolc', // optional. Ignored for compilerSource \"docker\". Can be used if compiler is located in a specific folder\n      libraries: {}, // optional. References to non-inlinable libraries\n      isSystem: false, // optional.  Enables Yul instructions available only for zkSync system contracts and libraries\n      forceEvmla: false, // optional. Falls back to EVM legacy assembly if there is a bug with Yul\n      optimizer: {\n        enabled: true, // optional. True by default\n        mode: '3' // optional. 3 by default, z to optimize bytecode size\n      },\n      experimental: {\n        dockerImage: '', // deprecated\n        tag: '' // deprecated\n      }\n    }\n  }\n};\n\n// if (config.networks?.polygon) {\n//   config.networks.polygon.minMaxPriorityFeePerGas = 30000000000;\n// }\n// if (config.networks?.fantom) {\n//   config.networks.fantom.minMaxPriorityFeePerGas = 30000000000;\n// }\n// if (config.networks?.bsc) {\n//   config.networks.bsc.minMaxPriorityFeePerGas = 3000000000;\n//   config.networks.bsc.minMaxFeePerGas = 3000000000;\n// }\n\nexport default config;\n"
  },
  {
    "path": "import-sorter.json",
    "content": "{\n  \"generalConfiguration.sortOnBeforeSave\": true,\n  \"importStringConfiguration.maximumNumberOfImportExpressionsPerLine.type\": \"newLineEachExpressionAfterCountLimitExceptIfOnlyOne\",\n  \"importStringConfiguration.maximumNumberOfImportExpressionsPerLine.count\": 120,\n  \"importStringConfiguration.tabSize\": 2,\n  \"importStringConfiguration.quoteMark\": \"single\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sgn-v2-contracts\",\n  \"version\": \"0.2.0\",\n  \"description\": \"SGN V2 Contracts\",\n  \"scripts\": {\n    \"clean\": \"hardhat clean\",\n    \"compile\": \"hardhat compile\",\n    \"generate:typechain\": \"rm -rf typechain && hardhat typechain\",\n    \"report-gas:summary\": \"REPORT_GAS=true hardhat test\",\n    \"report-gas:benchmark\": \"hardhat test benchmark/*.ts\",\n    \"size-contracts\": \"hardhat size-contracts | sed -r 's/[[:cntrl:]]\\\\[[0-9]{1,3}m//g' > reports/contract_sizes.txt\",\n    \"test\": \"hardhat compile && hardhat test\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/celer-network/sgn-v2-contracts.git\"\n  },\n  \"license\": \"GPL-3.0\",\n  \"bugs\": {\n    \"url\": \"https://github.com/celer-network/sgn-v2-contracts/issues\"\n  },\n  \"homepage\": \"https://github.com/celer-network/sgn-v2-contracts#readme\",\n  \"devDependencies\": {\n    \"@aws-sdk/client-kms\": \"^3.606.0\",\n    \"@nomicfoundation/hardhat-chai-matchers\": \"^2.0.7\",\n    \"@nomicfoundation/hardhat-ignition\": \"^0.15.5\",\n    \"@nomicfoundation/hardhat-ignition-ethers\": \"^0.15.0\",\n    \"@nomicfoundation/hardhat-network-helpers\": \"^1.0.11\",\n    \"@nomicfoundation/hardhat-toolbox\": \"^5.0.0\",\n    \"@nomicfoundation/ignition-core\": \"^0.15.5\",\n    \"@oasisprotocol/sapphire-hardhat\": \"^2.19.4\",\n    \"@openzeppelin/contracts\": \"4.5.0\",\n    \"@openzeppelin/contracts-upgradeable\": \"4.5.0\",\n    \"@openzeppelin/hardhat-upgrades\": \"^3.2.0\",\n    \"@openzeppelin/upgrades-core\": \"^1.34.1\",\n    \"@typechain/ethers-v5\": \"^11.1.2\",\n    \"@typechain/ethers-v6\": \"^0.5.1\",\n    \"@typechain/hardhat\": \"^9.1.0\",\n    \"@types/chai\": \"^4.3.16\",\n    \"@types/mocha\": \"^10.0.7\",\n    \"@types/node\": \"^20.14.9\",\n    \"@types/sinon-chai\": \"^3.2.12\",\n    \"@typescript-eslint/eslint-plugin\": \"^7.14.1\",\n    \"@typescript-eslint/parser\": \"^7.14.1\",\n    \"chai\": \"^4.4.1\",\n    \"eslint\": \"^9.6.0\",\n    \"eslint-config-prettier\": \"^9.1.0\",\n    \"eslint-plugin-import\": \"^2.29.1\",\n    \"ethers\": \"^6.13.1\",\n    \"fs-extra\": \"^11.2.0\",\n    \"hardhat\": \"^2.22.5\",\n    \"hardhat-contract-sizer\": \"^2.10.0\",\n    \"hardhat-deploy\": \"^0.12.4\",\n    \"hardhat-deploy-ethers\": \"^0.4.2\",\n    \"hardhat-gas-reporter\": \"^2.2.0\",\n    \"husky\": \"^9.0.11\",\n    \"prettier\": \"^3.3.2\",\n    \"prettier-plugin-solidity\": \"^1.3.1\",\n    \"protobufjs\": \"^7.3.2\",\n    \"solc\": \"0.8.17\",\n    \"solidity-coverage\": \"^0.8.1\",\n    \"ts-node\": \"^10.9.2\",\n    \"typechain\": \"^8.3.2\",\n    \"typescript\": \"^5.5.2\"\n  },\n  \"dependencies\": {\n    \"@matterlabs/hardhat-zksync-solc\": \"^1.2.0\",\n    \"@matterlabs/hardhat-zksync-upgradable\": \"^1.5.1\",\n    \"@matterlabs/hardhat-zksync-verify\": \"^1.5.0\",\n    \"@nomicfoundation/hardhat-ethers\": \"^3.0.6\",\n    \"@nomicfoundation/hardhat-verify\": \"^2.0.8\",\n    \"dotenv\": \"^16.4.5\",\n    \"hardhat-signer-kms\": \"^1.2.1\"\n  }\n}\n"
  },
  {
    "path": "reports/contract_sizes.txt",
    "content": "Nothing to compile\nNo need to generate any newer typings.\n ·---------------------------------------------|-------------|---------------·\n |  Contract Name                              ·  Size (KB)  ·  Change (KB)  │\n ··············································|·············|················\n |  Address                                    ·      0.086  ·               │\n ··············································|·············|················\n |  BatchTransfer                              ·      9.934  ·               │\n ··············································|·············|················\n |  Bridge                                     ·     20.788  ·               │\n ··············································|·············|················\n |  BridgeTransferLib                          ·      0.086  ·               │\n ··············································|·············|················\n |  ContractAsLP                               ·      5.276  ·               │\n ··············································|·············|················\n |  ContractAsSender                           ·     15.679  ·               │\n ··············································|·············|················\n |  Counters                                   ·      0.086  ·               │\n ··············································|·············|················\n |  CrossChainSwap                             ·      8.237  ·               │\n ··············································|·············|················\n |  DataTypes                                  ·      0.086  ·               │\n ··············································|·············|················\n |  DummySwap                                  ·      2.184  ·               │\n ··············································|·············|················\n |  ECDSA                                      ·      0.086  ·               │\n ··············································|·············|················\n |  ERC20                                      ·      2.346  ·               │\n ··············································|·············|················\n |  ERC721                                     ·      5.021  ·               │\n ··············································|·············|················\n |  FarmingRewards                             ·      6.980  ·               │\n ··············································|·············|················\n |  Faucet                                     ·      2.730  ·               │\n ··············································|·············|················\n |  FraxBridgeToken                            ·      5.697  ·               │\n ··············································|·············|················\n |  Govern                                     ·      5.242  ·               │\n ··············································|·············|················\n |  GovernedOwnerProxy                         ·     10.598  ·               │\n ··············································|·············|················\n |  IntermediaryBridgeToken                    ·      5.375  ·               │\n ··············································|·············|················\n |  IntermediaryOriginalToken                  ·      5.077  ·               │\n ··············································|·············|················\n |  MaiBridgeToken                             ·      5.976  ·               │\n ··············································|·············|················\n |  MCNNFT                                     ·     10.483  ·               │\n ··············································|·············|················\n |  MessageBus                                 ·     16.196  ·               │\n ··············································|·············|················\n |  MessageBusReceiver                         ·     13.034  ·               │\n ··············································|·············|················\n |  MessageBusSender                           ·      4.219  ·               │\n ··············································|·············|················\n |  MessageSenderLib                           ·      0.086  ·               │\n ··············································|·············|················\n |  MintableERC20                              ·      3.745  ·               │\n ··············································|·············|················\n |  MintSwapCanonicalToken                     ·      5.988  ·               │\n ··············································|·············|················\n |  MintSwapCanonicalTokenPermit               ·      7.722  ·               │\n ··············································|·············|················\n |  MintSwapCanonicalTokenUpgradable           ·      6.691  ·               │\n ··············································|·············|················\n |  MintSwapCanonicalTokenUpgradableFreezable  ·      7.483  ·               │\n ··············································|·············|················\n |  MsgDataTypes                               ·      0.086  ·               │\n ··············································|·············|················\n |  MsgExampleBasic                            ·      2.991  ·               │\n ··············································|·············|················\n |  MsgExampleBasicTransfer                    ·      7.901  ·               │\n ··············································|·············|················\n |  MsgExampleInOrder                          ·      3.725  ·               │\n ··············································|·············|················\n |  MsgTest                                    ·     10.301  ·               │\n ··············································|·············|················\n |  MultiBridgeToken                           ·      4.317  ·               │\n ··············································|·············|················\n |  MultiBridgeTokenPermit                     ·      6.051  ·               │\n ··············································|·············|················\n |  NFTBridge                                  ·     15.231  ·               │\n ··············································|·············|················\n |  OntologyBridgeToken                        ·      5.925  ·               │\n ··············································|·············|················\n |  OriginalTokenVault                         ·     13.441  ·               │\n ··············································|·············|················\n |  OriginalTokenVaultV2                       ·     13.488  ·               │\n ··············································|·············|················\n |  OrigNFT                                    ·      6.809  ·               │\n ··············································|·············|················\n |  OwnerDataTypes                             ·      0.086  ·               │\n ··············································|·············|················\n |  Pb                                         ·      0.086  ·               │\n ··············································|·············|················\n |  PbBridge                                   ·      0.086  ·               │\n ··············································|·············|················\n |  PbFarming                                  ·      0.086  ·               │\n ··············································|·············|················\n |  PbPegged                                   ·      0.086  ·               │\n ··············································|·············|················\n |  PbPool                                     ·      0.086  ·               │\n ··············································|·············|················\n |  PbSgn                                      ·      0.086  ·               │\n ··············································|·············|················\n |  PbStaking                                  ·      0.086  ·               │\n ··············································|·············|················\n |  PeggedTokenBridge                          ·     10.885  ·               │\n ··············································|·············|················\n |  PeggedTokenBridgeV2                        ·     12.104  ·               │\n ··············································|·············|················\n |  PegNFT                                     ·      6.952  ·               │\n ··············································|·············|················\n |  Pool                                       ·     16.467  ·               │\n ··············································|·············|················\n |  RestrictedMultiBridgeTokenOwner            ·      1.682  ·               │\n ··············································|·············|················\n |  RFQ                                        ·     18.534  ·               │\n ··············································|·············|················\n |  SafeERC20                                  ·      0.086  ·               │\n ··············································|·············|················\n |  SGN                                        ·      7.973  ·               │\n ··············································|·············|················\n |  Signers                                    ·      5.525  ·               │\n ··············································|·············|················\n |  SimpleGovernance                           ·     10.223  ·               │\n ··············································|·············|················\n |  SingleBridgeToken                          ·      4.131  ·               │\n ··············································|·············|················\n |  SingleBridgeTokenPermit                    ·      5.865  ·               │\n ··············································|·············|················\n |  Staking                                    ·     24.325  ·               │\n ··············································|·············|················\n |  StakingReward                              ·      6.114  ·               │\n ··············································|·············|················\n |  Strings                                    ·      0.086  ·               │\n ··············································|·············|················\n |  SwapBridgeToken                            ·      5.961  ·               │\n ··············································|·············|················\n |  TestERC20                                  ·      2.376  ·               │\n ··············································|·············|················\n |  TransferAgent                              ·      6.955  ·               │\n ··············································|·············|················\n |  TransferSwap                               ·     12.189  ·               │\n ··············································|·············|················\n |  Viewer                                     ·      5.636  ·               │\n ··············································|·············|················\n |  WETH                                       ·      3.336  ·               │\n ··············································|·············|················\n |  WithdrawInbox                              ·      2.547  ·               │\n ··············································|·············|················\n |  WrappedBridgeToken                         ·      5.754  ·               │\n ·---------------------------------------------|-------------|---------------·\n"
  },
  {
    "path": "reports/gas_usage/relay.txt",
    "content": "<validatorNum, quorumSigNum, gasCost> for cbr testErc20 relay tx\n\n5\t3\t169749\n7\t3\t140538\n7\t5\t151548\n9\t3\t142563\n9\t5\t156553\n9\t7\t167603\n11\t3\t150373\n11\t5\t161415\n11\t7\t169631\n13\t3\t155344\n13\t5\t166299\n13\t7\t177433\n13\t9\t188496\n15\t3\t157390\n15\t5\t171304\n15\t7\t182267\n15\t9\t193458\n15\t11\t204450\n17\t3\t162350\n17\t5\t176260\n17\t7\t187255\n17\t9\t198338\n17\t11\t209362\n19\t3\t167237\n19\t5\t181220\n19\t7\t192139\n19\t9\t203346\n19\t11\t214350\n19\t13\t222556\n21\t3\t175015\n21\t5\t186070\n21\t7\t197201\n21\t9\t208149\n21\t11\t216499\n21\t13\t230435\n21\t15\t241492\nper sig cost: 5540\n\n12\t8\t180470\n13\t8\t182962\n14\t8\t182576\n15\t8\t187980\n16\t8\t190284\n17\t8\t192816\n18\t8\t195284\n19\t8\t194926\n20\t8\t200142\n21\t8\t202690\nper validator cost: 2469\n"
  },
  {
    "path": "reports/gas_usage/summary.txt",
    "content": "·------------------------------------------------------|---------------------------|-------------|-----------------------------·\n|                 Solc version: 0.8.17                 ·  Optimizer enabled: true  ·  Runs: 800  ·  Block limit: 30000000 gas  │\n·······················································|···························|·············|······························\n|  Methods                                                                                                                     │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Contract            ·  Method                       ·  Min        ·  Max        ·  Avg        ·  # calls      ·  eur (avg)  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  addLiquidity                 ·          -  ·          -  ·      94648  ·            5  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  executeDelayedTransfer       ·          -  ·          -  ·      54503  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  relay                        ·     117698  ·     188532  ·     150251  ·           11  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  resetSigners                 ·      73421  ·      77571  ·      76188  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  send                         ·          -  ·          -  ·      84544  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setDelayPeriod               ·          -  ·          -  ·      46876  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setDelayThresholds           ·          -  ·          -  ·      49436  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setEpochLength               ·          -  ·          -  ·      46909  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setEpochVolumeCaps           ·          -  ·          -  ·      49478  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setMaxSend                   ·          -  ·          -  ·      49457  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  setMinAdd                    ·          -  ·          -  ·      49433  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  transferOwnership            ·      28650  ·      28662  ·      28660  ·            5  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  updateSigners                ·      67619  ·      72217  ·      69918  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Bridge              ·  withdraw                     ·          -  ·          -  ·     114249  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  DummySwap           ·  setFakeSlippage              ·          -  ·          -  ·      26428  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  FarmingRewards      ·  claimRewards                 ·     112992  ·     130084  ·     121538  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  FarmingRewards      ·  contributeToRewardPool       ·      50400  ·      67500  ·      64389  ·           11  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  FarmingRewards      ·  pause                        ·          -  ·          -  ·      29783  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Govern              ·  confirmParamProposal         ·      98331  ·      99264  ·      98798  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Govern              ·  createParamProposal          ·          -  ·          -  ·     229948  ·           10  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Govern              ·  voteParam                    ·          -  ·          -  ·      56052  ·           10  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy  ·  initGov                      ·      46215  ·      46227  ·      46225  ·            5  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy  ·  proposeResetSigners          ·          -  ·          -  ·     136167  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy  ·  proposeTransferOwnership     ·          -  ·          -  ·     113155  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy  ·  proposeUpdateGovernor        ·          -  ·          -  ·     113926  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy  ·  proposeUpdatePauser          ·     113988  ·     131050  ·     122519  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  MessageBus          ·  executeMessage               ·      83923  ·      85618  ·      84891  ·            9  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  MessageBus          ·  executeMessage               ·          -  ·          -  ·      85001  ·            3  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  MessageBus          ·  transferAndExecuteMsg        ·          -  ·          -  ·     175691  ·            3  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  PeggedTokenBridge   ·  mint                         ·          -  ·          -  ·     154806  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  PeggedTokenBridge   ·  transferOwnership            ·      28639  ·      28651  ·      28649  ·            5  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SGN                 ·  updateSgnAddr                ·      49524  ·      85921  ·      82399  ·           31  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  createParamChangeProposal    ·          -  ·          -  ·     121155  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  createProxyUpdateProposal    ·          -  ·          -  ·     111384  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  createTransferTokenProposal  ·          -  ·          -  ·     122482  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  createVoterUpdateProposal    ·          -  ·          -  ·     127937  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  executeProposal              ·      88875  ·     138438  ·     113083  ·           18  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  SimpleGovernance    ·  voteProposal                 ·      49931  ·      49943  ·      49935  ·            6  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  addWhitelisted               ·          -  ·          -  ·      47514  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  bondValidator                ·      86379  ·     137685  ·     101835  ·          206  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  collectForfeiture            ·          -  ·          -  ·      57812  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  completeUndelegate           ·      57653  ·      68124  ·      62431  ·           10  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  confirmUnbondedValidator     ·          -  ·          -  ·      33838  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  delegate                     ·      69552  ·      86664  ·      75270  ·          256  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  drainToken                   ·          -  ·          -  ·      40813  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  initializeValidator          ·     244311  ·     282523  ·     256848  ·          214  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  pause                        ·          -  ·          -  ·      29806  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  setGovContract               ·      46153  ·      46165  ·      46164  ·            9  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  setMaxSlashFactor            ·          -  ·          -  ·      23898  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  setRewardContract            ·          -  ·          -  ·      46188  ·            6  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  setWhitelistEnabled          ·          -  ·          -  ·      45892  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  slash                        ·     148219  ·     227830  ·     183068  ·           16  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  undelegateShares             ·      54778  ·     189800  ·     135745  ·           21  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  undelegateTokens             ·     100199  ·     112066  ·     104155  ·            3  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  unpause                      ·          -  ·          -  ·      29777  ·            1  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  updateCommissionRate         ·          -  ·          -  ·      32393  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  updateMinSelfDelegation      ·      34512  ·      41901  ·      36623  ·            7  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Staking             ·  updateValidatorSigner        ·      53445  ·      55683  ·      54564  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  StakingReward       ·  claimReward                  ·     102379  ·     119459  ·     110919  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  StakingReward       ·  contributeToRewardPool       ·      52719  ·      69819  ·      66710  ·           11  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  StakingReward       ·  pause                        ·          -  ·          -  ·      29783  ·            2  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TestERC20           ·  approve                      ·      46283  ·      46295  ·      46294  ·          398  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TestERC20           ·  transfer                     ·      51555  ·      51579  ·      51577  ·          461  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  executeMessageWithTransfer   ·      45082  ·     122433  ·      94105  ·            8  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  setMessageBus                ·          -  ·          -  ·      30056  ·            4  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  setMinSwapAmount             ·          -  ·          -  ·      46338  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  setSupportedDex              ·      26516  ·      26528  ·      26526  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  transferWithSwap             ·     136832  ·     228452  ·     171209  ·           10  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  TransferSwap        ·  transferWithSwapNative       ·          -  ·          -  ·     228511  ·            3  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  WETH                ·  deposit                      ·          -  ·          -  ·      67647  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  WETH                ·  transfer                     ·      46767  ·      46779  ·      46777  ·           12  ·          -  │\n·······················|·······························|·············|·············|·············|···············|··············\n|  Deployments                                         ·                                         ·  % of limit   ·             │\n·······················································|·············|·············|·············|···············|··············\n|  Bridge                                              ·          -  ·          -  ·    4642238  ·       15.5 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  DummySwap                                           ·          -  ·          -  ·     569089  ·        1.9 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  FarmingRewards                                      ·    1611954  ·    1611966  ·    1611965  ·        5.4 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  Govern                                              ·    1184346  ·    1184370  ·    1184367  ·        3.9 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  GovernedOwnerProxy                                  ·          -  ·          -  ·    2365331  ·        7.9 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  MessageBus                                          ·          -  ·          -  ·    3611043  ·         12 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  MsgTest                                             ·          -  ·          -  ·    2325332  ·        7.8 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  PeggedTokenBridge                                   ·          -  ·          -  ·    2483141  ·        8.3 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  SGN                                                 ·    1825005  ·    1825017  ·    1825016  ·        6.1 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  SimpleGovernance                                    ·          -  ·          -  ·    2587562  ·        8.6 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  SingleBridgeToken                                   ·    1051856  ·    1051868  ·    1051867  ·        3.5 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  Staking                                             ·    5537490  ·    5537502  ·    5537500  ·       18.5 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  StakingReward                                       ·    1424076  ·    1424088  ·    1424087  ·        4.7 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  TestERC20                                           ·          -  ·          -  ·     668912  ·        2.2 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  TransferSwap                                        ·    2778901  ·    2778925  ·    2778922  ·        9.3 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  Viewer                                              ·    1268098  ·    1268110  ·    1268109  ·        4.2 %  ·          -  │\n·······················································|·············|·············|·············|···············|··············\n|  WETH                                                ·          -  ·          -  ·     824506  ·        2.7 %  ·          -  │\n·------------------------------------------------------|-------------|-------------|-------------|---------------|-------------·"
  },
  {
    "path": "scripts/common.ts",
    "content": "import { Numeric, Overrides, parseUnits } from 'ethers';\nimport { ethers, getNamedAccounts } from 'hardhat';\n\nimport { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';\n\nexport async function getDeployerSigner(): Promise<SignerWithAddress> {\n  const deployer = (await getNamedAccounts())['deployer'];\n  return await ethers.getSigner(deployer);\n}\n\nexport async function getFeeOverrides(): Promise<Overrides> {\n  const feeData = await ethers.provider.getFeeData();\n  // const network = await ethers.provider.getNetwork();\n  // if (network.chainId == BigInt(59144)) {\n  //   // for Linea\n  //   return { maxFeePerGas: 5000000000, maxPriorityFeePerGas: 4900000000 };\n  // }\n  if (feeData.maxFeePerGas) {\n    return { maxFeePerGas: feeData.maxFeePerGas, maxPriorityFeePerGas: feeData.maxPriorityFeePerGas || 0 };\n  }\n  return { gasPrice: feeData.gasPrice || 0 };\n}\n\nexport function getParseUnitsCallback(\n  unitNames: (string | Numeric)[]\n): (value: string, index: number, array: string[]) => bigint {\n  return (s, i) => parseUnits(s, unitNames[i]);\n}\n"
  },
  {
    "path": "scripts/init_governed_owner.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { GovernedOwnerProxy__factory } from '../typechain';\nimport { getDeployerSigner, getFeeOverrides } from './common';\n\ndotenv.config();\n\nasync function initGov(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const ownerProxyAddr = process.env.GOVERNED_OWNER_PROXY as string;\n  if (!ownerProxyAddr) {\n    return;\n  }\n  const simpleGovernanceAddr = process.env.SIMPLE_GOVERNANCE as string;\n  if (!simpleGovernanceAddr) {\n    return;\n  }\n  const governedOwnerProxy = GovernedOwnerProxy__factory.connect(ownerProxyAddr, deployerSigner);\n  await (await governedOwnerProxy.initGov(simpleGovernanceAddr, feeOverrides)).wait();\n  console.log('initGov', ownerProxyAddr, simpleGovernanceAddr);\n}\n\ninitGov();\n"
  },
  {
    "path": "scripts/oasys_token_factory.ts",
    "content": "import * as dotenv from 'dotenv';\nimport { AbiCoder, parseUnits } from 'ethers';\n\nimport { L1StandardERC20__factory, L1StandardERC20Factory__factory } from '../typechain';\nimport { getDeployerSigner } from './common';\n\ndotenv.config();\n\nasync function createToken(): Promise<void> {\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  const deployerSigner = await getDeployerSigner();\n\n  const pegbrV2Addr = process.env.PEGGED_TOKEN_BRIDGE as string;\n  const factoryAddr = process.env.OASYS_TOKEN_FACTORY as string;\n  const tokenName = process.env.OASYS_FACTORY_TOKEN_NAME as string;\n  const tokenSymbol = process.env.OASYS_FACTORY_TOKEN_SYMBOL as string;\n\n  console.log(\n    'params',\n    '\\nPEGGED_TOKEN_BRIDGE',\n    pegbrV2Addr,\n    '\\nOASYS_TOKEN_FACTORY',\n    factoryAddr,\n    '\\nOASYS_FACTORY_TOKEN_NAME',\n    tokenName,\n    '\\nOASYS_FACTORY_TOKEN_SYMBOL',\n    tokenSymbol\n  );\n\n  console.log('creating ERC20 token, calling token factory', factoryAddr);\n  const factory = L1StandardERC20Factory__factory.connect(factoryAddr, deployerSigner);\n  const tx = await factory.createStandardERC20(tokenName, tokenSymbol, {\n    gasLimit: 5_000_000,\n    maxFeePerGas: parseUnits('10', 'gwei'),\n    maxPriorityFeePerGas: parseUnits('8', 'gwei')\n  });\n  console.log('tx hash', tx.hash, 'nonce', tx.nonce, 'waiting for 5 block confirmations...');\n  await tx.wait(5);\n  console.log('deployed, finding tx receipt for logs...');\n  const receipt = (await deployerSigner.provider.getTransactionReceipt(tx.hash))!;\n  const log = receipt.logs.find((log) => log.address === factoryAddr);\n  if (!log || log.topics.length !== 3) {\n    console.error('token creation log not found');\n    process.exit(1);\n  }\n  const tokenAddr = abiCoder.decode(['address'], log.topics[2]).toString();\n\n  console.log('deployed token address', tokenAddr);\n  const token = L1StandardERC20__factory.connect(tokenAddr, deployerSigner);\n  const minterRole = await token.MINTER_ROLE();\n  console.log('adding minter role', minterRole);\n  const setRoleTx = await token.grantRole(minterRole, pegbrV2Addr, {\n    gasLimit: 5_000_000,\n    nonce: tx.nonce + 1,\n    maxFeePerGas: parseUnits('10', 'gwei'),\n    maxPriorityFeePerGas: parseUnits('8', 'gwei')\n  });\n  console.log('tx hash', setRoleTx.hash);\n  await setRoleTx.wait(2);\n}\n\ncreateToken();\n\n// ABI of https://github.com/oasysgames/oasys-optimism/blob/develop/packages/contracts/contracts/oasys/L1/token/L1StandardERC20Factory.sol\n// [\n//   {\n//     \"anonymous\": false,\n//     \"inputs\": [\n//       {\n//         \"indexed\": true,\n//         \"internalType\": \"string\",\n//         \"name\": \"_symbol\",\n//         \"type\": \"string\"\n//       },\n//       {\n//         \"indexed\": true,\n//         \"internalType\": \"address\",\n//         \"name\": \"_address\",\n//         \"type\": \"address\"\n//       }\n//     ],\n//     \"name\": \"ERC20Created\",\n//     \"type\": \"event\"\n//   },\n//   {\n//     \"inputs\": [\n//       {\n//         \"internalType\": \"string\",\n//         \"name\": \"_name\",\n//         \"type\": \"string\"\n//       },\n//       {\n//         \"internalType\": \"string\",\n//         \"name\": \"_symbol\",\n//         \"type\": \"string\"\n//       }\n//     ],\n//     \"name\": \"createStandardERC20\",\n//     \"outputs\": [],\n//     \"stateMutability\": \"nonpayable\",\n//     \"type\": \"function\"\n//   }\n// ]\n"
  },
  {
    "path": "scripts/pb_gen_sol.sh",
    "content": "PROTOC_VER=\"\"\nGEN_SOL_VER=\"\"\n\n# need to install and run npx prettier -w contracts\n\nprepare_tools() {\n  setup_git\n}\n\ngen_sol() {\n  echo \"TBD\"\n}\n\nadd_to_pr() {\n  echo \"TBD\"\n}\n\nsetup_git() {\n  git config --global user.email \"build@celer.network\"\n  git config --global user.name \"Build Bot\"\n  git config --global push.default \"current\"\n}\n"
  },
  {
    "path": "scripts/reset_signers.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Bridge__factory } from '../typechain';\nimport { getDeployerSigner, getFeeOverrides } from './common';\n\ndotenv.config();\n\nasync function resetSigners(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const bridgeAddr = process.env.BRIDGE as string;\n  if (!bridgeAddr) {\n    return;\n  }\n  const bridge = Bridge__factory.connect(bridgeAddr, deployerSigner);\n  // Uncomment if needed\n  // await (await bridge.notifyResetSigners(feeOverrides)).wait();\n\n  const signers = (process.env.BRIDGE_SIGNERS as string).split(',');\n  const powers = (process.env.BRIDGE_POWERS as string).split(',');\n  await (await bridge.resetSigners(signers, powers, feeOverrides)).wait();\n  console.log('resetSigners', signers, powers);\n}\n\nresetSigners();\n"
  },
  {
    "path": "scripts/sentinel/sentinel_guard.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Sentinel__factory } from '../../typechain';\nimport { getDeployerSigner, getFeeOverrides } from '../common';\n\ndotenv.config();\n\nasync function guard(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const sentinelAddr = process.env.SENTINEL as string;\n  if (!sentinelAddr) {\n    return;\n  }\n  const sentinel = Sentinel__factory.connect(sentinelAddr, deployerSigner);\n  await (await sentinel.guard(feeOverrides)).wait();\n}\n\nguard();\n"
  },
  {
    "path": "scripts/sentinel/sentinel_relax.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Sentinel__factory } from '../../typechain';\nimport { getDeployerSigner, getFeeOverrides } from '../common';\n\ndotenv.config();\n\nasync function relax(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const sentinelAddr = process.env.SENTINEL as string;\n  if (!sentinelAddr) {\n    return;\n  }\n  const sentinel = Sentinel__factory.connect(sentinelAddr, deployerSigner);\n  await (await sentinel.relax(feeOverrides)).wait();\n}\n\nrelax();\n"
  },
  {
    "path": "scripts/sentinel/sentinel_set_limits.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Sentinel__factory } from '../../typechain';\nimport { TypedContractMethod } from '../../typechain/common';\nimport { getDeployerSigner, getFeeOverrides, getParseUnitsCallback } from '../common';\n\nimport type { AddressLike, BigNumberish, Overrides } from 'ethers';\ndotenv.config();\n\nasync function setLimitIfSpecified(\n  limitEnv: string,\n  target: string,\n  tokens: string[],\n  decimals: string[],\n  methodName: string,\n  method: TypedContractMethod<\n    [_target: string, _tokens: AddressLike[], _amounts: BigNumberish[]],\n    [void],\n    'nonpayable'\n  >,\n  feeOverrides: Overrides\n): Promise<void> {\n  if (limitEnv) {\n    const limitStr = limitEnv.split(',');\n    if (limitEnv.length > 0 && limitStr.length === decimals.length) {\n      const limits = limitStr.map(getParseUnitsCallback(decimals.map(Number)));\n      await (await method(target, tokens, limits, feeOverrides)).wait();\n      console.log(\n        methodName,\n        target,\n        tokens,\n        limits.map((limit) => limit.toString())\n      );\n    }\n  }\n}\n\nasync function setBridgeLimits(sentinelAddr: string): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const bridgeAddr = process.env.BRIDGE as string;\n  if (!bridgeAddr) {\n    return;\n  }\n  const tokensStr = process.env.BRIDGE_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const sentinel = Sentinel__factory.connect(sentinelAddr, deployerSigner);\n  const tokens = (process.env.BRIDGE_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.BRIDGE_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MIN_ADDS as string,\n    bridgeAddr,\n    tokens,\n    decimals,\n    'setMinAdd',\n    sentinel.setMinAdd,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MIN_SENDS as string,\n    bridgeAddr,\n    tokens,\n    decimals,\n    'setMinSend',\n    sentinel.setMinSend,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MAX_SENDS as string,\n    bridgeAddr,\n    tokens,\n    decimals,\n    'setMaxSend',\n    sentinel.setMaxSend,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_EPOCH_VOLUME_CAPS as string,\n    bridgeAddr,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    sentinel.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_DELAY_THRESHOLDS as string,\n    bridgeAddr,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    sentinel.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setOriginalTokenVaultLimits(sentinelAddr: string): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const originalTokenVaultAddr = process.env.ORIGINAL_TOKEN_VAULT as string;\n  if (!originalTokenVaultAddr) {\n    return;\n  }\n  const tokensStr = process.env.ORIGINAL_TOKEN_VAULT_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const sentinel = Sentinel__factory.connect(sentinelAddr, deployerSigner);\n  const tokens = (process.env.ORIGINAL_TOKEN_VAULT_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.ORIGINAL_TOKEN_VAULT_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_MIN_DEPOSITS as string,\n    originalTokenVaultAddr,\n    tokens,\n    decimals,\n    'setMinDeposit',\n    sentinel.setMinDeposit,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_MAX_DEPOSITS as string,\n    originalTokenVaultAddr,\n    tokens,\n    decimals,\n    'setMaxDeposit',\n    sentinel.setMaxDeposit,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_EPOCH_VOLUME_CAPS as string,\n    originalTokenVaultAddr,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    sentinel.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_DELAY_THRESHOLDS as string,\n    originalTokenVaultAddr,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    sentinel.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setPeggedTokenBridgeLimits(sentinelAddr: string): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const peggedTokenBridgeAddr = process.env.PEGGED_TOKEN_BRIDGE as string;\n  if (!peggedTokenBridgeAddr) {\n    return;\n  }\n  const tokensStr = process.env.PEGGED_TOKEN_BRIDGE_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const sentinel = Sentinel__factory.connect(sentinelAddr, deployerSigner);\n  const tokens = (process.env.PEGGED_TOKEN_BRIDGE_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.PEGGED_TOKEN_BRIDGE_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_MIN_BURNS as string,\n    peggedTokenBridgeAddr,\n    tokens,\n    decimals,\n    'setMinBurn',\n    sentinel.setMinBurn,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_MAX_BURNS as string,\n    peggedTokenBridgeAddr,\n    tokens,\n    decimals,\n    'setMaxBurn',\n    sentinel.setMaxBurn,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_EPOCH_VOLUME_CAPS as string,\n    peggedTokenBridgeAddr,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    sentinel.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_DELAY_THRESHOLDS as string,\n    peggedTokenBridgeAddr,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    sentinel.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setLimits(): Promise<void> {\n  const sentinelAddr = process.env.SENTINEL as string;\n  if (!sentinelAddr) {\n    return;\n  }\n  await setBridgeLimits(sentinelAddr);\n  await setOriginalTokenVaultLimits(sentinelAddr);\n  await setPeggedTokenBridgeLimits(sentinelAddr);\n}\n\nsetLimits();\n"
  },
  {
    "path": "scripts/set_basics.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport {\n  Bridge__factory,\n  MessageBus__factory,\n  OriginalTokenVault__factory,\n  PeggedNativeTokenBridge__factory,\n  PeggedTokenBridge__factory,\n  RFQ__factory\n} from '../typechain';\nimport { getDeployerSigner, getFeeOverrides } from './common';\n\ndotenv.config();\n\nasync function setBridgeBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const bridgeAddr = process.env.BRIDGE as string;\n  if (!bridgeAddr) {\n    return;\n  }\n  const bridge = Bridge__factory.connect(bridgeAddr, deployerSigner);\n  const pausers = (process.env.BRIDGE_PAUSERS as string).split(',');\n  if (pausers[0].length > 0) {\n    for (let i = 0; i < pausers.length; i++) {\n      const pauser = pausers[i];\n      await (await bridge.addPauser(pauser, feeOverrides)).wait();\n      console.log('addPauser', pauser);\n    }\n  }\n  const governors = (process.env.BRIDGE_GOVERNORS as string).split(',');\n  if (governors[0].length > 0) {\n    for (let i = 0; i < governors.length; i++) {\n      const governor = governors[i];\n      await (await bridge.addGovernor(governor, feeOverrides)).wait();\n      console.log('addGovernor', governor);\n    }\n  }\n  const delayPeriod = process.env.BRIDGE_DELAY_PERIOD as string;\n  if (delayPeriod) {\n    await (await bridge.setDelayPeriod(delayPeriod, feeOverrides)).wait();\n    console.log('setDelayPeriod', delayPeriod);\n  }\n  const epochLength = process.env.BRIDGE_EPOCH_LENGTH as string;\n  if (epochLength) {\n    await (await bridge.setEpochLength(epochLength, feeOverrides)).wait();\n    console.log('setEpochLength', epochLength);\n  }\n  const minimalMaxSlippage = process.env.BRIDGE_MINIMAL_MAX_SLIPPAGE as string;\n  if (minimalMaxSlippage) {\n    await (await bridge.setMinimalMaxSlippage(minimalMaxSlippage, feeOverrides)).wait();\n    console.log('setMinimalMaxSlippage', minimalMaxSlippage);\n  }\n  const weth = process.env.BRIDGE_WETH as string;\n  if (weth) {\n    await (await bridge.setWrap(weth, feeOverrides)).wait();\n    console.log('setWrap', weth);\n  }\n}\n\nasync function setPeggedTokenBridgeBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const peggedTokenBridgeAddr = process.env.PEGGED_TOKEN_BRIDGE as string;\n  if (!peggedTokenBridgeAddr) {\n    return;\n  }\n  const peggedTokenBridge = PeggedTokenBridge__factory.connect(peggedTokenBridgeAddr, deployerSigner);\n  const pausers = (process.env.PEGGED_TOKEN_BRIDGE_PAUSERS as string).split(',');\n  if (pausers[0].length > 0) {\n    for (let i = 0; i < pausers.length; i++) {\n      const pauser = pausers[i];\n      await (await peggedTokenBridge.addPauser(pauser, feeOverrides)).wait();\n      console.log('addPauser', pauser);\n    }\n  }\n  const governors = (process.env.PEGGED_TOKEN_BRIDGE_GOVERNORS as string).split(',');\n  if (governors[0].length > 0) {\n    for (let i = 0; i < governors.length; i++) {\n      const governor = governors[i];\n      await (await peggedTokenBridge.addGovernor(governor, feeOverrides)).wait();\n      console.log('addGovernor', governor);\n    }\n  }\n  const delayPeriod = process.env.PEGGED_TOKEN_BRIDGE_DELAY_PERIOD as string;\n  if (delayPeriod) {\n    await (await peggedTokenBridge.setDelayPeriod(delayPeriod, feeOverrides)).wait();\n    console.log('setDelayPeriod', delayPeriod);\n  }\n  const epochLength = process.env.PEGGED_TOKEN_BRIDGE_EPOCH_LENGTH as string;\n  if (epochLength) {\n    await (await peggedTokenBridge.setEpochLength(epochLength, feeOverrides)).wait();\n    console.log('setEpochLength', epochLength);\n  }\n}\n\nasync function setPeggedNativeTokenBridgeBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const peggedTokenBridgeAddr = process.env.PEGGED_TOKEN_BRIDGE as string;\n  if (!peggedTokenBridgeAddr) {\n    return;\n  }\n  const peggedNativeTokenBridge = PeggedNativeTokenBridge__factory.connect(peggedTokenBridgeAddr, deployerSigner);\n  const nativeVault = process.env.PEGGED_TOKEN_BRIDGE_NATIVE_VAULT as string;\n  if (nativeVault) {\n    await (await peggedNativeTokenBridge.setNativeVault(nativeVault, feeOverrides)).wait();\n    console.log('setNativeVault', nativeVault);\n  }\n}\n\nasync function setOriginalTokenVaultBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const originalTokenVaultAddr = process.env.ORIGINAL_TOKEN_VAULT as string;\n  if (!originalTokenVaultAddr) {\n    return;\n  }\n  const originalTokenVault = OriginalTokenVault__factory.connect(originalTokenVaultAddr, deployerSigner);\n  const pausers = (process.env.ORIGINAL_TOKEN_VAULT_PAUSERS as string).split(',');\n  if (pausers[0].length > 0) {\n    for (let i = 0; i < pausers.length; i++) {\n      const pauser = pausers[i];\n      await (await originalTokenVault.addPauser(pauser, feeOverrides)).wait();\n      console.log('addPauser', pauser);\n    }\n  }\n  const governors = (process.env.ORIGINAL_TOKEN_VAULT_GOVERNORS as string).split(',');\n  if (governors[0].length > 0) {\n    for (let i = 0; i < governors.length; i++) {\n      const governor = governors[i];\n      await (await originalTokenVault.addGovernor(governor, feeOverrides)).wait();\n      console.log('addGovernor', governor);\n    }\n  }\n  const delayPeriod = process.env.ORIGINAL_TOKEN_VAULT_DELAY_PERIOD as string;\n  if (delayPeriod) {\n    await (await originalTokenVault.setDelayPeriod(delayPeriod, feeOverrides)).wait();\n    console.log('setDelayPeriod', delayPeriod);\n  }\n  const epochLength = process.env.ORIGINAL_TOKEN_VAULT_EPOCH_LENGTH as string;\n  if (epochLength) {\n    await (await originalTokenVault.setEpochLength(epochLength, feeOverrides)).wait();\n    console.log('setEpochLength', epochLength);\n  }\n  const weth = process.env.ORIGINAL_TOKEN_VAULT_WETH as string;\n  if (weth) {\n    await (await originalTokenVault.setWrap(weth, feeOverrides)).wait();\n    console.log('setWrap', weth);\n  }\n}\n\nasync function setMessageBusBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const messageBusAddr = process.env.MESSAGE_BUS as string;\n  if (!messageBusAddr) {\n    return;\n  }\n  const messageBus = MessageBus__factory.connect(messageBusAddr, deployerSigner);\n  const liquidityBridge = process.env.MESSAGE_BUS_LIQUIDITY_BRIDGE as string;\n  if (liquidityBridge) {\n    await (await messageBus.setLiquidityBridge(liquidityBridge, feeOverrides)).wait();\n    console.log('setLiquidityBridge', liquidityBridge);\n  }\n  const pegVault = process.env.MESSAGE_BUS_PEG_VAULT as string;\n  if (pegVault) {\n    await (await messageBus.setPegVault(pegVault, feeOverrides)).wait();\n    console.log('setPegVault', pegVault);\n  }\n  const pegBridge = process.env.MESSAGE_BUS_PEG_BRIDGE as string;\n  if (pegBridge) {\n    await (await messageBus.setPegBridge(pegBridge, feeOverrides)).wait();\n    console.log('setPegBridge', pegBridge);\n  }\n  const pegVaultV2 = process.env.MESSAGE_BUS_PEG_VAULT_V2 as string;\n  if (pegVaultV2) {\n    await (await messageBus.setPegVaultV2(pegVaultV2, feeOverrides)).wait();\n    console.log('setPegVaultV2', pegVaultV2);\n  }\n  const pegBridgeV2 = process.env.MESSAGE_BUS_PEG_BRIDGE_V2 as string;\n  if (pegBridgeV2) {\n    await (await messageBus.setPegBridgeV2(pegBridgeV2, feeOverrides)).wait();\n    console.log('setPegBridgeV2', pegBridgeV2);\n  }\n}\n\nasync function setRFQBasics(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const rfqAddr = process.env.RFQ_ADDR as string;\n  if (!rfqAddr) {\n    return;\n  }\n  const rfq = RFQ__factory.connect(rfqAddr, deployerSigner);\n  const pausers = (process.env.RFQ_PAUSERS as string).split(',');\n  if (pausers[0].length > 0) {\n    for (let i = 0; i < pausers.length; i++) {\n      const pauser = pausers[i];\n      await (await rfq.addPauser(pauser, feeOverrides)).wait();\n      console.log('addPauser', pauser);\n    }\n  }\n  const governors = (process.env.RFQ_GOVERNORS as string).split(',');\n  if (governors[0].length > 0) {\n    for (let i = 0; i < governors.length; i++) {\n      const governor = governors[i];\n      await (await rfq.addGovernor(governor, feeOverrides)).wait();\n      console.log('addGovernor', governor);\n    }\n  }\n  const remoteChains = (process.env.RFQ_REMOTE_CHAINS as string).split(',');\n  const remoteRFQs = (process.env.RFQ_REMOTE_RFQS as string).split(',');\n  if (remoteChains[0].length > 0 && remoteChains.length == remoteRFQs.length) {\n    await (await rfq.setRemoteRfqContracts(remoteChains, remoteRFQs, feeOverrides)).wait();\n    console.log('setRemoteRfqContracts', remoteChains, remoteRFQs);\n  }\n  const feeChains = (process.env.RFQ_FEE_CHAINS as string).split(',');\n  const feePercs = (process.env.RFQ_FEE_PERCS as string).split(',');\n  if (feeChains[0].length > 0 && feeChains.length == feePercs.length) {\n    await (await rfq.setFeePerc(feeChains, feePercs, feeOverrides)).wait();\n    console.log('setFeePerc', feeChains, feePercs);\n  }\n  const treasuryAddr = process.env.RFQ_TREASURE_ADDR as string;\n  if (treasuryAddr) {\n    await (await rfq.setTreasuryAddr(treasuryAddr, feeOverrides)).wait();\n    console.log('setTreasuryAddr', treasuryAddr);\n  }\n  const weth = process.env.RFQ_NATIVE_WRAP as string;\n  if (weth) {\n    await (await rfq.setNativeWrap(weth, feeOverrides)).wait();\n    console.log('setNativeWrap', weth);\n  }\n}\n\nasync function setBasics(): Promise<void> {\n  await setBridgeBasics();\n  await setOriginalTokenVaultBasics();\n  await setPeggedTokenBridgeBasics();\n  await setMessageBusBasics();\n  await setPeggedNativeTokenBridgeBasics();\n  await setRFQBasics();\n}\n\nsetBasics();\n"
  },
  {
    "path": "scripts/set_limits.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Bridge__factory, OriginalTokenVault__factory, PeggedTokenBridge__factory } from '../typechain';\nimport { TypedContractMethod } from '../typechain/common';\nimport { getDeployerSigner, getFeeOverrides, getParseUnitsCallback } from './common';\n\nimport type { AddressLike, BigNumberish, Overrides } from 'ethers';\n\ndotenv.config();\n\nasync function setLimitIfSpecified(\n  limitEnv: string,\n  tokens: string[],\n  decimals: string[],\n  methodName: string,\n  method: TypedContractMethod<[_tokens: AddressLike[], _amounts: BigNumberish[]], [void], 'nonpayable'>,\n  feeOverrides: Overrides\n): Promise<void> {\n  if (limitEnv) {\n    const limitStr = limitEnv.split(',');\n    if (limitEnv.length > 0 && limitStr.length === decimals.length) {\n      const limits = limitStr.map(getParseUnitsCallback(decimals.map(Number)));\n      await (await method(tokens, limits, feeOverrides)).wait();\n      console.log(\n        methodName,\n        tokens,\n        limits.map((limit) => limit.toString())\n      );\n    }\n  }\n}\n\nasync function setBridgeLimits(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const bridgeAddr = process.env.BRIDGE as string;\n  if (!bridgeAddr) {\n    return;\n  }\n  const tokensStr = process.env.BRIDGE_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const bridge = Bridge__factory.connect(bridgeAddr, deployerSigner);\n  const tokens = (process.env.BRIDGE_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.BRIDGE_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MIN_ADDS as string,\n    tokens,\n    decimals,\n    'setMinAdd',\n    bridge.setMinAdd,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MIN_SENDS as string,\n    tokens,\n    decimals,\n    'setMinSend',\n    bridge.setMinSend,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_MAX_SENDS as string,\n    tokens,\n    decimals,\n    'setMaxSend',\n    bridge.setMaxSend,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_EPOCH_VOLUME_CAPS as string,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    bridge.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.BRIDGE_LIMIT_DELAY_THRESHOLDS as string,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    bridge.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setOriginalTokenVaultLimits(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const originalTokenVaultAddr = process.env.ORIGINAL_TOKEN_VAULT as string;\n  if (!originalTokenVaultAddr) {\n    return;\n  }\n  const tokensStr = process.env.ORIGINAL_TOKEN_VAULT_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const originalTokenVault = OriginalTokenVault__factory.connect(originalTokenVaultAddr, deployerSigner);\n  const tokens = (process.env.ORIGINAL_TOKEN_VAULT_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.ORIGINAL_TOKEN_VAULT_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_MIN_DEPOSITS as string,\n    tokens,\n    decimals,\n    'setMinDeposit',\n    originalTokenVault.setMinDeposit,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_MAX_DEPOSITS as string,\n    tokens,\n    decimals,\n    'setMaxDeposit',\n    originalTokenVault.setMaxDeposit,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_EPOCH_VOLUME_CAPS as string,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    originalTokenVault.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.ORIGINAL_TOKEN_VAULT_LIMIT_DELAY_THRESHOLDS as string,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    originalTokenVault.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setPeggedTokenBridgeLimits(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const peggedTokenBridgeAddr = process.env.PEGGED_TOKEN_BRIDGE as string;\n  if (!peggedTokenBridgeAddr) {\n    return;\n  }\n  const tokensStr = process.env.PEGGED_TOKEN_BRIDGE_LIMIT_TOKENS;\n  if (!tokensStr) {\n    return;\n  }\n  const peggedTokenBridge = PeggedTokenBridge__factory.connect(peggedTokenBridgeAddr, deployerSigner);\n  const tokens = (process.env.PEGGED_TOKEN_BRIDGE_LIMIT_TOKENS as string).split(',');\n  const decimals = (process.env.PEGGED_TOKEN_BRIDGE_LIMIT_DECIMALS as string).split(',');\n\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_MIN_BURNS as string,\n    tokens,\n    decimals,\n    'setMinBurn',\n    peggedTokenBridge.setMinBurn,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_MAX_BURNS as string,\n    tokens,\n    decimals,\n    'setMaxBurn',\n    peggedTokenBridge.setMaxBurn,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_EPOCH_VOLUME_CAPS as string,\n    tokens,\n    decimals,\n    'setEpochVolumeCaps',\n    peggedTokenBridge.setEpochVolumeCaps,\n    feeOverrides\n  );\n  await setLimitIfSpecified(\n    process.env.PEGGED_TOKEN_BRIDGE_LIMIT_DELAY_THRESHOLDS as string,\n    tokens,\n    decimals,\n    'setDelayThresholds',\n    peggedTokenBridge.setDelayThresholds,\n    feeOverrides\n  );\n}\n\nasync function setLimits(): Promise<void> {\n  await setBridgeLimits();\n  await setOriginalTokenVaultLimits();\n  await setPeggedTokenBridgeLimits();\n}\n\nsetLimits();\n"
  },
  {
    "path": "scripts/set_msg_fee.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\nimport { deployments } from 'hardhat';\n\nimport { MessageBus__factory } from '../typechain';\nimport { getDeployerSigner } from './common';\n\ndotenv.config();\n\nasync function setMsgFees(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const dep = await deployments.get('MessageBus_Proxy');\n  console.log('msgbus', dep.address);\n  const msgbus = MessageBus__factory.connect(dep.address, deployerSigner);\n  const owner = await msgbus.owner();\n  const bridge = await msgbus.liquidityBridge();\n  console.log('owner', owner);\n  console.log('bridge', bridge);\n  const tx0 = await msgbus.setFeeBase('1400000000000000');\n  tx0.wait(1);\n  const tx1 = await msgbus.setFeePerByte('14000000000000');\n  tx1.wait(10);\n\n  const feeBase = await msgbus.feeBase();\n  const feePerByte = await msgbus.feePerByte();\n\n  console.log(`new feeBase ${feeBase} feePerByte ${feePerByte}`);\n}\n\nsetMsgFees();\n"
  },
  {
    "path": "scripts/solc_abigen.sh",
    "content": "# script for solc/abigen solidity files\n# below env variables are set by github action\n\n# PRID: ${{ github.event.number }}\n# BRANCH: ${{ github.head_ref }}\n# GH_TOKEN: ${{ secrets.GH_TOKEN }}\n\nSOLC_VER=\"v0.8.17+commit.8df45f5f\"\nOPENZEPPELIN=\"openzeppelin-contracts-4.5.0\"          # if change, also need to change the url in dld_solc\nGETH_VER=\"geth-alltools-linux-amd64-1.10.7-12f0ff40\" # for abigen\nCNTRDIR=\"contracts\"                                  # folder name for all contracts code\nGO_REPO=https://${GH_TOKEN}@github.com/celer-network/sgn-v2\n\n# xx.sol under contracts/, no need for .sol suffix, if sol file is in subfolder, just add the relative path\nsolFiles=(\n  staking/Staking\n  staking/SGN\n  staking/StakingReward\n  staking/Govern\n  staking/Viewer\n  liquidity-bridge/FarmingRewards\n  interfaces/ISigsVerifier\n)\n\ndld_solc() {\n  curl -L \"https://binaries.soliditylang.org/linux-amd64/solc-linux-amd64-${SOLC_VER}\" -o solc && chmod +x solc\n  sudo mv solc /usr/local/bin/\n  # only need oz's contracts subfolder, files will be at $CNTRDIR/$OPENZEPPELIN/contracts\n  curl -L \"https://github.com/OpenZeppelin/openzeppelin-contracts/archive/v4.5.0.tar.gz\" | tar -xz -C $CNTRDIR $OPENZEPPELIN/contracts/\n}\n\ndld_abigen() {\n  curl -sL https://gethstore.blob.core.windows.net/builds/$GETH_VER.tar.gz | sudo tar -xz -C /usr/local/bin --strip 1 $GETH_VER/abigen\n  sudo chmod +x /usr/local/bin/abigen\n}\n\n# MUST run this under $CNTRDIR\ngen_dtHelper() {\n  pushd libraries\n  DTFILE=\"staking/DataTypes.sol\"\n  CTRNAME=\"DtHelper\"\n  SOLFILE=\"$CTRNAME.sol\"\n  cat >$SOLFILE <<EOF\n// SPDX-License-Identifier: MIT\n// Auto generated. DO NOT MODIFY MAUALLY\npragma solidity >=0.8.0;\npragma abicoder v2;\nimport {DataTypes as dt} from \"./$DTFILE\";\ncontract $CTRNAME {\nEOF\n\n  grep -Eo \"struct [^ ]*\" $DTFILE | cut -d' ' -f2 | while read STRUCT; do\n    echo \"  function $STRUCT(dt.$STRUCT calldata _in) public pure {}\" >>$SOLFILE\n  done\n\n  echo \"}\" >>$SOLFILE\n  popd\n}\n\n# MUST run this under repo root\n# will generate a single combined.json under $CNTRDIR\nrun_solc() {\n  pushd $CNTRDIR\n  gen_dtHelper\n  solc --base-path $PWD --allow-paths . --overwrite --optimize --optimize-runs 800 --pretty-json --combined-json abi,bin -o . '@openzeppelin/'=$OPENZEPPELIN/ \\\n    $(for f in ${solFiles[@]}; do echo -n \"$f.sol \"; done)\n  no_openzeppelin combined.json # combined.json file name is hardcoded in solc\n  popd\n}\n\n# remove openzeppelin from combined.json. solc will also include all openzeppelin in combined.json but we don't want to generate go for them\n# $1 is the json file from solc output\nno_openzeppelin() {\n  jq '.\"contracts\"|=with_entries(select(.key|test(\"^openzeppelin\")|not))' $1 >tmp.json\n  mv tmp.json $1\n}\n\n# MUST run this under contract repo root\nrun_abigen() {\n  PR_COMMIT_ID=$(git rev-parse --short HEAD)\n  git clone $GO_REPO\n  pushd sgn-v2\n  git fetch\n  BR=\"$BRANCH-binding\"\n  git checkout $BR || git checkout -b $BR\n\n  mkdir -p eth\n  abigen -combined-json ../$CNTRDIR/combined.json -pkg eth -out eth/bindings.go\n\n  #pushd eth\n  #go build # make sure eth pkg can build\n  #popd\n\n  if [[ $(git status --porcelain) ]]; then\n    echo \"Sync-ing go binding\"\n    git add .\n    git commit -m \"Sync go binding based on contract PR $PRID\" -m \"contract repo commit: $PR_COMMIT_ID\"\n    git push origin $BR\n  fi\n  popd\n}\n\nsetup_git() {\n  git config --global user.email \"build@celer.network\"\n  git config --global user.name \"Build Bot\"\n  git config --global push.default \"current\"\n}\n"
  },
  {
    "path": "scripts/solt.sh",
    "content": "#!/bin/sh\n\n# Script to run solt and generate standard-json files for Etherscan verification.\n\nsolFiles=(\n  circle-usdc/CircleBridgeProxy\n  governed-owner/GovernedOwnerProxy\n  governed-owner/SimpleGovernance\n  integration-examples/ContractAsLP\n  integration-examples/ContractAsSender\n  interfaces/ISigsVerifier\n  liquidity-bridge/Bridge\n  liquidity-bridge/FarmingRewards\n  liquidity-bridge/WithdrawInbox\n  message/apps/TransferSwap\n  message/apps/OrigNFT\n  message/apps/PegNFT\n  message/apps/MCNNFT\n  message/apps/NFTBridge\n  message/apps/MsgTest\n  message/apps/RFQ\n  message/apps/adapter/MessageReceiverAdapter\n  message/messagebus/MessageBus\n  miscs/Faucet\n  miscs/MintableERC20\n  pegged-bridge/OriginalTokenVault\n  pegged-bridge/OriginalTokenVaultV2\n  pegged-bridge/PeggedTokenBridge\n  pegged-bridge/PeggedTokenBridgeV2\n  pegged-bridge/tokens/ERC20Permit/MintSwapCanonicalTokenPermit\n  pegged-bridge/tokens/ERC20Permit/MultiBridgeTokenPermit\n  pegged-bridge/tokens/ERC20Permit/SingleBridgeTokenPermit\n  pegged-bridge/tokens/MintSwapCanonicalToken\n  pegged-bridge/tokens/MultiBridgeToken\n  pegged-bridge/tokens/SingleBridgeToken\n  pegged-bridge/tokens/SwapBridgeToken\n  pegged-bridge/tokens/customized/FraxBridgeToken\n  pegged-bridge/tokens/customized/CircleBridgeToken\n  pegged-bridge/tokens/owners/RestrictedMultiBridgeTokenOwner\n  pegged-bridge/customized/PeggedNativeTokenBridge\n  proxy/TransferAgent\n  staking/Govern\n  staking/SGN\n  staking/Staking\n  staking/StakingReward\n  staking/Viewer\n  test-helpers/DummySwap\n  test-helpers/WETH\n)\n\nrun_solt_write() {\n  for f in ${solFiles[@]}; do\n    solt write contracts/$f.sol --npm --runs 800\n  done\n}\n"
  },
  {
    "path": "scripts/transfer_ownership.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { Ownable__factory } from '../typechain';\nimport { getDeployerSigner, getFeeOverrides } from './common';\n\ndotenv.config();\n\nasync function transferOwnership(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const contractAddr = process.env.TRANSFER_OWNERSHIP_CONTRACT as string;\n  if (!contractAddr) {\n    return;\n  }\n  const newOwner = process.env.TRANSFER_OWNERSHIP_NEW_OWNER as string;\n  if (!newOwner) {\n    return;\n  }\n  const contract = Ownable__factory.connect(contractAddr, deployerSigner);\n  await (await contract.transferOwnership(newOwner, feeOverrides)).wait();\n  console.log('transferOwnership', contractAddr, newOwner);\n}\n\ntransferOwnership();\n"
  },
  {
    "path": "scripts/update_bridge_supply_cap.ts",
    "content": "import 'hardhat-deploy';\n\nimport * as dotenv from 'dotenv';\n\nimport { MintSwapCanonicalToken__factory } from '../typechain';\nimport { getDeployerSigner, getFeeOverrides } from './common';\n\ndotenv.config();\n\nasync function updateBridgeSupplyCap(): Promise<void> {\n  const deployerSigner = await getDeployerSigner();\n  const feeOverrides = await getFeeOverrides();\n\n  const tokenAddr = process.env.MINT_SWAP_CANONICAL_TOKEN as string;\n  if (!tokenAddr) {\n    return;\n  }\n  const bridgeAddr = process.env.MINT_SWAP_CANONICAL_TOKEN_BRIDGE as string;\n  if (!bridgeAddr) {\n    return;\n  }\n  const mintSwapCanonicalToken = MintSwapCanonicalToken__factory.connect(tokenAddr, deployerSigner);\n  const cap = process.env.MINT_SWAP_CANONICAL_TOKEN_CAP as string;\n  await (await mintSwapCanonicalToken.updateBridgeSupplyCap(bridgeAddr, cap, feeOverrides)).wait();\n  console.log('updateBridgeSupplyCap', tokenAddr, bridgeAddr, cap);\n}\n\nupdateBridgeSupplyCap();\n"
  },
  {
    "path": "test/Basics.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, solidityPackedKeccak256, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { SGN, Staking, TestERC20, Viewer } from '../typechain';\nimport { advanceBlockNumber, deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\n\ndescribe('Basic Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, sgn, viewer, celr } = await deployContracts(admin);\n    return { admin, staking, sgn, viewer, celr };\n  }\n\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  let staking: Staking;\n  let sgn: SGN;\n  let viewer: Viewer;\n  let celr: TestERC20;\n  let admin: HardhatEthersSigner;\n  let validator: Wallet;\n  let delegator: Wallet;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    sgn = res.sgn;\n    viewer = res.viewer;\n    celr = res.celr;\n    admin = res.admin;\n    const accounts = await getAccounts(res.admin, [celr], 2);\n    validator = accounts[0];\n    delegator = accounts[1];\n    const stakingAddress = await staking.getAddress();\n    await celr.connect(validator).approve(stakingAddress, parseUnits('100'));\n    await celr.connect(delegator).approve(stakingAddress, parseUnits('100'));\n  });\n\n  it('should fail to delegate to an uninitialized validator', async function () {\n    await expect(staking.delegate(validator.address, consts.DELEGATOR_STAKE)).to.be.revertedWith(\n      'Validator is not initialized'\n    );\n  });\n\n  it('should fail to initialize a validator when paused', async function () {\n    await staking.pause();\n    await expect(\n      staking\n        .connect(validator)\n        .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n    ).to.be.revertedWith('Pausable: paused');\n  });\n\n  it('should fail to initialize a validator with insufficient min self delegation', async function () {\n    await expect(\n      staking.connect(validator).initializeValidator(validator.address, parseUnits('1'), consts.COMMISSION_RATE)\n    ).to.be.revertedWith('Insufficient min self delegation');\n  });\n\n  it('should fail to initialize a validator if self delegation fails', async function () {\n    await expect(\n      staking.initializeValidator(admin.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n    ).to.be.revertedWith('ERC20: insufficient allowance');\n  });\n\n  it('should fail to initialize a non-whitelisted validator when whitelist is enabled', async function () {\n    await staking.setWhitelistEnabled(true);\n    await expect(\n      staking\n        .connect(validator)\n        .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n    ).to.be.revertedWith('Caller is not whitelisted');\n  });\n\n  it('should initialize a whitelisted validator successfully when whitelist is enabled', async function () {\n    await staking.setWhitelistEnabled(true);\n    await staking.addWhitelisted(validator.address);\n\n    const data = abiCoder.encode(\n      ['address', 'uint256', 'uint256'],\n      [validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE]\n    );\n    await expect(\n      staking\n        .connect(validator)\n        .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n    )\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validator.address, 'init', data, ZeroAddress);\n  });\n\n  it('should initialize a validator and update sgn address successfully', async function () {\n    const data = abiCoder.encode(\n      ['address', 'uint256', 'uint256'],\n      [validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE]\n    );\n    await expect(\n      staking\n        .connect(validator)\n        .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n    )\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validator.address, 'init', data, ZeroAddress);\n\n    const sgnAddr = solidityPackedKeccak256(['string'], ['sgnaddr1']);\n    await expect(sgn.connect(validator).updateSgnAddr(sgnAddr))\n      .to.emit(sgn, 'SgnAddrUpdate')\n      .withArgs(validator.address, '0x', sgnAddr)\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validator.address, 'sgn-addr', sgnAddr, sgn.getAddress());\n  });\n\n  describe('after one validator finishes initialization', async () => {\n    const sgnAddr = solidityPackedKeccak256(['string'], ['sgnaddr']);\n    beforeEach(async () => {\n      await staking\n        .connect(validator)\n        .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      await sgn.connect(validator).updateSgnAddr(sgnAddr);\n    });\n\n    it('should fail to initialize the same validator twice', async function () {\n      await expect(\n        staking\n          .connect(validator)\n          .initializeValidator(validator.address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE)\n      ).to.be.revertedWith('Validator is initialized');\n    });\n\n    it('should pass sgn address update', async function () {\n      const newSgnAddr = solidityPackedKeccak256(['string'], ['sgnaddr_new']);\n      await expect(sgn.connect(validator).updateSgnAddr(newSgnAddr))\n        .to.emit(sgn, 'SgnAddrUpdate')\n        .withArgs(validator.address, sgnAddr, newSgnAddr)\n        .to.emit(staking, 'ValidatorNotice')\n        .withArgs(validator.address, 'sgn-addr', newSgnAddr, sgn.getAddress());\n\n      await expect(sgn.connect(admin).updateSgnAddr(newSgnAddr)).to.be.revertedWith('Not unbonded validator');\n    });\n\n    it('should fail to delegate when paused', async function () {\n      await staking.pause();\n      await expect(staking.delegate(validator.address, consts.DELEGATOR_STAKE)).to.be.revertedWith('Pausable: paused');\n    });\n\n    it('should delegate to validator by a delegator successfully', async function () {\n      await expect(staking.connect(delegator).delegate(validator.address, consts.DELEGATOR_STAKE))\n        .to.emit(staking, 'DelegationUpdate')\n        .withArgs(\n          validator.address,\n          delegator.address,\n          consts.DELEGATOR_STAKE + consts.MIN_SELF_DELEGATION,\n          consts.DELEGATOR_STAKE,\n          consts.DELEGATOR_STAKE\n        );\n    });\n\n    it('should fail to bondValidator before delegating enough stake', async function () {\n      const shouldBond = await viewer.shouldBondValidator(validator.address);\n      expect(shouldBond).to.equal(false);\n      await expect(staking.connect(validator).bondValidator()).to.be.revertedWith('Not have min tokens');\n    });\n\n    describe('after one delegator delegates enough stake to the validator', async () => {\n      beforeEach(async () => {\n        await staking.connect(delegator).delegate(validator.address, consts.DELEGATOR_STAKE);\n      });\n\n      it('should fail to bondValidator before self delegating minSelfDelegation', async function () {\n        await staking.connect(validator).undelegateShares(validator.address, parseUnits('1'));\n        await expect(staking.connect(validator).bondValidator()).to.be.revertedWith('Not have min tokens');\n      });\n\n      it('should undelegate from unbonded validator by delegator successfully', async function () {\n        await expect(staking.connect(delegator).undelegateShares(validator.address, consts.DELEGATOR_STAKE))\n          .to.emit(staking, 'Undelegated')\n          .withArgs(validator.address, delegator.address, consts.DELEGATOR_STAKE);\n      });\n\n      it('should fail to undelegate from unbonded validator more than it delegated', async function () {\n        await expect(staking.connect(delegator).undelegateShares(validator.address, consts.DELEGATOR_STAKE + 1000n)).to\n          .be.reverted;\n      });\n\n      it('should fail to undelegate from unbonded validator with amount smaller than 1 share', async function () {\n        await expect(staking.connect(delegator).undelegateShares(validator.address, 1000)).to.be.revertedWith(\n          'Minimal amount is 1 share'\n        );\n      });\n\n      it('should fail to drain token when not paused', async function () {\n        await expect(staking.drainToken(consts.DELEGATOR_STAKE)).to.be.revertedWith('Pausable: not paused');\n      });\n\n      it('should drainToken successfully when paused', async function () {\n        await staking.pause();\n        const balanceBefore = await celr.balanceOf(admin.address);\n        await staking.drainToken(consts.DELEGATOR_STAKE);\n        const balanceAfter = await celr.balanceOf(admin.address);\n        expect(balanceAfter - balanceBefore).to.equal(consts.DELEGATOR_STAKE);\n      });\n\n      describe('after one delegator delegates enough stake to the validator', async () => {\n        beforeEach(async () => {\n          await staking.connect(validator).delegate(validator.address, consts.VALIDATOR_STAKE);\n        });\n\n        it('should bondValidator successfully', async function () {\n          const shouldBond = await viewer.shouldBondValidator(validator.address);\n          expect(shouldBond).to.equal(true);\n          await expect(staking.connect(validator).bondValidator())\n            .to.emit(staking, 'ValidatorStatusUpdate')\n            .withArgs(validator.address, consts.STATUS_BONDED);\n        });\n\n        it('should increase min self delegation and bondValidator successfully', async function () {\n          const higherMinSelfDelegation = consts.MIN_SELF_DELEGATION + 1000000n;\n          const data = abiCoder.encode(['uint256'], [higherMinSelfDelegation]);\n          await expect(staking.connect(validator).updateMinSelfDelegation(higherMinSelfDelegation))\n            .to.emit(staking, 'ValidatorNotice')\n            .withArgs(validator.address, 'min-self-delegation', data, ZeroAddress);\n\n          await expect(staking.connect(validator).bondValidator())\n            .to.emit(staking, 'ValidatorStatusUpdate')\n            .withArgs(validator.address, consts.STATUS_BONDED);\n        });\n\n        it('should decrease min self delegation and only able to bondValidator after notice period', async function () {\n          let minSelfDelegation = consts.MIN_SELF_DELEGATION + 1000000n;\n          await staking.connect(validator).updateMinSelfDelegation(minSelfDelegation);\n          minSelfDelegation = consts.MIN_SELF_DELEGATION + 10n;\n          const data = abiCoder.encode(['uint256'], [minSelfDelegation]);\n          await expect(staking.connect(validator).updateMinSelfDelegation(minSelfDelegation))\n            .to.emit(staking, 'ValidatorNotice')\n            .withArgs(validator.address, 'min-self-delegation', data, ZeroAddress);\n\n          await expect(staking.connect(validator).bondValidator()).to.be.revertedWith('Bond block not reached');\n\n          await advanceBlockNumber(consts.ADVANCE_NOTICE_PERIOD);\n          await expect(staking.connect(validator).bondValidator())\n            .to.emit(staking, 'ValidatorStatusUpdate')\n            .withArgs(validator.address, consts.STATUS_BONDED);\n        });\n\n        describe('after one validator bondValidator', async () => {\n          beforeEach(async () => {\n            await staking.connect(validator).bondValidator();\n          });\n\n          it('should fail to undelegate with amount smaller than 1 share', async function () {\n            await expect(staking.connect(delegator).undelegateShares(validator.address, 1000)).to.be.revertedWith(\n              'Minimal amount is 1 share'\n            );\n          });\n\n          it('should fail to undelegate more than it delegated', async function () {\n            await expect(staking.connect(delegator).undelegateShares(validator.address, consts.DELEGATOR_STAKE + 1000n))\n              .to.be.reverted;\n          });\n\n          it('should remove the validator after validator undelegate to become under minSelfDelegation', async function () {\n            await expect(staking.connect(validator).undelegateShares(validator.address, consts.MIN_SELF_DELEGATION))\n              .to.emit(staking, 'ValidatorStatusUpdate')\n              .withArgs(validator.address, consts.STATUS_UNBONDING);\n          });\n\n          it('should remove the validator after delegator undelegate to become under minStakingPool', async function () {\n            await expect(staking.connect(delegator).undelegateShares(validator.address, consts.DELEGATOR_STAKE))\n              .to.emit(staking, 'ValidatorStatusUpdate')\n              .withArgs(validator.address, consts.STATUS_UNBONDING)\n              .to.emit(staking, 'DelegationUpdate')\n              .withArgs(\n                validator.address,\n                delegator.address,\n                consts.VALIDATOR_STAKE + consts.MIN_SELF_DELEGATION,\n                0,\n                parseUnits('0') - consts.DELEGATOR_STAKE\n              );\n          });\n\n          it('should pass min self delegation updates', async function () {\n            let minSelfDelegation = consts.MIN_SELF_DELEGATION + 1000000n;\n            const data = abiCoder.encode(['uint256'], [minSelfDelegation]);\n            await expect(staking.connect(validator).updateMinSelfDelegation(minSelfDelegation))\n              .to.emit(staking, 'ValidatorNotice')\n              .withArgs(validator.address, 'min-self-delegation', data, ZeroAddress);\n\n            minSelfDelegation = consts.MIN_SELF_DELEGATION + 100n;\n            await expect(staking.connect(validator).updateMinSelfDelegation(minSelfDelegation)).to.be.revertedWith(\n              'Validator is bonded'\n            );\n\n            minSelfDelegation = consts.MIN_SELF_DELEGATION - 100n;\n            await expect(staking.connect(validator).updateMinSelfDelegation(minSelfDelegation)).to.be.revertedWith(\n              'Insufficient min self delegation'\n            );\n          });\n\n          describe('after a delegator undelegate', async () => {\n            beforeEach(async () => {\n              await staking.connect(delegator).undelegateShares(validator.address, parseUnits('2'));\n            });\n\n            it('should fail to undelegate with a total more than it delegated', async function () {\n              await expect(staking.connect(delegator).undelegateShares(validator.address, consts.DELEGATOR_STAKE)).to.be\n                .reverted;\n            });\n\n            it('should completeUndelegate successfully', async function () {\n              // before withdrawTimeout\n              await expect(staking.connect(delegator).completeUndelegate(validator.address)).to.be.revertedWith(\n                'No undelegation ready to be completed'\n              );\n\n              // after withdrawTimeout\n              await advanceBlockNumber(consts.UNBONDING_PERIOD);\n              // first completeUndelegate\n              await expect(staking.connect(delegator).completeUndelegate(validator.address))\n                .to.emit(staking, 'Undelegated')\n                .withArgs(validator.address, delegator.address, parseUnits('2'));\n\n              // second completeUndelegate\n              await expect(staking.connect(delegator).completeUndelegate(validator.address)).to.be.revertedWith(\n                'No undelegation ready to be completed'\n              );\n            });\n\n            it('should pass with multiple undelegations', async function () {\n              await staking.connect(delegator).undelegateShares(validator.address, parseUnits('1'));\n\n              let res = await staking.getDelegatorInfo(validator.address, delegator.address);\n              expect(res.shares).to.equal(parseUnits('3'));\n              expect(res.undelegations[0].shares).to.equal(parseUnits('2'));\n              expect(res.undelegations[1].shares).to.equal(parseUnits('1'));\n\n              await advanceBlockNumber(consts.UNBONDING_PERIOD);\n              await staking.connect(delegator).undelegateShares(validator.address, parseUnits('1'));\n\n              await expect(staking.connect(delegator).completeUndelegate(validator.address))\n                .to.emit(staking, 'Undelegated')\n                .withArgs(validator.address, delegator.address, parseUnits('3'));\n\n              res = await staking.getDelegatorInfo(validator.address, delegator.address);\n              expect(res.shares).to.equal(parseUnits('2'));\n              expect(res.undelegations[0].shares).to.equal(parseUnits('1'));\n\n              await expect(staking.connect(delegator).completeUndelegate(validator.address)).to.be.revertedWith(\n                'No undelegation ready to be completed'\n              );\n\n              await advanceBlockNumber(consts.UNBONDING_PERIOD);\n              await expect(staking.connect(delegator).completeUndelegate(validator.address))\n                .to.emit(staking, 'Undelegated')\n                .withArgs(validator.address, delegator.address, parseUnits('1'));\n\n              res = await staking.getDelegatorInfo(validator.address, delegator.address);\n              expect(res.shares).to.equal(parseUnits('2'));\n              expect(res.undelegations.length).to.equal(0);\n            });\n          });\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/Bridge.spec.ts",
    "content": "import { expect } from 'chai';\nimport { getBytes, parseUnits, solidityPacked, solidityPackedKeccak256, toNumber, Wallet } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Bridge, PeggedTokenBridge, SingleBridgeToken, TestERC20 } from '../typechain';\nimport { deployBridgeContracts, getAccounts, getAddrs, getBlockTime } from './lib/common';\nimport { calculateSignatures, getMintRequest, getRelayRequest, getWithdrawRequest } from './lib/proto';\n\nasync function getUpdateSignersSigs(\n  triggerTime: number,\n  newSignerAddrs: string[],\n  newPowers: bigint[],\n  currSigners: Wallet[],\n  chainId: number,\n  contractAddress: string\n) {\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, contractAddress, 'UpdateSigners']);\n  const data = solidityPacked(\n    ['bytes32', 'uint256', 'address[]', 'uint256[]'],\n    [domain, triggerTime, newSignerAddrs, newPowers]\n  );\n  const hash = solidityPackedKeccak256(['bytes'], [data]);\n  const sigs = await calculateSignatures(currSigners, getBytes(hash));\n  return sigs;\n}\n\ndescribe('Bridge Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { bridge, token, pegBridge, pegToken } = await deployBridgeContracts(admin);\n    return { admin, bridge, token, pegBridge, pegToken };\n  }\n\n  let bridge: Bridge;\n  let token: TestERC20;\n  let pegBridge: PeggedTokenBridge;\n  let pegToken: SingleBridgeToken;\n  let accounts: Wallet[];\n  let chainId: number;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    bridge = res.bridge;\n    token = res.token;\n    pegBridge = res.pegBridge;\n    pegToken = res.pegToken;\n    accounts = await getAccounts(res.admin, [token], 4);\n    chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n  });\n\n  it('should update signers correctly', async function () {\n    await expect(bridge.resetSigners([accounts[0].address], [parseUnits('1')]))\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs([accounts[0].address], [parseUnits('1')]);\n\n    let newSigners = [accounts[2], accounts[1], accounts[0], accounts[3]];\n    let newPowers = [parseUnits('12'), parseUnits('11'), parseUnits('10'), parseUnits('9')];\n    let triggerTime = 1;\n    let sigs = await getUpdateSignersSigs(\n      triggerTime,\n      getAddrs(newSigners),\n      newPowers,\n      [accounts[0]],\n      chainId,\n      await bridge.getAddress()\n    );\n    await expect(\n      bridge.updateSigners(triggerTime, getAddrs(newSigners), newPowers, sigs, [accounts[0].address], [parseUnits('1')])\n    ).to.be.revertedWith('New signers not in ascending order');\n\n    newSigners = [accounts[0], accounts[1], accounts[2], accounts[3]];\n    triggerTime = 2;\n    sigs = await getUpdateSignersSigs(\n      triggerTime,\n      getAddrs(newSigners),\n      newPowers,\n      [accounts[0]],\n      chainId,\n      await bridge.getAddress()\n    );\n    await expect(\n      bridge.updateSigners(triggerTime, getAddrs(newSigners), newPowers, sigs, [accounts[0].address], [parseUnits('1')])\n    )\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(newSigners), newPowers);\n\n    await expect(\n      bridge.updateSigners(\n        triggerTime,\n        getAddrs(newSigners),\n        newPowers,\n        sigs,\n        [accounts[0].address],\n        [parseUnits('10')]\n      )\n    ).to.be.revertedWith('Trigger time is not increasing');\n\n    await expect(\n      bridge.updateSigners(\n        parseUnits('1'),\n        getAddrs(newSigners),\n        newPowers,\n        sigs,\n        [accounts[0].address],\n        [parseUnits('10')]\n      )\n    ).to.be.revertedWith('Trigger time is too large');\n\n    triggerTime = 3;\n    await expect(\n      bridge.updateSigners(\n        triggerTime,\n        getAddrs(newSigners),\n        newPowers,\n        sigs,\n        [accounts[0].address],\n        [parseUnits('10')]\n      )\n    ).to.be.revertedWith('Mismatch current signers');\n\n    const curSigners = newSigners;\n    const curPowers = newPowers;\n    newSigners = [accounts[1], accounts[3]];\n    newPowers = [parseUnits('15'), parseUnits('50')];\n    sigs = await getUpdateSignersSigs(\n      triggerTime,\n      getAddrs(newSigners),\n      newPowers,\n      curSigners,\n      chainId,\n      await bridge.getAddress()\n    );\n\n    await expect(\n      bridge.updateSigners(\n        triggerTime,\n        getAddrs(newSigners),\n        newPowers,\n        [sigs[0], sigs[1]],\n        getAddrs(curSigners),\n        curPowers\n      )\n    ).to.be.revertedWith('quorum not reached');\n\n    await expect(\n      bridge.updateSigners(\n        triggerTime,\n        getAddrs(newSigners),\n        newPowers,\n        [sigs[1], sigs[0]],\n        getAddrs(curSigners),\n        curPowers\n      )\n    ).to.be.revertedWith('signers not in ascending order');\n\n    await expect(\n      bridge.updateSigners(\n        triggerTime,\n        getAddrs(newSigners),\n        newPowers,\n        [sigs[0], sigs[1], sigs[2]],\n        getAddrs(curSigners),\n        curPowers\n      )\n    )\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(newSigners), newPowers);\n  });\n\n  it('should send and relay successfully', async function () {\n    await bridge.setDelayThresholds([await token.getAddress()], [parseUnits('5')]);\n    await bridge.setDelayPeriod(10);\n    const signers = [accounts[0], accounts[1], accounts[2]];\n    const powers = [parseUnits('10'), parseUnits('10'), parseUnits('10')];\n    await expect(bridge.resetSigners(getAddrs(signers), powers))\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(signers), powers);\n\n    const sender = accounts[0];\n    const receiver = accounts[1];\n    const amount = parseUnits('1');\n    const nonce = 0;\n    const slippage = 1000;\n\n    const srcXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'uint64'],\n      [sender.address, receiver.address, await token.getAddress(), amount, chainId, nonce, chainId]\n    );\n\n    await token.connect(sender).approve(await bridge.getAddress(), parseUnits('100'));\n    await bridge.connect(sender).addLiquidity(await token.getAddress(), parseUnits('50'));\n    await expect(\n      bridge.connect(sender).send(receiver.address, await token.getAddress(), amount, chainId, nonce, slippage)\n    )\n      .to.emit(bridge, 'Send')\n      .withArgs(\n        srcXferId,\n        sender.address,\n        receiver.address,\n        await token.getAddress(),\n        amount,\n        chainId,\n        nonce,\n        slippage\n      );\n\n    let req = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      amount,\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n    let dstXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n      [sender.address, receiver.address, await token.getAddress(), amount, chainId, chainId, srcXferId]\n    );\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'Relay')\n      .withArgs(dstXferId, sender.address, receiver.address, await token.getAddress(), amount, chainId, srcXferId);\n\n    const largeAmount = parseUnits('10');\n    req = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      largeAmount,\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n    dstXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n      [sender.address, receiver.address, await token.getAddress(), largeAmount, chainId, chainId, srcXferId]\n    );\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'Relay')\n      .withArgs(dstXferId, sender.address, receiver.address, await token.getAddress(), largeAmount, chainId, srcXferId)\n      .to.emit(bridge, 'DelayedTransferAdded')\n      .withArgs(dstXferId);\n\n    await expect(bridge.executeDelayedTransfer(dstXferId)).to.be.revertedWith('delayed transfer still locked');\n    await ethers.provider.send('evm_increaseTime', [100]);\n    await ethers.provider.send('evm_mine', []);\n    await expect(bridge.executeDelayedTransfer(dstXferId))\n      .to.emit(bridge, 'DelayedTransferExecuted')\n      .withArgs(dstXferId, receiver.address, await token.getAddress(), largeAmount);\n    await expect(bridge.executeDelayedTransfer(dstXferId)).to.be.revertedWith('delayed transfer not exist');\n  });\n\n  it('should pass risk control tests', async function () {\n    const signers = [accounts[0], accounts[1], accounts[2]];\n    const powers = [parseUnits('10'), parseUnits('10'), parseUnits('10')];\n    await expect(bridge.resetSigners(getAddrs(signers), powers))\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(signers), powers);\n\n    const epochLength = 20;\n    await bridge.setEpochLength(epochLength);\n    await bridge.setEpochVolumeCaps([await token.getAddress()], [parseUnits('5')]);\n    await bridge.setMaxSend([await token.getAddress()], [parseUnits('5')]);\n\n    const sender = accounts[0];\n    const receiver = accounts[1];\n\n    await token.connect(sender).approve(await bridge.getAddress(), parseUnits('100'));\n    await expect(\n      bridge.connect(sender).send(receiver.address, await token.getAddress(), parseUnits('10'), chainId, 0, 10000)\n    ).to.be.revertedWith('amount too large');\n\n    await bridge.setMinAdd([await token.getAddress()], [parseUnits('10')]);\n    await expect(bridge.connect(sender).addLiquidity(await token.getAddress(), parseUnits('50')))\n      .to.emit(bridge, 'LiquidityAdded')\n      .withArgs(1, sender.address, await token.getAddress(), parseUnits('50'));\n    await expect(bridge.connect(sender).addLiquidity(await token.getAddress(), parseUnits('5'))).to.be.revertedWith(\n      'amount too small'\n    );\n\n    const srcXferId = solidityPackedKeccak256(['string'], ['srcId']);\n    let dstXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n      [sender.address, receiver.address, await token.getAddress(), parseUnits('2'), chainId, chainId, srcXferId]\n    );\n\n    let req = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      parseUnits('2'),\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n\n    let blockTime = await getBlockTime();\n    let epochStartTime = Math.floor(blockTime / epochLength) * epochLength;\n    await ethers.provider.send('evm_setNextBlockTimestamp', [epochStartTime + epochLength]);\n    await ethers.provider.send('evm_mine', []);\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'Relay')\n      .withArgs(\n        dstXferId,\n        sender.address,\n        receiver.address,\n        await token.getAddress(),\n        parseUnits('2'),\n        chainId,\n        srcXferId\n      );\n\n    req = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      parseUnits('4'),\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n    dstXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n      [sender.address, receiver.address, await token.getAddress(), parseUnits('4'), chainId, chainId, srcXferId]\n    );\n\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers)).to.be.revertedWith(\n      'volume exceeds cap'\n    );\n    blockTime = await getBlockTime();\n    epochStartTime = Math.floor(blockTime / epochLength) * epochLength;\n    await ethers.provider.send('evm_setNextBlockTimestamp', [epochStartTime + epochLength]);\n    await ethers.provider.send('evm_mine', []);\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'Relay')\n      .withArgs(\n        dstXferId,\n        sender.address,\n        receiver.address,\n        await token.getAddress(),\n        parseUnits('4'),\n        chainId,\n        srcXferId\n      );\n\n    req = await getRelayRequest(\n      sender.address,\n      receiver.address,\n      await token.getAddress(),\n      parseUnits('3'),\n      chainId,\n      chainId,\n      srcXferId,\n      signers,\n      await bridge.getAddress()\n    );\n    dstXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n      [sender.address, receiver.address, await token.getAddress(), parseUnits('3'), chainId, chainId, srcXferId]\n    );\n    for (let i = 0; i < 3; i++) {\n      await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers)).to.be.revertedWith(\n        'volume exceeds cap'\n      );\n    }\n\n    blockTime = await getBlockTime();\n    epochStartTime = Math.floor(blockTime / epochLength) * epochLength;\n    await ethers.provider.send('evm_setNextBlockTimestamp', [epochStartTime + epochLength]);\n    await ethers.provider.send('evm_mine', []);\n    await expect(bridge.relay(req.relayBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'Relay')\n      .withArgs(\n        dstXferId,\n        sender.address,\n        receiver.address,\n        await token.getAddress(),\n        parseUnits('3'),\n        chainId,\n        srcXferId\n      );\n  });\n\n  it('should withdraw liquidity correctly', async function () {\n    const signers = [accounts[0], accounts[1], accounts[2]];\n    const powers = [parseUnits('10'), parseUnits('10'), parseUnits('10')];\n    await expect(bridge.resetSigners(getAddrs(signers), powers))\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(signers), powers);\n\n    const account = accounts[0];\n    await token.connect(account).approve(await bridge.getAddress(), parseUnits('100'));\n    await bridge.connect(account).addLiquidity(await token.getAddress(), parseUnits('50'));\n\n    const refId = solidityPackedKeccak256(['string'], ['random']);\n    const seqnum = 1;\n    const amount = parseUnits('10');\n    const req = await getWithdrawRequest(\n      chainId,\n      seqnum,\n      account.address,\n      await token.getAddress(),\n      amount,\n      refId,\n      signers,\n      await bridge.getAddress()\n    );\n    const wdId = solidityPackedKeccak256(\n      ['uint64', 'uint64', 'address', 'address', 'uint256'],\n      [chainId, seqnum, account.address, await token.getAddress(), amount]\n    );\n    await expect(bridge.withdraw(req.withdrawBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(bridge, 'WithdrawDone')\n      .withArgs(wdId, seqnum, account.address, await token.getAddress(), amount, refId);\n  });\n\n  it('should mint successfully', async function () {\n    const signers = [accounts[0], accounts[1], accounts[2]];\n    const powers = [parseUnits('10'), parseUnits('10'), parseUnits('10')];\n    await expect(bridge.resetSigners(getAddrs(signers), powers))\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs(getAddrs(signers), powers);\n\n    const account = accounts[0];\n    const amount = parseUnits('10');\n    const refChainId = 101;\n    const refId = solidityPackedKeccak256(['string'], ['random']);\n    const req = await getMintRequest(\n      await pegToken.getAddress(),\n      account.address,\n      amount,\n      account.address,\n      refChainId,\n      refId,\n      signers,\n      chainId,\n      await pegBridge.getAddress()\n    );\n\n    const mintId = solidityPackedKeccak256(\n      ['address', 'address', 'uint256', 'address', 'uint64', 'bytes32'],\n      [account.address, await pegToken.getAddress(), amount, account.address, refChainId, refId]\n    );\n\n    await expect(pegBridge.mint(req.mintBytes, req.sigs, getAddrs(signers), powers))\n      .to.emit(pegBridge, 'Mint')\n      .withArgs(mintId, await pegToken.getAddress(), account.address, amount, refChainId, refId, account.address);\n  });\n});\n"
  },
  {
    "path": "test/FarmingRewards.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, toNumber, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { FarmingRewards, Staking, TestERC20 } from '../typechain';\nimport { deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\nimport { getFarmingRewardsRequest } from './lib/proto';\n\ndescribe('FarmingRewards Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, farmingRewards, celr } = await deployContracts(admin);\n    return { admin, staking, farmingRewards, celr };\n  }\n\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  let staking: Staking;\n  let rewards: FarmingRewards;\n  let celr: TestERC20;\n  let validators: Wallet[];\n  let signers: Wallet[];\n  let chainId: number;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    rewards = res.farmingRewards;\n    celr = res.celr;\n    chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n    const accounts = await getAccounts(res.admin, [celr], 6);\n    validators = [accounts[0], accounts[1], accounts[2], accounts[3]];\n    signers = [accounts[0], accounts[1], accounts[4], accounts[5]];\n    for (let i = 0; i < 4; i++) {\n      await celr.connect(validators[i]).approve(staking.getAddress(), parseUnits('100'));\n      await celr.connect(validators[i]).approve(rewards.getAddress(), parseUnits('100'));\n      await staking\n        .connect(validators[i])\n        .initializeValidator(signers[i].address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      await staking.connect(validators[i]).delegate(validators[i].address, consts.MIN_VALIDATOR_TOKENS);\n      await staking.connect(validators[i]).bondValidator();\n    }\n    await rewards.connect(validators[0]).contributeToRewardPool(celr.getAddress(), 100);\n  });\n\n  it('should fail to contribute to reward pool when paused', async function () {\n    await rewards.pause();\n    await expect(rewards.contributeToRewardPool(celr.getAddress(), 100)).to.be.revertedWith('Pausable: paused');\n  });\n\n  it('should contribute to reward pool successfully', async function () {\n    await expect(rewards.connect(validators[0]).contributeToRewardPool(celr.getAddress(), 100))\n      .to.emit(rewards, 'FarmingRewardContributed')\n      .withArgs(validators[0].address, celr.getAddress(), 100);\n  });\n\n  it('should update the commission rate lock successfully', async function () {\n    const newRate = consts.COMMISSION_RATE + 10;\n    const data = abiCoder.encode(['uint256'], [newRate]);\n    await expect(staking.connect(validators[0]).updateCommissionRate(newRate))\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validators[0].address, 'commission', data, ZeroAddress);\n  });\n\n  it('should fail to claim reward when paused', async function () {\n    await rewards.pause();\n    const r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('100', 'wei')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], [])).to.be.revertedWith('Pausable: paused');\n  });\n\n  it('should claim reward successfully', async function () {\n    let r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('40', 'wei')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], []))\n      .to.emit(rewards, 'FarmingRewardClaimed')\n      .withArgs(validators[0].address, await celr.getAddress(), 40);\n\n    r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('90', 'wei')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], []))\n      .to.emit(rewards, 'FarmingRewardClaimed')\n      .withArgs(validators[0].address, await celr.getAddress(), 50);\n  });\n\n  it('should fail to claim reward more than amount in reward pool', async function () {\n    const r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('101', 'wei')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], [])).to.be.revertedWith(\n      'ERC20: transfer amount exceeds balance'\n    );\n  });\n\n  it('should fail to claim reward if there is no new reward', async function () {\n    const r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('0')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], [])).to.be.revertedWith('No new reward');\n  });\n\n  it('should fail to claim reward with insufficient signatures', async function () {\n    const r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('10', 'wei')],\n      [signers[0], signers[1]],\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(rewards.claimRewards(r.rewardBytes, r.sigs, [], [])).to.be.revertedWith('Quorum not reached');\n  });\n\n  it('should fail to claim reward with disordered signatures', async function () {\n    const r = await getFarmingRewardsRequest(\n      validators[0].address,\n      [await celr.getAddress()],\n      [parseUnits('10', 'wei')],\n      signers,\n      chainId,\n      await rewards.getAddress()\n    );\n    await expect(\n      rewards.claimRewards(r.rewardBytes, [r.sigs[0], r.sigs[2], r.sigs[1], r.sigs[3]], [], [])\n    ).to.be.revertedWith('Signers not in ascending order');\n    await expect(\n      rewards.claimRewards(r.rewardBytes, [r.sigs[0], r.sigs[0], r.sigs[1], r.sigs[2]], [], [])\n    ).to.be.revertedWith('Signers not in ascending order');\n  });\n});\n"
  },
  {
    "path": "test/Governance.spec.ts",
    "content": "import { expect } from 'chai';\nimport { parseUnits, Wallet } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Govern, Staking, TestERC20 } from '../typechain';\nimport { advanceBlockNumber, deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\n\ndescribe('Governance Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, govern, celr } = await deployContracts(admin);\n    return { admin, staking, govern, celr };\n  }\n\n  let staking: Staking;\n  let govern: Govern;\n  let celr: TestERC20;\n  let admin: HardhatEthersSigner;\n  let validators: Wallet[];\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    govern = res.govern;\n    celr = res.celr;\n    admin = res.admin;\n    const stakingAddress = await staking.getAddress();\n    const governAddress = await govern.getAddress();\n    await staking.setGovContract(governAddress);\n    validators = await getAccounts(res.admin, [celr], 4);\n    for (let i = 0; i < 4; i++) {\n      await celr.connect(validators[i]).approve(stakingAddress, parseUnits('100'));\n      await celr.connect(validators[i]).approve(governAddress, parseUnits('100'));\n      await staking\n        .connect(validators[i])\n        .initializeValidator(validators[i].address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      await staking.connect(validators[i]).delegate(validators[i].address, parseUnits('6'));\n      await staking.connect(validators[i]).bondValidator();\n    }\n    await celr.approve(stakingAddress, parseUnits('100'));\n    await celr.approve(governAddress, parseUnits('100'));\n  });\n\n  it('should createParamProposal successfully', async function () {\n    const newUnbondingPeriod = consts.UNBONDING_PERIOD + 1;\n    const blockNumber = await ethers.provider.getBlockNumber();\n    await expect(govern.createParamProposal(consts.ENUM_UNBONDING_PERIOD, newUnbondingPeriod))\n      .to.emit(govern, 'CreateParamProposal')\n      .withArgs(\n        0,\n        admin.address,\n        consts.PROPOSAL_DEPOSIT,\n        consts.VOTING_PERIOD + blockNumber + 1,\n        consts.ENUM_UNBONDING_PERIOD,\n        newUnbondingPeriod\n      );\n  });\n\n  describe('after createParamProposal successfully', async () => {\n    const proposalId = 0;\n    const paramType = consts.ENUM_MAX_VALIDATOR_NUM;\n    const paramValue = 25;\n\n    beforeEach(async () => {\n      await govern.createParamProposal(paramType, paramValue);\n    });\n\n    it('should fail to voteParam if not validator', async function () {\n      await expect(govern.voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES)).to.be.revertedWith(\n        'Voter is not a bonded validator'\n      );\n    });\n\n    it('should fail to voteParam for a proposal with an invalid status', async function () {\n      await expect(\n        govern.connect(validators[0]).voteParam(proposalId + 1, consts.ENUM_VOTE_OPTION_YES)\n      ).to.be.revertedWith('Invalid proposal status');\n    });\n\n    it('should vote successfully as a validator', async function () {\n      await expect(govern.connect(validators[0]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES))\n        .to.emit(govern, 'VoteParam')\n        .withArgs(proposalId, validators[0].address, consts.ENUM_VOTE_OPTION_YES);\n    });\n\n    describe('after a validator votes successfully', async () => {\n      beforeEach(async () => {\n        await govern.connect(validators[0]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES);\n      });\n\n      it('should fail to vote for the same proposal twice', async function () {\n        await expect(\n          govern.connect(validators[0]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES)\n        ).to.be.revertedWith('Voter has voted');\n      });\n\n      it('should fail to confirmParamProposal before the vote deadline', async function () {\n        await expect(govern.confirmParamProposal(proposalId)).to.be.revertedWith('Vote deadline not reached');\n      });\n\n      it('should accept proposal after over 2/3 voted for Yes', async function () {\n        await expect(govern.connect(validators[1]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES))\n          .to.emit(govern, 'VoteParam')\n          .withArgs(proposalId, validators[1].address, consts.ENUM_VOTE_OPTION_YES);\n        await govern.connect(validators[2]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES);\n        await advanceBlockNumber(consts.VOTING_PERIOD);\n        await expect(govern.confirmParamProposal(proposalId))\n          .to.emit(govern, 'ConfirmParamProposal')\n          .withArgs(proposalId, true, paramType, paramValue);\n\n        const val = await staking.params(consts.ENUM_MAX_VALIDATOR_NUM);\n        expect(val).to.equal(paramValue);\n      });\n\n      describe('after passing the vote deadline with less than 2/3 votes', async () => {\n        beforeEach(async () => {\n          await advanceBlockNumber(consts.VOTING_PERIOD);\n        });\n\n        it('should fail to vote after the vote deadline', async function () {\n          await expect(\n            govern.connect(validators[2]).voteParam(proposalId, consts.ENUM_VOTE_OPTION_YES)\n          ).to.be.revertedWith('Vote deadline passed');\n        });\n\n        it('should reject proposal successfully', async function () {\n          await expect(govern.confirmParamProposal(proposalId))\n            .to.emit(govern, 'ConfirmParamProposal')\n            .withArgs(proposalId, false, paramType, paramValue);\n\n          const val = await staking.params(consts.ENUM_MAX_VALIDATOR_NUM);\n          expect(val).to.equal(consts.MAX_VALIDATOR_NUM);\n        });\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/GovernedOwner.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, solidityPackedKeccak256, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Bridge, GovernedOwnerProxy, PeggedTokenBridge, SimpleGovernance, TestERC20 } from '../typechain';\nimport { advanceBlockTime, deployBridgeContracts, deployGovernedOwner, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\n\ndescribe('GovernedOwner Tests', function () {\n  const initVoterNum = 4;\n  const InitFastPassThreshold = 40;\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { bridge, token, pegBridge } = await deployBridgeContracts(admin);\n    const { gov, proxy } = await deployGovernedOwner(admin, initVoterNum);\n    return { admin, bridge, token, pegBridge, gov, proxy };\n  }\n\n  let bridge: Bridge;\n  let token: TestERC20;\n  let pegBridge: PeggedTokenBridge;\n  let gov: SimpleGovernance;\n  let proxy: GovernedOwnerProxy;\n  let voters: Wallet[];\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    bridge = res.bridge;\n    token = res.token;\n    pegBridge = res.pegBridge;\n    gov = res.gov;\n    proxy = res.proxy;\n    voters = await getAccounts(res.admin, [], 5);\n    const govAddress = await gov.getAddress();\n    await bridge.transferOwnership(govAddress);\n    await pegBridge.transferOwnership(govAddress);\n  });\n\n  it('should pass param change tests', async function () {\n    expect(await gov.params(consts.GovParamFastPassThreshold)).to.equal(InitFastPassThreshold);\n\n    const newThreshold = 10;\n    await expect(gov.connect(voters[0]).createParamChangeProposal(consts.GovParamFastPassThreshold, newThreshold))\n      .to.emit(gov, 'ParamChangeProposalCreated')\n      .withArgs(0, consts.GovParamFastPassThreshold, 10);\n\n    const data = abiCoder.encode(['uint8', 'uint256'], [consts.GovParamFastPassThreshold, 10]);\n    await expect(\n      gov.connect(voters[2]).executeProposal(0, consts.GovInternalParamChange, ZeroAddress, data)\n    ).to.be.revertedWith('not enough votes');\n\n    await gov.connect(voters[1]).voteProposal(0, true);\n\n    await expect(gov.connect(voters[2]).executeProposal(0, consts.GovInternalParamChange, ZeroAddress, data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(0);\n\n    expect(await gov.params(consts.GovParamFastPassThreshold)).to.equal(newThreshold);\n  });\n\n  it('should pass voter and proxy update tests', async function () {\n    // voter update tests\n    expect((await gov.getVoters())[0]).to.deep.equal([\n      voters[0].address,\n      voters[1].address,\n      voters[2].address,\n      voters[3].address\n    ]);\n    await expect(gov.connect(voters[0]).createVoterUpdateProposal([voters[1].address, voters[4].address], [0, 100]))\n      .to.emit(gov, 'VoterUpdateProposalCreated')\n      .withArgs(0, [voters[1].address, voters[4].address], [0, 100]);\n\n    await gov.connect(voters[1]).voteProposal(0, true);\n\n    let data = abiCoder.encode(\n      ['address[]', 'uint256[]'],\n      [\n        [voters[1].address, voters[4].address],\n        [0, 100]\n      ]\n    );\n    await expect(gov.connect(voters[2]).executeProposal(0, consts.GovInternalVoterUpdate, ZeroAddress, data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(0);\n\n    expect((await gov.getVoters())[0]).to.deep.equal([\n      voters[0].address,\n      voters[3].address,\n      voters[2].address,\n      voters[4].address\n    ]);\n    expect(await gov.voterPowers(voters[1].address)).to.equal(0);\n    expect(await gov.voterPowers(voters[4].address)).to.equal(100);\n\n    // proxy update tests\n    expect(await gov.proposerProxies(proxy.getAddress())).to.equal(true);\n    await expect(\n      gov.connect(voters[4]).createProxyUpdateProposal([proxy.getAddress(), voters[3].address], [false, true])\n    )\n      .to.emit(gov, 'ProxyUpdateProposalCreated')\n      .withArgs(1, [await proxy.getAddress(), voters[3].address], [false, true]);\n\n    await gov.connect(voters[0]).voteProposal(1, true);\n\n    data = abiCoder.encode(\n      ['address[]', 'bool[]'],\n      [\n        [await proxy.getAddress(), voters[3].address],\n        [false, true]\n      ]\n    );\n    await expect(gov.connect(voters[2]).executeProposal(1, consts.GovInternalProxyUpdate, ZeroAddress, data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(1);\n\n    expect(await gov.proposerProxies(proxy.getAddress())).to.equal(false);\n    expect(await gov.proposerProxies(voters[3].address)).to.equal(true);\n  });\n\n  it('should pass transfer token tests', async function () {\n    expect(await token.balanceOf(voters[0].address)).to.equal(0);\n\n    const amount = parseUnits('1');\n    await token.transfer(gov.getAddress(), amount);\n    await expect(gov.connect(voters[0]).createTransferTokenProposal(voters[0].address, token.getAddress(), amount))\n      .to.emit(gov, 'TransferTokenProposalCreated')\n      .withArgs(0, voters[0].address, token.getAddress(), amount);\n    await gov.connect(voters[1]).voteProposal(0, true);\n    const data = abiCoder.encode(\n      ['address', 'address', 'uint256'],\n      [voters[0].address, await token.getAddress(), amount]\n    );\n    await expect(gov.connect(voters[2]).executeProposal(0, consts.GovInternalTokenTransfer, ZeroAddress, data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(0);\n\n    expect(await token.balanceOf(voters[0].address)).to.equal(amount);\n  });\n\n  it('should pass common owner proxy tests', async function () {\n    // proposal 0: add pauser - success\n    const pauser = voters[2].address;\n    await expect(proxy.connect(voters[0]).proposeUpdatePauser(bridge.getAddress(), 1, pauser))\n      .to.emit(proxy, 'UpdatePauserProposalCreated')\n      .withArgs(0, bridge.getAddress(), 1, pauser);\n\n    let data = (await bridge.addPauser.populateTransaction(pauser)).data || '';\n    await expect(\n      gov.connect(voters[3]).executeProposal(0, consts.GovExternalFastPass, bridge.getAddress(), '0x01')\n    ).to.be.revertedWith('data hash not match');\n    await expect(\n      gov.connect(voters[3]).executeProposal(0, consts.GovExternalFastPass, voters[0].address, data)\n    ).to.be.revertedWith('data hash not match');\n    await expect(gov.connect(voters[3]).executeProposal(0, consts.GovExternalFastPass, bridge.getAddress(), data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(0)\n      .to.emit(bridge, 'PauserAdded')\n      .withArgs(pauser);\n\n    // proposal 1: remove pauser - fail due to timeout\n    await expect(proxy.connect(voters[0]).proposeUpdatePauser(bridge.getAddress(), 2, pauser))\n      .to.emit(proxy, 'UpdatePauserProposalCreated')\n      .withArgs(1, bridge.getAddress(), 2, pauser);\n    await advanceBlockTime(10000);\n    data = (await bridge.removePauser.populateTransaction(pauser)).data || '';\n    await expect(\n      gov.connect(voters[1]).executeProposal(1, consts.GovExternalFastPass, bridge.getAddress(), data)\n    ).to.be.revertedWith('deadline passed');\n    await expect(gov.connect(voters[1]).voteProposal(1, true)).to.be.revertedWith('deadline passed');\n\n    // proposal 2: transfer ownership\n    expect(await bridge.owner()).to.equal(await gov.getAddress());\n\n    const newOwner = voters[0].address;\n    await expect(proxy.connect(voters[0]).proposeTransferOwnership(bridge.getAddress(), newOwner))\n      .to.emit(proxy, 'TransferOwnershipProposalCreated')\n      .withArgs(2, bridge.getAddress(), newOwner);\n\n    data = (await bridge.transferOwnership.populateTransaction(newOwner)).data || '';\n    await expect(\n      gov.connect(voters[1]).executeProposal(2, consts.GovExternalDefault, bridge.getAddress(), data)\n    ).to.be.revertedWith('not enough votes');\n    await gov.connect(voters[1]).voteProposal(2, true);\n    await expect(gov.voteProposal(0, true)).to.be.revertedWith('invalid voter');\n\n    await gov.connect(voters[2]).executeProposal(2, consts.GovExternalDefault, bridge.getAddress(), data);\n    expect(await bridge.owner()).to.equal(newOwner);\n\n    await expect(\n      gov.connect(voters[2]).executeProposal(2, consts.GovExternalDefault, bridge.getAddress(), data)\n    ).to.be.revertedWith('deadline passed');\n  });\n\n  it('should pass bridge owner proxy tests', async function () {\n    // reset signers\n    await expect(proxy.connect(voters[0]).proposeResetSigners(bridge.getAddress(), [voters[0].address], [1000]))\n      .to.emit(proxy, 'ResetSignersProposalCreated')\n      .withArgs(0, bridge.getAddress(), [voters[0].address], [1000]);\n\n    await gov.connect(voters[1]).voteProposal(0, true);\n\n    let data = (await bridge.resetSigners.populateTransaction([voters[0].address], [1000])).data || '';\n    await expect(gov.connect(voters[2]).executeProposal(0, consts.GovExternalDefault, bridge.getAddress(), data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(0)\n      .to.emit(bridge, 'SignersUpdated')\n      .withArgs([voters[0].address], [1000]);\n\n    expect(await bridge.ssHash()).to.equal(\n      solidityPackedKeccak256(['address[]', 'uint256[]'], [[voters[0].address], [1000]])\n    );\n\n    // update governor\n    await expect(proxy.connect(voters[0]).proposeUpdateGovernor(bridge.getAddress(), 1, voters[0].address))\n      .to.emit(proxy, 'UpdateGovernorProposalCreated')\n      .withArgs(1, bridge.getAddress(), 1, voters[0].address);\n\n    data = (await bridge.addGovernor.populateTransaction(voters[0].address)).data || '';\n    await expect(gov.connect(voters[2]).executeProposal(1, consts.GovExternalFastPass, bridge.getAddress(), data))\n      .to.emit(gov, 'ProposalExecuted')\n      .withArgs(1)\n      .to.emit(bridge, 'GovernorAdded')\n      .withArgs(voters[0].address);\n  });\n});\n"
  },
  {
    "path": "test/Message.spec.ts",
    "content": "import { expect } from 'chai';\nimport {\n  AbiCoder,\n  AbstractSigner,\n  getBytes,\n  parseUnits,\n  solidityPacked,\n  solidityPackedKeccak256,\n  toNumber\n} from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { HardhatEthersSigner, SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Bridge, MessageBus, MsgTest, TestERC20 } from '../typechain';\nimport { deployMessageContracts } from './lib/common';\nimport * as consts from './lib/constants';\nimport { calculateSignatures, getRelayRequest } from './lib/proto';\n\ntype RouteInfoStruct = {\n  sender: string;\n  receiver: string;\n  srcChainId: number;\n  srcTxHash: string;\n};\n\ntype RouteInfo2Struct = {\n  sender: Uint8Array;\n  receiver: string;\n  srcChainId: number;\n  srcTxHash: string;\n};\n\nconst abiCoder = AbiCoder.defaultAbiCoder();\n\nasync function computeMessageIdAndSigs(\n  chainId: number,\n  msgbus: string,\n  route: RouteInfoStruct,\n  message: string,\n  signers: AbstractSigner[]\n) {\n  const messageId = solidityPackedKeccak256(\n    ['uint8', 'address', 'address', 'uint64', 'bytes32', 'uint64', 'bytes'],\n    [consts.TYPE_MSG_ONLY, route.sender, route.receiver, route.srcChainId, route.srcTxHash, chainId, message]\n  );\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, msgbus, 'Message']);\n  const signedData = solidityPacked(['bytes32', 'bytes32'], [domain, messageId]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n  return { messageId, sigs };\n}\n\nasync function computeMessage2IdAndSigs(\n  chainId: number,\n  msgbus: string,\n  route: RouteInfo2Struct,\n  message: string,\n  signers: AbstractSigner[]\n) {\n  const messageId = solidityPackedKeccak256(\n    ['uint8', 'bytes', 'address', 'uint64', 'bytes32', 'uint64', 'bytes'],\n    [consts.TYPE_MSG_ONLY, route.sender, route.receiver, route.srcChainId, route.srcTxHash, chainId, message]\n  );\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, msgbus, 'Message2']);\n  const signedData = solidityPacked(['bytes32', 'bytes32'], [domain, messageId]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n  return { messageId, sigs };\n}\n\nasync function getMessageWithTransferRequest(\n  chainId: number,\n  msgbus: string,\n  bridge: string,\n  sender: string,\n  receiver: string,\n  token: string,\n  amount: bigint,\n  srcChainId: number,\n  refId: string,\n  srcTxHash: string,\n  message: string,\n  signer: SignerWithAddress,\n  power: bigint\n) {\n  const relayReq = await getRelayRequest(\n    sender,\n    receiver,\n    token,\n    amount,\n    srcChainId,\n    chainId,\n    refId, // fake src transfer Id\n    [signer],\n    bridge\n  );\n  const bridgeTransferParams = {\n    request: relayReq.relayBytes,\n    sigs: relayReq.sigs,\n    signers: [signer.address],\n    powers: [power]\n  };\n\n  const xferId = solidityPackedKeccak256(\n    ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'bytes32'],\n    [sender, receiver, token, amount, srcChainId, chainId, refId]\n  );\n  const messageId = solidityPackedKeccak256(['uint8', 'address', 'bytes32'], [consts.TYPE_MSG_XFER, bridge, xferId]);\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, msgbus, 'MessageWithTransfer']);\n  const signedData = solidityPacked(\n    ['bytes32', 'bytes32', 'bytes', 'bytes32'],\n    [domain, messageId, message, srcTxHash]\n  );\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures([signer], getBytes(signedDataHash));\n\n  const transferInfo = {\n    t: consts.XFER_TYPE_LQ_RELAY,\n    sender: sender,\n    receiver: receiver,\n    token: token,\n    amount: amount,\n    wdseq: 0,\n    srcChainId: srcChainId,\n    refId: refId,\n    srcTxHash: srcTxHash\n  };\n\n  const executionParams = {\n    message: message,\n    transfer: transferInfo,\n    sigs: sigs,\n    signers: [signer.address],\n    powers: [power]\n  };\n\n  return { bridgeTransferParams, executionParams, messageId };\n}\n\ndescribe('Message Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { bridge, msgBus, msgTest, token } = await deployMessageContracts(admin);\n    await bridge.resetSigners([admin.address], [parseUnits('1')]);\n    return { admin, bridge, msgBus, msgTest, token };\n  }\n\n  let admin: HardhatEthersSigner;\n  let bridge: Bridge;\n  let msgBus: MessageBus;\n  let msgTest: MsgTest;\n  let token: TestERC20;\n  let chainId: number;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    admin = res.admin;\n    bridge = res.bridge;\n    token = res.token;\n    msgBus = res.msgBus;\n    msgTest = res.msgTest;\n    chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n  });\n\n  it('should execute msg correctly', async function () {\n    const hash = solidityPackedKeccak256(['string'], ['hello']);\n    const srcChainId = 5;\n    const routeInfo = {\n      sender: admin.address,\n      receiver: await msgTest.getAddress(),\n      srcChainId: srcChainId,\n      srcTxHash: hash // fake tx hash\n    };\n    let nonce = 1;\n    let message = abiCoder.encode(['uint64', 'bytes'], [nonce, hash]);\n    let res = await computeMessageIdAndSigs(chainId, await msgBus.getAddress(), routeInfo, message, [admin]);\n    await expect(\n      msgBus.getFunction('executeMessage(bytes,(address,address,uint64,bytes32),bytes[],address[],uint256[])')(\n        message,\n        routeInfo,\n        res.sigs,\n        [admin.address],\n        [parseUnits('1')]\n      )\n    )\n      .to.emit(msgTest, 'MessageReceived')\n      .withArgs(admin.address, srcChainId, nonce, hash)\n      .to.emit(msgBus, 'Executed')\n      .withArgs(\n        consts.TYPE_MSG_ONLY,\n        res.messageId,\n        consts.MSG_TX_SUCCESS,\n        await msgTest.getAddress(),\n        srcChainId,\n        hash\n      );\n\n    const routeInfo2 = {\n      sender: getBytes(admin.address),\n      receiver: await msgTest.getAddress(),\n      srcChainId: srcChainId,\n      srcTxHash: hash // fake tx hash\n    };\n    nonce = 2;\n    message = abiCoder.encode(['uint64', 'bytes'], [nonce, hash]);\n    res = await computeMessage2IdAndSigs(chainId, await msgBus.getAddress(), routeInfo2, message, [admin]);\n    await expect(\n      msgBus.getFunction('executeMessage(bytes,(bytes,address,uint64,bytes32),bytes[],address[],uint256[])')(\n        message,\n        routeInfo2,\n        res.sigs,\n        [admin.address],\n        [parseUnits('1')]\n      )\n    )\n      .to.emit(msgTest, 'Message2Received')\n      .withArgs(solidityPacked(['address'], [admin.address]), srcChainId, nonce, hash)\n      .to.emit(msgBus, 'Executed')\n      .withArgs(\n        consts.TYPE_MSG_ONLY,\n        res.messageId,\n        consts.MSG_TX_SUCCESS,\n        await msgTest.getAddress(),\n        srcChainId,\n        hash\n      );\n\n    nonce = 100000000000001;\n    message = abiCoder.encode(['uint64', 'bytes'], [nonce, hash]);\n    res = await computeMessageIdAndSigs(chainId, await msgBus.getAddress(), routeInfo, message, [admin]);\n    await expect(\n      msgBus.getFunction('executeMessage(bytes,(address,address,uint64,bytes32),bytes[],address[],uint256[])')(\n        message,\n        routeInfo,\n        res.sigs,\n        [admin.address],\n        [parseUnits('1')]\n      )\n    )\n      .to.emit(msgBus, 'CallReverted')\n      .withArgs('invalid nonce')\n      .to.emit(msgBus, 'Executed')\n      .withArgs(consts.TYPE_MSG_ONLY, res.messageId, consts.MSG_TX_FAIL, await msgTest.getAddress(), srcChainId, hash);\n\n    nonce = 100000000000002;\n    message = abiCoder.encode(['uint64', 'bytes'], [nonce, hash]);\n    res = await computeMessageIdAndSigs(chainId, await msgBus.getAddress(), routeInfo, message, [admin]);\n    await expect(\n      msgBus.getFunction('executeMessage(bytes,(address,address,uint64,bytes32),bytes[],address[],uint256[])')(\n        message,\n        routeInfo,\n        res.sigs,\n        [admin.address],\n        [parseUnits('1')]\n      )\n    )\n      .to.emit(msgBus, 'CallReverted')\n      .withArgs('Transaction reverted silently')\n      .to.emit(msgBus, 'Executed')\n      .withArgs(consts.TYPE_MSG_ONLY, res.messageId, consts.MSG_TX_FAIL, await msgTest.getAddress(), srcChainId, hash);\n\n    nonce = 100000000000004;\n    message = abiCoder.encode(['uint64', 'bytes'], [nonce, hash]);\n    res = await computeMessageIdAndSigs(chainId, await msgBus.getAddress(), routeInfo, message, [admin]);\n    await expect(\n      msgBus.getFunction('executeMessage(bytes,(address,address,uint64,bytes32),bytes[],address[],uint256[])')(\n        message,\n        routeInfo,\n        res.sigs,\n        [admin.address],\n        [parseUnits('1')]\n      )\n    ).to.be.revertedWith('MSG::ABORT:invalid nonce');\n  });\n\n  it('should execute msg with transfer correctly', async function () {\n    await token.approve(bridge.getAddress(), parseUnits('100'));\n    await bridge.addLiquidity(token.getAddress(), parseUnits('50'));\n    const hash = solidityPackedKeccak256(['string'], ['hello']);\n    const amount = parseUnits('1');\n    const srcChainId = 5;\n    const message = abiCoder.encode(['address', 'bytes'], [admin.address, hash]);\n    const res = await getMessageWithTransferRequest(\n      chainId,\n      await msgBus.getAddress(),\n      await bridge.getAddress(),\n      admin.address,\n      await msgTest.getAddress(),\n      await token.getAddress(),\n      amount,\n      srcChainId,\n      hash, // fake src transfer Id\n      hash, // fake src srcTxHash\n      message,\n      admin,\n      parseUnits('1')\n    );\n    await expect(msgBus.transferAndExecuteMsg(res.bridgeTransferParams, res.executionParams))\n      .to.emit(msgBus, 'Executed')\n      .withArgs(\n        consts.TYPE_MSG_XFER,\n        res.messageId,\n        consts.MSG_TX_SUCCESS,\n        await msgTest.getAddress(),\n        srcChainId,\n        hash\n      )\n      .to.emit(msgTest, 'MessageReceivedWithTransfer')\n      .withArgs(await token.getAddress(), amount, admin.address, srcChainId, admin.address, hash);\n\n    await expect(\n      msgBus.executeMessageWithTransfer(\n        message,\n        res.executionParams.transfer,\n        res.executionParams.sigs,\n        res.executionParams.signers,\n        res.executionParams.powers\n      )\n    ).to.be.revertedWith('transfer already executed');\n  });\n});\n"
  },
  {
    "path": "test/MultiValidator.spec.ts",
    "content": "import { expect } from 'chai';\nimport { parseUnits, Wallet } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Staking, TestERC20 } from '../typechain';\nimport { advanceBlockNumber, deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\n\ndescribe('Multiple validators Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, celr } = await deployContracts(admin);\n    return { admin, staking, celr };\n  }\n  let staking: Staking;\n  let celr: TestERC20;\n  let validators: Wallet[];\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    celr = res.celr;\n    validators = await getAccounts(res.admin, [celr], 8);\n    await celr.approve(staking.getAddress(), parseUnits('100'));\n    const stakes = [\n      parseUnits('6'),\n      parseUnits('2'),\n      parseUnits('10'),\n      parseUnits('5'),\n      parseUnits('7'),\n      parseUnits('3'),\n      parseUnits('9')\n    ];\n    for (let i = 0; i < 8; i++) {\n      await celr.connect(validators[i]).approve(staking.getAddress(), parseUnits('100'));\n      await staking\n        .connect(validators[i])\n        .initializeValidator(validators[i].address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      if (i < 7) {\n        await staking.connect(validators[i]).delegate(validators[i].address, consts.VALIDATOR_STAKE);\n        await staking.delegate(validators[i].address, stakes[i]);\n        await staking.connect(validators[i]).bondValidator();\n      }\n    }\n  });\n\n  it('should getQuorumTokens successfully', async function () {\n    const quorum = await staking.getQuorumTokens();\n    expect(quorum).to.equal(parseUnits('42') + 1n);\n  });\n\n  it('should fail to bondValidator before delegating enough stake', async function () {\n    await staking.connect(validators[7]).delegate(validators[7].address, parseUnits('2'));\n    await expect(staking.connect(validators[7]).bondValidator()).to.be.revertedWith('Insufficient tokens');\n  });\n\n  it('should replace a current validator by calling bondValidator with enough stake', async function () {\n    await staking.connect(validators[7]).delegate(validators[7].address, parseUnits('8'));\n    await expect(staking.connect(validators[7]).bondValidator())\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[1].address, consts.STATUS_UNBONDING)\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[7].address, consts.STATUS_BONDED);\n\n    const quorum = await staking.getQuorumTokens();\n    expect(quorum).to.equal((parseUnits('68') * 2n) / 3n + 1n);\n  });\n\n  it('should remove validator due to undelegate and add new validator successfully', async function () {\n    await expect(staking.connect(validators[1]).undelegateShares(validators[1].address, parseUnits('2')))\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[1].address, consts.STATUS_UNBONDING);\n    let quorum = await staking.getQuorumTokens();\n    expect(quorum).to.equal((parseUnits('58') * 2n) / 3n + 1n);\n\n    await staking.connect(validators[7]).delegate(validators[7].address, parseUnits('8'));\n    await expect(staking.connect(validators[7]).bondValidator())\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[7].address, consts.STATUS_BONDED);\n    quorum = await staking.getQuorumTokens();\n    expect(quorum).to.equal((parseUnits('68') * 2n) / 3n + 1n);\n  });\n\n  describe('after one validator is replaced', async () => {\n    beforeEach(async () => {\n      await staking.connect(validators[7]).delegate(validators[7].address, parseUnits('8'));\n      await staking.connect(validators[7]).bondValidator();\n    });\n\n    it('should confirmUnbondedValidator only after unbondBlock', async function () {\n      const res = await staking.validators(validators[1].address);\n      expect(res.status).to.equal(consts.STATUS_UNBONDING);\n\n      await expect(staking.confirmUnbondedValidator(validators[1].address)).to.be.revertedWith(\n        'Unbond block not reached'\n      );\n\n      await advanceBlockNumber(consts.UNBONDING_PERIOD);\n      await expect(staking.confirmUnbondedValidator(validators[1].address))\n        .to.emit(staking, 'ValidatorStatusUpdate')\n        .withArgs(validators[1].address, consts.STATUS_UNBONDED);\n    });\n\n    it('should replace current min stake validator with the unbonding validator', async function () {\n      await staking.connect(validators[1]).delegate(validators[1].address, parseUnits('5'));\n      await expect(staking.connect(validators[1]).bondValidator())\n        .to.emit(staking, 'ValidatorStatusUpdate')\n        .withArgs(validators[5].address, consts.STATUS_UNBONDING)\n        .to.emit(staking, 'ValidatorStatusUpdate')\n        .withArgs(validators[1].address, consts.STATUS_BONDED);\n\n      await staking.delegate(validators[1].address, parseUnits('5'));\n      await staking.delegate(validators[5].address, parseUnits('3'));\n      const quorum = await staking.getQuorumTokens();\n      expect(quorum).to.equal((parseUnits('77') * 2n) / 3n + 1n);\n    });\n  });\n});\n"
  },
  {
    "path": "test/Sentinel.spec.ts",
    "content": "import { expect } from 'chai';\nimport { Wallet } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Bridge, PeggedTokenBridge, Sentinel } from '../typechain';\nimport { deployBridgeContracts, deploySentinel, getAccounts } from './lib/common';\n\ndescribe('Sentinel Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { bridge, pegBridge } = await deployBridgeContracts(admin);\n    const sentinel = await deploySentinel(admin);\n    return { admin, bridge, pegBridge, sentinel };\n  }\n\n  let bridge: Bridge;\n  let pegBridge: PeggedTokenBridge;\n  let sentinel: Sentinel;\n  let guards: Wallet[];\n  let pausers: Wallet[];\n  let governor: Wallet;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    const accounts = await getAccounts(res.admin, [], 5);\n    bridge = res.bridge;\n    pegBridge = res.pegBridge;\n    sentinel = res.sentinel;\n    guards = [accounts[0], accounts[1]];\n    pausers = [accounts[2], accounts[3]];\n    governor = accounts[4];\n    const sentinelAddress = await sentinel.getAddress();\n    await sentinel.updateGuards([guards[0].address, guards[1].address], [], 2);\n    await sentinel.addPausers([pausers[0].address, pausers[1].address], [1, 2]);\n    await sentinel.addGovernors([governor.address]);\n    await bridge.addPauser(sentinelAddress);\n    await pegBridge.addPauser(sentinelAddress);\n    await bridge.addGovernor(sentinelAddress);\n  });\n\n  it('should pass guard tests', async function () {\n    await expect(sentinel.updateGuards([pausers[0].address], [], 2))\n      .to.emit(sentinel, 'GuardUpdated')\n      .withArgs(pausers[0].address, 1);\n\n    expect(await sentinel.numGuards()).to.equal(3);\n    expect(await sentinel.relaxThreshold()).to.equal(2);\n    expect(await sentinel.numRelaxedGuards()).to.equal(0);\n\n    await sentinel.connect(guards[0]).relax();\n    await expect(sentinel.connect(guards[1]).relax()).to.emit(sentinel, 'RelaxStatusUpdated').withArgs(true);\n\n    await expect(sentinel.updateGuards([pausers[1].address], [], 3))\n      .to.emit(sentinel, 'RelaxStatusUpdated')\n      .withArgs(false);\n\n    expect(await sentinel.numGuards()).to.equal(4);\n    expect(await sentinel.relaxThreshold()).to.equal(3);\n    expect(await sentinel.numRelaxedGuards()).to.equal(2);\n\n    await expect(sentinel.updateGuards([], [guards[0].address, pausers[0].address], 2))\n      .to.emit(sentinel, 'GuardUpdated')\n      .withArgs(guards[0].address, 0)\n      .to.emit(sentinel, 'GuardUpdated')\n      .withArgs(pausers[0].address, 0);\n    expect(await sentinel.numGuards()).to.equal(2);\n    expect(await sentinel.relaxThreshold()).to.equal(2);\n    expect(await sentinel.numRelaxedGuards()).to.equal(1);\n\n    await expect(sentinel.updateGuards([], [pausers[1].address], 1))\n      .to.emit(sentinel, 'RelaxStatusUpdated')\n      .withArgs(true);\n    expect(await sentinel.numGuards()).to.equal(1);\n    expect(await sentinel.relaxThreshold()).to.equal(1);\n    expect(await sentinel.numRelaxedGuards()).to.equal(1);\n  });\n\n  it('should pass pauser tests', async function () {\n    await expect(sentinel.connect(governor).getFunction('pause(address)')(bridge.getAddress())).to.be.revertedWith(\n      'invalid caller'\n    );\n\n    await expect(\n      sentinel.connect(pausers[1]).getFunction('pause(address[])')([bridge.getAddress(), pegBridge.getAddress()])\n    )\n      .to.emit(bridge, 'Paused')\n      .to.emit(pegBridge, 'Paused');\n\n    await expect(\n      sentinel.connect(pausers[0]).getFunction('pause(address[])')([bridge.getAddress(), pegBridge.getAddress()])\n    ).to.be.revertedWith('pause failed for all targets');\n\n    await expect(\n      sentinel.connect(pausers[0]).getFunction('unpause(address[])')([bridge.getAddress(), pegBridge.getAddress()])\n    ).to.be.revertedWith('not in relaxed mode');\n\n    await sentinel.connect(guards[0]).relax();\n    await expect(sentinel.connect(pausers[0]).getFunction('unpause(address)')(bridge.getAddress())).to.be.revertedWith(\n      'not in relaxed mode'\n    );\n\n    await sentinel.connect(guards[1]).relax();\n\n    await expect(sentinel.connect(pausers[1]).getFunction('unpause(address)')(bridge.getAddress())).to.be.revertedWith(\n      'invalid caller'\n    );\n    await expect(sentinel.connect(pausers[0]).getFunction('unpause(address)')(bridge.getAddress())).to.emit(\n      bridge,\n      'Unpaused'\n    );\n\n    await expect(\n      sentinel.connect(pausers[0]).getFunction('unpause(address[])')([bridge.getAddress(), pegBridge.getAddress()])\n    )\n      .to.emit(pegBridge, 'Unpaused')\n      .to.emit(sentinel, 'Failed')\n      .withArgs(await bridge.getAddress(), 'Pausable: not paused');\n  });\n\n  it('should pass governor tests', async function () {\n    await expect(sentinel.connect(governor).setDelayPeriod(bridge.getAddress(), 10))\n      .to.emit(bridge, 'DelayPeriodUpdated')\n      .withArgs(10);\n\n    await expect(sentinel.connect(governor).setDelayPeriod(bridge.getAddress(), 5)).to.be.revertedWith(\n      'not in relax mode, can only increase period'\n    );\n\n    await sentinel.connect(guards[0]).relax();\n    await sentinel.connect(guards[1]).relax();\n\n    await expect(sentinel.connect(governor).setDelayPeriod(bridge.getAddress(), 5))\n      .to.emit(bridge, 'DelayPeriodUpdated')\n      .withArgs(5);\n  });\n});\n"
  },
  {
    "path": "test/Slash.spec.ts",
    "content": "import { expect } from 'chai';\nimport { parseUnits, toNumber, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Staking, StakingReward, TestERC20 } from '../typechain';\nimport { advanceBlockNumber, deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\nimport { getSlashRequest } from './lib/proto';\n\ndescribe('Slash Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, stakingReward, celr } = await deployContracts(admin);\n    return { admin, staking, stakingReward, celr };\n  }\n\n  let staking: Staking;\n  let reward: StakingReward;\n  let celr: TestERC20;\n  let admin: HardhatEthersSigner;\n  let validators: Wallet[];\n  let signers: Wallet[];\n  let expireTime: number;\n  let chainId: number;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    reward = res.stakingReward;\n    celr = res.celr;\n    admin = res.admin;\n    const accounts = await getAccounts(res.admin, [celr], 7);\n    validators = [accounts[0], accounts[1], accounts[2], accounts[3]];\n    signers = [accounts[0], accounts[4], accounts[5], accounts[6]];\n    await staking.setRewardContract(reward.getAddress());\n    await celr.approve(staking.getAddress(), parseUnits('100'));\n    for (let i = 0; i < 4; i++) {\n      await celr.connect(validators[i]).approve(staking.getAddress(), parseUnits('100'));\n      await staking\n        .connect(validators[i])\n        .initializeValidator(signers[i].address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      await staking.delegate(validators[i].address, consts.DELEGATOR_STAKE);\n      await staking.connect(validators[i]).bondValidator();\n      const blockNumber = await ethers.provider.getBlockNumber();\n      const blockTime = (await ethers.provider.getBlock(blockNumber))!.timestamp;\n      expireTime = blockTime + 100;\n      chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n    }\n  });\n\n  it('should slash successfully (only once using the same nonce)', async function () {\n    const adminBalanceBefore = await celr.balanceOf(admin.address);\n    const val1BalanceBefore = await celr.balanceOf(validators[1].address);\n    const rewardPoolBefore = await celr.balanceOf(reward.getAddress());\n\n    const request = await getSlashRequest(\n      validators[0].address,\n      1,\n      consts.SLASH_FACTOR,\n      expireTime,\n      0,\n      [validators[1].address, ZeroAddress],\n      [parseUnits('0.1'), parseUnits('0.01')],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(validators[0].address, ZeroAddress, parseUnits('7.6'), 0, parseUnits('-0.4'))\n      .to.emit(staking, 'Slash')\n      .withArgs(validators[0].address, 1, parseUnits('0.4'))\n      .to.emit(staking, 'SlashAmtCollected')\n      .withArgs(admin.address, parseUnits('0.01'))\n      .to.emit(staking, 'SlashAmtCollected')\n      .withArgs(validators[1].address, parseUnits('0.1'));\n\n    await staking.collectForfeiture();\n    const adminBalanceAfter = await celr.balanceOf(admin.address);\n    const val1BalanceAfter = await celr.balanceOf(validators[1].address);\n    const rewardPoolAfter = await celr.balanceOf(reward.getAddress());\n    expect(adminBalanceAfter - parseUnits('0.01')).to.equal(adminBalanceBefore);\n    expect(val1BalanceAfter - parseUnits('0.1')).to.equal(val1BalanceBefore);\n    expect(rewardPoolAfter - parseUnits('0.29')).to.equal(rewardPoolBefore);\n\n    await expect(staking.slash(request.slashBytes, request.sigs)).to.be.revertedWith('Used slash nonce');\n  });\n\n  it('should slash successfully with undelegations and redelegations', async function () {\n    await staking.undelegateShares(validators[0].address, parseUnits('1'));\n    await staking.undelegateTokens(validators[0].address, parseUnits('2'));\n    await staking.connect(validators[0]).undelegateShares(validators[0].address, parseUnits('1'));\n\n    const request = await getSlashRequest(\n      validators[0].address,\n      1,\n      consts.SLASH_FACTOR,\n      expireTime,\n      0,\n      [],\n      [],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(validators[0].address, ZeroAddress, parseUnits('3.8'), 0, parseUnits('-0.2'))\n      .to.emit(staking, 'Slash')\n      .withArgs(validators[0].address, 1, parseUnits('0.4'));\n\n    // check and complete pending undelegations\n    let dinfo = await staking.getDelegatorInfo(validators[0].address, admin.address);\n    expect(dinfo.tokens).to.equal(parseUnits('2.85'));\n    expect(dinfo.shares).to.equal(parseUnits('3'));\n    expect(dinfo.undelegations[0].shares).to.equal(parseUnits('1'));\n    expect(dinfo.undelegations[1].shares).to.equal(parseUnits('2'));\n    await advanceBlockNumber(consts.UNBONDING_PERIOD);\n    await expect(staking.completeUndelegate(validators[0].address))\n      .to.emit(staking, 'Undelegated')\n      .withArgs(validators[0].address, admin.address, parseUnits('2.85'));\n\n    // do additional undelegation\n    await expect(staking.undelegateShares(validators[0].address, parseUnits('1')))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(validators[0].address, admin.address, parseUnits('2.85'), parseUnits('2'), parseUnits('-0.95'));\n    await advanceBlockNumber(consts.UNBONDING_PERIOD);\n    await expect(staking.completeUndelegate(validators[0].address))\n      .to.emit(staking, 'Undelegated')\n      .withArgs(validators[0].address, admin.address, parseUnits('0.95'));\n\n    // redelegate\n    await expect(staking.delegate(validators[0].address, parseUnits('1')))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(\n        validators[0].address,\n        admin.address,\n        parseUnits('3.85'),\n        parseUnits('3052631578947368421', 'wei'),\n        parseUnits('1')\n      );\n\n    await expect(staking.undelegateShares(validators[0].address, parseUnits('1052631578947368419', 'wei')))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(\n        validators[0].address,\n        admin.address,\n        parseUnits('2850000000000000002', 'wei'),\n        parseUnits('2000000000000000002', 'wei'),\n        parseUnits('-999999999999999998', 'wei')\n      );\n\n    await expect(staking.undelegateTokens(validators[0].address, parseUnits('1'))).to.be.revertedWith(\n      'not enough remaining shares'\n    );\n\n    await expect(staking.undelegateTokens(validators[0].address, parseUnits('1900000000000000001', 'wei')))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(\n        validators[0].address,\n        admin.address,\n        parseUnits('950000000000000001', 'wei'),\n        0,\n        parseUnits('-1900000000000000001', 'wei')\n      );\n\n    dinfo = await staking.getDelegatorInfo(validators[0].address, admin.address);\n    expect(dinfo.tokens).to.equal(0);\n    expect(dinfo.shares).to.equal(0);\n  });\n\n  it('should unbond validator due to slash', async function () {\n    const request = await getSlashRequest(\n      validators[0].address,\n      1,\n      1e5,\n      expireTime,\n      0,\n      [],\n      [],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs))\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(validators[0].address, ZeroAddress, parseUnits('7.2'), 0, parseUnits('-0.8'))\n      .to.emit(staking, 'Slash')\n      .withArgs(validators[0].address, 1, parseUnits('0.8'))\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[0].address, consts.STATUS_UNBONDING);\n  });\n\n  it('should unbond validator due to slash with jail period', async function () {\n    const request = await getSlashRequest(\n      validators[0].address,\n      1,\n      0,\n      expireTime,\n      10,\n      [],\n      [],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs))\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[0].address, consts.STATUS_UNBONDING)\n      .to.emit(staking, 'DelegationUpdate')\n      .withArgs(validators[0].address, ZeroAddress, parseUnits('8'), 0, 0)\n      .to.emit(staking, 'Slash')\n      .withArgs(validators[0].address, 1, 0);\n\n    await expect(staking.connect(validators[0]).bondValidator()).to.be.revertedWith('Bond block not reached');\n    await advanceBlockNumber(10);\n    await expect(staking.connect(validators[0]).bondValidator())\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[0].address, consts.STATUS_BONDED);\n  });\n\n  it('should fail to slash with invalid requests', async function () {\n    let request = await getSlashRequest(\n      validators[0].address,\n      1,\n      consts.SLASH_FACTOR,\n      expireTime,\n      0,\n      [],\n      [],\n      [signers[1], signers[2]],\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs)).to.be.revertedWith('Quorum not reached');\n\n    request = await getSlashRequest(\n      validators[0].address,\n      1,\n      consts.SLASH_FACTOR,\n      0,\n      0,\n      [],\n      [],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs)).to.be.revertedWith('Slash expired');\n  });\n\n  it('should fail to slash when paused or slash disabled', async function () {\n    await staking.pause();\n    const request = await getSlashRequest(\n      validators[0].address,\n      1,\n      consts.SLASH_FACTOR,\n      expireTime,\n      0,\n      [],\n      [],\n      signers,\n      chainId,\n      await staking.getAddress()\n    );\n    await expect(staking.slash(request.slashBytes, request.sigs)).to.be.revertedWith('Pausable: paused');\n    await staking.unpause();\n\n    await staking.setMaxSlashFactor(0);\n    await expect(staking.slash(request.slashBytes, request.sigs)).to.be.revertedWith('Exceed max slash factor');\n  });\n});\n"
  },
  {
    "path": "test/StakingReward.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, toNumber, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Staking, StakingReward, TestERC20 } from '../typechain';\nimport { deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\nimport { getStakingRewardRequest } from './lib/proto';\n\ndescribe('StakingReward Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, stakingReward, celr } = await deployContracts(admin);\n    return { admin, staking, stakingReward, celr };\n  }\n\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  let staking: Staking;\n  let reward: StakingReward;\n  let celr: TestERC20;\n  let validators: Wallet[];\n  let signers: Wallet[];\n  let chainId: number;\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    reward = res.stakingReward;\n    celr = res.celr;\n    const accounts = await getAccounts(res.admin, [celr], 6);\n    validators = [accounts[0], accounts[1], accounts[2], accounts[3]];\n    signers = [accounts[0], accounts[1], accounts[4], accounts[5]];\n    for (let i = 0; i < 4; i++) {\n      await celr.connect(validators[i]).approve(staking.getAddress(), parseUnits('100'));\n      await celr.connect(validators[i]).approve(reward.getAddress(), parseUnits('100'));\n      await staking\n        .connect(validators[i])\n        .initializeValidator(signers[i].address, consts.MIN_SELF_DELEGATION, consts.COMMISSION_RATE);\n      await staking.connect(validators[i]).delegate(validators[i].address, consts.MIN_VALIDATOR_TOKENS);\n      await staking.connect(validators[i]).bondValidator();\n    }\n    await reward.connect(validators[0]).contributeToRewardPool(100);\n    chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n  });\n\n  it('should fail to contribute to reward pool when paused', async function () {\n    await reward.pause();\n    await expect(reward.contributeToRewardPool(100)).to.be.revertedWith('Pausable: paused');\n  });\n\n  it('should contribute to reward pool successfully', async function () {\n    await expect(reward.connect(validators[0]).contributeToRewardPool(100))\n      .to.emit(reward, 'StakingRewardContributed')\n      .withArgs(validators[0].address, 100);\n  });\n\n  it('should update the commission rate lock successfully', async function () {\n    const newRate = consts.COMMISSION_RATE + 10;\n    const data = abiCoder.encode(['uint256'], [newRate]);\n    await expect(staking.connect(validators[0]).updateCommissionRate(newRate))\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validators[0].address, 'commission', data, ZeroAddress);\n  });\n\n  it('should fail to claim reward when paused', async function () {\n    await reward.pause();\n    const r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('100', 'wei'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs)).to.be.revertedWith('Pausable: paused');\n  });\n\n  it('should claim reward successfully', async function () {\n    let r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('40', 'wei'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs))\n      .to.emit(reward, 'StakingRewardClaimed')\n      .withArgs(validators[0].address, 40);\n\n    r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('90', 'wei'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs))\n      .to.emit(reward, 'StakingRewardClaimed')\n      .withArgs(validators[0].address, 50);\n  });\n\n  it('should fail to claim reward more than amount in reward pool', async function () {\n    const r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('101', 'wei'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs)).to.be.revertedWith(\n      'ERC20: transfer amount exceeds balance'\n    );\n  });\n\n  it('should fail to claim reward if there is no new reward', async function () {\n    const r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('0'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs)).to.be.revertedWith('No new reward');\n  });\n\n  it('should fail to claim reward with insufficient signatures', async function () {\n    const r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('10', 'wei'),\n      [signers[0], signers[1]],\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, r.sigs)).to.be.revertedWith('Quorum not reached');\n  });\n\n  it('should fail to claim reward with disordered signatures', async function () {\n    const r = await getStakingRewardRequest(\n      validators[0].address,\n      parseUnits('10', 'wei'),\n      signers,\n      chainId,\n      await reward.getAddress()\n    );\n    await expect(reward.claimReward(r.rewardBytes, [r.sigs[0], r.sigs[2], r.sigs[1], r.sigs[3]])).to.be.revertedWith(\n      'Signers not in ascending order'\n    );\n    await expect(reward.claimReward(r.rewardBytes, [r.sigs[0], r.sigs[0], r.sigs[1], r.sigs[2]])).to.be.revertedWith(\n      'Signers not in ascending order'\n    );\n  });\n});\n"
  },
  {
    "path": "test/TransferSwap.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, solidityPackedKeccak256, toNumber, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\nimport { Address } from 'hardhat-deploy/types';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { Bridge, DummySwap, TestERC20, TransferSwap, WETH } from '../typechain';\nimport { deploySwapContracts, getAccounts } from './lib/common';\n\nconst UINT64_MAX = '9223372036854775807';\n\nconst abiCoder = AbiCoder.defaultAbiCoder();\n\nasync function swapFixture() {\n  const [admin] = await ethers.getSigners();\n  const res = await deploySwapContracts(admin);\n  return { admin, ...res };\n}\n\nfunction computeId(sender: string, srcChainId: number, dstChainId: number, message: string) {\n  return solidityPackedKeccak256(['address', 'uint64', 'uint64', 'bytes'], [sender, srcChainId, dstChainId, message]);\n}\n\nfunction computeDirectSwapId(sender: string, srcChainId: number, receiver: Address, nonce: number, swap: Swap) {\n  const swapStruct = [swap.path, swap.dex, swap.deadline, swap.minRecvAmt];\n  const encoded = abiCoder.encode(\n    ['address', 'uint64', 'address', 'uint64', '(address[], address, uint256, uint256)'],\n    [sender, srcChainId, receiver, nonce, swapStruct]\n  );\n  return solidityPackedKeccak256(['bytes'], [encoded]);\n}\n\nfunction encodeMessage(\n  dstSwap: { dex: string; path: string[]; deadline: bigint; minRecvAmt: bigint },\n  receiver: string,\n  nonce: number,\n  nativeOut: boolean\n) {\n  const encoded = abiCoder.encode(\n    ['((address[], address , uint256, uint256), address, uint64, bool)'],\n    [[[dstSwap.path, dstSwap.dex, dstSwap.deadline, dstSwap.minRecvAmt], receiver, nonce, nativeOut]]\n  );\n  return encoded;\n}\n\nfunction slip(amount: bigint, perc: number) {\n  const percent = 100 - perc;\n  return (amount * parseUnits(percent.toString(), 4)) / parseUnits('100', 4);\n}\n\nlet tokenA: TestERC20;\nlet tokenB: TestERC20;\nlet xswap: TransferSwap;\nlet dex: DummySwap;\nlet bridge: Bridge;\nlet accounts: Wallet[];\nlet admin: HardhatEthersSigner;\nlet chainId: number;\nlet sender: Wallet;\nlet receiver: Wallet;\nlet weth: WETH;\n\nlet amountIn: bigint;\nconst maxBridgeSlippage = parseUnits('100', 4); // 100%\nconst expectNonce = 1;\n\ninterface Swap {\n  dex: string;\n  path: string[];\n  deadline: bigint;\n  minRecvAmt: bigint;\n}\nlet srcSwap: Swap;\nlet dstSwap: Swap;\n\nasync function prepare() {\n  const res = await loadFixture(swapFixture);\n  admin = res.admin;\n  tokenA = res.tokenA;\n  tokenB = res.tokenB;\n  weth = res.weth;\n  xswap = res.transferSwap;\n  dex = res.swap;\n  bridge = res.bridge;\n  accounts = await getAccounts(res.admin, [tokenA, tokenB], 4);\n  chainId = toNumber((await ethers.provider.getNetwork()).chainId);\n  sender = accounts[0];\n  receiver = accounts[1];\n  amountIn = parseUnits('100');\n\n  srcSwap = {\n    dex: await dex.getAddress(),\n    path: [] as string[],\n    deadline: BigInt(UINT64_MAX),\n    minRecvAmt: slip(amountIn, 10)\n  };\n  dstSwap = {\n    dex: await dex.getAddress(),\n    path: [await tokenA.getAddress(), await tokenB.getAddress()],\n    deadline: BigInt(UINT64_MAX),\n    minRecvAmt: slip(amountIn, 10)\n  };\n\n  await xswap.setMinSwapAmount(tokenA.getAddress(), parseUnits('10'));\n  await xswap.setSupportedDex(dex.getAddress(), true);\n\n  await dex.setFakeSlippage(parseUnits('5', 4));\n\n  await tokenA.connect(res.admin).transfer(dex.getAddress(), parseUnits('1000'));\n  await tokenB.connect(res.admin).transfer(dex.getAddress(), parseUnits('1000'));\n  await weth.connect(res.admin).deposit({ value: parseUnits('100') });\n  await weth.connect(res.admin).transfer(dex.getAddress(), parseUnits('100'));\n  return { admin, tokenA, tokenB, xswap, dex, bridge, accounts, chainId };\n}\n\ndescribe('Test transferWithSwap', function () {\n  let srcChainId: number;\n  const dstChainId = 2; // doesn't matter\n\n  beforeEach(async () => {\n    await prepare();\n    srcChainId = chainId;\n  });\n\n  it('should revert if paths are empty', async function () {\n    srcSwap.path = [];\n    dstSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    await tokenA.connect(sender).approve(xswap.getAddress(), amountIn);\n    await expect(\n      xswap\n        .connect(sender)\n        .transferWithSwap(receiver.address, amountIn, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1)\n    ).to.be.reverted;\n  });\n\n  it('should revert if min swap amount is not satisfied', async function () {\n    amountIn = parseUnits('5');\n    srcSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    dstSwap.path = [await tokenB.getAddress(), await tokenA.getAddress()];\n    await tokenA.connect(sender).approve(xswap.getAddress(), amountIn);\n    await expect(\n      xswap\n        .connect(sender)\n        .transferWithSwap(receiver.address, amountIn, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1)\n    ).to.be.revertedWith('amount must be greater than min swap amount');\n  });\n\n  it('should swap and send', async function () {\n    srcSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    dstSwap.path = [await tokenB.getAddress(), await tokenA.getAddress()];\n\n    await tokenA.connect(sender).approve(xswap.getAddress(), amountIn);\n    const tx = await xswap\n      .connect(sender)\n      .transferWithSwap(receiver.address, amountIn, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1);\n    const message = encodeMessage(dstSwap, sender.address, expectNonce, false);\n    const expectId = computeId(sender.address, srcChainId, dstChainId, message);\n\n    await expect(tx)\n      .to.emit(xswap, 'SwapRequestSent')\n      .withArgs(expectId, dstChainId, amountIn, srcSwap.path[0], dstSwap.path[1]);\n\n    const expectedSendAmt = slip(amountIn, 5);\n    const srcXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'uint64'],\n      [\n        await xswap.getAddress(),\n        receiver.address,\n        await tokenB.getAddress(),\n        expectedSendAmt,\n        dstChainId,\n        expectNonce,\n        srcChainId\n      ]\n    );\n    await expect(tx)\n      .to.emit(bridge, 'Send')\n      .withArgs(\n        srcXferId,\n        xswap.getAddress(),\n        receiver.address,\n        tokenB.getAddress(),\n        expectedSendAmt,\n        dstChainId,\n        expectNonce,\n        maxBridgeSlippage\n      );\n  });\n\n  it('should directly send', async function () {\n    srcSwap.path = [await tokenB.getAddress()];\n    dstSwap.path = [await tokenB.getAddress(), await tokenA.getAddress()];\n\n    await tokenB.connect(sender).approve(xswap.getAddress(), amountIn);\n    const tx = await xswap\n      .connect(sender)\n      .transferWithSwap(receiver.address, amountIn, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1);\n    const message = encodeMessage(dstSwap, sender.address, expectNonce, false);\n    const id = computeId(sender.address, srcChainId, dstChainId, message);\n\n    await expect(tx)\n      .to.emit(xswap, 'SwapRequestSent')\n      .withArgs(id, dstChainId, amountIn, srcSwap.path[0], dstSwap.path[1]);\n\n    const srcXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'uint64'],\n      [\n        await xswap.getAddress(),\n        receiver.address,\n        await tokenB.getAddress(),\n        amountIn,\n        dstChainId,\n        expectNonce,\n        srcChainId\n      ]\n    );\n    await expect(tx)\n      .to.emit(bridge, 'Send')\n      .withArgs(\n        srcXferId,\n        xswap.getAddress(),\n        receiver.address,\n        tokenB.getAddress(),\n        amountIn,\n        dstChainId,\n        expectNonce,\n        maxBridgeSlippage\n      );\n  });\n\n  it('should directly swap', async function () {\n    srcSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    dstSwap.path = [];\n\n    await tokenA.connect(sender).approve(xswap.getAddress(), amountIn);\n    const recvBalBefore = await tokenB.connect(receiver).balanceOf(receiver.address);\n    const tx = await xswap\n      .connect(sender)\n      .transferWithSwap(receiver.address, amountIn, chainId, srcSwap, dstSwap, maxBridgeSlippage, 1);\n    const recvBalAfter = await tokenB.connect(receiver).balanceOf(receiver.address);\n    const expectId = computeDirectSwapId(sender.address, srcChainId, receiver.address, expectNonce, srcSwap);\n\n    await expect(tx).to.not.emit(xswap, 'SwapRequestSent');\n    await expect(tx).to.not.emit(bridge, 'Send');\n    await expect(tx)\n      .to.emit(xswap, 'DirectSwap')\n      .withArgs(expectId, chainId, amountIn, tokenA.getAddress(), slip(amountIn, 5), tokenB.getAddress());\n    expect(recvBalAfter).equal(recvBalBefore + slip(amountIn, 5));\n  });\n\n  it('should revert if the tx results in a noop', async function () {\n    srcSwap.path = [await tokenA.getAddress()];\n    dstSwap.path = [];\n\n    await tokenA.connect(sender).approve(xswap.getAddress(), amountIn);\n    await expect(\n      xswap\n        .connect(sender)\n        .transferWithSwap(receiver.address, amountIn, chainId, srcSwap, dstSwap, maxBridgeSlippage, 1)\n    ).to.be.revertedWith('noop is not allowed');\n  });\n});\n\ndescribe('Test transferWithSwapNative', function () {\n  let srcChainId: number;\n  const dstChainId = 2; // doesn't matter\n\n  const amountIn2 = parseUnits('10');\n\n  beforeEach(async () => {\n    await prepare();\n    srcChainId = chainId;\n  });\n\n  it('should revert if native in does not match amountIn (native in)', async function () {\n    srcSwap.path = [await weth.getAddress(), await tokenB.getAddress()];\n    dstSwap.path = [await tokenB.getAddress(), await weth.getAddress()];\n\n    await expect(\n      xswap\n        .connect(sender)\n        .transferWithSwapNative(receiver.address, amountIn2, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1, true, {\n          value: amountIn2 / 2n\n        })\n    ).to.be.revertedWith('Amount insufficient');\n  });\n\n  it('should swap and send (native in)', async function () {\n    srcSwap.path = [await weth.getAddress(), await tokenB.getAddress()];\n    srcSwap.minRecvAmt = slip(amountIn2, 10);\n    dstSwap.path = [await tokenB.getAddress(), await weth.getAddress()];\n    dstSwap.minRecvAmt = slip(amountIn2, 10);\n\n    const balBefore = await ethers.provider.getBalance(sender);\n    const tx = await xswap\n      .connect(sender)\n      .transferWithSwapNative(receiver.address, amountIn2, dstChainId, srcSwap, dstSwap, maxBridgeSlippage, 1, true, {\n        value: amountIn2\n      });\n\n    const balAfter = await ethers.provider.getBalance(sender);\n    expect(balAfter <= balBefore - amountIn2);\n    const message = encodeMessage(dstSwap, sender.address, expectNonce, true);\n    const expectId = computeId(sender.address, srcChainId, dstChainId, message);\n\n    await expect(tx)\n      .to.emit(xswap, 'SwapRequestSent')\n      .withArgs(expectId, dstChainId, amountIn2, srcSwap.path[0], dstSwap.path[1]);\n\n    const expectedSendAmt = slip(amountIn2, 5);\n    const srcXferId = solidityPackedKeccak256(\n      ['address', 'address', 'address', 'uint256', 'uint64', 'uint64', 'uint64'],\n      [\n        await xswap.getAddress(),\n        receiver.address,\n        await tokenB.getAddress(),\n        expectedSendAmt,\n        dstChainId,\n        expectNonce,\n        srcChainId\n      ]\n    );\n    await expect(tx)\n      .to.emit(bridge, 'Send')\n      .withArgs(\n        srcXferId,\n        xswap.getAddress(),\n        receiver.address,\n        tokenB.getAddress(),\n        expectedSendAmt,\n        dstChainId,\n        expectNonce,\n        maxBridgeSlippage\n      );\n  });\n});\n\ndescribe('Test executeMessageWithTransfer', function () {\n  beforeEach(async () => {\n    await prepare();\n    // impersonate MessageBus as admin to gain access to calling executeMessageWithTransfer\n    await xswap.connect(admin).setMessageBus(admin.address);\n  });\n\n  const srcChainId = 1;\n\n  it('should swap', async function () {\n    dstSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    const message = encodeMessage(dstSwap, receiver.address, expectNonce, false);\n\n    const balB1 = await tokenB.connect(admin).balanceOf(receiver.address);\n    await tokenA.connect(admin).transfer(xswap.getAddress(), amountIn);\n    const tx = await xswap\n      .connect(admin)\n      .executeMessageWithTransfer(ZeroAddress, tokenA.getAddress(), amountIn, srcChainId, message, ZeroAddress);\n    const balB2 = await tokenB.connect(admin).balanceOf(receiver.address);\n    const id = computeId(receiver.address, srcChainId, chainId, message);\n    const dstAmount = slip(amountIn, 5);\n    const expectStatus = 1; // SwapStatus.Succeeded\n    await expect(tx).to.emit(xswap, 'SwapRequestDone').withArgs(id, dstAmount, expectStatus);\n    expect(balB2).to.equal(balB1 + dstAmount);\n  });\n\n  it('should swap and send native', async function () {\n    dstSwap.path = [await tokenA.getAddress(), await weth.getAddress()];\n    const amountIn2 = parseUnits('10');\n    dstSwap.minRecvAmt = slip(amountIn2, 10);\n    const message = encodeMessage(dstSwap, receiver.address, expectNonce, true);\n    const bal1 = await ethers.provider.getBalance(receiver);\n    await tokenA.connect(admin).transfer(xswap.getAddress(), amountIn2);\n    const tx = await xswap\n      .connect(admin)\n      .executeMessageWithTransfer(ZeroAddress, tokenA.getAddress(), amountIn2, srcChainId, message, ZeroAddress);\n    const bal2 = await ethers.provider.getBalance(receiver);\n    const id = computeId(receiver.address, srcChainId, chainId, message);\n    const dstAmount = slip(amountIn2, 5);\n    const expectStatus = 1; // SwapStatus.Succeeded\n    await expect(tx).to.emit(xswap, 'SwapRequestDone').withArgs(id, dstAmount, expectStatus);\n    expect(bal2 == bal1 + dstAmount);\n  });\n\n  it('should send bridge token to receiver if no dst swap specified', async function () {\n    dstSwap.path = [await tokenA.getAddress()];\n    const message = encodeMessage(dstSwap, receiver.address, expectNonce, false);\n    await tokenA.connect(admin).transfer(xswap.getAddress(), amountIn);\n\n    const balA1 = await tokenA.connect(receiver).balanceOf(receiver.address);\n    const tx = await xswap\n      .connect(admin)\n      .executeMessageWithTransfer(ZeroAddress, tokenA.getAddress(), amountIn, srcChainId, message, ZeroAddress);\n    const balA2 = await tokenA.connect(receiver).balanceOf(receiver.address);\n    const id = computeId(receiver.address, srcChainId, chainId, message);\n    const expectStatus = 1; // SwapStatus.Succeeded\n    await expect(tx).to.emit(xswap, 'SwapRequestDone').withArgs(id, amountIn, expectStatus);\n    expect(balA2).to.equal(balA1 + amountIn);\n  });\n\n  it('should send bridge token to receiver if swap fails on dst chain', async function () {\n    srcSwap.path = [await tokenA.getAddress(), await tokenB.getAddress()];\n    dstSwap.path = [await tokenB.getAddress(), await tokenA.getAddress()];\n    const bridgeAmount = slip(amountIn, 5);\n    dstSwap.minRecvAmt = bridgeAmount; // dst chain swap should fail due to slippage\n    const msg = encodeMessage(dstSwap, receiver.address, expectNonce, false);\n    const balA1 = await tokenA.balanceOf(receiver.address);\n    const balB1 = await tokenB.balanceOf(receiver.address);\n    await tokenB.connect(admin).transfer(xswap.getAddress(), bridgeAmount);\n    const tx = xswap\n      .connect(admin)\n      .executeMessageWithTransfer(ZeroAddress, tokenB.getAddress(), bridgeAmount, srcChainId, msg, ZeroAddress);\n    const expectId = computeId(receiver.address, srcChainId, chainId, msg);\n    const expectStatus = 3; // SwapStatus.Fallback\n    await expect(tx).to.emit(xswap, 'SwapRequestDone').withArgs(expectId, slip(amountIn, 5), expectStatus);\n    const balA2 = await tokenA.balanceOf(receiver.address);\n    const balB2 = await tokenB.balanceOf(receiver.address);\n    expect(balA2, 'balance A after').equals(balA1);\n    expect(balB2, 'balance B after').equals(balB1 + bridgeAmount);\n  });\n});\n"
  },
  {
    "path": "test/ValidatorSigner.spec.ts",
    "content": "import { expect } from 'chai';\nimport { AbiCoder, parseUnits, solidityPackedKeccak256, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport { HardhatEthersSigner } from '@nomicfoundation/hardhat-ethers/signers';\nimport { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers';\n\nimport { SGN, Staking, TestERC20 } from '../typechain';\nimport { deployContracts, getAccounts } from './lib/common';\nimport * as consts from './lib/constants';\n\ndescribe('Validator Signer Tests', function () {\n  async function fixture() {\n    const [admin] = await ethers.getSigners();\n    const { staking, sgn, celr } = await deployContracts(admin);\n    return { admin, staking, sgn, celr };\n  }\n\n  const abiCoder = AbiCoder.defaultAbiCoder();\n\n  let staking: Staking;\n  let sgn: SGN;\n  let celr: TestERC20;\n  let admin: HardhatEthersSigner;\n  let validators: Wallet[];\n  let signers: Wallet[];\n\n  beforeEach(async () => {\n    const res = await loadFixture(fixture);\n    staking = res.staking;\n    sgn = res.sgn;\n    celr = res.celr;\n    admin = res.admin;\n    const accounts = await getAccounts(res.admin, [celr], 4);\n    validators = [accounts[0], accounts[1]];\n    signers = [accounts[2], accounts[3]];\n    const stakingAddress = await staking.getAddress();\n    await celr.connect(validators[0]).approve(stakingAddress, parseUnits('100'));\n    await celr.connect(validators[1]).approve(stakingAddress, parseUnits('100'));\n    await staking\n      .connect(validators[0])\n      .initializeValidator(signers[0].address, consts.MIN_VALIDATOR_TOKENS, consts.COMMISSION_RATE);\n  });\n\n  it('should fail to initialize a validator using another validator as signer', async function () {\n    await expect(\n      staking\n        .connect(validators[1])\n        .initializeValidator(validators[0].address, consts.MIN_VALIDATOR_TOKENS, consts.COMMISSION_RATE)\n    ).to.be.revertedWith('Signer is other validator');\n  });\n\n  it('should fail to initialize a validator using a signer being used by another validator', async function () {\n    await expect(\n      staking\n        .connect(validators[1])\n        .initializeValidator(signers[0].address, consts.MIN_VALIDATOR_TOKENS, consts.COMMISSION_RATE)\n    ).to.be.revertedWith('Signer already used');\n  });\n\n  it('should be able to bond validator using signer address', async function () {\n    await expect(staking.connect(signers[0]).bondValidator())\n      .to.emit(staking, 'ValidatorStatusUpdate')\n      .withArgs(validators[0].address, consts.STATUS_BONDED);\n  });\n\n  it('should update sgn address using signer address', async function () {\n    const sgnAddr = solidityPackedKeccak256(['string'], ['sgnaddr1']);\n    await expect(sgn.connect(signers[0]).updateSgnAddr(sgnAddr))\n      .to.emit(sgn, 'SgnAddrUpdate')\n      .withArgs(validators[0].address, '0x', sgnAddr)\n      .to.emit(staking, 'ValidatorNotice')\n      .withArgs(validators[0].address, 'sgn-addr', sgnAddr, sgn.getAddress());\n  });\n\n  describe('after both validators are bonded', async () => {\n    beforeEach(async () => {\n      await staking\n        .connect(validators[1])\n        .initializeValidator(signers[1].address, consts.MIN_VALIDATOR_TOKENS, consts.COMMISSION_RATE);\n      await staking.connect(validators[0]).bondValidator();\n      await staking.connect(validators[1]).bondValidator();\n    });\n\n    it('should update signer correctly', async function () {\n      let data = abiCoder.encode(['address'], [admin.address]);\n\n      await expect(staking.connect(validators[0]).updateValidatorSigner(admin.address))\n        .to.emit(staking, 'ValidatorNotice')\n        .withArgs(validators[0].address, 'signer', data, ZeroAddress);\n\n      data = abiCoder.encode(['address'], [validators[0].address]);\n      await expect(staking.connect(validators[0]).updateValidatorSigner(validators[0].address))\n        .to.emit(staking, 'ValidatorNotice')\n        .withArgs(validators[0].address, 'signer', data, ZeroAddress);\n    });\n\n    it('should fail to update signer with invalid inputs', async function () {\n      await expect(staking.connect(validators[0]).updateValidatorSigner(signers[0].address)).to.be.revertedWith(\n        'Signer already used'\n      );\n      await expect(staking.connect(validators[0]).updateValidatorSigner(signers[1].address)).to.be.revertedWith(\n        'Signer already used'\n      );\n      await expect(staking.connect(validators[0]).updateValidatorSigner(validators[1].address)).to.be.revertedWith(\n        'Signer is other validator'\n      );\n    });\n  });\n});\n"
  },
  {
    "path": "test/lib/common.ts",
    "content": "import { parseUnits, Wallet, ZeroAddress } from 'ethers';\nimport { ethers } from 'hardhat';\n\nimport {\n  Bridge,\n  Bridge__factory,\n  DummySwap,\n  DummySwap__factory,\n  FarmingRewards,\n  FarmingRewards__factory,\n  Govern,\n  Govern__factory,\n  GovernedOwnerProxy,\n  GovernedOwnerProxy__factory,\n  MessageBus,\n  MessageBus__factory,\n  MsgTest,\n  MsgTest__factory,\n  PeggedTokenBridge,\n  PeggedTokenBridge__factory,\n  Sentinel,\n  Sentinel__factory,\n  SGN,\n  SGN__factory,\n  SimpleGovernance,\n  SimpleGovernance__factory,\n  SingleBridgeToken,\n  SingleBridgeToken__factory,\n  Staking,\n  Staking__factory,\n  StakingReward,\n  StakingReward__factory,\n  TestERC20,\n  TestERC20__factory,\n  TransferSwap,\n  TransferSwap__factory,\n  Viewer,\n  Viewer__factory,\n  WETH,\n  WETH__factory\n} from '../../typechain';\nimport * as consts from './constants';\n\nimport type { AbstractSigner, AddressLike, ContractRunner } from 'ethers';\ninterface DeploymentInfo {\n  staking: Staking;\n  sgn: SGN;\n  stakingReward: StakingReward;\n  farmingRewards: FarmingRewards;\n  govern: Govern;\n  viewer: Viewer;\n  celr: TestERC20;\n}\n\nexport async function deployContracts(admin: ContractRunner): Promise<DeploymentInfo> {\n  const testERC20Factory = new TestERC20__factory();\n  const celr = await testERC20Factory.connect(admin).deploy();\n  const celrAddress = await celr.getAddress();\n\n  const stakingFactory = new Staking__factory();\n  const staking = await stakingFactory\n    .connect(admin)\n    .deploy(\n      celrAddress,\n      consts.PROPOSAL_DEPOSIT,\n      consts.VOTING_PERIOD,\n      consts.UNBONDING_PERIOD,\n      consts.MAX_VALIDATOR_NUM,\n      consts.MIN_VALIDATOR_TOKENS,\n      consts.MIN_SELF_DELEGATION,\n      consts.ADVANCE_NOTICE_PERIOD,\n      consts.VALIDATOR_BOND_INTERVAL,\n      consts.MAX_SLASH_FACTOR\n    );\n  const stakingAddress = await staking.getAddress();\n\n  const sgnFactory = new SGN__factory();\n  const sgn = await sgnFactory.connect(admin).deploy(stakingAddress);\n\n  const stakingRewardFactory = new StakingReward__factory();\n  const stakingReward = await stakingRewardFactory.connect(admin).deploy(stakingAddress);\n  const stakingRewardAddress = await stakingReward.getAddress();\n\n  const farmingRewardsFactory = new FarmingRewards__factory();\n  const farmingRewards = await farmingRewardsFactory.connect(admin).deploy(stakingAddress);\n\n  const governFactory = new Govern__factory();\n  const govern = await governFactory.connect(admin).deploy(stakingAddress, celrAddress, stakingRewardAddress);\n\n  const viewerFactory = new Viewer__factory();\n  const viewer = await viewerFactory.connect(admin).deploy(stakingAddress);\n\n  return { staking, sgn, stakingReward, farmingRewards, govern, viewer, celr };\n}\n\ninterface BridgeInfo {\n  bridge: Bridge;\n  token: TestERC20;\n  pegBridge: PeggedTokenBridge;\n  pegToken: SingleBridgeToken;\n}\n\nexport async function deployBridgeContracts(admin: ContractRunner): Promise<BridgeInfo> {\n  const testERC20Factory = new TestERC20__factory();\n  const token = await testERC20Factory.connect(admin).deploy();\n  await token.waitForDeployment();\n\n  const bridgeFactory = new Bridge__factory();\n  const bridge = await bridgeFactory.connect(admin).deploy();\n  await bridge.waitForDeployment();\n\n  const pegBridgeFactory = new PeggedTokenBridge__factory();\n  const pegBridge = await pegBridgeFactory.connect(admin).deploy(bridge.getAddress());\n  await pegBridge.waitForDeployment();\n\n  const pegTokenFactory = new SingleBridgeToken__factory();\n  const pegToken = await pegTokenFactory.connect(admin).deploy('PegToken', 'PGT', 18, pegBridge.getAddress());\n  await pegToken.waitForDeployment();\n\n  return { bridge, token, pegBridge, pegToken };\n}\n\ninterface MessageInfo {\n  bridge: Bridge;\n  msgBus: MessageBus;\n  msgTest: MsgTest;\n  token: TestERC20;\n}\n\nexport async function deployMessageContracts(admin: ContractRunner): Promise<MessageInfo> {\n  const testERC20Factory = new TestERC20__factory();\n  const token = await testERC20Factory.connect(admin).deploy();\n\n  const bridgeFactory = new Bridge__factory();\n  const bridge = await bridgeFactory.connect(admin).deploy();\n  const bridgeAddress = await bridge.getAddress();\n\n  const msgBusFactory = new MessageBus__factory();\n  const msgBus = await msgBusFactory\n    .connect(admin)\n    .deploy(bridgeAddress, bridgeAddress, ZeroAddress, ZeroAddress, ZeroAddress, ZeroAddress);\n\n  const msgTestFactory = new MsgTest__factory();\n  const msgTest = await msgTestFactory.connect(admin).deploy(msgBus.getAddress());\n\n  return { bridge, msgBus, msgTest, token };\n}\n\ninterface GovernedOwnerInfo {\n  gov: SimpleGovernance;\n  proxy: GovernedOwnerProxy;\n}\n\nexport async function deployGovernedOwner(\n  admin: ContractRunner & AddressLike,\n  initVoterNum: number\n): Promise<GovernedOwnerInfo> {\n  const proxyFactory = new GovernedOwnerProxy__factory();\n  const proxy = await proxyFactory.connect(admin).deploy(admin);\n\n  const voters: string[] = [];\n  const powers: number[] = [];\n  for (let i = 0; i < initVoterNum; i++) {\n    const voter = new Wallet(consts.userPrivKeys[i]).connect(ethers.provider);\n    voters.push(voter.address);\n    powers.push(100);\n  }\n\n  const govFactory = new SimpleGovernance__factory();\n  const gov = await govFactory.connect(admin).deploy(voters, powers, [proxy.getAddress()], 3600, 60, 40);\n\n  await proxy.initGov(gov.getAddress());\n\n  return { gov, proxy };\n}\n\nexport async function deploySentinel(admin: ContractRunner): Promise<Sentinel> {\n  const factory = new Sentinel__factory();\n  const sentinel = await factory.connect(admin).deploy([], [], []);\n  return sentinel;\n}\n\ninterface SwapInfo {\n  transferSwap: TransferSwap;\n  tokenA: TestERC20;\n  tokenB: TestERC20;\n  bridge: Bridge;\n  swap: DummySwap;\n  weth: WETH;\n}\n\nexport async function deploySwapContracts(admin: ContractRunner): Promise<SwapInfo> {\n  const testERC20FactoryA = new TestERC20__factory();\n  const tokenA = await testERC20FactoryA.connect(admin).deploy();\n\n  const testERC20FactoryB = new TestERC20__factory();\n  const tokenB = await testERC20FactoryB.connect(admin).deploy();\n\n  const bridgeFactory = new Bridge__factory();\n  const bridge = await bridgeFactory.connect(admin).deploy();\n  const bridgeAddress = await bridge.getAddress();\n\n  const busFactory = new MessageBus__factory();\n  const bus = await busFactory\n    .connect(admin)\n    .deploy(bridgeAddress, bridgeAddress, ZeroAddress, ZeroAddress, ZeroAddress, ZeroAddress);\n\n  const swapFactory = new DummySwap__factory();\n  const swap = await swapFactory.connect(admin).deploy(parseUnits('5')); // 5% fixed fake slippage\n\n  const wethFactory = new WETH__factory();\n  const weth = await wethFactory.connect(admin).deploy();\n\n  const transferSwapFactory = new TransferSwap__factory();\n  const transferSwap = await transferSwapFactory\n    .connect(admin)\n    .deploy(bus.getAddress(), swap.getAddress(), weth.getAddress());\n\n  return { tokenA, tokenB, transferSwap, swap, bridge, weth };\n}\n\nexport async function getAccounts(admin: AbstractSigner, assets: TestERC20[], num: number): Promise<Wallet[]> {\n  const accounts: Wallet[] = [];\n  for (let i = 0; i < num; i++) {\n    accounts.push(new ethers.Wallet(consts.userPrivKeys[i]).connect(ethers.provider));\n    await admin.sendTransaction({\n      to: accounts[i].address,\n      value: parseUnits('11')\n    });\n    for (let j = 0; j < assets.length; j++) {\n      await assets[j].transfer(accounts[i].address, parseUnits('1000'));\n    }\n  }\n  accounts.sort((a, b) => (a.address.toLowerCase() > b.address.toLowerCase() ? 1 : -1));\n  return accounts;\n}\n\nexport async function advanceBlockNumber(blkNum: number): Promise<void> {\n  const promises = [];\n  for (let i = 0; i < blkNum; i++) {\n    promises.push(ethers.provider.send('evm_mine', []));\n  }\n  await Promise.all(promises);\n}\n\nexport async function advanceBlockNumberTo(target: number): Promise<void> {\n  const blockNumber = await ethers.provider.getBlockNumber();\n  const promises = [];\n  for (let i = blockNumber; i < target; i++) {\n    promises.push(ethers.provider.send('evm_mine', []));\n  }\n  await Promise.all(promises);\n}\n\nexport async function getBlockTime() {\n  const blockNumber = await ethers.provider.getBlockNumber();\n  const block = await ethers.provider.getBlock(blockNumber);\n  if (block) {\n    return block.timestamp;\n  }\n  throw Error('invalid block number');\n}\n\nexport async function advanceBlockTime(blkTime: number) {\n  const currBlkTime = await getBlockTime();\n  await ethers.provider.send('evm_setNextBlockTimestamp', [currBlkTime + blkTime]);\n  await ethers.provider.send('evm_mine', []);\n}\n\nexport function getAddrs(signers: Wallet[]) {\n  const addrs: string[] = [];\n  for (let i = 0; i < signers.length; i++) {\n    addrs.push(signers[i].address);\n  }\n  return addrs;\n}\n"
  },
  {
    "path": "test/lib/constants.ts",
    "content": "import { parseUnits } from 'ethers';\n\nexport const PROPOSAL_DEPOSIT = 100;\nexport const VOTING_PERIOD = 20;\nexport const UNBONDING_PERIOD = 50;\nexport const MAX_VALIDATOR_NUM = 7;\nexport const MIN_VALIDATOR_TOKENS = parseUnits('4');\nexport const ADVANCE_NOTICE_PERIOD = 10;\nexport const VALIDATOR_BOND_INTERVAL = 0;\nexport const MAX_SLASH_FACTOR = 1e5; // 10%\n\nexport const MIN_SELF_DELEGATION = parseUnits('2');\nexport const VALIDATOR_STAKE = parseUnits('1'); // smaller than MIN_VALIDATOR_TOKENS for testing purpose\nexport const DELEGATOR_STAKE = parseUnits('6');\n\nexport const COMMISSION_RATE = 100;\nexport const SLASH_FACTOR = 50000; // 5%\n\nexport const STATUS_UNBONDED = 1;\nexport const STATUS_UNBONDING = 2;\nexport const STATUS_BONDED = 3;\n\nexport const ENUM_PROPOSAL_DEPOSIT = 0;\nexport const ENUM_VOTING_PERIOD = 1;\nexport const ENUM_UNBONDING_PERIOD = 2;\nexport const ENUM_MAX_VALIDATOR_NUM = 3;\nexport const ENUM_MIN_VALIDATOR_TOKENS = 4;\nexport const ENUM_MIN_SELF_DELEGATION = 5;\nexport const ENUM_ADVANCE_NOTICE_PERIOD = 6;\n\nexport const ENUM_VOTE_OPTION_UNVOTED = 0;\nexport const ENUM_VOTE_OPTION_YES = 1;\nexport const ENUM_VOTE_OPTION_ABSTAIN = 2;\nexport const ENUM_VOTE_OPTION_NO = 3;\n\nexport const SUB_FEE = parseUnits('100000000', 'wei');\n\nexport const TYPE_MSG_XFER = 0;\nexport const TYPE_MSG_ONLY = 1;\n\nexport const MSG_TX_NULL = 0;\nexport const MSG_TX_SUCCESS = 1;\nexport const MSG_TX_FAIL = 2;\nexport const MSG_TX_FALLBACK = 3;\n\nexport const XFER_TYPE_LQ_RELAY = 1;\nexport const XFER_TYPE_LQ_WITHDRAW = 2;\nexport const XFER_TYPE_PEG_MINT = 3;\nexport const XFER_TYPE_PEG_WITHDRAW = 4;\nexport const XFER_TYPE_PEGV2_MINT = 5;\nexport const XFER_TYPE_PEGV2_WITHDRAW = 6;\n\nexport const GovExternalDefault = 0;\nexport const GovExternalFastPass = 1;\nexport const GovInternalParamChange = 2;\nexport const GovInternalVoterUpdate = 3;\nexport const GovInternalProxyUpdate = 4;\nexport const GovInternalTokenTransfer = 5;\n\nexport const GovParamActivePeriod = 0;\nexport const GovParamQuorumThreshold = 1;\nexport const GovParamFastPassThreshold = 2;\n\nexport const userPrivKeys = [\n  '0x36f2243a51a0f879b1859fff1a663ac04aeebca1bcff4d7dc5a8b38e53211199',\n  '0xc0bf10873ddb6d554838f5e4f0c000e85d3307754151add9813ff331b746390d',\n  '0x68888cc706520c4d5049d38933e0b502e2863781d75de09c499cf0e4e00ba2de',\n  '0x400e64f3b8fe65ecda0bad60627c41fa607172cf0970fbe2551d6d923fd82f78',\n  '0xab4c840e48b11840f923a371ba453e4d8884fd23eee1b579f5a3910c9b00a4b6',\n  '0x0168ea2aa71023864b1c8eb65997996d726e5068c12b20dea81076ef56380465',\n  '0xd3733feb467076219337afed04787c48f5aa23c9c998bee1dc7742d12f7628e9',\n  '0xf9d76b9beacb01e431440a2750cb8aec04b782dfa7a7ef62584550e5db7347d6',\n  '0xecdd83652d7fddaffa0993f7a7d58d423a76737d7a90e81d06a32aecb8d470db',\n  '0xf37790438d0d8108a5bc588f727b84f041e355e825bbb8b36a6c83efa9ad3176',\n  '0x212f9e9d305ecd326ef88da498cd869f2e3ff2909f315f88f563594da8663990',\n  '0xdf0c45cfa93acf88ed0becd7a41df47e42217c0febc2be4aa7126decbccdc887',\n  '0x33209d3c38365492473417af8b03e3e6d3eb4f6269fe33f47edba62882732dbb',\n  '0xfe964eb524f182ea08aa2528fc5ec660238908fe77a5dca5f2954388bbe2cfc8',\n  '0x03abc71ae40f50de995e97421d28394818352ac91de757411293ec1177563806',\n  '0xce86dac0655a8822db84e505c1fdc36410d7e98ac227a9671f309d9dae2c741f',\n  '0xbb08d5f77da4a71cefa19f254e347caa0c837d2639d10c8a2bf37aea75c97d15',\n  '0x6b2e4b681206a0abca47ad72f194158c18f1a755b96d1fd8797baaec870f6000',\n  '0x1f7ede2316ee0423bb38127c6a1fc4d87f5804460757ffb6c3249335e3f2ecb4',\n  '0x0f072d61ec1b47e8f09c20e935d942c291d3229932ab1560cc567e32c2f87e94',\n  '0xee15c0525cc1f3292e9905d36f78a9ad48551e365174e351ad867c0bb4f54d9e'\n];\n"
  },
  {
    "path": "test/lib/proto.ts",
    "content": "import { expect } from 'chai';\nimport {\n  AbstractSigner,\n  BigNumberish,\n  getBytes,\n  solidityPacked,\n  solidityPackedKeccak256,\n  toBeArray,\n  Wallet\n} from 'ethers';\nimport protobuf from 'protobufjs';\n\nimport { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers';\n\nprotobuf.common('google/protobuf/descriptor.proto', {});\n\ninterface Proto {\n  Slash: protobuf.Type;\n  StakingReward: protobuf.Type;\n  FarmingRewards: protobuf.Type;\n  AcctAmtPair: protobuf.Type;\n  Relay: protobuf.Type;\n  WithdrawMsg: protobuf.Type;\n  Mint: protobuf.Type;\n}\n\nasync function getProtos(): Promise<Proto> {\n  const staking = await protobuf.load(`${__dirname}/../../contracts/libraries/proto/staking.proto`);\n  const farming = await protobuf.load(`${__dirname}/../../contracts/libraries/proto/farming.proto`);\n  const bridge = await protobuf.load(`${__dirname}/../../contracts/libraries/proto/bridge.proto`);\n  const pool = await protobuf.load(`${__dirname}/../../contracts/libraries/proto/pool.proto`);\n  const pegged = await protobuf.load(`${__dirname}/../../contracts/libraries/proto/pegged.proto`);\n\n  const Slash = staking.lookupType('staking.Slash');\n  const StakingReward = staking.lookupType('staking.StakingReward');\n  const FarmingRewards = farming.lookupType('farming.FarmingRewards');\n  const AcctAmtPair = staking.lookupType('staking.AcctAmtPair');\n\n  const Relay = bridge.lookupType('bridge.Relay');\n  const WithdrawMsg = pool.lookupType('pool.WithdrawMsg');\n  const Mint = pegged.lookupType('pegged.Mint');\n\n  return {\n    Slash,\n    StakingReward,\n    FarmingRewards,\n    AcctAmtPair,\n    Relay,\n    WithdrawMsg,\n    Mint\n  };\n}\n\nexport async function calculateSignatures(signers: AbstractSigner[], hash: Uint8Array): Promise<string[]> {\n  const sigs = [];\n  for (let i = 0; i < signers.length; i++) {\n    const sig = await signers[i].signMessage(hash);\n    sigs.push(sig);\n  }\n  return sigs;\n}\n\nexport async function getStakingRewardRequest(\n  recipient: string,\n  cumulativeRewardAmount: BigNumberish,\n  signers: Wallet[],\n  chainId: number,\n  contractAddress: string\n): Promise<{ rewardBytes: Uint8Array; sigs: string[] }> {\n  const { StakingReward } = await getProtos();\n  const reward = {\n    recipient: getBytes(recipient),\n    cumulativeRewardAmount: toBeArray(cumulativeRewardAmount)\n  };\n  const rewardProto = StakingReward.create(reward);\n  const rewardBytes = StakingReward.encode(rewardProto).finish();\n\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, contractAddress, 'StakingReward']);\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, rewardBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n\n  return { rewardBytes, sigs };\n}\n\nexport async function getFarmingRewardsRequest(\n  recipient: string,\n  tokenAddresses: string[],\n  cumulativeRewardAmounts: BigNumberish[],\n  signers: Wallet[],\n  chainId: number,\n  contractAddress: string\n): Promise<{ rewardBytes: Uint8Array; sigs: string[] }> {\n  const { FarmingRewards } = await getProtos();\n  const reward = {\n    recipient: getBytes(recipient),\n    tokenAddresses: tokenAddresses.map((addr) => getBytes(addr)),\n    cumulativeRewardAmounts: cumulativeRewardAmounts.map(toBeArray)\n  };\n  const rewardProto = FarmingRewards.create(reward);\n  const rewardBytes = FarmingRewards.encode(rewardProto).finish();\n\n  const domain = solidityPackedKeccak256(\n    ['uint256', 'address', 'string'],\n    [chainId, contractAddress, 'FarmingRewards']\n  );\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, rewardBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n\n  return { rewardBytes, sigs };\n}\n\nasync function getAcctAmtPairs(accounts: string[], amounts: bigint[]): Promise<protobuf.Message[]> {\n  const { AcctAmtPair } = await getProtos();\n  expect(accounts.length).to.equal(amounts.length);\n  const pairs = [];\n  for (let i = 0; i < accounts.length; i++) {\n    const pair = {\n      account: getBytes(accounts[i]),\n      amount: toBeArray(amounts[i])\n    };\n    const pairProto = AcctAmtPair.create(pair);\n    pairs.push(pairProto);\n  }\n  return pairs;\n}\n\nexport async function getSlashRequest(\n  validatorAddr: string,\n  nonce: number,\n  slashFactor: number,\n  expireTime: number,\n  jailPeriod: number,\n  collectorAddrs: string[],\n  collectorAmts: bigint[],\n  signers: Wallet[],\n  chainId: number,\n  contractAddress: string\n): Promise<{ slashBytes: Uint8Array; sigs: string[] }> {\n  const { Slash } = await getProtos();\n\n  const collectors = await getAcctAmtPairs(collectorAddrs, collectorAmts);\n  const slash = {\n    validator: getBytes(validatorAddr),\n    nonce,\n    slashFactor,\n    expireTime,\n    jailPeriod,\n    collectors\n  };\n  const slashProto = Slash.create(slash);\n  const slashBytes = Slash.encode(slashProto).finish();\n\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, contractAddress, 'Slash']);\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, slashBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n\n  return { slashBytes, sigs };\n}\n\nexport async function getRelayRequest(\n  sender: string,\n  receiver: string,\n  token: string,\n  amount: bigint,\n  srcChainId: number,\n  dstChainId: number,\n  srcTransferId: string,\n  signers: (Wallet | SignerWithAddress)[],\n  contractAddress: string\n): Promise<{ relayBytes: Uint8Array; sigs: string[] }> {\n  const { Relay } = await getProtos();\n  const relay = {\n    sender: getBytes(sender),\n    receiver: getBytes(receiver),\n    token: getBytes(token),\n    amount: toBeArray(amount),\n    srcChainId,\n    dstChainId,\n    srcTransferId: getBytes(srcTransferId)\n  };\n  const relayProto = Relay.create(relay);\n  const relayBytes = Relay.encode(relayProto).finish();\n\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [dstChainId, contractAddress, 'Relay']);\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, relayBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n\n  const signerAddrs = [];\n  for (let i = 0; i < signers.length; i++) {\n    signerAddrs.push(signers[i].address);\n  }\n\n  signers.sort((a, b) => (a.address.toLowerCase() > b.address.toLowerCase() ? 1 : -1));\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n\n  return { relayBytes, sigs };\n}\n\nexport async function getWithdrawRequest(\n  chainId: number,\n  seqnum: number,\n  receiver: string,\n  token: string,\n  amount: bigint,\n  refid: string,\n  signers: Wallet[],\n  contractAddress: string\n): Promise<{ withdrawBytes: Uint8Array; sigs: string[] }> {\n  const { WithdrawMsg } = await getProtos();\n  const withdraw = {\n    chainid: chainId,\n    seqnum: seqnum,\n    receiver: getBytes(receiver),\n    token: getBytes(token),\n    amount: toBeArray(amount),\n    refid: getBytes(refid)\n  };\n  const withdrawProto = WithdrawMsg.create(withdraw);\n  const withdrawBytes = WithdrawMsg.encode(withdrawProto).finish();\n\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, contractAddress, 'WithdrawMsg']);\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, withdrawBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n\n  const signerAddrs = [];\n  for (let i = 0; i < signers.length; i++) {\n    signerAddrs.push(signers[i].address);\n  }\n\n  signers.sort((a, b) => (a.address.toLowerCase() > b.address.toLowerCase() ? 1 : -1));\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n  return { withdrawBytes, sigs };\n}\n\nexport async function getMintRequest(\n  token: string,\n  account: string,\n  amount: bigint,\n  depositor: string,\n  refChainId: number,\n  refId: string,\n  signers: Wallet[],\n  chainId: number,\n  contractAddress: string\n): Promise<{ mintBytes: Uint8Array; sigs: string[] }> {\n  const { Mint } = await getProtos();\n  const mint = {\n    token: getBytes(token),\n    account: getBytes(account),\n    amount: toBeArray(amount),\n    depositor: getBytes(depositor),\n    refChainId,\n    refId: getBytes(refId)\n  };\n  const mintProto = Mint.create(mint);\n  const mintBytes = Mint.encode(mintProto).finish();\n\n  const domain = solidityPackedKeccak256(['uint256', 'address', 'string'], [chainId, contractAddress, 'Mint']);\n  const signedData = solidityPacked(['bytes32', 'bytes'], [domain, mintBytes]);\n  const signedDataHash = solidityPackedKeccak256(['bytes'], [signedData]);\n\n  const signerAddrs = [];\n  for (let i = 0; i < signers.length; i++) {\n    signerAddrs.push(signers[i].address);\n  }\n\n  signers.sort((a, b) => (a.address.toLowerCase() > b.address.toLowerCase() ? 1 : -1));\n  const sigs = await calculateSignatures(signers, getBytes(signedDataHash));\n  return { mintBytes, sigs };\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"lib\": [\"es5\", \"es6\"],\n    \"module\": \"commonjs\",\n    \"moduleResolution\": \"node\",\n    \"noImplicitAny\": true,\n    \"outDir\": \"dist\",\n    \"resolveJsonModule\": true,\n    \"sourceMap\": true,\n    \"strict\": true,\n    \"target\": \"es2020\"\n  },\n  \"exclude\": [\"artifacts\", \"build\", \"cache\", \"node_modules\"],\n  \"include\": [\n    \"deploy/**/*\",\n    \"scripts/**/*\",\n    \"test/**/*\",\n    \"typechain/**/*\",\n    \"types/**/*\",\n    \"hardhat.config.ts\"\n  ]\n}\n"
  }
]