Commit 3d908f6d authored by 贾浩@五瓣科技's avatar 贾浩@五瓣科技

init

parents
Pipeline #732 failed with stages
MNEMONIC="test test test test test test test test test test test junk"
INFURA_PROJECT_ID=""
ETHERSCAN_API_KEY=""
GASTOKEN_ADDR=""
zkproverjs/
\ No newline at end of file
module.exports = {
plugins: [
'mocha',
],
env: {
node: true,
mocha: true,
},
extends: 'airbnb-base',
rules: {
indent: ['error', 4],
'mocha/no-exclusive-tests': 'error',
'max-len': ['error', {
code: 140, comments: 200, ignoreStrings: true, ignoreTemplateLiterals: true,
}],
'no-unused-vars': [2, { varsIgnorePattern: 'export^' }],
'no-return-assign': [0],
'no-underscore-dangle': [0],
'no-plusplus': ['error', { allowForLoopAfterthoughts: true }],
'func-names': [0],
'class-methods-use-this': [0],
'no-bitwise': [0],
'no-param-reassign': 'off',
'no-console': [2, { allow: ['warn', 'error'] }],
'import/prefer-default-export': [0],
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'multiline-comment-style': 'error',
'import/no-extraneous-dependencies': 'off'
},
};
#!/bin/bash
## To use this hook execute in the project root:
## git config --local core.hooksPath .githooks/
if npm run lint; then
npx hardhat compile --force
npm run docgen
git add docs
cp artifacts/contracts/PolygonZkEVMBridge.sol/PolygonZkEVMBridge.json compiled-contracts/
cp artifacts/contracts/PolygonZkEVMGlobalExitRoot.sol/PolygonZkEVMGlobalExitRoot.json compiled-contracts/
cp artifacts/contracts/PolygonZkEVMGlobalExitRootL2.sol/PolygonZkEVMGlobalExitRootL2.json compiled-contracts/
cp artifacts/contracts/lib/TokenWrapped.sol/TokenWrapped.json compiled-contracts/
cp artifacts/contracts/mocks/PolygonZkEVMBridgeMock.sol/PolygonZkEVMBridgeMock.json compiled-contracts/
cp artifacts/contracts/mocks/ERC20PermitMock.sol/ERC20PermitMock.json compiled-contracts/
cp artifacts/contracts/mocks/PolygonZkEVMGlobalExitRootL2Mock.sol/PolygonZkEVMGlobalExitRootL2Mock.json compiled-contracts/
cp artifacts/contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol/PolygonZkEVMGlobalExitRootMock.json compiled-contracts/
cp artifacts/contracts/mocks/CDKValidiumMock.sol/CDKValidiumMock.json compiled-contracts/
cp artifacts/contracts/mocks/VerifierRollupHelperMock.sol/VerifierRollupHelperMock.json compiled-contracts/
cp artifacts/contracts/CDKValidium.sol/CDKValidium.json compiled-contracts/
cp artifacts/contracts/CDKDataCommittee.sol/CDKDataCommittee.json compiled-contracts/
cp artifacts/contracts/verifiers/FflonkVerifier.sol/FflonkVerifier.json compiled-contracts/
cp artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json compiled-contracts/
cp artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json compiled-contracts/
cp artifacts/contracts/deployment/CDKValidiumDeployer.sol/CDKValidiumDeployer.json compiled-contracts/
cp artifacts/contracts/CDKValidiumTimelock.sol/CDKValidiumTimelock.json compiled-contracts/
git add compiled-contracts
exit 0
else
exit 1
fi
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Main CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: setup
run: |
npm install -g npm@latest
npm i
- name: linter
run: npm run lint
- name: test
run: npm run test
coverage.json
.env
cache
build
yarn.lock
node_modules
coverage.json
coverage
.coverage_*
.openzeppelin
artifacts/
docs/interfaces
docs/mocks
.vscode/launch.json
deploy_output.json
deploy_parameters.json
deployments
upgrade_parameters.json
docker/gethData/
*.ignore/
\ No newline at end of file
{
"overrides": [
{
"files": "*.sol",
"options": {
"printWidth": 80,
"tabWidth": 4,
"useTabs": false,
"singleQuote": false,
"bracketSpacing": false,
"explicitTypes": "always"
}
}
]
}
\ No newline at end of file
module.exports = {
skipFiles: ['mocks', 'interfaces']
};
\ No newline at end of file
{
"extends": "solhint:recommended",
"rules": {
"mark-callable-contracts": "off",
"no-empty-blocks": "off",
"compiler-version": ["error", "0.8.20"],
"private-vars-leading-underscore": "error",
"bracket-align": "off",
"reason-string": "off",
"not-rely-on-time": "off",
"no-inline-assembly": "off",
"check-send-result": "off"
}
}
{
"editor.formatOnSave": false,
"solidity.linter": "solhint",
"solidity.compileUsingRemoteVersion": "v0.8.20+commit.e14f2714",
"solidity.remappings": ["@openzeppelin/=node_modules/@openzeppelin"],
}
Polygon zkEVM Mainnet Beta
Copyright (C) 2023 Catenable AG
This program is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
# Polygon CDK Validium Contracts
### Core Contracts for the Polygon CDK Validium
The cdk-validium-contracts repository contains the smart contract implementations designed for use with CDK chains configured with Validium.
## Overview of Validium
For a full overview of the Polygon CDK Validium, please reference the [CDK documentation](https://wiki.polygon.technology/docs/cdk/).
The CDK Validium solution is made up of several components; start with the [CDK Validium Node](https://github.com/0xPolygon/cdk-validium-node). For quick reference, the complete list of components are outlined below:
| Component | Description |
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------- |
| [CDK Validium Node](https://github.com/0xPolygon/cdk-validium-node) | Node implementation for the CDK networks in Validium mode |
| [CDK Validium Contracts](https://github.com/0xPolygon/cdk-validium-contracts) | Smart contract implementation for the CDK networks in Validium mode |
| [CDK Data Availability](https://github.com/0xPolygon/cdk-data-availability) | Data availability implementation for the CDK networks |
| [Prover / Executor](https://github.com/0xPolygonHermez/zkevm-prover) | zkEVM engine and prover implementation |
| [Bridge Service](https://github.com/0xPolygonHermez/zkevm-bridge-service) | Bridge service implementation for CDK networks |
| [Bridge UI](https://github.com/0xPolygonHermez/zkevm-bridge-ui) | UI for the CDK networks bridge |
---
## Important Note
The private keys and mnemonics included in this repository are intended solely for internal testing. **Do not use them in production environments.**
## Prerequisites
- Node.js version: 16.x
- npm version: 7.x
## Repository Structure
- `contracts`: Core contracts
- `PolygonZkEVMBridge.sol`: Facilitates asset transfers between chains
- `PolygonZkEVMGlobalExitRoot.sol`: Manages the global exit root on L1
- `PolygonZkEVMGlobalExitRootL2.sol`: Manages the global exit root on L2
- `CDKValidium.sol`: Consensus algorithm for Validium CDK chains
- `docs`: Specifications and useful resources
- `test`: Contract test suites
## Activate Github Hook
To activate the GitHub hook, run the following command:
```bash
git config --local core.hooksPath .githooks/
```
## Install
```bash
npm i
```
## Run Tests
Execute the test suite with:
```bash
npm run test
```
## Linting
To check for linting errors, run:
```bash
npm run lint
```
To automatically fix linting errors, run:
```bash
npm run lint:fix
```
## Build Docker Image
To build the Docker image, run:
```bash
npm run docker:contracts
```
This will create a new Docker image named `hermeznetwork/geth-cdk-validium-contracts`, which includes a Geth node with the deployed contracts. The deployment output can be found at `docker/deploymentOutput/deploy_output.json`.
To run the Docker container, use:
```bash
docker run -p 8545:8545 hermeznetwork/geth-cdk-validium-contracts
```
## Note
For testing purposes, the following private keys are being used. These keys are not intended for production use:
- **Private key**: 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
- **Address**: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
- **Private key**: 0xdfd01798f92667dbf91df722434e8fbe96af0211d4d1b82bbbbc8f1def7a814f
- **Address**: 0xc949254d682d8c9ad5682521675b8f43b102aec4
## License
The cdk-validium-contracts project is licensed under the [GNU Affero General Public License](LICENSE) free software license.
# Verify Deployed Smart Contracts
To verify that the smartcontracts of this repository are the same deployed on mainnet, you could follow the instructions described [document](verifyMainnetDeployment/verifyDeployment.md)
The smartcontract used to verify a proof, it's a generated contract from zkEVM Rom and Pil (constraints). To verify the deployment of this smartcontract you could follow the instructions described in this [document](verifyMainnetDeployment/verifyMainnetProofVerifier.md)
{
"_format": "hh-sol-artifact-1",
"contractName": "CDKDataCommittee",
"sourceName": "contracts/CDKDataCommittee.sol",
"abi": [
{
"inputs": [],
"name": "CommitteeAddressDoesntExist",
"type": "error"
},
{
"inputs": [],
"name": "EmptyURLNotAllowed",
"type": "error"
},
{
"inputs": [],
"name": "TooManyRequiredSignatures",
"type": "error"
},
{
"inputs": [],
"name": "UnexpectedAddrsAndSignaturesSize",
"type": "error"
},
{
"inputs": [],
"name": "UnexpectedAddrsBytesLength",
"type": "error"
},
{
"inputs": [],
"name": "UnexpectedCommitteeHash",
"type": "error"
},
{
"inputs": [],
"name": "WrongAddrOrder",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bytes32",
"name": "committeeHash",
"type": "bytes32"
}
],
"name": "CommitteeUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [],
"name": "committeeHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getAmountOfMembers",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "members",
"outputs": [
{
"internalType": "string",
"name": "url",
"type": "string"
},
{
"internalType": "address",
"name": "addr",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "requiredAmountOfSignatures",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_requiredAmountOfSignatures",
"type": "uint256"
},
{
"internalType": "string[]",
"name": "urls",
"type": "string[]"
},
{
"internalType": "bytes",
"name": "addrsBytes",
"type": "bytes"
}
],
"name": "setupCommittee",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "signedHash",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "signaturesAndAddrs",
"type": "bytes"
}
],
"name": "verifySignatures",
"outputs": [],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b506115e0806100206000396000f3fe608060405234801561001057600080fd5b50600436106100be5760003560e01c80638129fc1c11610076578063c7a823e01161005b578063c7a823e01461015a578063dce1e2b61461016d578063f2fde38b1461017557600080fd5b80638129fc1c1461012a5780638da5cb5b1461013257600080fd5b8063609d4544116100a7578063609d4544146101025780636beedd3914610119578063715018a61461012257600080fd5b8063078fba2a146100c35780635daf08ca146100d8575b600080fd5b6100d66100d1366004610fa9565b610188565b005b6100eb6100e6366004611054565b61048c565b6040516100f992919061106d565b60405180910390f35b61010b60665481565b6040519081526020016100f9565b61010b60655481565b6100d661055e565b6100d6610572565b60335460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100d66101683660046110f6565b610709565b60675461010b565b6100d6610183366004611142565b61095c565b610190610a10565b82858110156101cb576040517f2e7dcd6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d66014826111ae565b821461020e576040517f2ab6a12900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61021a60676000610eb5565b6000805b828110156104305760006102336014836111ae565b905060008682876102456014836111c5565b92610252939291906111d8565b61025b91611202565b60601c90508888848181106102725761027261124a565b90506020028101906102849190611279565b90506000036102bf576040517fb54b70e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1610610324576040517fd53cfbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b809350606760405180604001604052808b8b878181106103465761034661124a565b90506020028101906103589190611279565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093855250505073ffffffffffffffffffffffffffffffffffffffff851660209283015283546001810185559381522081519192600202019081906103cc90826113af565b5060209190910151600190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905550819050610428816114c9565b91505061021e565b508383604051610441929190611501565b6040519081900381206066819055606589905581527f831403fd381b3e6ac875d912ec2eee0e0203d0d29f7b3e0c96fc8f582d6db6579060200160405180910390a150505050505050565b6067818154811061049c57600080fd5b90600052602060002090600202016000915090508060000180546104bf9061130d565b80601f01602080910402602001604051908101604052809291908181526020018280546104eb9061130d565b80156105385780601f1061050d57610100808354040283529160200191610538565b820191906000526020600020905b81548152906001019060200180831161051b57829003601f168201915b5050506001909301549192505073ffffffffffffffffffffffffffffffffffffffff1682565b610566610a10565b6105706000610a91565b565b600054610100900460ff16158080156105925750600054600160ff909116105b806105ac5750303b1580156105ac575060005460ff166001145b61063d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561069b57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6106a3610b08565b801561070657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000606554604161071a91906111ae565b90508082108061073e575060146107318284611511565b61073b9190611553565b15155b15610775576040517f6b8eec4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606654610784838381876111d8565b604051610792929190611501565b6040518091039020146107d1576040517f6b156b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060146107e08486611511565b6107ea9190611567565b905060005b60655481101561095357600061086a88888861080c6041876111ae565b90604161081981896111ae565b61082391906111c5565b92610830939291906111d8565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610ba892505050565b90506000845b848110156109065760006108856014836111ae565b61088f90896111c5565b905060008a828b6108a16014836111c5565b926108ae939291906111d8565b6108b791611202565b60601c905073ffffffffffffffffffffffffffffffffffffffff851681036108f1576108e48360016111c5565b9750600193505050610906565b505080806108fe906114c9565b915050610870565b508061093e576040517f8431721300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050808061094b906114c9565b9150506107ef565b50505050505050565b610964610a10565b73ffffffffffffffffffffffffffffffffffffffff8116610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610634565b61070681610a91565b60335473ffffffffffffffffffffffffffffffffffffffff163314610570576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610634565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610b9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610634565b61057033610a91565b6000806000610bb78585610bce565b91509150610bc481610c13565b5090505b92915050565b6000808251604103610c045760208301516040840151606085015160001a610bf887828585610dc6565b94509450505050610c0c565b506000905060025b9250929050565b6000816004811115610c2757610c2761157b565b03610c2f5750565b6001816004811115610c4357610c4361157b565b03610caa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610634565b6002816004811115610cbe57610cbe61157b565b03610d25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610634565b6003816004811115610d3957610d3961157b565b03610706576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610634565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115610dfd5750600090506003610eac565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015610e51573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116610ea557600060019250925050610eac565b9150600090505b94509492505050565b508054600082556002029060005260206000209081019061070691905b80821115610f19576000610ee68282610f1d565b506001810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600201610ed2565b5090565b508054610f299061130d565b6000825580601f10610f39575050565b601f01602090049060005260206000209081019061070691905b80821115610f195760008155600101610f53565b60008083601f840112610f7957600080fd5b50813567ffffffffffffffff811115610f9157600080fd5b602083019150836020828501011115610c0c57600080fd5b600080600080600060608688031215610fc157600080fd5b85359450602086013567ffffffffffffffff80821115610fe057600080fd5b818801915088601f830112610ff457600080fd5b81358181111561100357600080fd5b8960208260051b850101111561101857600080fd5b60208301965080955050604088013591508082111561103657600080fd5b5061104388828901610f67565b969995985093965092949392505050565b60006020828403121561106657600080fd5b5035919050565b604081526000835180604084015260005b8181101561109b576020818701810151606086840101520161107e565b5060006060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b60008060006040848603121561110b57600080fd5b83359250602084013567ffffffffffffffff81111561112957600080fd5b61113586828701610f67565b9497909650939450505050565b60006020828403121561115457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461117857600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610bc857610bc861117f565b80820180821115610bc857610bc861117f565b600080858511156111e857600080fd5b838611156111f557600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081358181169160148510156112425780818660140360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126112ae57600080fd5b83018035915067ffffffffffffffff8211156112c957600080fd5b602001915036819003821315610c0c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600181811c9082168061132157607f821691505b60208210810361135a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156113aa57600081815260208120601f850160051c810160208610156113875750805b601f850160051c820191505b818110156113a657828155600101611393565b5050505b505050565b815167ffffffffffffffff8111156113c9576113c96112de565b6113dd816113d7845461130d565b84611360565b602080601f83116001811461143057600084156113fa5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556113a6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561147d5788860151825594840194600190910190840161145e565b50858210156114b957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036114fa576114fa61117f565b5060010190565b8183823760009101908152919050565b81810381811115610bc857610bc861117f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261156257611562611524565b500690565b60008261157657611576611524565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea2646970667358221220a54a2ecac47f39fb27609b998291b1e8046737fbc346d3fc4d56c25e13d40d7e64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100be5760003560e01c80638129fc1c11610076578063c7a823e01161005b578063c7a823e01461015a578063dce1e2b61461016d578063f2fde38b1461017557600080fd5b80638129fc1c1461012a5780638da5cb5b1461013257600080fd5b8063609d4544116100a7578063609d4544146101025780636beedd3914610119578063715018a61461012257600080fd5b8063078fba2a146100c35780635daf08ca146100d8575b600080fd5b6100d66100d1366004610fa9565b610188565b005b6100eb6100e6366004611054565b61048c565b6040516100f992919061106d565b60405180910390f35b61010b60665481565b6040519081526020016100f9565b61010b60655481565b6100d661055e565b6100d6610572565b60335460405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100f9565b6100d66101683660046110f6565b610709565b60675461010b565b6100d6610183366004611142565b61095c565b610190610a10565b82858110156101cb576040517f2e7dcd6e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6101d66014826111ae565b821461020e576040517f2ab6a12900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61021a60676000610eb5565b6000805b828110156104305760006102336014836111ae565b905060008682876102456014836111c5565b92610252939291906111d8565b61025b91611202565b60601c90508888848181106102725761027261124a565b90506020028101906102849190611279565b90506000036102bf576040517fb54b70e400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1610610324576040517fd53cfbe000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b809350606760405180604001604052808b8b878181106103465761034661124a565b90506020028101906103589190611279565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092018290525093855250505073ffffffffffffffffffffffffffffffffffffffff851660209283015283546001810185559381522081519192600202019081906103cc90826113af565b5060209190910151600190910180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff90921691909117905550819050610428816114c9565b91505061021e565b508383604051610441929190611501565b6040519081900381206066819055606589905581527f831403fd381b3e6ac875d912ec2eee0e0203d0d29f7b3e0c96fc8f582d6db6579060200160405180910390a150505050505050565b6067818154811061049c57600080fd5b90600052602060002090600202016000915090508060000180546104bf9061130d565b80601f01602080910402602001604051908101604052809291908181526020018280546104eb9061130d565b80156105385780601f1061050d57610100808354040283529160200191610538565b820191906000526020600020905b81548152906001019060200180831161051b57829003601f168201915b5050506001909301549192505073ffffffffffffffffffffffffffffffffffffffff1682565b610566610a10565b6105706000610a91565b565b600054610100900460ff16158080156105925750600054600160ff909116105b806105ac5750303b1580156105ac575060005460ff166001145b61063d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055801561069b57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b6106a3610b08565b801561070657600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50565b6000606554604161071a91906111ae565b90508082108061073e575060146107318284611511565b61073b9190611553565b15155b15610775576040517f6b8eec4600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606654610784838381876111d8565b604051610792929190611501565b6040518091039020146107d1576040517f6b156b2800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060146107e08486611511565b6107ea9190611567565b905060005b60655481101561095357600061086a88888861080c6041876111ae565b90604161081981896111ae565b61082391906111c5565b92610830939291906111d8565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610ba892505050565b90506000845b848110156109065760006108856014836111ae565b61088f90896111c5565b905060008a828b6108a16014836111c5565b926108ae939291906111d8565b6108b791611202565b60601c905073ffffffffffffffffffffffffffffffffffffffff851681036108f1576108e48360016111c5565b9750600193505050610906565b505080806108fe906114c9565b915050610870565b508061093e576040517f8431721300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050808061094b906114c9565b9150506107ef565b50505050505050565b610964610a10565b73ffffffffffffffffffffffffffffffffffffffff8116610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610634565b61070681610a91565b60335473ffffffffffffffffffffffffffffffffffffffff163314610570576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610634565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610b9f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610634565b61057033610a91565b6000806000610bb78585610bce565b91509150610bc481610c13565b5090505b92915050565b6000808251604103610c045760208301516040840151606085015160001a610bf887828585610dc6565b94509450505050610c0c565b506000905060025b9250929050565b6000816004811115610c2757610c2761157b565b03610c2f5750565b6001816004811115610c4357610c4361157b565b03610caa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610634565b6002816004811115610cbe57610cbe61157b565b03610d25576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610634565b6003816004811115610d3957610d3961157b565b03610706576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c60448201527f75650000000000000000000000000000000000000000000000000000000000006064820152608401610634565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115610dfd5750600090506003610eac565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015610e51573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff8116610ea557600060019250925050610eac565b9150600090505b94509492505050565b508054600082556002029060005260206000209081019061070691905b80821115610f19576000610ee68282610f1d565b506001810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600201610ed2565b5090565b508054610f299061130d565b6000825580601f10610f39575050565b601f01602090049060005260206000209081019061070691905b80821115610f195760008155600101610f53565b60008083601f840112610f7957600080fd5b50813567ffffffffffffffff811115610f9157600080fd5b602083019150836020828501011115610c0c57600080fd5b600080600080600060608688031215610fc157600080fd5b85359450602086013567ffffffffffffffff80821115610fe057600080fd5b818801915088601f830112610ff457600080fd5b81358181111561100357600080fd5b8960208260051b850101111561101857600080fd5b60208301965080955050604088013591508082111561103657600080fd5b5061104388828901610f67565b969995985093965092949392505050565b60006020828403121561106657600080fd5b5035919050565b604081526000835180604084015260005b8181101561109b576020818701810151606086840101520161107e565b5060006060828501015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505073ffffffffffffffffffffffffffffffffffffffff831660208301529392505050565b60008060006040848603121561110b57600080fd5b83359250602084013567ffffffffffffffff81111561112957600080fd5b61113586828701610f67565b9497909650939450505050565b60006020828403121561115457600080fd5b813573ffffffffffffffffffffffffffffffffffffffff8116811461117857600080fd5b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082028115828204841417610bc857610bc861117f565b80820180821115610bc857610bc861117f565b600080858511156111e857600080fd5b838611156111f557600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000081358181169160148510156112425780818660140360031b1b83161692505b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126112ae57600080fd5b83018035915067ffffffffffffffff8211156112c957600080fd5b602001915036819003821315610c0c57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600181811c9082168061132157607f821691505b60208210810361135a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b601f8211156113aa57600081815260208120601f850160051c810160208610156113875750805b601f850160051c820191505b818110156113a657828155600101611393565b5050505b505050565b815167ffffffffffffffff8111156113c9576113c96112de565b6113dd816113d7845461130d565b84611360565b602080601f83116001811461143057600084156113fa5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556113a6565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561147d5788860151825594840194600190910190840161145e565b50858210156114b957878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036114fa576114fa61117f565b5060010190565b8183823760009101908152919050565b81810381811115610bc857610bc861117f565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60008261156257611562611524565b500690565b60008261157657611576611524565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fdfea2646970667358221220a54a2ecac47f39fb27609b998291b1e8046737fbc346d3fc4d56c25e13d40d7e64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"_format": "hh-sol-artifact-1",
"contractName": "CDKValidiumDeployer",
"sourceName": "contracts/deployment/CDKValidiumDeployer.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [],
"name": "FunctionCall",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "newContractAddress",
"type": "address"
}
],
"name": "NewDeterministicDeployment",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initBytecode",
"type": "bytes"
}
],
"name": "deployDeterministic",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "initBytecode",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "dataCall",
"type": "bytes"
}
],
"name": "deployDeterministicAndCall",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "targetAddress",
"type": "address"
},
{
"internalType": "bytes",
"name": "dataCall",
"type": "bytes"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "functionCall",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "bytecodeHash",
"type": "bytes32"
}
],
"name": "predictDeterministicAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b50604051610c71380380610c7183398101604081905261002f91610097565b61003833610047565b61004181610047565b506100c7565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156100a957600080fd5b81516001600160a01b03811681146100c057600080fd5b9392505050565b610b9b806100d66000396000f3fe6080604052600436106100705760003560e01c8063715018a61161004e578063715018a6146100e65780638da5cb5b146100fb578063e11ae6cb14610126578063f2fde38b1461013957600080fd5b80632b79805a146100755780634a94d4871461008a5780636d07dbf81461009d575b600080fd5b610088610083366004610927565b610159565b005b6100886100983660046109c7565b6101cb565b3480156100a957600080fd5b506100bd6100b8366004610a1e565b61020d565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b50610088610220565b34801561010757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100bd565b610088610134366004610a40565b610234565b34801561014557600080fd5b50610088610154366004610a90565b61029b565b610161610357565b600061016e8585856103d8565b905061017a8183610537565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a15050505050565b6101d3610357565b6101de83838361057b565b506040517f25adb19089b6a549831a273acdf7908cff8b7ee5f551f8d1d37996cf01c5df5b90600090a1505050565b600061021983836105a9565b9392505050565b610228610357565b61023260006105b6565b565b61023c610357565b60006102498484846103d8565b60405173ffffffffffffffffffffffffffffffffffffffff821681529091507fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a150505050565b6102a3610357565b73ffffffffffffffffffffffffffffffffffffffff811661034b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610354816105b6565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610232576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610342565b600083471015610444576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e63650000006044820152606401610342565b81516000036104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610342565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610219576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610342565b6060610219838360006040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525061062b565b60606105a1848484604051806060016040528060298152602001610b3d6029913961062b565b949350505050565b6000610219838330610744565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6060824710156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610342565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516106e69190610acf565b60006040518083038185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50915091506107398783838761076e565b979650505050505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b606083156108045782516000036107fd5773ffffffffffffffffffffffffffffffffffffffff85163b6107fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610342565b50816105a1565b6105a183838151156108195781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103429190610aeb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261088d57600080fd5b813567ffffffffffffffff808211156108a8576108a861084d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156108ee576108ee61084d565b8160405283815286602085880101111561090757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561093d57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561096357600080fd5b61096f8883890161087c565b9350606087013591508082111561098557600080fd5b506109928782880161087c565b91505092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff811681146109c257600080fd5b919050565b6000806000606084860312156109dc57600080fd5b6109e58461099e565b9250602084013567ffffffffffffffff811115610a0157600080fd5b610a0d8682870161087c565b925050604084013590509250925092565b60008060408385031215610a3157600080fd5b50508035926020909101359150565b600080600060608486031215610a5557600080fd5b8335925060208401359150604084013567ffffffffffffffff811115610a7a57600080fd5b610a868682870161087c565b9150509250925092565b600060208284031215610aa257600080fd5b6102198261099e565b60005b83811015610ac6578181015183820152602001610aae565b50506000910152565b60008251610ae1818460208701610aab565b9190910192915050565b6020815260008251806020840152610b0a816040850160208701610aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220b9c8f668b6482fae01777910c9918cfb563dddb115f78f0c84c030100648725964736f6c63430008140033",
"deployedBytecode": "0x6080604052600436106100705760003560e01c8063715018a61161004e578063715018a6146100e65780638da5cb5b146100fb578063e11ae6cb14610126578063f2fde38b1461013957600080fd5b80632b79805a146100755780634a94d4871461008a5780636d07dbf81461009d575b600080fd5b610088610083366004610927565b610159565b005b6100886100983660046109c7565b6101cb565b3480156100a957600080fd5b506100bd6100b8366004610a1e565b61020d565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b50610088610220565b34801561010757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100bd565b610088610134366004610a40565b610234565b34801561014557600080fd5b50610088610154366004610a90565b61029b565b610161610357565b600061016e8585856103d8565b905061017a8183610537565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a15050505050565b6101d3610357565b6101de83838361057b565b506040517f25adb19089b6a549831a273acdf7908cff8b7ee5f551f8d1d37996cf01c5df5b90600090a1505050565b600061021983836105a9565b9392505050565b610228610357565b61023260006105b6565b565b61023c610357565b60006102498484846103d8565b60405173ffffffffffffffffffffffffffffffffffffffff821681529091507fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a150505050565b6102a3610357565b73ffffffffffffffffffffffffffffffffffffffff811661034b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610354816105b6565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610232576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610342565b600083471015610444576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e63650000006044820152606401610342565b81516000036104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610342565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610219576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610342565b6060610219838360006040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525061062b565b60606105a1848484604051806060016040528060298152602001610b3d6029913961062b565b949350505050565b6000610219838330610744565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6060824710156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610342565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516106e69190610acf565b60006040518083038185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50915091506107398783838761076e565b979650505050505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b606083156108045782516000036107fd5773ffffffffffffffffffffffffffffffffffffffff85163b6107fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610342565b50816105a1565b6105a183838151156108195781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103429190610aeb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261088d57600080fd5b813567ffffffffffffffff808211156108a8576108a861084d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156108ee576108ee61084d565b8160405283815286602085880101111561090757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561093d57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561096357600080fd5b61096f8883890161087c565b9350606087013591508082111561098557600080fd5b506109928782880161087c565b91505092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff811681146109c257600080fd5b919050565b6000806000606084860312156109dc57600080fd5b6109e58461099e565b9250602084013567ffffffffffffffff811115610a0157600080fd5b610a0d8682870161087c565b925050604084013590509250925092565b60008060408385031215610a3157600080fd5b50508035926020909101359150565b600080600060608486031215610a5557600080fd5b8335925060208401359150604084013567ffffffffffffffff811115610a7a57600080fd5b610a868682870161087c565b9150509250925092565b600060208284031215610aa257600080fd5b6102198261099e565b60005b83811015610ac6578181015183820152602001610aae565b50506000910152565b60008251610ae1818460208701610aab565b9190910192915050565b6020815260008251806020840152610b0a816040850160208701610aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a2646970667358221220b9c8f668b6482fae01777910c9918cfb563dddb115f78f0c84c030100648725964736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "CDKValidiumTimelock",
"sourceName": "contracts/CDKValidiumTimelock.sol",
"abi": [
{
"inputs": [
{
"internalType": "uint256",
"name": "minDelay",
"type": "uint256"
},
{
"internalType": "address[]",
"name": "proposers",
"type": "address[]"
},
{
"internalType": "address[]",
"name": "executors",
"type": "address[]"
},
{
"internalType": "address",
"name": "admin",
"type": "address"
},
{
"internalType": "contract CDKValidium",
"name": "_cdkValidium",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "target",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "CallExecuted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "uint256",
"name": "index",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "target",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "uint256",
"name": "delay",
"type": "uint256"
}
],
"name": "CallScheduled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "Cancelled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "oldDuration",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newDuration",
"type": "uint256"
}
],
"name": "MinDelayChange",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "previousAdminRole",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "newAdminRole",
"type": "bytes32"
}
],
"name": "RoleAdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleGranted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleRevoked",
"type": "event"
},
{
"inputs": [],
"name": "CANCELLER_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DEFAULT_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "EXECUTOR_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PROPOSER_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TIMELOCK_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "cancel",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "cdkValidium",
"outputs": [
{
"internalType": "contract CDKValidium",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "payload",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "execute",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "targets",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "values",
"type": "uint256[]"
},
{
"internalType": "bytes[]",
"name": "payloads",
"type": "bytes[]"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "executeBatch",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "getMinDelay",
"outputs": [
{
"internalType": "uint256",
"name": "duration",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
}
],
"name": "getRoleAdmin",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "getTimestamp",
"outputs": [
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "grantRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "hasRole",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "hashOperation",
"outputs": [
{
"internalType": "bytes32",
"name": "hash",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "targets",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "values",
"type": "uint256[]"
},
{
"internalType": "bytes[]",
"name": "payloads",
"type": "bytes[]"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"name": "hashOperationBatch",
"outputs": [
{
"internalType": "bytes32",
"name": "hash",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "isOperation",
"outputs": [
{
"internalType": "bool",
"name": "registered",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "isOperationDone",
"outputs": [
{
"internalType": "bool",
"name": "done",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "isOperationPending",
"outputs": [
{
"internalType": "bool",
"name": "pending",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "id",
"type": "bytes32"
}
],
"name": "isOperationReady",
"outputs": [
{
"internalType": "bool",
"name": "ready",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256[]",
"name": "",
"type": "uint256[]"
},
{
"internalType": "uint256[]",
"name": "",
"type": "uint256[]"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC1155BatchReceived",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC1155Received",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC721Received",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "renounceRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "revokeRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "target",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "delay",
"type": "uint256"
}
],
"name": "schedule",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "targets",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "values",
"type": "uint256[]"
},
{
"internalType": "bytes[]",
"name": "payloads",
"type": "bytes[]"
},
{
"internalType": "bytes32",
"name": "predecessor",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "delay",
"type": "uint256"
}
],
"name": "scheduleBatch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "newDelay",
"type": "uint256"
}
],
"name": "updateDelay",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
],
"bytecode": "0x60a06040523480156200001157600080fd5b5060405162002d2f38038062002d2f83398101604081905262000034916200042b565b848484846200005360008051602062002caf8339815191528062000242565b6200007d60008051602062002ccf83398151915260008051602062002caf83398151915262000242565b620000a760008051602062002cef83398151915260008051602062002caf83398151915262000242565b620000d160008051602062002d0f83398151915260008051602062002caf83398151915262000242565b620000ec60008051602062002caf833981519152306200028d565b6001600160a01b0381161562000117576200011760008051602062002caf833981519152826200028d565b60005b83518110156200019d576200016160008051602062002ccf8339815191528583815181106200014d576200014d620004cc565b60200260200101516200028d60201b60201c565b6200018a60008051602062002d0f8339815191528583815181106200014d576200014d620004cc565b6200019581620004e2565b90506200011a565b5060005b8251811015620001e757620001d460008051602062002cef8339815191528483815181106200014d576200014d620004cc565b620001df81620004e2565b9050620001a1565b5060028490556040805160008152602081018690527f11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5910160405180910390a1505050506001600160a01b0316608052506200050a92505050565b600082815260208190526040808220600101805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b6200029982826200029d565b5050565b6000828152602081815260408083206001600160a01b038516845290915290205460ff1662000299576000828152602081815260408083206001600160a01b03851684529091529020805460ff19166001179055620002f93390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b634e487b7160e01b600052604160045260246000fd5b6001600160a01b03811681146200036957600080fd5b50565b8051620003798162000353565b919050565b600082601f8301126200039057600080fd5b815160206001600160401b0380831115620003af57620003af6200033d565b8260051b604051601f19603f83011681018181108482111715620003d757620003d76200033d565b604052938452858101830193838101925087851115620003f657600080fd5b83870191505b84821015620004205762000410826200036c565b83529183019190830190620003fc565b979650505050505050565b600080600080600060a086880312156200044457600080fd5b855160208701519095506001600160401b03808211156200046457600080fd5b6200047289838a016200037e565b955060408801519150808211156200048957600080fd5b5062000498888289016200037e565b9350506060860151620004ab8162000353565b6080870151909250620004be8162000353565b809150509295509295909350565b634e487b7160e01b600052603260045260246000fd5b6000600182016200050357634e487b7160e01b600052601160045260246000fd5b5060010190565b60805161277b620005346000396000818161041a0152818161116501526111a5015261277b6000f3fe6080604052600436106101c65760003560e01c806364d62353116100f7578063b1c5f42711610095578063d547741f11610064578063d547741f14610661578063e38335e514610681578063f23a6e6114610694578063f27a0c92146106d957600080fd5b8063b1c5f427146105af578063bc197c81146105cf578063c4d252f514610614578063d45c44351461063457600080fd5b80638f61f4f5116100d15780638f61f4f5146104e157806391d1485414610515578063a217fddf14610566578063b08e51c01461057b57600080fd5b806364d62353146104815780638065657f146104a15780638f2a0bb0146104c157600080fd5b8063248a9ca31161016457806331d507501161013e57806331d50750146103c857806336568abe146103e85780633951b92f14610408578063584b153e1461046157600080fd5b8063248a9ca3146103475780632ab0f529146103775780632f2ff15d146103a857600080fd5b80630d3cf6fc116101a05780630d3cf6fc1461026b578063134008d31461029f57806313bc9f20146102b2578063150b7a02146102d257600080fd5b806301d5062a146101d257806301ffc9a7146101f457806307bd02651461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611c52565b6106ee565b005b34801561020057600080fd5b5061021461020f366004611cc7565b610783565b60405190151581526020015b60405180910390f35b34801561023557600080fd5b5061025d7fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6381565b604051908152602001610220565b34801561027757600080fd5b5061025d7f5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca581565b6101f26102ad366004611d09565b6107df565b3480156102be57600080fd5b506102146102cd366004611d75565b6108d7565b3480156102de57600080fd5b506103166102ed366004611e9a565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610220565b34801561035357600080fd5b5061025d610362366004611d75565b60009081526020819052604090206001015490565b34801561038357600080fd5b50610214610392366004611d75565b6000908152600160208190526040909120541490565b3480156103b457600080fd5b506101f26103c3366004611f02565b6108fd565b3480156103d457600080fd5b506102146103e3366004611d75565b610927565b3480156103f457600080fd5b506101f2610403366004611f02565b610940565b34801561041457600080fd5b5061043c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610220565b34801561046d57600080fd5b5061021461047c366004611d75565b6109f8565b34801561048d57600080fd5b506101f261049c366004611d75565b610a0e565b3480156104ad57600080fd5b5061025d6104bc366004611d09565b610ade565b3480156104cd57600080fd5b506101f26104dc366004611f73565b610b1d565b3480156104ed57600080fd5b5061025d7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc181565b34801561052157600080fd5b50610214610530366004611f02565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b34801561057257600080fd5b5061025d600081565b34801561058757600080fd5b5061025d7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f78381565b3480156105bb57600080fd5b5061025d6105ca366004612025565b610d4f565b3480156105db57600080fd5b506103166105ea36600461214e565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b34801561062057600080fd5b506101f261062f366004611d75565b610d94565b34801561064057600080fd5b5061025d61064f366004611d75565b60009081526001602052604090205490565b34801561066d57600080fd5b506101f261067c366004611f02565b610e8f565b6101f261068f366004612025565b610eb4565b3480156106a057600080fd5b506103166106af3660046121f8565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b3480156106e557600080fd5b5061025d611161565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc161071881611244565b6000610728898989898989610ade565b90506107348184611251565b6000817f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8b8b8b8b8b8a604051610770969594939291906122a6565b60405180910390a3505050505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e00000000000000000000000000000000000000000000000000000000014806107d957506107d98261139e565b92915050565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff1661085c5761085c8133611435565b600061086c888888888888610ade565b905061087881856114ed565b6108848888888861162a565b6000817fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588a8a8a8a6040516108bc94939291906122f1565b60405180910390a36108cd8161172e565b5050505050505050565b6000818152600160205260408120546001811180156108f65750428111155b9392505050565b60008281526020819052604090206001015461091881611244565b61092283836117d7565b505050565b60008181526001602052604081205481905b1192915050565b73ffffffffffffffffffffffffffffffffffffffff811633146109ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6109f482826118c7565b5050565b6000818152600160208190526040822054610939565b333014610a9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f54696d656c6f636b436f6e74726f6c6c65723a2063616c6c6572206d7573742060448201527f62652074696d656c6f636b00000000000000000000000000000000000000000060648201526084016109e1565b60025460408051918252602082018390527f11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5910160405180910390a1600255565b6000868686868686604051602001610afb969594939291906122a6565b6040516020818303038152906040528051906020012090509695505050505050565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1610b4781611244565b888714610bd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b888514610c65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b6000610c778b8b8b8b8b8b8b8b610d4f565b9050610c838184611251565b60005b8a811015610d415780827f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8e8e85818110610cc357610cc3612331565b9050602002016020810190610cd89190612360565b8d8d86818110610cea57610cea612331565b905060200201358c8c87818110610d0357610d03612331565b9050602002810190610d15919061237b565b8c8b604051610d29969594939291906122a6565b60405180910390a3610d3a8161240f565b9050610c86565b505050505050505050505050565b60008888888888888888604051602001610d709897969594939291906124f7565b60405160208183030381529060405280519060200120905098975050505050505050565b7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783610dbe81611244565b610dc7826109f8565b610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603160248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20636160448201527f6e6e6f742062652063616e63656c6c656400000000000000000000000000000060648201526084016109e1565b6000828152600160205260408082208290555183917fbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb7091a25050565b600082815260208190526040902060010154610eaa81611244565b61092283836118c7565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff16610f3157610f318133611435565b878614610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b87841461104f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b60006110618a8a8a8a8a8a8a8a610d4f565b905061106d81856114ed565b60005b8981101561114b5760008b8b8381811061108c5761108c612331565b90506020020160208101906110a19190612360565b905060008a8a848181106110b7576110b7612331565b9050602002013590503660008a8a868181106110d5576110d5612331565b90506020028101906110e7919061237b565b915091506110f78484848461162a565b84867fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588686868660405161112e94939291906122f1565b60405180910390a350505050806111449061240f565b9050611070565b506111558161172e565b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161580159061123257507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561120e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123291906125be565b1561123d5750600090565b5060025490565b61124e8133611435565b50565b61125a82610927565b156112e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20616c60448201527f7265616479207363686564756c6564000000000000000000000000000000000060648201526084016109e1565b6112ef611161565b81101561137e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a20696e73756666696369656e7460448201527f2064656c6179000000000000000000000000000000000000000000000000000060648201526084016109e1565b61138881426125e0565b6000928352600160205260409092209190915550565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806107d957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146107d9565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f4576114738161197e565b61147e83602061199d565b60405160200161148f929190612617565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526109e191600401612698565b6114f6826108d7565b611582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b80158061159e5750600081815260016020819052604090912054145b6109f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a206d697373696e67206465706560448201527f6e64656e6379000000000000000000000000000000000000000000000000000060648201526084016109e1565b60008473ffffffffffffffffffffffffffffffffffffffff168484846040516116549291906126e9565b60006040518083038185875af1925050503d8060008114611691576040519150601f19603f3d011682016040523d82523d6000602084013e611696565b606091505b5050905080611727576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f54696d656c6f636b436f6e74726f6c6c65723a20756e6465726c79696e67207460448201527f72616e73616374696f6e2072657665727465640000000000000000000000000060648201526084016109e1565b5050505050565b611737816108d7565b6117c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b600090815260016020819052604090912055565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556118693390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60606107d973ffffffffffffffffffffffffffffffffffffffff831660145b606060006119ac8360026126f9565b6119b79060026125e0565b67ffffffffffffffff8111156119cf576119cf611d8e565b6040519080825280601f01601f1916602001820160405280156119f9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611a3057611a30612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611a9357611a93612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611acf8460026126f9565b611ada9060016125e0565b90505b6001811115611b77577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110611b1b57611b1b612331565b1a60f81b828281518110611b3157611b31612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93611b7081612710565b9050611add565b5083156108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109e1565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c0457600080fd5b919050565b60008083601f840112611c1b57600080fd5b50813567ffffffffffffffff811115611c3357600080fd5b602083019150836020828501011115611c4b57600080fd5b9250929050565b600080600080600080600060c0888a031215611c6d57600080fd5b611c7688611be0565b965060208801359550604088013567ffffffffffffffff811115611c9957600080fd5b611ca58a828b01611c09565b989b979a50986060810135976080820135975060a09091013595509350505050565b600060208284031215611cd957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146108f657600080fd5b60008060008060008060a08789031215611d2257600080fd5b611d2b87611be0565b955060208701359450604087013567ffffffffffffffff811115611d4e57600080fd5b611d5a89828a01611c09565b979a9699509760608101359660809091013595509350505050565b600060208284031215611d8757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611e0457611e04611d8e565b604052919050565b600082601f830112611e1d57600080fd5b813567ffffffffffffffff811115611e3757611e37611d8e565b611e6860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611dbd565b818152846020838601011115611e7d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215611eb057600080fd5b611eb985611be0565b9350611ec760208601611be0565b925060408501359150606085013567ffffffffffffffff811115611eea57600080fd5b611ef687828801611e0c565b91505092959194509250565b60008060408385031215611f1557600080fd5b82359150611f2560208401611be0565b90509250929050565b60008083601f840112611f4057600080fd5b50813567ffffffffffffffff811115611f5857600080fd5b6020830191508360208260051b8501011115611c4b57600080fd5b600080600080600080600080600060c08a8c031215611f9157600080fd5b893567ffffffffffffffff80821115611fa957600080fd5b611fb58d838e01611f2e565b909b50995060208c0135915080821115611fce57600080fd5b611fda8d838e01611f2e565b909950975060408c0135915080821115611ff357600080fd5b506120008c828d01611f2e565b9a9d999c50979a969997986060880135976080810135975060a0013595509350505050565b60008060008060008060008060a0898b03121561204157600080fd5b883567ffffffffffffffff8082111561205957600080fd5b6120658c838d01611f2e565b909a50985060208b013591508082111561207e57600080fd5b61208a8c838d01611f2e565b909850965060408b01359150808211156120a357600080fd5b506120b08b828c01611f2e565b999c989b509699959896976060870135966080013595509350505050565b600082601f8301126120df57600080fd5b8135602067ffffffffffffffff8211156120fb576120fb611d8e565b8160051b61210a828201611dbd565b928352848101820192828101908785111561212457600080fd5b83870192505b848310156121435782358252918301919083019061212a565b979650505050505050565b600080600080600060a0868803121561216657600080fd5b61216f86611be0565b945061217d60208701611be0565b9350604086013567ffffffffffffffff8082111561219a57600080fd5b6121a689838a016120ce565b945060608801359150808211156121bc57600080fd5b6121c889838a016120ce565b935060808801359150808211156121de57600080fd5b506121eb88828901611e0c565b9150509295509295909350565b600080600080600060a0868803121561221057600080fd5b61221986611be0565b945061222760208701611be0565b93506040860135925060608601359150608086013567ffffffffffffffff81111561225157600080fd5b6121eb88828901611e0c565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a0604082015260006122dc60a08301868861225d565b60608301949094525060800152949350505050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152600061232760608301848661225d565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561237257600080fd5b6108f682611be0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126123b057600080fd5b83018035915067ffffffffffffffff8211156123cb57600080fd5b602001915036819003821315611c4b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612440576124406123e0565b5060010190565b81835260006020808501808196508560051b810191508460005b878110156124ea57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126124a057600080fd5b8701858101903567ffffffffffffffff8111156124bc57600080fd5b8036038213156124cb57600080fd5b6124d686828461225d565b9a87019a9550505090840190600101612461565b5091979650505050505050565b60a0808252810188905260008960c08301825b8b8110156125455773ffffffffffffffffffffffffffffffffffffffff61253084611be0565b1682526020928301929091019060010161250a565b5083810360208501528881527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89111561257e57600080fd5b8860051b9150818a602083013701828103602090810160408501526125a69082018789612447565b60608401959095525050608001529695505050505050565b6000602082840312156125d057600080fd5b815180151581146108f657600080fd5b808201808211156107d9576107d96123e0565b60005b8381101561260e5781810151838201526020016125f6565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161264f8160178501602088016125f3565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161268c8160288401602088016125f3565b01602801949350505050565b60208152600082518060208401526126b78160408501602087016125f3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b8183823760009101908152919050565b80820281158282048414176107d9576107d96123e0565b60008161271f5761271f6123e0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019056fea26469706673582212202598f10b0aa6be57502acbbd2ca4b1684f661f7b9c247fd0e894c5abf7bb71d264736f6c634300081400335f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5b09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1d8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e63fd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783",
"deployedBytecode": "0x6080604052600436106101c65760003560e01c806364d62353116100f7578063b1c5f42711610095578063d547741f11610064578063d547741f14610661578063e38335e514610681578063f23a6e6114610694578063f27a0c92146106d957600080fd5b8063b1c5f427146105af578063bc197c81146105cf578063c4d252f514610614578063d45c44351461063457600080fd5b80638f61f4f5116100d15780638f61f4f5146104e157806391d1485414610515578063a217fddf14610566578063b08e51c01461057b57600080fd5b806364d62353146104815780638065657f146104a15780638f2a0bb0146104c157600080fd5b8063248a9ca31161016457806331d507501161013e57806331d50750146103c857806336568abe146103e85780633951b92f14610408578063584b153e1461046157600080fd5b8063248a9ca3146103475780632ab0f529146103775780632f2ff15d146103a857600080fd5b80630d3cf6fc116101a05780630d3cf6fc1461026b578063134008d31461029f57806313bc9f20146102b2578063150b7a02146102d257600080fd5b806301d5062a146101d257806301ffc9a7146101f457806307bd02651461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611c52565b6106ee565b005b34801561020057600080fd5b5061021461020f366004611cc7565b610783565b60405190151581526020015b60405180910390f35b34801561023557600080fd5b5061025d7fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6381565b604051908152602001610220565b34801561027757600080fd5b5061025d7f5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca581565b6101f26102ad366004611d09565b6107df565b3480156102be57600080fd5b506102146102cd366004611d75565b6108d7565b3480156102de57600080fd5b506103166102ed366004611e9a565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610220565b34801561035357600080fd5b5061025d610362366004611d75565b60009081526020819052604090206001015490565b34801561038357600080fd5b50610214610392366004611d75565b6000908152600160208190526040909120541490565b3480156103b457600080fd5b506101f26103c3366004611f02565b6108fd565b3480156103d457600080fd5b506102146103e3366004611d75565b610927565b3480156103f457600080fd5b506101f2610403366004611f02565b610940565b34801561041457600080fd5b5061043c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610220565b34801561046d57600080fd5b5061021461047c366004611d75565b6109f8565b34801561048d57600080fd5b506101f261049c366004611d75565b610a0e565b3480156104ad57600080fd5b5061025d6104bc366004611d09565b610ade565b3480156104cd57600080fd5b506101f26104dc366004611f73565b610b1d565b3480156104ed57600080fd5b5061025d7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc181565b34801561052157600080fd5b50610214610530366004611f02565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b34801561057257600080fd5b5061025d600081565b34801561058757600080fd5b5061025d7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f78381565b3480156105bb57600080fd5b5061025d6105ca366004612025565b610d4f565b3480156105db57600080fd5b506103166105ea36600461214e565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b34801561062057600080fd5b506101f261062f366004611d75565b610d94565b34801561064057600080fd5b5061025d61064f366004611d75565b60009081526001602052604090205490565b34801561066d57600080fd5b506101f261067c366004611f02565b610e8f565b6101f261068f366004612025565b610eb4565b3480156106a057600080fd5b506103166106af3660046121f8565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b3480156106e557600080fd5b5061025d611161565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc161071881611244565b6000610728898989898989610ade565b90506107348184611251565b6000817f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8b8b8b8b8b8a604051610770969594939291906122a6565b60405180910390a3505050505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e00000000000000000000000000000000000000000000000000000000014806107d957506107d98261139e565b92915050565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff1661085c5761085c8133611435565b600061086c888888888888610ade565b905061087881856114ed565b6108848888888861162a565b6000817fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588a8a8a8a6040516108bc94939291906122f1565b60405180910390a36108cd8161172e565b5050505050505050565b6000818152600160205260408120546001811180156108f65750428111155b9392505050565b60008281526020819052604090206001015461091881611244565b61092283836117d7565b505050565b60008181526001602052604081205481905b1192915050565b73ffffffffffffffffffffffffffffffffffffffff811633146109ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6109f482826118c7565b5050565b6000818152600160208190526040822054610939565b333014610a9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f54696d656c6f636b436f6e74726f6c6c65723a2063616c6c6572206d7573742060448201527f62652074696d656c6f636b00000000000000000000000000000000000000000060648201526084016109e1565b60025460408051918252602082018390527f11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5910160405180910390a1600255565b6000868686868686604051602001610afb969594939291906122a6565b6040516020818303038152906040528051906020012090509695505050505050565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1610b4781611244565b888714610bd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b888514610c65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b6000610c778b8b8b8b8b8b8b8b610d4f565b9050610c838184611251565b60005b8a811015610d415780827f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8e8e85818110610cc357610cc3612331565b9050602002016020810190610cd89190612360565b8d8d86818110610cea57610cea612331565b905060200201358c8c87818110610d0357610d03612331565b9050602002810190610d15919061237b565b8c8b604051610d29969594939291906122a6565b60405180910390a3610d3a8161240f565b9050610c86565b505050505050505050505050565b60008888888888888888604051602001610d709897969594939291906124f7565b60405160208183030381529060405280519060200120905098975050505050505050565b7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783610dbe81611244565b610dc7826109f8565b610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603160248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20636160448201527f6e6e6f742062652063616e63656c6c656400000000000000000000000000000060648201526084016109e1565b6000828152600160205260408082208290555183917fbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb7091a25050565b600082815260208190526040902060010154610eaa81611244565b61092283836118c7565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff16610f3157610f318133611435565b878614610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b87841461104f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b60006110618a8a8a8a8a8a8a8a610d4f565b905061106d81856114ed565b60005b8981101561114b5760008b8b8381811061108c5761108c612331565b90506020020160208101906110a19190612360565b905060008a8a848181106110b7576110b7612331565b9050602002013590503660008a8a868181106110d5576110d5612331565b90506020028101906110e7919061237b565b915091506110f78484848461162a565b84867fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588686868660405161112e94939291906122f1565b60405180910390a350505050806111449061240f565b9050611070565b506111558161172e565b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161580159061123257507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561120e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123291906125be565b1561123d5750600090565b5060025490565b61124e8133611435565b50565b61125a82610927565b156112e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20616c60448201527f7265616479207363686564756c6564000000000000000000000000000000000060648201526084016109e1565b6112ef611161565b81101561137e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a20696e73756666696369656e7460448201527f2064656c6179000000000000000000000000000000000000000000000000000060648201526084016109e1565b61138881426125e0565b6000928352600160205260409092209190915550565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806107d957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146107d9565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f4576114738161197e565b61147e83602061199d565b60405160200161148f929190612617565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526109e191600401612698565b6114f6826108d7565b611582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b80158061159e5750600081815260016020819052604090912054145b6109f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a206d697373696e67206465706560448201527f6e64656e6379000000000000000000000000000000000000000000000000000060648201526084016109e1565b60008473ffffffffffffffffffffffffffffffffffffffff168484846040516116549291906126e9565b60006040518083038185875af1925050503d8060008114611691576040519150601f19603f3d011682016040523d82523d6000602084013e611696565b606091505b5050905080611727576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f54696d656c6f636b436f6e74726f6c6c65723a20756e6465726c79696e67207460448201527f72616e73616374696f6e2072657665727465640000000000000000000000000060648201526084016109e1565b5050505050565b611737816108d7565b6117c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b600090815260016020819052604090912055565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556118693390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60606107d973ffffffffffffffffffffffffffffffffffffffff831660145b606060006119ac8360026126f9565b6119b79060026125e0565b67ffffffffffffffff8111156119cf576119cf611d8e565b6040519080825280601f01601f1916602001820160405280156119f9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611a3057611a30612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611a9357611a93612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611acf8460026126f9565b611ada9060016125e0565b90505b6001811115611b77577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110611b1b57611b1b612331565b1a60f81b828281518110611b3157611b31612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93611b7081612710565b9050611add565b5083156108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109e1565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c0457600080fd5b919050565b60008083601f840112611c1b57600080fd5b50813567ffffffffffffffff811115611c3357600080fd5b602083019150836020828501011115611c4b57600080fd5b9250929050565b600080600080600080600060c0888a031215611c6d57600080fd5b611c7688611be0565b965060208801359550604088013567ffffffffffffffff811115611c9957600080fd5b611ca58a828b01611c09565b989b979a50986060810135976080820135975060a09091013595509350505050565b600060208284031215611cd957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146108f657600080fd5b60008060008060008060a08789031215611d2257600080fd5b611d2b87611be0565b955060208701359450604087013567ffffffffffffffff811115611d4e57600080fd5b611d5a89828a01611c09565b979a9699509760608101359660809091013595509350505050565b600060208284031215611d8757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611e0457611e04611d8e565b604052919050565b600082601f830112611e1d57600080fd5b813567ffffffffffffffff811115611e3757611e37611d8e565b611e6860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611dbd565b818152846020838601011115611e7d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215611eb057600080fd5b611eb985611be0565b9350611ec760208601611be0565b925060408501359150606085013567ffffffffffffffff811115611eea57600080fd5b611ef687828801611e0c565b91505092959194509250565b60008060408385031215611f1557600080fd5b82359150611f2560208401611be0565b90509250929050565b60008083601f840112611f4057600080fd5b50813567ffffffffffffffff811115611f5857600080fd5b6020830191508360208260051b8501011115611c4b57600080fd5b600080600080600080600080600060c08a8c031215611f9157600080fd5b893567ffffffffffffffff80821115611fa957600080fd5b611fb58d838e01611f2e565b909b50995060208c0135915080821115611fce57600080fd5b611fda8d838e01611f2e565b909950975060408c0135915080821115611ff357600080fd5b506120008c828d01611f2e565b9a9d999c50979a969997986060880135976080810135975060a0013595509350505050565b60008060008060008060008060a0898b03121561204157600080fd5b883567ffffffffffffffff8082111561205957600080fd5b6120658c838d01611f2e565b909a50985060208b013591508082111561207e57600080fd5b61208a8c838d01611f2e565b909850965060408b01359150808211156120a357600080fd5b506120b08b828c01611f2e565b999c989b509699959896976060870135966080013595509350505050565b600082601f8301126120df57600080fd5b8135602067ffffffffffffffff8211156120fb576120fb611d8e565b8160051b61210a828201611dbd565b928352848101820192828101908785111561212457600080fd5b83870192505b848310156121435782358252918301919083019061212a565b979650505050505050565b600080600080600060a0868803121561216657600080fd5b61216f86611be0565b945061217d60208701611be0565b9350604086013567ffffffffffffffff8082111561219a57600080fd5b6121a689838a016120ce565b945060608801359150808211156121bc57600080fd5b6121c889838a016120ce565b935060808801359150808211156121de57600080fd5b506121eb88828901611e0c565b9150509295509295909350565b600080600080600060a0868803121561221057600080fd5b61221986611be0565b945061222760208701611be0565b93506040860135925060608601359150608086013567ffffffffffffffff81111561225157600080fd5b6121eb88828901611e0c565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a0604082015260006122dc60a08301868861225d565b60608301949094525060800152949350505050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152600061232760608301848661225d565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561237257600080fd5b6108f682611be0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126123b057600080fd5b83018035915067ffffffffffffffff8211156123cb57600080fd5b602001915036819003821315611c4b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612440576124406123e0565b5060010190565b81835260006020808501808196508560051b810191508460005b878110156124ea57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126124a057600080fd5b8701858101903567ffffffffffffffff8111156124bc57600080fd5b8036038213156124cb57600080fd5b6124d686828461225d565b9a87019a9550505090840190600101612461565b5091979650505050505050565b60a0808252810188905260008960c08301825b8b8110156125455773ffffffffffffffffffffffffffffffffffffffff61253084611be0565b1682526020928301929091019060010161250a565b5083810360208501528881527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89111561257e57600080fd5b8860051b9150818a602083013701828103602090810160408501526125a69082018789612447565b60608401959095525050608001529695505050505050565b6000602082840312156125d057600080fd5b815180151581146108f657600080fd5b808201808211156107d9576107d96123e0565b60005b8381101561260e5781810151838201526020016125f6565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161264f8160178501602088016125f3565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161268c8160288401602088016125f3565b01602801949350505050565b60208152600082518060208401526126b78160408501602087016125f3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b8183823760009101908152919050565b80820281158282048414176107d9576107d96123e0565b60008161271f5761271f6123e0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019056fea26469706673582212202598f10b0aa6be57502acbbd2ca4b1684f661f7b9c247fd0e894c5abf7bb71d264736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "ERC20PermitMock",
"sourceName": "contracts/mocks/ERC20PermitMock.sol",
"abi": [
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "address",
"name": "initialAccount",
"type": "address"
},
{
"internalType": "uint256",
"name": "initialBalance",
"type": "uint256"
}
],
"stateMutability": "payable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "EIP712DOMAIN_HASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "NAME_HASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PERMIT_TYPEHASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "VERSION_HASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approveInternal",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "getChainId",
"outputs": [
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferInternal",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x6080604052604051620018e6380380620018e6833981016040819052620000269162000201565b8383600362000036838262000322565b50600462000045828262000322565b5050506200005a82826200007160201b60201c565b505081516020909201919091206006555062000416565b6001600160a01b038216620000cc5760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b8060026000828254620000e09190620003ee565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b505050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126200016457600080fd5b81516001600160401b03808211156200018157620001816200013c565b604051601f8301601f19908116603f01168101908282118183101715620001ac57620001ac6200013c565b81604052838152602092508683858801011115620001c957600080fd5b600091505b83821015620001ed5785820183015181830184015290820190620001ce565b600093810190920192909252949350505050565b600080600080608085870312156200021857600080fd5b84516001600160401b03808211156200023057600080fd5b6200023e8883890162000152565b955060208701519150808211156200025557600080fd5b50620002648782880162000152565b604087015190945090506001600160a01b03811681146200028457600080fd5b6060959095015193969295505050565b600181811c90821680620002a957607f821691505b602082108103620002ca57634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200013757600081815260208120601f850160051c81016020861015620002f95750805b601f850160051c820191505b818110156200031a5782815560010162000305565b505050505050565b81516001600160401b038111156200033e576200033e6200013c565b62000356816200034f845462000294565b84620002d0565b602080601f8311600181146200038e5760008415620003755750858301515b600019600386901b1c1916600185901b1785556200031a565b600085815260208120601f198616915b82811015620003bf578886015182559484019460019091019084016200039e565b5085821015620003de5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b808201808211156200041057634e487b7160e01b600052601160045260246000fd5b92915050565b6114c080620004266000396000f3fe608060405234801561001057600080fd5b506004361061018d5760003560e01c806340c10f19116100e35780639e4e73181161008c578063c473af3311610066578063c473af33146103d6578063d505accf146103fd578063dd62ed3e1461041057600080fd5b80639e4e731814610389578063a457c2d7146103b0578063a9059cbb146103c357600080fd5b806370a08231116100bd57806370a082311461032b5780637ecebe001461036157806395d89b411461038157600080fd5b806340c10f19146102f257806342966c681461030557806356189cb41461031857600080fd5b806323b872dd116101455780633408e4701161011f5780633408e4701461024c5780633644e5151461025257806339509351146102df57600080fd5b806323b872dd1461020357806330adf81f14610216578063313ce5671461023d57600080fd5b8063095ea7b311610176578063095ea7b3146101c357806318160ddd146101e6578063222f5be0146101ee57600080fd5b806304622c2e1461019257806306fdde03146101ae575b600080fd5b61019b60065481565b6040519081526020015b60405180910390f35b6101b6610456565b6040516101a591906111e1565b6101d66101d1366004611276565b6104e8565b60405190151581526020016101a5565b60025461019b565b6102016101fc3660046112a0565b610502565b005b6101d66102113660046112a0565b610512565b61019b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b604051601281526020016101a5565b4661019b565b61019b6006546000907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f907fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646604080516020810195909552840192909252606083015260808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6101d66102ed366004611276565b610536565b610201610300366004611276565b610582565b6102016103133660046112dc565b610590565b6102016103263660046112a0565b61059d565b61019b6103393660046112f5565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61019b61036f3660046112f5565b60056020526000908152604090205481565b6101b66105a8565b61019b7fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc681565b6101d66103be366004611276565b6105b7565b6101d66103d1366004611276565b61068d565b61019b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b61020161040b366004611317565b61069b565b61019b61041e36600461138a565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b606060038054610465906113bd565b80601f0160208091040260200160405190810160405280929190818152602001828054610491906113bd565b80156104de5780601f106104b3576101008083540402835291602001916104de565b820191906000526020600020905b8154815290600101906020018083116104c157829003601f168201915b5050505050905090565b6000336104f68185856107e2565b60019150505b92915050565b61050d838383610995565b505050565b600033610520858285610c06565b61052b858585610995565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104f6908290869061057d90879061143f565b6107e2565b61058c8282610cd7565b5050565b61059a3382610dca565b50565b61050d8383836107e2565b606060048054610465906113bd565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61052b82868684036107e2565b6000336104f6818585610995565b42841015610705576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f48455a3a3a7065726d69743a20415554485f45585049524544000000000000006044820152606401610677565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a91908661075f83611452565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506107cd8882868686610f8e565b6107d88888886107e2565b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610884576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff8216610927576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316610a38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff8216610adb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610b91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35b50505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c005781811015610cca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610677565b610c0084848484036107e2565b73ffffffffffffffffffffffffffffffffffffffff8216610d54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610677565b8060026000828254610d66919061143f565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610e6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610f23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600654604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602080830191909152818301939093527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a0808301919091528251808303909101815260c08201909252815191909201207f190100000000000000000000000000000000000000000000000000000000000060e083015260e2820181905261010282018690529060009061012201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa1580156110da573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580159061115557508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6107d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f48455a3a3a5f76616c69646174655369676e6564446174613a20494e56414c4960448201527f445f5349474e41545552450000000000000000000000000000000000000000006064820152608401610677565b600060208083528351808285015260005b8181101561120e578581018301518582016040015282016111f2565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461127157600080fd5b919050565b6000806040838503121561128957600080fd5b6112928361124d565b946020939093013593505050565b6000806000606084860312156112b557600080fd5b6112be8461124d565b92506112cc6020850161124d565b9150604084013590509250925092565b6000602082840312156112ee57600080fd5b5035919050565b60006020828403121561130757600080fd5b6113108261124d565b9392505050565b600080600080600080600060e0888a03121561133257600080fd5b61133b8861124d565b96506113496020890161124d565b95506040880135945060608801359350608088013560ff8116811461136d57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561139d57600080fd5b6113a68361124d565b91506113b46020840161124d565b90509250929050565b600181811c908216806113d157607f821691505b60208210810361140a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104fc576104fc611410565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361148357611483611410565b506001019056fea26469706673582212208b36599ef550f0c194373b0c511974c888d8ed7226087368a04dca3579711bcd64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061018d5760003560e01c806340c10f19116100e35780639e4e73181161008c578063c473af3311610066578063c473af33146103d6578063d505accf146103fd578063dd62ed3e1461041057600080fd5b80639e4e731814610389578063a457c2d7146103b0578063a9059cbb146103c357600080fd5b806370a08231116100bd57806370a082311461032b5780637ecebe001461036157806395d89b411461038157600080fd5b806340c10f19146102f257806342966c681461030557806356189cb41461031857600080fd5b806323b872dd116101455780633408e4701161011f5780633408e4701461024c5780633644e5151461025257806339509351146102df57600080fd5b806323b872dd1461020357806330adf81f14610216578063313ce5671461023d57600080fd5b8063095ea7b311610176578063095ea7b3146101c357806318160ddd146101e6578063222f5be0146101ee57600080fd5b806304622c2e1461019257806306fdde03146101ae575b600080fd5b61019b60065481565b6040519081526020015b60405180910390f35b6101b6610456565b6040516101a591906111e1565b6101d66101d1366004611276565b6104e8565b60405190151581526020016101a5565b60025461019b565b6102016101fc3660046112a0565b610502565b005b6101d66102113660046112a0565b610512565b61019b7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b604051601281526020016101a5565b4661019b565b61019b6006546000907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f907fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc646604080516020810195909552840192909252606083015260808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6101d66102ed366004611276565b610536565b610201610300366004611276565b610582565b6102016103133660046112dc565b610590565b6102016103263660046112a0565b61059d565b61019b6103393660046112f5565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b61019b61036f3660046112f5565b60056020526000908152604090205481565b6101b66105a8565b61019b7fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc681565b6101d66103be366004611276565b6105b7565b6101d66103d1366004611276565b61068d565b61019b7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b61020161040b366004611317565b61069b565b61019b61041e36600461138a565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b606060038054610465906113bd565b80601f0160208091040260200160405190810160405280929190818152602001828054610491906113bd565b80156104de5780601f106104b3576101008083540402835291602001916104de565b820191906000526020600020905b8154815290600101906020018083116104c157829003601f168201915b5050505050905090565b6000336104f68185856107e2565b60019150505b92915050565b61050d838383610995565b505050565b600033610520858285610c06565b61052b858585610995565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104f6908290869061057d90879061143f565b6107e2565b61058c8282610cd7565b5050565b61059a3382610dca565b50565b61050d8383836107e2565b606060048054610465906113bd565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610680576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f00000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b61052b82868684036107e2565b6000336104f6818585610995565b42841015610705576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f48455a3a3a7065726d69743a20415554485f45585049524544000000000000006044820152606401610677565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a91908661075f83611452565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506107cd8882868686610f8e565b6107d88888886107e2565b5050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610884576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff8216610927576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8316610a38576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff8216610adb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610b91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35b50505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610c005781811015610cca576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610677565b610c0084848484036107e2565b73ffffffffffffffffffffffffffffffffffffffff8216610d54576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610677565b8060026000828254610d66919061143f565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff8216610e6d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015610f23576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610677565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b600654604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f602080830191909152818301939093527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a0808301919091528251808303909101815260c08201909252815191909201207f190100000000000000000000000000000000000000000000000000000000000060e083015260e2820181905261010282018690529060009061012201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa1580156110da573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff81161580159061115557508773ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b6107d8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f48455a3a3a5f76616c69646174655369676e6564446174613a20494e56414c4960448201527f445f5349474e41545552450000000000000000000000000000000000000000006064820152608401610677565b600060208083528351808285015260005b8181101561120e578581018301518582016040015282016111f2565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461127157600080fd5b919050565b6000806040838503121561128957600080fd5b6112928361124d565b946020939093013593505050565b6000806000606084860312156112b557600080fd5b6112be8461124d565b92506112cc6020850161124d565b9150604084013590509250925092565b6000602082840312156112ee57600080fd5b5035919050565b60006020828403121561130757600080fd5b6113108261124d565b9392505050565b600080600080600080600060e0888a03121561133257600080fd5b61133b8861124d565b96506113496020890161124d565b95506040880135945060608801359350608088013560ff8116811461136d57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561139d57600080fd5b6113a68361124d565b91506113b46020840161124d565b90509250929050565b600181811c908216806113d157607f821691505b60208210810361140a577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104fc576104fc611410565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361148357611483611410565b506001019056fea26469706673582212208b36599ef550f0c194373b0c511974c888d8ed7226087368a04dca3579711bcd64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "FflonkVerifier",
"sourceName": "contracts/verifiers/FflonkVerifier.sol",
"abi": [
{
"inputs": [
{
"internalType": "bytes32[24]",
"name": "proof",
"type": "bytes32[24]"
},
{
"internalType": "uint256[1]",
"name": "pubSignals",
"type": "uint256[1]"
}
],
"name": "verifyProof",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b506159ee80620000216000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80639121da8a14610030575b600080fd5b61004361003e366004615973565b610057565b604051901515815260200160405180910390f35b6000615901565b6040516104c08201518082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e0840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610500840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610520840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001608061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610620840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a0840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610760840151820990508082526102e4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309600114610514576000805260206000f35b8091506020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161076085015183099150806107608501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06106a001850151830991508060a06106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806106a001850151830991508060806106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106a001850151830991508060606106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106a001850151830991508060406106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106a001850151830991508060206106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a085015183099150806106a08501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106200185015183099150806060610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106200185015183099150806040610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106200185015183099150806020610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161062085015183099150806106208501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e061052001850151830991508060e0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c061052001850151830991508060c0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a061052001850151830991508060a0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806105200185015183099150806080610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606105200185015183099150806060610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406105200185015183099150806040610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206105200185015183099150806020610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161052085015183099150806105208501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161050085015183099150806105008501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e085015183099150806104e085015250806104c0840152505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018110610d7a576000805260206000f35b50565b803560208201357f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478085860985090891507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478182099050808214610e08576000805260206000f35b505050565b610e176004610d7d565b610e216044610d7d565b610e2b6084610d7d565b610e3560c4610d7d565b610e4161010435610d49565b610e4d61012435610d49565b610e5961014435610d49565b610e6561016435610d49565b610e7161018435610d49565b610e7d6101a435610d49565b610e896101c435610d49565b610e956101e435610d49565b610ea161020435610d49565b610ead61022435610d49565b610eb961024435610d49565b610ec561026435610d49565b610ed161028435610d49565b610edd6102a435610d49565b610ee96102c435610d49565b610ef56102e435610d49565b565b7f10711a639fed66ba6cd6001188b8fe7285cb9bd01afc1f90598223550aa57e3661078082019081527f28c937a4cb758326763015d30fff3568f5cbed932cdc7c411a435d3de04549ef6107a0830190815283356107c084019081526004356107e085015260243561080085015260a083207f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908190066020808701918252902081900660408601819052845260443590925260643590526060909120819006608083018190529081800960a0830181905260808301517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001910960e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8060e0840151096101008301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f70363660e0840151096101208301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a56660e0840151096101408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000060e0840151096101608301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a18160e0840151096101808301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb60e0840151096101a08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b60e0840151096101c083015260e08201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019080096101e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036366101e0840151096102008301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006101e0840151096102208301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb6101e0840151096102408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08301516101e0840151096102608301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23610260840151096102808301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd610260840151096102a08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f283ce45a2e5b8e4e78f9fbaf5f6a348bfcfaf76dd28e5ca7121b74ef68fdec2e610260840151096102c08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f236102c0840151096102e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd6102c0840151096103008301526102608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019081818009098060c08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018182097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000909101066104a084018190526104c0840152506107808201526101e06101046107a083013761020061078082019081207f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019081900680845282526084356107a084015260a4356107c08401526060918290200691015250565b60e0810151606082015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018160080960e0850151935090507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183810381900683087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105208601526101c08501516101008601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105408601526101a08501516101208601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105608601526101808501516101408601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830961058086015261016085015193507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105a08601526101408501516101808601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105c08601526101208501516101a08601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105e08601526101008501516101c08601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830960e0610520018601525050505050565b6101e0810151606082015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001816004096101e0850151935090507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183810381900683087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096106208601526102408501516102008601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830961064086015261022085015193507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096106608601526102008501516102408601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096060610620018601525050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c0870151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030660c0850151087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610260850151600309096102608301517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181810381900684087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096106a08601526102a08501516102808601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096106c08601526102808501516102a08601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828409840960406106a0018601527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c08801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103067f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c08a015109087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c0880151600309096102c086015190935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096107008601526103008501516102e08601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096107208601526102e08501516103008601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828409840960a06106a0018601525050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806101e08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030682087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102208601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102408601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840882099050806104e08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102608501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306830890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102a08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102e08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840882099050806105008401526127ff8361194e565b61280883611d96565b61281183611fac565b505060c081015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000083018190066301000000096107608401525050610d7a8161005e565b6104a08101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610760840151096107608301525050565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185356107608601510983030106905080610320830152505050565b600160608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306820860e083015190915060009081807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600061052001890151880984098508935061010086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061052001890151880984098508935061012086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061052001890151880984098508935061014086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061052001890151880984098508935061016086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001608061052001890151880984098508935061018086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06105200189015188098409850893506101a086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c06105200189015188098409850893506101c086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e4350983089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e0610520018801518709830984089250505080610340840152505050565b600160608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068208905060006102043561022435610244357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183610104350993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180836101243509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018486096101443509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161018435850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610320870151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c087015185096101e08701519094506000908490827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600002610620018d01518c098509850893508692506102008a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600102610620018d01518c098509850893508692506102208a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600202610620018d01518c098509850893508692506102408a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858509098408925050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600302610620018b01518a09830983089150508061036088015250505050505050565b606081015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309905060017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c08901510908820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306830860c08501519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190817f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b82090990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089150506000915060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c085015160208601510960408501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828408610204350892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160028709086102243508840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160038709086102443508840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161026435840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a43560208b01510908610204350894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c43560208c015109086102243508860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e43560208c015109086102443508860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161028435860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c0870151840992506001610264350394507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610760870151860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c08701518609610260870151909550915060009050807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206000026106a0018a0151880983098308915061028087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001026106a0018a015188098309830891506102a087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206002026106a0018a015188098309830891506102c087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206003026106a0018a015188098309830891506102e087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206004026106a0018a0151880983098308915061030087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206005026106a0018a015188098309830891505080610380870152505050505050565b6040518151815260208201516020820152825160408201526020830151606082015260408260808360066107d05a03fa905080610e08576000805260206000f35b6000604051833581526020840135602082015284604082015260408160608360076107d05a03fa91508161514e576000805260206000f35b825160408201526020830151606082015260408360808360066107d05a03fa9150508061517f576000805260206000f35b50505050565b600060405183815284602082015285604082015260408160608360076107d05a03fa9150816151b8576000805260206000f35b825160408201526020830151606082015260408360808360066107d05a03fa915050806151e9576000805260206000f35b5050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060e08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030682087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101208601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101408601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101608601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806104e0850151830984510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610500850151830984517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908009097f10711a639fed66ba6cd6001188b8fe7285cb9bd01afc1f90598223550aa57e366103a08501527f28c937a4cb758326763015d30fff3568f5cbed932cdc7c411a435d3de04549ef60206103a0018501526156408360046103a08701615116565b6156508160446103a08701615116565b6156dd7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161038088015185097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161036089015188090861034087015108600260016103e08801615185565b50610e088160846104208601615116565b604051610400820180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd479081038190069091526104408301805182039190910690526000906157466103e084016103a085016150d5565b61575861042084016103a085016150d5565b61576c606084015160c46103a08601615116565b6103a083015181526103c08301516020808301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a083015260c43560c08301527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e43581030660e08301527f26186a2d65ee4d2f9c9a5b91f86597d35f192cd120caf7e935d8443d1938e23d6101008301527f30441fd1b5d3370482c42152a8899027716989a6996c2535bc9f7fee8aaef79e6101208301527f1970ea81dd6992adfbc571effb03503adbbb6a857f578403c6c40e22d65b3c026101408301527f054793348f12c0cf5622c340573cb277586319de359ab9389778f689786b1e48610160830152816101808160086107d05a03fa90511692915050565b6040516107808101604052615914610e0d565b61591e8382610ef7565b615927816123b7565b61593081612878565b61593a83826128b1565b61594381612911565b61594c81613e61565b61595581614559565b61595e816151f0565b615967816156ee565b90508060005260206000f35b60008061032080848603121561598857600080fd5b61030084018581111561599a57600080fd5b8493508582860111156159ac57600080fd5b8092505050925092905056fea26469706673582212206422aa8659e1f535f6e105d19c28cea4f8dd509a371ec1b475975112bc7759de64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80639121da8a14610030575b600080fd5b61004361003e366004615973565b610057565b604051901515815260200160405180910390f35b6000615901565b6040516104c08201518082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e0840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610500840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610520840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001608061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e061052001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610620840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061062001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a0840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06106a001840151820990508082526020820191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610760840151820990508082526102e4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309600114610514576000805260206000f35b8091506020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161076085015183099150806107608501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06106a001850151830991508060a06106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806106a001850151830991508060806106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106a001850151830991508060606106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106a001850151830991508060406106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106a001850151830991508060206106a0018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016106a085015183099150806106a08501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606106200185015183099150806060610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406106200185015183099150806040610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206106200185015183099150806020610620018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161062085015183099150806106208501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e061052001850151830991508060e0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c061052001850151830991508060c0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a061052001850151830991508060a0610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160806105200185015183099150806080610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160606105200185015183099150806060610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160406105200185015183099150806040610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206105200185015183099150806020610520018501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161052085015183099150806105208501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161050085015183099150806105008501526020830392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018351830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e085015183099150806104e085015250806104c0840152505050565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018110610d7a576000805260206000f35b50565b803560208201357f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478085860985090891507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478182099050808214610e08576000805260206000f35b505050565b610e176004610d7d565b610e216044610d7d565b610e2b6084610d7d565b610e3560c4610d7d565b610e4161010435610d49565b610e4d61012435610d49565b610e5961014435610d49565b610e6561016435610d49565b610e7161018435610d49565b610e7d6101a435610d49565b610e896101c435610d49565b610e956101e435610d49565b610ea161020435610d49565b610ead61022435610d49565b610eb961024435610d49565b610ec561026435610d49565b610ed161028435610d49565b610edd6102a435610d49565b610ee96102c435610d49565b610ef56102e435610d49565b565b7f10711a639fed66ba6cd6001188b8fe7285cb9bd01afc1f90598223550aa57e3661078082019081527f28c937a4cb758326763015d30fff3568f5cbed932cdc7c411a435d3de04549ef6107a0830190815283356107c084019081526004356107e085015260243561080085015260a083207f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908190066020808701918252902081900660408601819052845260443590925260643590526060909120819006608083018190529081800960a0830181905260808301517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001910960e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e8060e0840151096101008301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f70363660e0840151096101208301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a56660e0840151096101408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000060e0840151096101608301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a18160e0840151096101808301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb60e0840151096101a08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b60e0840151096101c083015260e08201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019080096101e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f7036366101e0840151096102008301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000006101e0840151096102208301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb6101e0840151096102408301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a08301516101e0840151096102608301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23610260840151096102808301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd610260840151096102a08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f283ce45a2e5b8e4e78f9fbaf5f6a348bfcfaf76dd28e5ca7121b74ef68fdec2e610260840151096102c08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f236102c0840151096102e08301527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000177b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd6102c0840151096103008301526102608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019081818009098060c08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018182097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000909101066104a084018190526104c0840152506107808201526101e06101046107a083013761020061078082019081207f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019081900680845282526084356107a084015260a4356107c08401526060918290200691015250565b60e0810151606082015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018160080960e0850151935090507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183810381900683087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105208601526101c08501516101008601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105408601526101a08501516101208601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105608601526101808501516101408601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830961058086015261016085015193507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105a08601526101408501516101808601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105c08601526101208501516101a08601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096105e08601526101008501516101c08601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830960e0610520018601525050505050565b6101e0810151606082015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001816004096101e0850151935090507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183810381900683087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096106208601526102408501516102008601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828609830961064086015261022085015193507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001848103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096106608601526102008501516102408601519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006840890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082860983096060610620018601525050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c0870151097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030660c0850151087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610260850151600309096102608301517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181810381900684087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096106a08601526102a08501516102808601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096106c08601526102808501516102a08601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828409840960406106a0018601527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c08801517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103067f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c08a015109087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c0880151600309096102c086015190935091507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096107008601526103008501516102e08601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082840984096107208601526102e08501516103008601519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908103819006850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828409840960a06106a0018601525050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806101e08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030682087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102208601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102408601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840882099050806104e08401527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102608501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306830890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102808601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102a08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102e08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840882099050806105008401526127ff8361194e565b61280883611d96565b61281183611fac565b505060c081015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000083018190066301000000096107608401525050610d7a8161005e565b6104a08101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610760840151096107608301525050565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185356107608601510983030106905080610320830152505050565b600160608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306820860e083015190915060009081807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600061052001890151880984098508935061010086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001602061052001890151880984098508935061012086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001604061052001890151880984098508935061014086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001606061052001890151880984098508935061016086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001608061052001890151880984098508935061018086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a06105200189015188098409850893506101a086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c06105200189015188098409850893506101c086015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180846101243509610104350891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101443509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101843509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101a43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101c43509830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101e4350983089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160e0610520018801518709830984089250505080610340840152505050565b600160608201517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068208905060006102043561022435610244357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183610104350993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180836101243509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018486096101443509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180826101643509850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161018435850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610320870151850893507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c087015185096101e08701519094506000908490827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600002610620018d01518c098509850893508692506102008a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600102610620018d01518c098509850893508692506102208a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850909840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600202610620018d01518c098509850893508692506102408a015191507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878409840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180868309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858509098408925050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016020600302610620018b01518a09830983089150508061036088015250505050505050565b606081015160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309905060017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b60c08901510908820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306830860c08501519092507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190817f0c9fabc7845d50d2852e2a0371c6441f145e0db82e8326961c25f1e3e32b045b82090990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089150506000915060007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160c085015160208601510960408501517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180828408610204350892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160028709086102243508840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160038709086102443508840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161026435840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a43560208b01510908610204350894507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c43560208c015109086102243508860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101e43560208c015109086102443508860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161028435860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010306840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c0870151840992506001610264350394507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610760870151860994507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c08701518609610260870151909550915060009050807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206000026106a0018a0151880983098308915061028087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206001026106a0018a015188098309830891506102a087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878509610264350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206002026106a0018a015188098309830891506102c087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206003026106a0018a015188098309830891506102e087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206004026106a0018a0151880983098308915061030087015192507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102a4358509610284350890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806102c4357f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186870909820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160206005026106a0018a015188098309830891505080610380870152505050505050565b6040518151815260208201516020820152825160408201526020830151606082015260408260808360066107d05a03fa905080610e08576000805260206000f35b6000604051833581526020840135602082015284604082015260408160608360076107d05a03fa91508161514e576000805260206000f35b825160408201526020830151606082015260408360808360066107d05a03fa9150508061517f576000805260206000f35b50505050565b600060405183815284602082015285604082015260408160608360076107d05a03fa9150816151b8576000805260206000f35b825160408201526020830151606082015260408360808360066107d05a03fa915050806151e9576000805260206000f35b5050505050565b60608101517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018060e08401517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001030682087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101008601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101208601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101408601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101608601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101808601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101a08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016101c08601517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103068408820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001806104e0850151830984510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610500850151830984517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001908009097f10711a639fed66ba6cd6001188b8fe7285cb9bd01afc1f90598223550aa57e366103a08501527f28c937a4cb758326763015d30fff3568f5cbed932cdc7c411a435d3de04549ef60206103a0018501526156408360046103a08701615116565b6156508160446103a08701615116565b6156dd7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161038088015185097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161036089015188090861034087015108600260016103e08801615185565b50610e088160846104208601615116565b604051610400820180517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd479081038190069091526104408301805182039190910690526000906157466103e084016103a085016150d5565b61575861042084016103a085016150d5565b61576c606084015160c46103a08601615116565b6103a083015181526103c08301516020808301919091527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c260408301527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed60608301527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b60808301527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a083015260c43560c08301527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e43581030660e08301527f26186a2d65ee4d2f9c9a5b91f86597d35f192cd120caf7e935d8443d1938e23d6101008301527f30441fd1b5d3370482c42152a8899027716989a6996c2535bc9f7fee8aaef79e6101208301527f1970ea81dd6992adfbc571effb03503adbbb6a857f578403c6c40e22d65b3c026101408301527f054793348f12c0cf5622c340573cb277586319de359ab9389778f689786b1e48610160830152816101808160086107d05a03fa90511692915050565b6040516107808101604052615914610e0d565b61591e8382610ef7565b615927816123b7565b61593081612878565b61593a83826128b1565b61594381612911565b61594c81613e61565b61595581614559565b61595e816151f0565b615967816156ee565b90508060005260206000f35b60008061032080848603121561598857600080fd5b61030084018581111561599a57600080fd5b8493508582860111156159ac57600080fd5b8092505050925092905056fea26469706673582212206422aa8659e1f535f6e105d19c28cea4f8dd509a371ec1b475975112bc7759de64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"_format": "hh-sol-artifact-1",
"contractName": "PolygonZkEVMGlobalExitRoot",
"sourceName": "contracts/PolygonZkEVMGlobalExitRoot.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_rollupAddress",
"type": "address"
},
{
"internalType": "address",
"name": "_bridgeAddress",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "OnlyAllowedContracts",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "mainnetExitRoot",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "rollupExitRoot",
"type": "bytes32"
}
],
"name": "UpdateGlobalExitRoot",
"type": "event"
},
{
"inputs": [],
"name": "bridgeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastGlobalExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "globalExitRootMap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastMainnetExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRollupExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rollupAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "newRoot",
"type": "bytes32"
}
],
"name": "updateExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60c060405234801561001057600080fd5b506040516103f83803806103f883398101604081905261002f91610062565b6001600160a01b0391821660a05216608052610095565b80516001600160a01b038116811461005d57600080fd5b919050565b6000806040838503121561007557600080fd5b61007e83610046565b915061008c60208401610046565b90509250929050565b60805160a0516103316100c76000396000818160e901526101bd015260008181610135015261017401526103316000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806333d6247d1161005b57806333d6247d146100c75780633ed691ef146100dc5780635ec6a8df146100e4578063a3c573eb1461013057600080fd5b806301fd904414610082578063257b36321461009e578063319cf735146100be575b600080fd5b61008b60005481565b6040519081526020015b60405180910390f35b61008b6100ac3660046102e2565b60026020526000908152604090205481565b61008b60015481565b6100da6100d53660046102e2565b610157565b005b61008b6102a6565b61010b7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610095565b61010b7f000000000000000000000000000000000000000000000000000000000000000081565b60005460015473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036101a65750600182905581610222565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036101f0576000839055829150610222565b6040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051602080820184905281830185905282518083038401815260609092019092528051910120600090600081815260026020526040812054919250036102a05760008181526002602052604080822042905551849184917f61014378f82a0d809aefaf87a8ac9505b89c321808287a6e7810f29304c1fce39190a35b50505050565b60006102dd600154600054604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b905090565b6000602082840312156102f457600080fd5b503591905056fea264697066735822122066368a84a416778eb0d212b7acdeed4f6b4a8b676fc470e5579fc4c25caed2f364736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c806333d6247d1161005b57806333d6247d146100c75780633ed691ef146100dc5780635ec6a8df146100e4578063a3c573eb1461013057600080fd5b806301fd904414610082578063257b36321461009e578063319cf735146100be575b600080fd5b61008b60005481565b6040519081526020015b60405180910390f35b61008b6100ac3660046102e2565b60026020526000908152604090205481565b61008b60015481565b6100da6100d53660046102e2565b610157565b005b61008b6102a6565b61010b7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610095565b61010b7f000000000000000000000000000000000000000000000000000000000000000081565b60005460015473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036101a65750600182905581610222565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036101f0576000839055829150610222565b6040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051602080820184905281830185905282518083038401815260609092019092528051910120600090600081815260026020526040812054919250036102a05760008181526002602052604080822042905551849184917f61014378f82a0d809aefaf87a8ac9505b89c321808287a6e7810f29304c1fce39190a35b50505050565b60006102dd600154600054604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b905090565b6000602082840312156102f457600080fd5b503591905056fea264697066735822122066368a84a416778eb0d212b7acdeed4f6b4a8b676fc470e5579fc4c25caed2f364736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "PolygonZkEVMGlobalExitRootL2",
"sourceName": "contracts/PolygonZkEVMGlobalExitRootL2.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_bridgeAddress",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "OnlyAllowedContracts",
"type": "error"
},
{
"inputs": [],
"name": "bridgeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "globalExitRootMap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRollupExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "newRoot",
"type": "bytes32"
}
],
"name": "updateExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60a060405234801561001057600080fd5b5060405161024238038061024283398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b6080516101b16100916000396000818160a7015261010601526101b16000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806301fd904414610051578063257b36321461006d57806333d6247d1461008d578063a3c573eb146100a2575b600080fd5b61005a60015481565b6040519081526020015b60405180910390f35b61005a61007b366004610162565b60006020819052908152604090205481565b6100a061009b366004610162565b6100ee565b005b6100c97f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610064565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461015d576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600155565b60006020828403121561017457600080fd5b503591905056fea2646970667358221220ea2171e2c85c8bff947affc409ef6fc6a8fe82fb8c174ddeda988651e595d66564736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806301fd904414610051578063257b36321461006d57806333d6247d1461008d578063a3c573eb146100a2575b600080fd5b61005a60015481565b6040519081526020015b60405180910390f35b61005a61007b366004610162565b60006020819052908152604090205481565b6100a061009b366004610162565b6100ee565b005b6100c97f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610064565b3373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461015d576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600155565b60006020828403121561017457600080fd5b503591905056fea2646970667358221220ea2171e2c85c8bff947affc409ef6fc6a8fe82fb8c174ddeda988651e595d66564736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "PolygonZkEVMGlobalExitRootL2Mock",
"sourceName": "contracts/mocks/PolygonZkEVMGlobalExitRootL2Mock.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_bridgeAddress",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "OnlyAllowedContracts",
"type": "error"
},
{
"inputs": [],
"name": "bridgeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "globalExitRootMap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRollupExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "newRoot",
"type": "bytes32"
}
],
"name": "setExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "globalExitRoot",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "blockNumber",
"type": "uint256"
}
],
"name": "setLastGlobalExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "newRoot",
"type": "bytes32"
}
],
"name": "updateExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60a060405234801561001057600080fd5b506040516102b93803806102b983398101604081905261002f91610040565b6001600160a01b0316608052610070565b60006020828403121561005257600080fd5b81516001600160a01b038116811461006957600080fd5b9392505050565b60805161022761009260003960008181610100015261015f01526102276000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c806333d6247d1161005057806333d6247d146100c857806396e07459146100db578063a3c573eb146100fb57600080fd5b806301fd904414610077578063116c40c314610093578063257b3632146100a8575b600080fd5b61008060015481565b6040519081526020015b60405180910390f35b6100a66100a13660046101b6565b600155565b005b6100806100b63660046101b6565b60006020819052908152604090205481565b6100a66100d63660046101b6565b610147565b6100a66100e93660046101cf565b60009182526020829052604090912055565b6101227f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008a565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100a1576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156101c857600080fd5b5035919050565b600080604083850312156101e257600080fd5b5050803592602090910135915056fea26469706673582212203dc131ea7b3c9a0e0d9f97c222b405f0086fc9beecf67bf79956cd48dcb6461d64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100725760003560e01c806333d6247d1161005057806333d6247d146100c857806396e07459146100db578063a3c573eb146100fb57600080fd5b806301fd904414610077578063116c40c314610093578063257b3632146100a8575b600080fd5b61008060015481565b6040519081526020015b60405180910390f35b6100a66100a13660046101b6565b600155565b005b6100806100b63660046101b6565b60006020819052908152604090205481565b6100a66100d63660046101b6565b610147565b6100a66100e93660046101cf565b60009182526020829052604090912055565b6101227f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008a565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146100a1576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000602082840312156101c857600080fd5b5035919050565b600080604083850312156101e257600080fd5b5050803592602090910135915056fea26469706673582212203dc131ea7b3c9a0e0d9f97c222b405f0086fc9beecf67bf79956cd48dcb6461d64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "PolygonZkEVMGlobalExitRootMock",
"sourceName": "contracts/mocks/PolygonZkEVMGlobalExitRootMock.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_rollupAddress",
"type": "address"
},
{
"internalType": "address",
"name": "_bridgeAddress",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "OnlyAllowedContracts",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "mainnetExitRoot",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "rollupExitRoot",
"type": "bytes32"
}
],
"name": "UpdateGlobalExitRoot",
"type": "event"
},
{
"inputs": [],
"name": "bridgeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getLastGlobalExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"name": "globalExitRootMap",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastMainnetExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRollupExitRoot",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "rollupAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "globalExitRoot",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "setGlobalExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "setLastGlobalExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "newRoot",
"type": "bytes32"
}
],
"name": "updateExitRoot",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x60c060405234801561001057600080fd5b5060405161049538038061049583398101604081905261002f91610062565b6001600160a01b0391821660a05216608052610095565b80516001600160a01b038116811461005d57600080fd5b919050565b6000806040838503121561007557600080fd5b61007e83610046565b915061008c60208401610046565b90509250929050565b60805160a0516103cd6100c860003960008181610142015261023701526000818161018e01526101ee01526103cd6000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c806333d6247d116100765780635bcef6731161005b5780635bcef6731461011d5780635ec6a8df1461013d578063a3c573eb1461018957600080fd5b806333d6247d146101025780633ed691ef1461011557600080fd5b806301fd9044146100a8578063051a9e28146100c4578063257b3632146100d9578063319cf735146100f9575b600080fd5b6100b160005481565b6040519081526020015b60405180910390f35b6100d76100d236600461035c565b6101b0565b005b6100b16100e736600461035c565b60026020526000908152604090205481565b6100b160015481565b6100d761011036600461035c565b6101d1565b6100b1610320565b6100d761012b366004610375565b60009182526002602052604090912055565b6101647f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bb565b6101647f000000000000000000000000000000000000000000000000000000000000000081565b80600260006101bd610320565b815260208101919091526040016000205550565b60005460015473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610220575060018290558161029c565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361026a57600083905582915061029c565b6040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808201849052818301859052825180830384018152606090920190925280519101206000906000818152600260205260408120549192500361031a5760008181526002602052604080822042905551849184917f61014378f82a0d809aefaf87a8ac9505b89c321808287a6e7810f29304c1fce39190a35b50505050565b6000610357600154600054604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b905090565b60006020828403121561036e57600080fd5b5035919050565b6000806040838503121561038857600080fd5b5050803592602090910135915056fea2646970667358221220eeb4ec189dc407e366db39253f2dd748a5e379bc3232254e15b86d12d0001bc064736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a35760003560e01c806333d6247d116100765780635bcef6731161005b5780635bcef6731461011d5780635ec6a8df1461013d578063a3c573eb1461018957600080fd5b806333d6247d146101025780633ed691ef1461011557600080fd5b806301fd9044146100a8578063051a9e28146100c4578063257b3632146100d9578063319cf735146100f9575b600080fd5b6100b160005481565b6040519081526020015b60405180910390f35b6100d76100d236600461035c565b6101b0565b005b6100b16100e736600461035c565b60026020526000908152604090205481565b6100b160015481565b6100d761011036600461035c565b6101d1565b6100b1610320565b6100d761012b366004610375565b60009182526002602052604090912055565b6101647f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bb565b6101647f000000000000000000000000000000000000000000000000000000000000000081565b80600260006101bd610320565b815260208101919091526040016000205550565b60005460015473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163303610220575060018290558161029c565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361026a57600083905582915061029c565b6040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080516020808201849052818301859052825180830384018152606090920190925280519101206000906000818152600260205260408120549192500361031a5760008181526002602052604080822042905551849184917f61014378f82a0d809aefaf87a8ac9505b89c321808287a6e7810f29304c1fce39190a35b50505050565b6000610357600154600054604080516020808201949094528082019290925280518083038201815260609092019052805191012090565b905090565b60006020828403121561036e57600080fd5b5035919050565b6000806040838503121561038857600080fd5b5050803592602090910135915056fea2646970667358221220eeb4ec189dc407e366db39253f2dd748a5e379bc3232254e15b86d12d0001bc064736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "ProxyAdmin",
"sourceName": "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol",
"abi": [
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"inputs": [
{
"internalType": "contract TransparentUpgradeableProxy",
"name": "proxy",
"type": "address"
},
{
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "changeProxyAdmin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract TransparentUpgradeableProxy",
"name": "proxy",
"type": "address"
}
],
"name": "getProxyAdmin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract TransparentUpgradeableProxy",
"name": "proxy",
"type": "address"
}
],
"name": "getProxyImplementation",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract TransparentUpgradeableProxy",
"name": "proxy",
"type": "address"
},
{
"internalType": "address",
"name": "implementation",
"type": "address"
}
],
"name": "upgrade",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract TransparentUpgradeableProxy",
"name": "proxy",
"type": "address"
},
{
"internalType": "address",
"name": "implementation",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "upgradeAndCall",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6108658061007e6000396000f3fe60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220c9867ffac53151bdb1305d8f5e3e883cd83e5270c7ec09cdc24e837b2e65239064736f6c63430008140033",
"deployedBytecode": "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220c9867ffac53151bdb1305d8f5e3e883cd83e5270c7ec09cdc24e837b2e65239064736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "TokenWrapped",
"sourceName": "contracts/lib/TokenWrapped.sol",
"abi": [
{
"inputs": [
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "symbol",
"type": "string"
},
{
"internalType": "uint8",
"name": "__decimals",
"type": "uint8"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "DOMAIN_SEPARATOR",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DOMAIN_TYPEHASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PERMIT_TYPEHASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "VERSION",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "bridgeAddress",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "subtractedValue",
"type": "uint256"
}
],
"name": "decreaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "deploymentChainId",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "addedValue",
"type": "uint256"
}
],
"name": "increaseAllowance",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "mint",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "nonces",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "deadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
}
],
"name": "permit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
],
"bytecode": "0x6101006040523480156200001257600080fd5b5060405162001b6638038062001b6683398101604081905262000035916200028d565b82826003620000458382620003a1565b506004620000548282620003a1565b50503360c0525060ff811660e052466080819052620000739062000080565b60a052506200046d915050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f620000ad6200012e565b805160209182012060408051808201825260018152603160f81b90840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b6060600380546200013f9062000312565b80601f01602080910402602001604051908101604052809291908181526020018280546200016d9062000312565b8015620001be5780601f106200019257610100808354040283529160200191620001be565b820191906000526020600020905b815481529060010190602001808311620001a057829003601f168201915b5050505050905090565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f057600080fd5b81516001600160401b03808211156200020d576200020d620001c8565b604051601f8301601f19908116603f01168101908282118183101715620002385762000238620001c8565b816040528381526020925086838588010111156200025557600080fd5b600091505b838210156200027957858201830151818301840152908201906200025a565b600093810190920192909252949350505050565b600080600060608486031215620002a357600080fd5b83516001600160401b0380821115620002bb57600080fd5b620002c987838801620001de565b94506020860151915080821115620002e057600080fd5b50620002ef86828701620001de565b925050604084015160ff811681146200030757600080fd5b809150509250925092565b600181811c908216806200032757607f821691505b6020821081036200034857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200039c57600081815260208120601f850160051c81016020861015620003775750805b601f850160051c820191505b81811015620003985782815560010162000383565b5050505b505050565b81516001600160401b03811115620003bd57620003bd620001c8565b620003d581620003ce845462000312565b846200034e565b602080601f8311600181146200040d5760008415620003f45750858301515b600019600386901b1c1916600185901b17855562000398565b600085815260208120601f198616915b828110156200043e578886015182559484019460019091019084016200041d565b50858210156200045d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e0516116aa620004bc6000396000610237015260008181610307015281816105c001526106a70152600061053a015260008181610379015261050401526116aa6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806370a08231116100d8578063a457c2d71161008c578063d505accf11610066578063d505accf1461039b578063dd62ed3e146103ae578063ffa1ad74146103f457600080fd5b8063a457c2d71461034e578063a9059cbb14610361578063cd0d00961461037457600080fd5b806395d89b41116100bd57806395d89b41146102e75780639dc29fac146102ef578063a3c573eb1461030257600080fd5b806370a08231146102915780637ecebe00146102c757600080fd5b806330adf81f1161012f5780633644e515116101145780633644e51514610261578063395093511461026957806340c10f191461027c57600080fd5b806330adf81f14610209578063313ce5671461023057600080fd5b806318160ddd1161016057806318160ddd146101bd57806320606b70146101cf57806323b872dd146101f657600080fd5b806306fdde031461017c578063095ea7b31461019a575b600080fd5b610184610430565b60405161019191906113e4565b60405180910390f35b6101ad6101a8366004611479565b6104c2565b6040519015158152602001610191565b6002545b604051908152602001610191565b6101c17f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b6101ad6102043660046114a3565b6104dc565b6101c17f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610191565b6101c1610500565b6101ad610277366004611479565b61055c565b61028f61028a366004611479565b6105a8565b005b6101c161029f3660046114df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101c16102d53660046114df565b60056020526000908152604090205481565b610184610680565b61028f6102fd366004611479565b61068f565b6103297f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610191565b6101ad61035c366004611479565b61075e565b6101ad61036f366004611479565b61082f565b6101c17f000000000000000000000000000000000000000000000000000000000000000081565b61028f6103a9366004611501565b61083d565b6101c16103bc366004611574565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6101846040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b60606003805461043f906115a7565b80601f016020809104026020016040519081016040528092919081815260200182805461046b906115a7565b80156104b85780601f1061048d576101008083540402835291602001916104b8565b820191906000526020600020905b81548152906001019060200180831161049b57829003601f168201915b5050505050905090565b6000336104d0818585610b73565b60019150505b92915050565b6000336104ea858285610d27565b6104f5858585610dfe565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610537576105324661106d565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104d090829086906105a3908790611629565b610b73565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d4272696467650000000000000000000000000000000060648201526084015b60405180910390fd5b61067c8282611135565b5050565b60606004805461043f906115a7565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610754576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d427269646765000000000000000000000000000000006064820152608401610669565b61067c8282611228565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610669565b6104f58286868403610b73565b6000336104d0818585610dfe565b834211156108cc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f546f6b656e577261707065643a3a7065726d69743a204578706972656420706560448201527f726d6974000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a9190866109268361163c565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610991610500565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015610a55573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610ad057508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610b5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e577261707065643a3a7065726d69743a20496e76616c696420736960448201527f676e6174757265000000000000000000000000000000000000000000000000006064820152608401610669565b610b678a8a8a610b73565b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610c15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610cb8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610df85781811015610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610669565b610df88484848403610b73565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610f44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610ffa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610df8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611098610430565b8051602091820120604080518082018252600181527f310000000000000000000000000000000000000000000000000000000000000090840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff82166111b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610669565b80600260008282546111c49190611629565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff82166112cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015611381576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610d1a565b600060208083528351808285015260005b81811015611411578581018301518582016040015282016113f5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461147457600080fd5b919050565b6000806040838503121561148c57600080fd5b61149583611450565b946020939093013593505050565b6000806000606084860312156114b857600080fd5b6114c184611450565b92506114cf60208501611450565b9150604084013590509250925092565b6000602082840312156114f157600080fd5b6114fa82611450565b9392505050565b600080600080600080600060e0888a03121561151c57600080fd5b61152588611450565b965061153360208901611450565b95506040880135945060608801359350608088013560ff8116811461155757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561158757600080fd5b61159083611450565b915061159e60208401611450565b90509250929050565b600181811c908216806115bb57607f821691505b6020821081036115f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104d6576104d66115fa565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361166d5761166d6115fa565b506001019056fea26469706673582212206fe049be388a1d04319b9913fe6d100c4c8e272fe0f5748eb056bd0622beabcc64736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101775760003560e01c806370a08231116100d8578063a457c2d71161008c578063d505accf11610066578063d505accf1461039b578063dd62ed3e146103ae578063ffa1ad74146103f457600080fd5b8063a457c2d71461034e578063a9059cbb14610361578063cd0d00961461037457600080fd5b806395d89b41116100bd57806395d89b41146102e75780639dc29fac146102ef578063a3c573eb1461030257600080fd5b806370a08231146102915780637ecebe00146102c757600080fd5b806330adf81f1161012f5780633644e515116101145780633644e51514610261578063395093511461026957806340c10f191461027c57600080fd5b806330adf81f14610209578063313ce5671461023057600080fd5b806318160ddd1161016057806318160ddd146101bd57806320606b70146101cf57806323b872dd146101f657600080fd5b806306fdde031461017c578063095ea7b31461019a575b600080fd5b610184610430565b60405161019191906113e4565b60405180910390f35b6101ad6101a8366004611479565b6104c2565b6040519015158152602001610191565b6002545b604051908152602001610191565b6101c17f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b6101ad6102043660046114a3565b6104dc565b6101c17f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610191565b6101c1610500565b6101ad610277366004611479565b61055c565b61028f61028a366004611479565b6105a8565b005b6101c161029f3660046114df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101c16102d53660046114df565b60056020526000908152604090205481565b610184610680565b61028f6102fd366004611479565b61068f565b6103297f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610191565b6101ad61035c366004611479565b61075e565b6101ad61036f366004611479565b61082f565b6101c17f000000000000000000000000000000000000000000000000000000000000000081565b61028f6103a9366004611501565b61083d565b6101c16103bc366004611574565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6101846040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b60606003805461043f906115a7565b80601f016020809104026020016040519081016040528092919081815260200182805461046b906115a7565b80156104b85780601f1061048d576101008083540402835291602001916104b8565b820191906000526020600020905b81548152906001019060200180831161049b57829003601f168201915b5050505050905090565b6000336104d0818585610b73565b60019150505b92915050565b6000336104ea858285610d27565b6104f5858585610dfe565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610537576105324661106d565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104d090829086906105a3908790611629565b610b73565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d4272696467650000000000000000000000000000000060648201526084015b60405180910390fd5b61067c8282611135565b5050565b60606004805461043f906115a7565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610754576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d427269646765000000000000000000000000000000006064820152608401610669565b61067c8282611228565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610669565b6104f58286868403610b73565b6000336104d0818585610dfe565b834211156108cc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f546f6b656e577261707065643a3a7065726d69743a204578706972656420706560448201527f726d6974000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a9190866109268361163c565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610991610500565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015610a55573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610ad057508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610b5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e577261707065643a3a7065726d69743a20496e76616c696420736960448201527f676e6174757265000000000000000000000000000000000000000000000000006064820152608401610669565b610b678a8a8a610b73565b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610c15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610cb8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610df85781811015610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610669565b610df88484848403610b73565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610f44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610ffa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610df8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611098610430565b8051602091820120604080518082018252600181527f310000000000000000000000000000000000000000000000000000000000000090840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff82166111b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610669565b80600260008282546111c49190611629565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff82166112cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015611381576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610d1a565b600060208083528351808285015260005b81811015611411578581018301518582016040015282016113f5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461147457600080fd5b919050565b6000806040838503121561148c57600080fd5b61149583611450565b946020939093013593505050565b6000806000606084860312156114b857600080fd5b6114c184611450565b92506114cf60208501611450565b9150604084013590509250925092565b6000602082840312156114f157600080fd5b6114fa82611450565b9392505050565b600080600080600080600060e0888a03121561151c57600080fd5b61152588611450565b965061153360208901611450565b95506040880135945060608801359350608088013560ff8116811461155757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561158757600080fd5b61159083611450565b915061159e60208401611450565b90509250929050565b600181811c908216806115bb57607f821691505b6020821081036115f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104d6576104d66115fa565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361166d5761166d6115fa565b506001019056fea26469706673582212206fe049be388a1d04319b9913fe6d100c4c8e272fe0f5748eb056bd0622beabcc64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "TransparentUpgradeableProxy",
"sourceName": "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol",
"abi": [
{
"inputs": [
{
"internalType": "address",
"name": "_logic",
"type": "address"
},
{
"internalType": "address",
"name": "admin_",
"type": "address"
},
{
"internalType": "bytes",
"name": "_data",
"type": "bytes"
}
],
"stateMutability": "payable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "previousAdmin",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "AdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "beacon",
"type": "address"
}
],
"name": "BeaconUpgraded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "implementation",
"type": "address"
}
],
"name": "Upgraded",
"type": "event"
},
{
"stateMutability": "payable",
"type": "fallback"
},
{
"inputs": [],
"name": "admin",
"outputs": [
{
"internalType": "address",
"name": "admin_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "changeAdmin",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "implementation",
"outputs": [
{
"internalType": "address",
"name": "implementation_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newImplementation",
"type": "address"
}
],
"name": "upgradeTo",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newImplementation",
"type": "address"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"name": "upgradeToAndCall",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
],
"bytecode": "0x608060405260405162000fa938038062000fa9833981016040819052620000269162000424565b828162000036828260006200004d565b50620000449050826200007f565b50505062000557565b6200005883620000f1565b600082511180620000665750805b156200007a5762000078838362000133565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000c160008051602062000f62833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000ee8162000162565b50565b620000fc8162000200565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200015b838360405180606001604052806027815260200162000f826027913962000297565b9392505050565b6001600160a01b038116620001cd5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b8060008051602062000f628339815191525b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b0381163b6200026f5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001c4565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc620001df565b6060600080856001600160a01b031685604051620002b6919062000504565b600060405180830381855af49150503d8060008114620002f3576040519150601f19603f3d011682016040523d82523d6000602084013e620002f8565b606091505b5090925090506200030c8683838762000316565b9695505050505050565b606083156200038a57825160000362000382576001600160a01b0385163b620003825760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001c4565b508162000396565b6200039683836200039e565b949350505050565b815115620003af5781518083602001fd5b8060405162461bcd60e51b8152600401620001c4919062000522565b80516001600160a01b0381168114620003e357600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200041b57818101518382015260200162000401565b50506000910152565b6000806000606084860312156200043a57600080fd5b6200044584620003cb565b92506200045560208501620003cb565b60408501519092506001600160401b03808211156200047357600080fd5b818601915086601f8301126200048857600080fd5b8151818111156200049d576200049d620003e8565b604051601f8201601f19908116603f01168101908382118183101715620004c857620004c8620003e8565b81604052828152896020848701011115620004e257600080fd5b620004f5836020830160208801620003fe565b80955050505050509250925092565b6000825162000518818460208701620003fe565b9190910192915050565b602081526000825180602084015262000543816040850160208701620003fe565b601f01601f19169190910160400192915050565b6109fb80620005676000396000f3fe60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100a85780638f283970146100e6578063f851a440146101065761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61011b565b005b61006b61011b565b34801561008157600080fd5b5061006b61009036600461086f565b610135565b61006b6100a336600461088a565b61017f565b3480156100b457600080fd5b506100bd6101f3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b5061006b61010136600461086f565b610231565b34801561011257600080fd5b506100bd61025e565b61012361028c565b61013361012e610363565b61036d565b565b61013d610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816040518060200160405280600081525060006103d1565b50565b61017461011b565b610187610391565b73ffffffffffffffffffffffffffffffffffffffff1633036101eb576101e68383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103d1915050565b505050565b6101e661011b565b60006101fd610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610363565b905090565b61022e61011b565b90565b610239610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816103fc565b6000610268610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610391565b610294610391565b73ffffffffffffffffffffffffffffffffffffffff163303610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b600061022161045d565b3660008037600080366000845af43d6000803e80801561038c573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6103da83610485565b6000825111806103e75750805b156101e6576103f683836104d2565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610425610391565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a1610174816104fe565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6103b5565b61048e8161060a565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606104f7838360405180606001604052806027815260200161099f602791396106d5565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81166105a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161035a565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b73ffffffffffffffffffffffffffffffffffffffff81163b6106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e747261637400000000000000000000000000000000000000606482015260840161035a565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105c4565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516106ff9190610931565b600060405180830381855af49150503d806000811461073a576040519150601f19603f3d011682016040523d82523d6000602084013e61073f565b606091505b50915091506107508683838761075a565b9695505050505050565b606083156107f05782516000036107e95773ffffffffffffffffffffffffffffffffffffffff85163b6107e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161035a565b50816107fa565b6107fa8383610802565b949350505050565b8151156108125781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035a919061094d565b803573ffffffffffffffffffffffffffffffffffffffff8116811461086a57600080fd5b919050565b60006020828403121561088157600080fd5b6104f782610846565b60008060006040848603121561089f57600080fd5b6108a884610846565b9250602084013567ffffffffffffffff808211156108c557600080fd5b818601915086601f8301126108d957600080fd5b8135818111156108e857600080fd5b8760208285010111156108fa57600080fd5b6020830194508093505050509250925092565b60005b83811015610928578181015183820152602001610910565b50506000910152565b6000825161094381846020870161090d565b9190910192915050565b602081526000825180602084015261096c81604085016020870161090d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220701a0c26bdd76686e63fc3c65e4f28a20ba3ecc8a60246733c0627e679c9804e64736f6c63430008140033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564",
"deployedBytecode": "0x60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100a85780638f283970146100e6578063f851a440146101065761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61011b565b005b61006b61011b565b34801561008157600080fd5b5061006b61009036600461086f565b610135565b61006b6100a336600461088a565b61017f565b3480156100b457600080fd5b506100bd6101f3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b5061006b61010136600461086f565b610231565b34801561011257600080fd5b506100bd61025e565b61012361028c565b61013361012e610363565b61036d565b565b61013d610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816040518060200160405280600081525060006103d1565b50565b61017461011b565b610187610391565b73ffffffffffffffffffffffffffffffffffffffff1633036101eb576101e68383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250600192506103d1915050565b505050565b6101e661011b565b60006101fd610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610363565b905090565b61022e61011b565b90565b610239610391565b73ffffffffffffffffffffffffffffffffffffffff16330361017757610174816103fc565b6000610268610391565b73ffffffffffffffffffffffffffffffffffffffff16330361022657610221610391565b610294610391565b73ffffffffffffffffffffffffffffffffffffffff163303610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b600061022161045d565b3660008037600080366000845af43d6000803e80801561038c573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b6103da83610485565b6000825111806103e75750805b156101e6576103f683836104d2565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f610425610391565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a1610174816104fe565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6103b5565b61048e8161060a565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606104f7838360405180606001604052806027815260200161099f602791396106d5565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff81166105a1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f6464726573730000000000000000000000000000000000000000000000000000606482015260840161035a565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b73ffffffffffffffffffffffffffffffffffffffff81163b6106ae576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e747261637400000000000000000000000000000000000000606482015260840161035a565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6105c4565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516106ff9190610931565b600060405180830381855af49150503d806000811461073a576040519150601f19603f3d011682016040523d82523d6000602084013e61073f565b606091505b50915091506107508683838761075a565b9695505050505050565b606083156107f05782516000036107e95773ffffffffffffffffffffffffffffffffffffffff85163b6107e9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161035a565b50816107fa565b6107fa8383610802565b949350505050565b8151156108125781518083602001fd5b806040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161035a919061094d565b803573ffffffffffffffffffffffffffffffffffffffff8116811461086a57600080fd5b919050565b60006020828403121561088157600080fd5b6104f782610846565b60008060006040848603121561089f57600080fd5b6108a884610846565b9250602084013567ffffffffffffffff808211156108c557600080fd5b818601915086601f8301126108d957600080fd5b8135818111156108e857600080fd5b8760208285010111156108fa57600080fd5b6020830194508093505050509250925092565b60005b83811015610928578181015183820152602001610910565b50506000910152565b6000825161094381846020870161090d565b9190910192915050565b602081526000825180602084015261096c81604085016020870161090d565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220701a0c26bdd76686e63fc3c65e4f28a20ba3ecc8a60246733c0627e679c9804e64736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
{
"_format": "hh-sol-artifact-1",
"contractName": "VerifierRollupHelperMock",
"sourceName": "contracts/mocks/VerifierRollupHelperMock.sol",
"abi": [
{
"inputs": [
{
"internalType": "bytes32[24]",
"name": "proof",
"type": "bytes32[24]"
},
{
"internalType": "uint256[1]",
"name": "pubSignals",
"type": "uint256[1]"
}
],
"name": "verifyProof",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
}
],
"bytecode": "0x608060405234801561001057600080fd5b50610158806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80639121da8a14610030575b600080fd5b61004661003e366004610089565b600192915050565b604051901515815260200160405180910390f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008061032080848603121561009e57600080fd5b6103008401858111156100b057600080fd5b8493508561031f8601126100c357600080fd5b604051602080820182811067ffffffffffffffff821117156100e7576100e761005a565b6040529286019281888511156100fc57600080fd5b5b8484101561011457833581529281019281016100fd565b50949790965094505050505056fea2646970667358221220a11a5105bc567bff17c1e8f9a53599bf5e54cbcab278c0720598328bbf4639b864736f6c63430008140033",
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80639121da8a14610030575b600080fd5b61004661003e366004610089565b600192915050565b604051901515815260200160405180910390f35b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008061032080848603121561009e57600080fd5b6103008401858111156100b057600080fd5b8493508561031f8601126100c357600080fd5b604051602080820182811067ffffffffffffffff821117156100e7576100e761005a565b6040529286019281888511156100fc57600080fd5b5b8484101561011457833581529281019281016100fd565b50949790965094505050505056fea2646970667358221220a11a5105bc567bff17c1e8f9a53599bf5e54cbcab278c0720598328bbf4639b864736f6c63430008140033",
"linkReferences": {},
"deployedLinkReferences": {}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "./interfaces/ICDKDataCommitteeErrors.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract CDKDataCommittee is
ICDKDataCommitteeErrors, OwnableUpgradeable {
/**
* @notice Struct which will store all the data of the committee members
* @param url string that represents the URL of the member to be used to access the data
* @param addr address of the member that will be used to sign
*/
struct Member {
string url;
address addr;
}
// Size of a signature in bytes
uint internal constant _SIGNATURE_SIZE = 65;
// Size of an address in bytes
uint internal constant _ADDR_SIZE = 20;
// Specifies the required amount of signatures from members in the data availability committee
uint public requiredAmountOfSignatures;
// Hash of the addresses of the committee
bytes32 public committeeHash;
// Register of the members of the committee
Member[] public members;
/**
* @dev Emitted when the committee is updated
* @param committeeHash hash of the addresses of the committee members
*/
event CommitteeUpdated(bytes32 committeeHash);
function initialize() external initializer {
// Initialize OZ contracts
__Ownable_init_unchained();
}
/**
* @notice Allows the admin to setup the members of the committee. Note that:
* The system will require N / M signatures where N => _requiredAmountOfSignatures and M => urls.length
* There must be the same amount of urls than addressess encoded in the addrsBytes
* A member is represented by the url and the address contained in urls[i] and addrsBytes[i*_ADDR_SIZE : i*_ADDR_SIZE + _ADDR_SIZE]
* @param _requiredAmountOfSignatures Required amount of signatures
* @param urls List of urls of the members of the committee
* @param addrsBytes Byte array that contains the addressess of the members of the committee
*/
function setupCommittee(
uint _requiredAmountOfSignatures,
string[] calldata urls,
bytes calldata addrsBytes
) external onlyOwner {
uint membersLength = urls.length;
if (membersLength < _requiredAmountOfSignatures) {
revert TooManyRequiredSignatures();
}
if (addrsBytes.length != membersLength * _ADDR_SIZE) {
revert UnexpectedAddrsBytesLength();
}
delete members;
address lastAddr;
for (uint i = 0; i < membersLength; i++) {
uint currentAddresStartingByte = i * _ADDR_SIZE;
address currentMemberAddr = address(bytes20(addrsBytes[
currentAddresStartingByte :
currentAddresStartingByte + _ADDR_SIZE
]));
if (bytes(urls[i]).length == 0) {
revert EmptyURLNotAllowed();
}
if (lastAddr >= currentMemberAddr) {
revert WrongAddrOrder();
}
lastAddr = currentMemberAddr;
members.push(Member({
url: urls[i],
addr: currentMemberAddr
}));
}
committeeHash = keccak256(addrsBytes);
requiredAmountOfSignatures = _requiredAmountOfSignatures;
emit CommitteeUpdated(committeeHash);
}
function getAmountOfMembers() public view returns(uint256) {
return members.length;
}
/**
* @notice Verifies that the given signedHash has been signed by requiredAmountOfSignatures committee members
* @param signedHash Hash that must have been signed by requiredAmountOfSignatures of committee members
* @param signaturesAndAddrs Byte array containing the signatures and all the addresses of the committee in ascending order
* [signature 0, ..., signature requiredAmountOfSignatures -1, address 0, ... address N]
* note that each ECDSA signatures are used, therefore each one must be 65 bytes
*/
function verifySignatures(
bytes32 signedHash,
bytes calldata signaturesAndAddrs
) external view {
// pre-check: byte array size
uint splitByte = _SIGNATURE_SIZE * requiredAmountOfSignatures;
if(
signaturesAndAddrs.length < splitByte ||
(signaturesAndAddrs.length - splitByte) % _ADDR_SIZE != 0
) {
revert UnexpectedAddrsAndSignaturesSize();
}
// hash the addresses part of the byte array and check that it's equal to committe hash
if (
keccak256(signaturesAndAddrs[splitByte:]) !=
committeeHash
) {
revert UnexpectedCommitteeHash();
}
// recover addresses from signatures and check that are part of the committee
uint lastAddrIndexUsed;
uint addrsLen = (signaturesAndAddrs.length - splitByte) / _ADDR_SIZE;
for (uint i = 0; i < requiredAmountOfSignatures; i++) {
address currentSigner = ECDSA.recover(
signedHash,
signaturesAndAddrs[i*_SIGNATURE_SIZE : i*_SIGNATURE_SIZE + _SIGNATURE_SIZE]
);
bool currentSignerIsPartOfCommittee = false;
for (uint j = lastAddrIndexUsed; j < addrsLen; j++) {
uint currentAddresStartingByte = splitByte + j*_ADDR_SIZE;
address committeeAddr = address(bytes20(signaturesAndAddrs[
currentAddresStartingByte :
currentAddresStartingByte + _ADDR_SIZE
]));
if (committeeAddr == currentSigner) {
lastAddrIndexUsed = j+1;
currentSignerIsPartOfCommittee = true;
break;
}
}
if (!currentSignerIsPartOfCommittee) {
revert CommitteeAddressDoesntExist();
}
}
}
}
\ No newline at end of file
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "./interfaces/IVerifierRollup.sol";
import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "./interfaces/IPolygonZkEVMBridge.sol";
import "./lib/EmergencyManager.sol";
import "./interfaces/ICDKValidiumErrors.sol";
import "./interfaces/ICDKDataCommittee.sol";
/**
* Contract responsible for managing the states and the updates of L2 network.
* There will be a trusted sequencer, which is able to send transactions.
* Any user can force some transaction and the sequencer will have a timeout to add them in the queue.
* The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof.
* The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network.
* To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks.
*/
contract CDKValidium is
OwnableUpgradeable,
EmergencyManager,
ICDKValidiumErrors
{
using SafeERC20Upgradeable for IERC20Upgradeable;
/**
* @notice Struct which will be used to call sequenceBatches
* @param transactionsHash keccak256 hash of the L2 ethereum transactions EIP-155 or pre-EIP-155 with signature:
* EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s
* pre-EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data) || v || r || s
* @param globalExitRoot Global exit root of the batch
* @param timestamp Sequenced timestamp of the batch
* @param minForcedTimestamp Minimum timestamp of the force batch data, empty when non forced batch
*/
struct BatchData {
bytes32 transactionsHash;
bytes32 globalExitRoot;
uint64 timestamp;
uint64 minForcedTimestamp;
}
/**
* @notice Struct which will be used to call sequenceForceBatches
* @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature:
* EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data, chainid, 0, 0,) || v || r || s
* pre-EIP-155: rlp(nonce, gasprice, gasLimit, to, value, data) || v || r || s
* @param globalExitRoot Global exit root of the batch
* @param minForcedTimestamp Indicates the minimum sequenced timestamp of the batch
*/
struct ForcedBatchData {
bytes transactions;
bytes32 globalExitRoot;
uint64 minForcedTimestamp;
}
/**
* @notice Struct which will be stored for every batch sequence
* @param accInputHash Hash chain that contains all the information to process a batch:
* keccak256(bytes32 oldAccInputHash, keccak256(bytes transactions), bytes32 globalExitRoot, uint64 timestamp, address seqAddress)
* @param sequencedTimestamp Sequenced timestamp
* @param previousLastBatchSequenced Previous last batch sequenced before the current one, this is used to properly calculate the fees
*/
struct SequencedBatchData {
bytes32 accInputHash;
uint64 sequencedTimestamp;
uint64 previousLastBatchSequenced;
}
/**
* @notice Struct to store the pending states
* Pending state will be an intermediary state, that after a timeout can be consolidated, which means that will be added
* to the state root mapping, and the global exit root will be updated
* This is a protection mechanism against soundness attacks, that will be turned off in the future
* @param timestamp Timestamp where the pending state is added to the queue
* @param lastVerifiedBatch Last batch verified batch of this pending state
* @param exitRoot Pending exit root
* @param stateRoot Pending state root
*/
struct PendingState {
uint64 timestamp;
uint64 lastVerifiedBatch;
bytes32 exitRoot;
bytes32 stateRoot;
}
/**
* @notice Struct to call initialize, this saves gas because pack the parameters and avoid stack too deep errors.
* @param admin Admin address
* @param trustedSequencer Trusted sequencer address
* @param pendingStateTimeout Pending state timeout
* @param trustedAggregator Trusted aggregator
* @param trustedAggregatorTimeout Trusted aggregator timeout
*/
struct InitializePackedParameters {
address admin;
address trustedSequencer;
uint64 pendingStateTimeout;
address trustedAggregator;
uint64 trustedAggregatorTimeout;
}
// Modulus zkSNARK
uint256 internal constant _RFIELD =
21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Max transactions bytes that can be added in a single batch
// Max keccaks circuit = (2**23 / 155286) * 44 = 2376
// Bytes per keccak = 136
// Minimum Static keccaks batch = 2
// Max bytes allowed = (2376 - 2) * 136 = 322864 bytes - 1 byte padding
// Rounded to 300000 bytes
// In order to process the transaction, the data is approximately hashed twice for ecrecover:
// 300000 bytes / 2 = 150000 bytes
// Since geth pool currently only accepts at maximum 128kb transactions:
// https://github.com/ethereum/go-ethereum/blob/master/core/txpool/txpool.go#L54
// We will limit this length to be compliant with the geth restrictions since our node will use it
// We let 8kb as a sanity margin
uint256 internal constant _MAX_TRANSACTIONS_BYTE_LENGTH = 120000;
// Max force batch transaction length
// This is used to avoid huge calldata attacks, where the attacker call force batches from another contract
uint256 internal constant _MAX_FORCE_BATCH_BYTE_LENGTH = 5000;
// If a sequenced batch exceeds this timeout without being verified, the contract enters in emergency mode
uint64 internal constant _HALT_AGGREGATION_TIMEOUT = 1 weeks;
// Maximum batches that can be verified in one call. It depends on our current metrics
// This should be a protection against someone that tries to generate huge chunk of invalid batches, and we can't prove otherwise before the pending timeout expires
uint64 internal constant _MAX_VERIFY_BATCHES = 1000;
// Max batch multiplier per verification
uint256 internal constant _MAX_BATCH_MULTIPLIER = 12;
// Max batch fee value
uint256 internal constant _MAX_BATCH_FEE = 1000 ether;
// Min value batch fee
uint256 internal constant _MIN_BATCH_FEE = 1 gwei;
// Goldilocks prime field
uint256 internal constant _GOLDILOCKS_PRIME_FIELD = 0xFFFFFFFF00000001; // 2 ** 64 - 2 ** 32 + 1
// Max uint64
uint256 internal constant _MAX_UINT_64 = type(uint64).max; // 0xFFFFFFFFFFFFFFFF
// MATIC token address
IERC20Upgradeable public immutable matic;
// Rollup verifier interface
IVerifierRollup public immutable rollupVerifier;
// Global Exit Root interface
IPolygonZkEVMGlobalExitRoot public immutable globalExitRootManager;
// PolygonZkEVM Bridge Address
IPolygonZkEVMBridge public immutable bridgeAddress;
// CDK Data Committee Address
ICDKDataCommittee public immutable dataCommitteeAddress;
// L2 chain identifier
uint64 public immutable chainID;
// L2 chain identifier
uint64 public immutable forkID;
// Time target of the verification of a batch
// Adaptatly the batchFee will be updated to achieve this target
uint64 public verifyBatchTimeTarget;
// Batch fee multiplier with 3 decimals that goes from 1000 - 1023
uint16 public multiplierBatchFee;
// Trusted sequencer address
address public trustedSequencer;
// Current matic fee per batch sequenced
uint256 public batchFee;
// Queue of forced batches with their associated data
// ForceBatchNum --> hashedForcedBatchData
// hashedForcedBatchData: hash containing the necessary information to force a batch:
// keccak256(keccak256(bytes transactions), bytes32 globalExitRoot, unint64 minForcedTimestamp)
mapping(uint64 => bytes32) public forcedBatches;
// Queue of batches that defines the virtual state
// SequenceBatchNum --> SequencedBatchData
mapping(uint64 => SequencedBatchData) public sequencedBatches;
// Last sequenced timestamp
uint64 public lastTimestamp;
// Last batch sent by the sequencers
uint64 public lastBatchSequenced;
// Last forced batch included in the sequence
uint64 public lastForceBatchSequenced;
// Last forced batch
uint64 public lastForceBatch;
// Last batch verified by the aggregators
uint64 public lastVerifiedBatch;
// Trusted aggregator address
address public trustedAggregator;
// State root mapping
// BatchNum --> state root
mapping(uint64 => bytes32) public batchNumToStateRoot;
// Trusted sequencer URL
string public trustedSequencerURL;
// L2 network name
string public networkName;
// Pending state mapping
// pendingStateNumber --> PendingState
mapping(uint256 => PendingState) public pendingStateTransitions;
// Last pending state
uint64 public lastPendingState;
// Last pending state consolidated
uint64 public lastPendingStateConsolidated;
// Once a pending state exceeds this timeout it can be consolidated
uint64 public pendingStateTimeout;
// Trusted aggregator timeout, if a sequence is not verified in this time frame,
// everyone can verify that sequence
uint64 public trustedAggregatorTimeout;
// Address that will be able to adjust contract parameters or stop the emergency state
address public admin;
// This account will be able to accept the admin role
address public pendingAdmin;
// Force batch timeout
uint64 public forceBatchTimeout;
// Indicates if forced batches are disallowed
bool public isForcedBatchDisallowed;
/**
* @dev Emitted when the trusted sequencer sends a new batch of transactions
*/
event SequenceBatches(uint64 indexed numBatch);
/**
* @dev Emitted when a batch is forced
*/
event ForceBatch(
uint64 indexed forceBatchNum,
bytes32 lastGlobalExitRoot,
address sequencer,
bytes transactions
);
/**
* @dev Emitted when forced batches are sequenced by not the trusted sequencer
*/
event SequenceForceBatches(uint64 indexed numBatch);
/**
* @dev Emitted when a aggregator verifies batches
*/
event VerifyBatches(
uint64 indexed numBatch,
bytes32 stateRoot,
address indexed aggregator
);
/**
* @dev Emitted when the trusted aggregator verifies batches
*/
event VerifyBatchesTrustedAggregator(
uint64 indexed numBatch,
bytes32 stateRoot,
address indexed aggregator
);
/**
* @dev Emitted when pending state is consolidated
*/
event ConsolidatePendingState(
uint64 indexed numBatch,
bytes32 stateRoot,
uint64 indexed pendingStateNum
);
/**
* @dev Emitted when the admin updates the trusted sequencer address
*/
event SetTrustedSequencer(address newTrustedSequencer);
/**
* @dev Emitted when the admin updates the sequencer URL
*/
event SetTrustedSequencerURL(string newTrustedSequencerURL);
/**
* @dev Emitted when the admin updates the trusted aggregator timeout
*/
event SetTrustedAggregatorTimeout(uint64 newTrustedAggregatorTimeout);
/**
* @dev Emitted when the admin updates the pending state timeout
*/
event SetPendingStateTimeout(uint64 newPendingStateTimeout);
/**
* @dev Emitted when the admin updates the trusted aggregator address
*/
event SetTrustedAggregator(address newTrustedAggregator);
/**
* @dev Emitted when the admin updates the multiplier batch fee
*/
event SetMultiplierBatchFee(uint16 newMultiplierBatchFee);
/**
* @dev Emitted when the admin updates the verify batch timeout
*/
event SetVerifyBatchTimeTarget(uint64 newVerifyBatchTimeTarget);
/**
* @dev Emitted when the admin update the force batch timeout
*/
event SetForceBatchTimeout(uint64 newforceBatchTimeout);
/**
* @dev Emitted when activate force batches
*/
event ActivateForceBatches();
/**
* @dev Emitted when the admin starts the two-step transfer role setting a new pending admin
*/
event TransferAdminRole(address newPendingAdmin);
/**
* @dev Emitted when the pending admin accepts the admin role
*/
event AcceptAdminRole(address newAdmin);
/**
* @dev Emitted when is proved a different state given the same batches
*/
event ProveNonDeterministicPendingState(
bytes32 storedStateRoot,
bytes32 provedStateRoot
);
/**
* @dev Emitted when the trusted aggregator overrides pending state
*/
event OverridePendingState(
uint64 indexed numBatch,
bytes32 stateRoot,
address indexed aggregator
);
/**
* @dev Emitted everytime the forkID is updated, this includes the first initialization of the contract
* This event is intended to be emitted for every upgrade of the contract with relevant changes for the nodes
*/
event UpdateZkEVMVersion(uint64 numBatch, uint64 forkID, string version);
/**
* @param _globalExitRootManager Global exit root manager address
* @param _matic MATIC token address
* @param _rollupVerifier Rollup verifier address
* @param _bridgeAddress Bridge address
* @param _dataCommitteeAddress Data committee address
* @param _chainID L2 chainID
* @param _forkID Fork Id
*/
constructor(
IPolygonZkEVMGlobalExitRoot _globalExitRootManager,
IERC20Upgradeable _matic,
IVerifierRollup _rollupVerifier,
IPolygonZkEVMBridge _bridgeAddress,
ICDKDataCommittee _dataCommitteeAddress,
uint64 _chainID,
uint64 _forkID
) {
globalExitRootManager = _globalExitRootManager;
matic = _matic;
rollupVerifier = _rollupVerifier;
bridgeAddress = _bridgeAddress;
dataCommitteeAddress = _dataCommitteeAddress;
chainID = _chainID;
forkID = _forkID;
}
/**
* @param initializePackedParameters Struct to save gas and avoid stack too deep errors
* @param genesisRoot Rollup genesis root
* @param _trustedSequencerURL Trusted sequencer URL
* @param _networkName L2 network name
*/
function initialize(
InitializePackedParameters calldata initializePackedParameters,
bytes32 genesisRoot,
string memory _trustedSequencerURL,
string memory _networkName,
string calldata _version
) external initializer {
admin = initializePackedParameters.admin;
trustedSequencer = initializePackedParameters.trustedSequencer;
trustedAggregator = initializePackedParameters.trustedAggregator;
batchNumToStateRoot[0] = genesisRoot;
trustedSequencerURL = _trustedSequencerURL;
networkName = _networkName;
// Check initialize parameters
if (
initializePackedParameters.pendingStateTimeout >
_HALT_AGGREGATION_TIMEOUT
) {
revert PendingStateTimeoutExceedHaltAggregationTimeout();
}
pendingStateTimeout = initializePackedParameters.pendingStateTimeout;
if (
initializePackedParameters.trustedAggregatorTimeout >
_HALT_AGGREGATION_TIMEOUT
) {
revert TrustedAggregatorTimeoutExceedHaltAggregationTimeout();
}
trustedAggregatorTimeout = initializePackedParameters
.trustedAggregatorTimeout;
// Constant deployment variables
batchFee = 0.1 ether; // 0.1 Matic
verifyBatchTimeTarget = 30 minutes;
multiplierBatchFee = 1002;
forceBatchTimeout = 5 days;
isForcedBatchDisallowed = true;
// Initialize OZ contracts
__Ownable_init_unchained();
// emit version event
emit UpdateZkEVMVersion(0, forkID, _version);
}
modifier onlyAdmin() {
if (admin != msg.sender) {
revert OnlyAdmin();
}
_;
}
modifier onlyTrustedSequencer() {
if (trustedSequencer != msg.sender) {
revert OnlyTrustedSequencer();
}
_;
}
modifier onlyTrustedAggregator() {
if (trustedAggregator != msg.sender) {
revert OnlyTrustedAggregator();
}
_;
}
modifier isForceBatchAllowed() {
if (isForcedBatchDisallowed) {
revert ForceBatchNotAllowed();
}
_;
}
/////////////////////////////////////
// Sequence/Verify batches functions
////////////////////////////////////
/**
* @notice Allows a sequencer to send multiple batches
* @param batches Struct array which holds the necessary data to append new batches to the sequence
* @param l2Coinbase Address that will receive the fees from L2
* @param signaturesAndAddrs Byte array containing the signatures and all the addresses of the committee in ascending order
* [signature 0, ..., signature requiredAmountOfSignatures -1, address 0, ... address N]
* note that each ECDSA signatures are used, therefore each one must be 65 bytes
*/
function sequenceBatches(
BatchData[] calldata batches,
address l2Coinbase,
bytes calldata signaturesAndAddrs
) external ifNotEmergencyState onlyTrustedSequencer {
uint256 batchesNum = batches.length;
if (batchesNum == 0) {
revert SequenceZeroBatches();
}
if (batchesNum > _MAX_VERIFY_BATCHES) {
revert ExceedMaxVerifyBatches();
}
// Store storage variables in memory, to save gas, because will be overrided multiple times
uint64 currentTimestamp = lastTimestamp;
uint64 currentBatchSequenced = lastBatchSequenced;
uint64 currentLastForceBatchSequenced = lastForceBatchSequenced;
bytes32 currentAccInputHash = sequencedBatches[currentBatchSequenced]
.accInputHash;
// Store in a temporal variable, for avoid access again the storage slot
uint64 initLastForceBatchSequenced = currentLastForceBatchSequenced;
for (uint256 i = 0; i < batchesNum; i++) {
// Load current sequence
BatchData memory currentBatch = batches[i];
// Check if it's a forced batch
if (currentBatch.minForcedTimestamp > 0) {
currentLastForceBatchSequenced++;
// Check forced data matches
bytes32 hashedForcedBatchData = keccak256(
abi.encodePacked(
currentBatch.transactionsHash,
currentBatch.globalExitRoot,
currentBatch.minForcedTimestamp
)
);
if (
hashedForcedBatchData !=
forcedBatches[currentLastForceBatchSequenced]
) {
revert ForcedDataDoesNotMatch();
}
// Delete forceBatch data since won't be used anymore
delete forcedBatches[currentLastForceBatchSequenced];
// Check timestamp is bigger than min timestamp
if (currentBatch.timestamp < currentBatch.minForcedTimestamp) {
revert SequencedTimestampBelowForcedTimestamp();
}
} else {
// Check global exit root exists with proper batch length. These checks are already done in the forceBatches call
// Note that the sequencer can skip setting a global exit root putting zeros
if (
currentBatch.globalExitRoot != bytes32(0) &&
globalExitRootManager.globalExitRootMap(
currentBatch.globalExitRoot
) ==
0
) {
revert GlobalExitRootNotExist();
}
}
// Check Batch timestamps are correct
if (
currentBatch.timestamp < currentTimestamp ||
currentBatch.timestamp > block.timestamp
) {
revert SequencedTimestampInvalid();
}
// Calculate next accumulated input hash
currentAccInputHash = keccak256(
abi.encodePacked(
currentAccInputHash,
currentBatch.transactionsHash,
currentBatch.globalExitRoot,
currentBatch.timestamp,
l2Coinbase
)
);
// Update timestamp
currentTimestamp = currentBatch.timestamp;
}
// Validate that the data committee has signed the accInputHash for this sequence
dataCommitteeAddress.verifySignatures(currentAccInputHash, signaturesAndAddrs);
// Update currentBatchSequenced
currentBatchSequenced += uint64(batchesNum);
// Sanity check, should be unreachable
if (currentLastForceBatchSequenced > lastForceBatch) {
revert ForceBatchesOverflow();
}
uint256 nonForcedBatchesSequenced = batchesNum -
(currentLastForceBatchSequenced - initLastForceBatchSequenced);
// Update sequencedBatches mapping
sequencedBatches[currentBatchSequenced] = SequencedBatchData({
accInputHash: currentAccInputHash,
sequencedTimestamp: uint64(block.timestamp),
previousLastBatchSequenced: lastBatchSequenced
});
// Store back the storage variables
lastTimestamp = currentTimestamp;
lastBatchSequenced = currentBatchSequenced;
if (currentLastForceBatchSequenced != initLastForceBatchSequenced)
lastForceBatchSequenced = currentLastForceBatchSequenced;
// Pay collateral for every non-forced batch submitted
matic.safeTransferFrom(
msg.sender,
address(this),
batchFee * nonForcedBatchesSequenced
);
// Consolidate pending state if possible
_tryConsolidatePendingState();
// Update global exit root if there are new deposits
bridgeAddress.updateGlobalExitRoot();
emit SequenceBatches(currentBatchSequenced);
}
/**
* @notice Allows an aggregator to verify multiple batches
* @param pendingStateNum Init pending state, 0 if consolidated state is used
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function verifyBatches(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external ifNotEmergencyState {
// Check if the trusted aggregator timeout expired,
// Note that the sequencedBatches struct must exists for this finalNewBatch, if not newAccInputHash will be 0
if (
sequencedBatches[finalNewBatch].sequencedTimestamp +
trustedAggregatorTimeout >
block.timestamp
) {
revert TrustedAggregatorTimeoutNotExpired();
}
if (finalNewBatch - initNumBatch > _MAX_VERIFY_BATCHES) {
revert ExceedMaxVerifyBatches();
}
_verifyAndRewardBatches(
pendingStateNum,
initNumBatch,
finalNewBatch,
newLocalExitRoot,
newStateRoot,
proof
);
// Update batch fees
_updateBatchFee(finalNewBatch);
if (pendingStateTimeout == 0) {
// Consolidate state
lastVerifiedBatch = finalNewBatch;
batchNumToStateRoot[finalNewBatch] = newStateRoot;
// Clean pending state if any
if (lastPendingState > 0) {
lastPendingState = 0;
lastPendingStateConsolidated = 0;
}
// Interact with globalExitRootManager
globalExitRootManager.updateExitRoot(newLocalExitRoot);
} else {
// Consolidate pending state if possible
_tryConsolidatePendingState();
// Update pending state
lastPendingState++;
pendingStateTransitions[lastPendingState] = PendingState({
timestamp: uint64(block.timestamp),
lastVerifiedBatch: finalNewBatch,
exitRoot: newLocalExitRoot,
stateRoot: newStateRoot
});
}
emit VerifyBatches(finalNewBatch, newStateRoot, msg.sender);
}
/**
* @notice Allows an aggregator to verify multiple batches
* @param pendingStateNum Init pending state, 0 if consolidated state is used
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function verifyBatchesTrustedAggregator(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external onlyTrustedAggregator {
_verifyAndRewardBatches(
pendingStateNum,
initNumBatch,
finalNewBatch,
newLocalExitRoot,
newStateRoot,
proof
);
// Consolidate state
lastVerifiedBatch = finalNewBatch;
batchNumToStateRoot[finalNewBatch] = newStateRoot;
// Clean pending state if any
if (lastPendingState > 0) {
lastPendingState = 0;
lastPendingStateConsolidated = 0;
}
// Interact with globalExitRootManager
globalExitRootManager.updateExitRoot(newLocalExitRoot);
emit VerifyBatchesTrustedAggregator(
finalNewBatch,
newStateRoot,
msg.sender
);
}
/**
* @notice Verify and reward batches internal function
* @param pendingStateNum Init pending state, 0 if consolidated state is used
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function _verifyAndRewardBatches(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) internal virtual {
bytes32 oldStateRoot;
uint64 currentLastVerifiedBatch = getLastVerifiedBatch();
// Use pending state if specified, otherwise use consolidated state
if (pendingStateNum != 0) {
// Check that pending state exist
// Already consolidated pending states can be used aswell
if (pendingStateNum > lastPendingState) {
revert PendingStateDoesNotExist();
}
// Check choosen pending state
PendingState storage currentPendingState = pendingStateTransitions[
pendingStateNum
];
// Get oldStateRoot from pending batch
oldStateRoot = currentPendingState.stateRoot;
// Check initNumBatch matches the pending state
if (initNumBatch != currentPendingState.lastVerifiedBatch) {
revert InitNumBatchDoesNotMatchPendingState();
}
} else {
// Use consolidated state
oldStateRoot = batchNumToStateRoot[initNumBatch];
if (oldStateRoot == bytes32(0)) {
revert OldStateRootDoesNotExist();
}
// Check initNumBatch is inside the range, sanity check
if (initNumBatch > currentLastVerifiedBatch) {
revert InitNumBatchAboveLastVerifiedBatch();
}
}
// Check final batch
if (finalNewBatch <= currentLastVerifiedBatch) {
revert FinalNumBatchBelowLastVerifiedBatch();
}
// Get snark bytes
bytes memory snarkHashBytes = getInputSnarkBytes(
initNumBatch,
finalNewBatch,
newLocalExitRoot,
oldStateRoot,
newStateRoot
);
// Calulate the snark input
uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD;
// Verify proof
if (!rollupVerifier.verifyProof(proof, [inputSnark])) {
revert InvalidProof();
}
// Get MATIC reward
matic.safeTransfer(
msg.sender,
calculateRewardPerBatch() *
(finalNewBatch - currentLastVerifiedBatch)
);
}
/**
* @notice Internal function to consolidate the state automatically once sequence or verify batches are called
* It tries to consolidate the first and the middle pending state in the queue
*/
function _tryConsolidatePendingState() internal {
// Check if there's any state to consolidate
if (lastPendingState > lastPendingStateConsolidated) {
// Check if it's possible to consolidate the next pending state
uint64 nextPendingState = lastPendingStateConsolidated + 1;
if (isPendingStateConsolidable(nextPendingState)) {
// Check middle pending state ( binary search of 1 step)
uint64 middlePendingState = nextPendingState +
(lastPendingState - nextPendingState) /
2;
// Try to consolidate it, and if not, consolidate the nextPendingState
if (isPendingStateConsolidable(middlePendingState)) {
_consolidatePendingState(middlePendingState);
} else {
_consolidatePendingState(nextPendingState);
}
}
}
}
/**
* @notice Allows to consolidate any pending state that has already exceed the pendingStateTimeout
* Can be called by the trusted aggregator, which can consolidate any state without the timeout restrictions
* @param pendingStateNum Pending state to consolidate
*/
function consolidatePendingState(uint64 pendingStateNum) external {
// Check if pending state can be consolidated
// If trusted aggregator is the sender, do not check the timeout or the emergency state
if (msg.sender != trustedAggregator) {
if (isEmergencyState) {
revert OnlyNotEmergencyState();
}
if (!isPendingStateConsolidable(pendingStateNum)) {
revert PendingStateNotConsolidable();
}
}
_consolidatePendingState(pendingStateNum);
}
/**
* @notice Internal function to consolidate any pending state that has already exceed the pendingStateTimeout
* @param pendingStateNum Pending state to consolidate
*/
function _consolidatePendingState(uint64 pendingStateNum) internal {
// Check if pendingStateNum is in correct range
// - not consolidated (implicity checks that is not 0)
// - exist ( has been added)
if (
pendingStateNum <= lastPendingStateConsolidated ||
pendingStateNum > lastPendingState
) {
revert PendingStateInvalid();
}
PendingState storage currentPendingState = pendingStateTransitions[
pendingStateNum
];
// Update state
uint64 newLastVerifiedBatch = currentPendingState.lastVerifiedBatch;
lastVerifiedBatch = newLastVerifiedBatch;
batchNumToStateRoot[newLastVerifiedBatch] = currentPendingState
.stateRoot;
// Update pending state
lastPendingStateConsolidated = pendingStateNum;
// Interact with globalExitRootManager
globalExitRootManager.updateExitRoot(currentPendingState.exitRoot);
emit ConsolidatePendingState(
newLastVerifiedBatch,
currentPendingState.stateRoot,
pendingStateNum
);
}
/**
* @notice Function to update the batch fee based on the new verified batches
* The batch fee will not be updated when the trusted aggregator verifies batches
* @param newLastVerifiedBatch New last verified batch
*/
function _updateBatchFee(uint64 newLastVerifiedBatch) internal {
uint64 currentLastVerifiedBatch = getLastVerifiedBatch();
uint64 currentBatch = newLastVerifiedBatch;
uint256 totalBatchesAboveTarget;
uint256 newBatchesVerified = newLastVerifiedBatch -
currentLastVerifiedBatch;
uint256 targetTimestamp = block.timestamp - verifyBatchTimeTarget;
while (currentBatch != currentLastVerifiedBatch) {
// Load sequenced batchdata
SequencedBatchData
storage currentSequencedBatchData = sequencedBatches[
currentBatch
];
// Check if timestamp is below the verifyBatchTimeTarget
if (
targetTimestamp < currentSequencedBatchData.sequencedTimestamp
) {
// update currentBatch
currentBatch = currentSequencedBatchData
.previousLastBatchSequenced;
} else {
// The rest of batches will be above
totalBatchesAboveTarget =
currentBatch -
currentLastVerifiedBatch;
break;
}
}
uint256 totalBatchesBelowTarget = newBatchesVerified -
totalBatchesAboveTarget;
// _MAX_BATCH_FEE --> (< 70 bits)
// multiplierBatchFee --> (< 10 bits)
// _MAX_BATCH_MULTIPLIER = 12
// multiplierBatchFee ** _MAX_BATCH_MULTIPLIER --> (< 128 bits)
// batchFee * (multiplierBatchFee ** _MAX_BATCH_MULTIPLIER)-->
// (< 70 bits) * (< 128 bits) = < 256 bits
// Since all the following operations cannot overflow, we can optimize this operations with unchecked
unchecked {
if (totalBatchesBelowTarget < totalBatchesAboveTarget) {
// There are more batches above target, fee is multiplied
uint256 diffBatches = totalBatchesAboveTarget -
totalBatchesBelowTarget;
diffBatches = diffBatches > _MAX_BATCH_MULTIPLIER
? _MAX_BATCH_MULTIPLIER
: diffBatches;
// For every multiplierBatchFee multiplication we must shift 3 zeroes since we have 3 decimals
batchFee =
(batchFee * (uint256(multiplierBatchFee) ** diffBatches)) /
(uint256(1000) ** diffBatches);
} else {
// There are more batches below target, fee is divided
uint256 diffBatches = totalBatchesBelowTarget -
totalBatchesAboveTarget;
diffBatches = diffBatches > _MAX_BATCH_MULTIPLIER
? _MAX_BATCH_MULTIPLIER
: diffBatches;
// For every multiplierBatchFee multiplication we must shift 3 zeroes since we have 3 decimals
uint256 accDivisor = (uint256(1 ether) *
(uint256(multiplierBatchFee) ** diffBatches)) /
(uint256(1000) ** diffBatches);
// multiplyFactor = multiplierBatchFee ** diffBatches / 10 ** (diffBatches * 3)
// accDivisor = 1E18 * multiplyFactor
// 1E18 * batchFee / accDivisor = batchFee / multiplyFactor
// < 60 bits * < 70 bits / ~60 bits --> overflow not possible
batchFee = (uint256(1 ether) * batchFee) / accDivisor;
}
}
// Batch fee must remain inside a range
if (batchFee > _MAX_BATCH_FEE) {
batchFee = _MAX_BATCH_FEE;
} else if (batchFee < _MIN_BATCH_FEE) {
batchFee = _MIN_BATCH_FEE;
}
}
////////////////////////////
// Force batches functions
////////////////////////////
/**
* @notice Allows a sequencer/user to force a batch of L2 transactions.
* This should be used only in extreme cases where the trusted sequencer does not work as expected
* Note The sequencer has certain degree of control on how non-forced and forced batches are ordered
* In order to assure that users force transactions will be processed properly, user must not sign any other transaction
* with the same nonce
* @param transactions L2 ethereum transactions EIP-155 or pre-EIP-155 with signature:
* @param maticAmount Max amount of MATIC tokens that the sender is willing to pay
*/
function forceBatch(
bytes calldata transactions,
uint256 maticAmount
) public isForceBatchAllowed ifNotEmergencyState {
// Calculate matic collateral
uint256 maticFee = getForcedBatchFee();
if (maticFee > maticAmount) {
revert NotEnoughMaticAmount();
}
if (transactions.length > _MAX_FORCE_BATCH_BYTE_LENGTH) {
revert TransactionsLengthAboveMax();
}
matic.safeTransferFrom(msg.sender, address(this), maticFee);
// Get globalExitRoot global exit root
bytes32 lastGlobalExitRoot = globalExitRootManager
.getLastGlobalExitRoot();
// Update forcedBatches mapping
lastForceBatch++;
forcedBatches[lastForceBatch] = keccak256(
abi.encodePacked(
keccak256(transactions),
lastGlobalExitRoot,
uint64(block.timestamp)
)
);
if (msg.sender == tx.origin) {
// Getting the calldata from an EOA is easy so no need to put the `transactions` in the event
emit ForceBatch(lastForceBatch, lastGlobalExitRoot, msg.sender, "");
} else {
// Getting internal transaction calldata is complicated (because it requires an archive node)
// Therefore it's worth it to put the `transactions` in the event, which is easy to query
emit ForceBatch(
lastForceBatch,
lastGlobalExitRoot,
msg.sender,
transactions
);
}
}
/**
* @notice Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period
* @param batches Struct array which holds the necessary data to append force batches
*/
function sequenceForceBatches(
ForcedBatchData[] calldata batches
) external isForceBatchAllowed ifNotEmergencyState {
uint256 batchesNum = batches.length;
if (batchesNum == 0) {
revert SequenceZeroBatches();
}
if (batchesNum > _MAX_VERIFY_BATCHES) {
revert ExceedMaxVerifyBatches();
}
if (
uint256(lastForceBatchSequenced) + batchesNum >
uint256(lastForceBatch)
) {
revert ForceBatchesOverflow();
}
// Store storage variables in memory, to save gas, because will be overrided multiple times
uint64 currentBatchSequenced = lastBatchSequenced;
uint64 currentLastForceBatchSequenced = lastForceBatchSequenced;
bytes32 currentAccInputHash = sequencedBatches[currentBatchSequenced]
.accInputHash;
// Sequence force batches
for (uint256 i = 0; i < batchesNum; i++) {
// Load current sequence
ForcedBatchData memory currentBatch = batches[i];
currentLastForceBatchSequenced++;
// Store the current transactions hash since it's used more than once for gas saving
bytes32 currentTransactionsHash = keccak256(
currentBatch.transactions
);
// Check forced data matches
bytes32 hashedForcedBatchData = keccak256(
abi.encodePacked(
currentTransactionsHash,
currentBatch.globalExitRoot,
currentBatch.minForcedTimestamp
)
);
if (
hashedForcedBatchData !=
forcedBatches[currentLastForceBatchSequenced]
) {
revert ForcedDataDoesNotMatch();
}
// Delete forceBatch data since won't be used anymore
delete forcedBatches[currentLastForceBatchSequenced];
if (i == (batchesNum - 1)) {
// The last batch will have the most restrictive timestamp
if (
currentBatch.minForcedTimestamp + forceBatchTimeout >
block.timestamp
) {
revert ForceBatchTimeoutNotExpired();
}
}
// Calculate next acc input hash
currentAccInputHash = keccak256(
abi.encodePacked(
currentAccInputHash,
currentTransactionsHash,
currentBatch.globalExitRoot,
uint64(block.timestamp),
msg.sender
)
);
}
// Update currentBatchSequenced
currentBatchSequenced += uint64(batchesNum);
lastTimestamp = uint64(block.timestamp);
// Store back the storage variables
sequencedBatches[currentBatchSequenced] = SequencedBatchData({
accInputHash: currentAccInputHash,
sequencedTimestamp: uint64(block.timestamp),
previousLastBatchSequenced: lastBatchSequenced
});
lastBatchSequenced = currentBatchSequenced;
lastForceBatchSequenced = currentLastForceBatchSequenced;
emit SequenceForceBatches(currentBatchSequenced);
}
//////////////////
// admin functions
//////////////////
/**
* @notice Allow the admin to set a new trusted sequencer
* @param newTrustedSequencer Address of the new trusted sequencer
*/
function setTrustedSequencer(
address newTrustedSequencer
) external onlyAdmin {
trustedSequencer = newTrustedSequencer;
emit SetTrustedSequencer(newTrustedSequencer);
}
/**
* @notice Allow the admin to set the trusted sequencer URL
* @param newTrustedSequencerURL URL of trusted sequencer
*/
function setTrustedSequencerURL(
string memory newTrustedSequencerURL
) external onlyAdmin {
trustedSequencerURL = newTrustedSequencerURL;
emit SetTrustedSequencerURL(newTrustedSequencerURL);
}
/**
* @notice Allow the admin to set a new trusted aggregator address
* @param newTrustedAggregator Address of the new trusted aggregator
*/
function setTrustedAggregator(
address newTrustedAggregator
) external onlyAdmin {
trustedAggregator = newTrustedAggregator;
emit SetTrustedAggregator(newTrustedAggregator);
}
/**
* @notice Allow the admin to set a new pending state timeout
* The timeout can only be lowered, except if emergency state is active
* @param newTrustedAggregatorTimeout Trusted aggregator timeout
*/
function setTrustedAggregatorTimeout(
uint64 newTrustedAggregatorTimeout
) external onlyAdmin {
if (newTrustedAggregatorTimeout > _HALT_AGGREGATION_TIMEOUT) {
revert TrustedAggregatorTimeoutExceedHaltAggregationTimeout();
}
if (!isEmergencyState) {
if (newTrustedAggregatorTimeout >= trustedAggregatorTimeout) {
revert NewTrustedAggregatorTimeoutMustBeLower();
}
}
trustedAggregatorTimeout = newTrustedAggregatorTimeout;
emit SetTrustedAggregatorTimeout(newTrustedAggregatorTimeout);
}
/**
* @notice Allow the admin to set a new trusted aggregator timeout
* The timeout can only be lowered, except if emergency state is active
* @param newPendingStateTimeout Trusted aggregator timeout
*/
function setPendingStateTimeout(
uint64 newPendingStateTimeout
) external onlyAdmin {
if (newPendingStateTimeout > _HALT_AGGREGATION_TIMEOUT) {
revert PendingStateTimeoutExceedHaltAggregationTimeout();
}
if (!isEmergencyState) {
if (newPendingStateTimeout >= pendingStateTimeout) {
revert NewPendingStateTimeoutMustBeLower();
}
}
pendingStateTimeout = newPendingStateTimeout;
emit SetPendingStateTimeout(newPendingStateTimeout);
}
/**
* @notice Allow the admin to set a new multiplier batch fee
* @param newMultiplierBatchFee multiplier batch fee
*/
function setMultiplierBatchFee(
uint16 newMultiplierBatchFee
) external onlyAdmin {
if (newMultiplierBatchFee < 1000 || newMultiplierBatchFee > 1023) {
revert InvalidRangeMultiplierBatchFee();
}
multiplierBatchFee = newMultiplierBatchFee;
emit SetMultiplierBatchFee(newMultiplierBatchFee);
}
/**
* @notice Allow the admin to set a new verify batch time target
* This value will only be relevant once the aggregation is decentralized, so
* the trustedAggregatorTimeout should be zero or very close to zero
* @param newVerifyBatchTimeTarget Verify batch time target
*/
function setVerifyBatchTimeTarget(
uint64 newVerifyBatchTimeTarget
) external onlyAdmin {
if (newVerifyBatchTimeTarget > 1 days) {
revert InvalidRangeBatchTimeTarget();
}
verifyBatchTimeTarget = newVerifyBatchTimeTarget;
emit SetVerifyBatchTimeTarget(newVerifyBatchTimeTarget);
}
/**
* @notice Allow the admin to set the forcedBatchTimeout
* The new value can only be lower, except if emergency state is active
* @param newforceBatchTimeout New force batch timeout
*/
function setForceBatchTimeout(
uint64 newforceBatchTimeout
) external onlyAdmin {
if (newforceBatchTimeout > _HALT_AGGREGATION_TIMEOUT) {
revert InvalidRangeForceBatchTimeout();
}
if (!isEmergencyState) {
if (newforceBatchTimeout >= forceBatchTimeout) {
revert InvalidRangeForceBatchTimeout();
}
}
forceBatchTimeout = newforceBatchTimeout;
emit SetForceBatchTimeout(newforceBatchTimeout);
}
/**
* @notice Allow the admin to turn on the force batches
* This action is not reversible
*/
function activateForceBatches() external onlyAdmin {
if (!isForcedBatchDisallowed) {
revert ForceBatchesAlreadyActive();
}
isForcedBatchDisallowed = false;
emit ActivateForceBatches();
}
/**
* @notice Starts the admin role transfer
* This is a two step process, the pending admin must accepted to finalize the process
* @param newPendingAdmin Address of the new pending admin
*/
function transferAdminRole(address newPendingAdmin) external onlyAdmin {
pendingAdmin = newPendingAdmin;
emit TransferAdminRole(newPendingAdmin);
}
/**
* @notice Allow the current pending admin to accept the admin role
*/
function acceptAdminRole() external {
if (pendingAdmin != msg.sender) {
revert OnlyPendingAdmin();
}
admin = pendingAdmin;
emit AcceptAdminRole(pendingAdmin);
}
/////////////////////////////////
// Soundness protection functions
/////////////////////////////////
/**
* @notice Allows the trusted aggregator to override the pending state
* if it's possible to prove a different state root given the same batches
* @param initPendingStateNum Init pending state, 0 if consolidated state is used
* @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function overridePendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external onlyTrustedAggregator {
_proveDistinctPendingState(
initPendingStateNum,
finalPendingStateNum,
initNumBatch,
finalNewBatch,
newLocalExitRoot,
newStateRoot,
proof
);
// Consolidate state state
lastVerifiedBatch = finalNewBatch;
batchNumToStateRoot[finalNewBatch] = newStateRoot;
// Clean pending state if any
if (lastPendingState > 0) {
lastPendingState = 0;
lastPendingStateConsolidated = 0;
}
// Interact with globalExitRootManager
globalExitRootManager.updateExitRoot(newLocalExitRoot);
// Update trusted aggregator timeout to max
trustedAggregatorTimeout = _HALT_AGGREGATION_TIMEOUT;
emit OverridePendingState(finalNewBatch, newStateRoot, msg.sender);
}
/**
* @notice Allows to halt the CDKValidium if its possible to prove a different state root given the same batches
* @param initPendingStateNum Init pending state, 0 if consolidated state is used
* @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function proveNonDeterministicPendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) external ifNotEmergencyState {
_proveDistinctPendingState(
initPendingStateNum,
finalPendingStateNum,
initNumBatch,
finalNewBatch,
newLocalExitRoot,
newStateRoot,
proof
);
emit ProveNonDeterministicPendingState(
batchNumToStateRoot[finalNewBatch],
newStateRoot
);
// Activate emergency state
_activateEmergencyState();
}
/**
* @notice Internal function that proves a different state root given the same batches to verify
* @param initPendingStateNum Init pending state, 0 if consolidated state is used
* @param finalPendingStateNum Final pending state, that will be used to compare with the newStateRoot
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proof fflonk proof
*/
function _proveDistinctPendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] calldata proof
) internal view virtual {
bytes32 oldStateRoot;
// Use pending state if specified, otherwise use consolidated state
if (initPendingStateNum != 0) {
// Check that pending state exist
// Already consolidated pending states can be used aswell
if (initPendingStateNum > lastPendingState) {
revert PendingStateDoesNotExist();
}
// Check choosen pending state
PendingState storage initPendingState = pendingStateTransitions[
initPendingStateNum
];
// Get oldStateRoot from init pending state
oldStateRoot = initPendingState.stateRoot;
// Check initNumBatch matches the init pending state
if (initNumBatch != initPendingState.lastVerifiedBatch) {
revert InitNumBatchDoesNotMatchPendingState();
}
} else {
// Use consolidated state
oldStateRoot = batchNumToStateRoot[initNumBatch];
if (oldStateRoot == bytes32(0)) {
revert OldStateRootDoesNotExist();
}
// Check initNumBatch is inside the range, sanity check
if (initNumBatch > lastVerifiedBatch) {
revert InitNumBatchAboveLastVerifiedBatch();
}
}
// Assert final pending state num is in correct range
// - exist ( has been added)
// - bigger than the initPendingstate
// - not consolidated
if (
finalPendingStateNum > lastPendingState ||
finalPendingStateNum <= initPendingStateNum ||
finalPendingStateNum <= lastPendingStateConsolidated
) {
revert FinalPendingStateNumInvalid();
}
// Check final num batch
if (
finalNewBatch !=
pendingStateTransitions[finalPendingStateNum].lastVerifiedBatch
) {
revert FinalNumBatchDoesNotMatchPendingState();
}
// Get snark bytes
bytes memory snarkHashBytes = getInputSnarkBytes(
initNumBatch,
finalNewBatch,
newLocalExitRoot,
oldStateRoot,
newStateRoot
);
// Calulate the snark input
uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD;
// Verify proof
if (!rollupVerifier.verifyProof(proof, [inputSnark])) {
revert InvalidProof();
}
if (
pendingStateTransitions[finalPendingStateNum].stateRoot ==
newStateRoot
) {
revert StoredRootMustBeDifferentThanNewRoot();
}
}
/**
* @notice Function to activate emergency state, which also enables the emergency mode on both CDKValidium and PolygonZkEVMBridge contracts
* If not called by the owner must be provided a batcnNum that does not have been aggregated in a _HALT_AGGREGATION_TIMEOUT period
* @param sequencedBatchNum Sequenced batch number that has not been aggreagated in _HALT_AGGREGATION_TIMEOUT
*/
function activateEmergencyState(uint64 sequencedBatchNum) external {
if (msg.sender != owner()) {
// Only check conditions if is not called by the owner
uint64 currentLastVerifiedBatch = getLastVerifiedBatch();
// Check that the batch has not been verified
if (sequencedBatchNum <= currentLastVerifiedBatch) {
revert BatchAlreadyVerified();
}
// Check that the batch has been sequenced and this was the end of a sequence
if (
sequencedBatchNum > lastBatchSequenced ||
sequencedBatches[sequencedBatchNum].sequencedTimestamp == 0
) {
revert BatchNotSequencedOrNotSequenceEnd();
}
// Check that has been passed _HALT_AGGREGATION_TIMEOUT since it was sequenced
if (
sequencedBatches[sequencedBatchNum].sequencedTimestamp +
_HALT_AGGREGATION_TIMEOUT >
block.timestamp
) {
revert HaltTimeoutNotExpired();
}
}
_activateEmergencyState();
}
/**
* @notice Function to deactivate emergency state on both CDKValidium and PolygonZkEVMBridge contracts
*/
function deactivateEmergencyState() external onlyAdmin {
// Deactivate emergency state on PolygonZkEVMBridge
bridgeAddress.deactivateEmergencyState();
// Deactivate emergency state on this contract
super._deactivateEmergencyState();
}
/**
* @notice Internal function to activate emergency state on both CDKValidium and PolygonZkEVMBridge contracts
*/
function _activateEmergencyState() internal override {
// Activate emergency state on PolygonZkEVM Bridge
bridgeAddress.activateEmergencyState();
// Activate emergency state on this contract
super._activateEmergencyState();
}
////////////////////////
// public/view functions
////////////////////////
/**
* @notice Get forced batch fee
*/
function getForcedBatchFee() public view returns (uint256) {
return batchFee * 100;
}
/**
* @notice Get the last verified batch
*/
function getLastVerifiedBatch() public view returns (uint64) {
if (lastPendingState > 0) {
return pendingStateTransitions[lastPendingState].lastVerifiedBatch;
} else {
return lastVerifiedBatch;
}
}
/**
* @notice Returns a boolean that indicates if the pendingStateNum is or not consolidable
* Note that his function does not check if the pending state currently exists, or if it's consolidated already
*/
function isPendingStateConsolidable(
uint64 pendingStateNum
) public view returns (bool) {
return (pendingStateTransitions[pendingStateNum].timestamp +
pendingStateTimeout <=
block.timestamp);
}
/**
* @notice Function to calculate the reward to verify a single batch
*/
function calculateRewardPerBatch() public view returns (uint256) {
uint256 currentBalance = matic.balanceOf(address(this));
// Total Sequenced Batches = forcedBatches to be sequenced (total forced Batches - sequenced Batches) + sequencedBatches
// Total Batches to be verified = Total Sequenced Batches - verified Batches
uint256 totalBatchesToVerify = ((lastForceBatch -
lastForceBatchSequenced) + lastBatchSequenced) -
getLastVerifiedBatch();
if (totalBatchesToVerify == 0) return 0;
return currentBalance / totalBatchesToVerify;
}
/**
* @notice Function to calculate the input snark bytes
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param oldStateRoot State root before batch is processed
* @param newStateRoot New State root once the batch is processed
*/
function getInputSnarkBytes(
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 oldStateRoot,
bytes32 newStateRoot
) public view returns (bytes memory) {
// sanity checks
bytes32 oldAccInputHash = sequencedBatches[initNumBatch].accInputHash;
bytes32 newAccInputHash = sequencedBatches[finalNewBatch].accInputHash;
if (initNumBatch != 0 && oldAccInputHash == bytes32(0)) {
revert OldAccInputHashDoesNotExist();
}
if (newAccInputHash == bytes32(0)) {
revert NewAccInputHashDoesNotExist();
}
// Check that new state root is inside goldilocks field
if (!checkStateRootInsidePrime(uint256(newStateRoot))) {
revert NewStateRootNotInsidePrime();
}
return
abi.encodePacked(
msg.sender,
oldStateRoot,
oldAccInputHash,
initNumBatch,
chainID,
forkID,
newStateRoot,
newAccInputHash,
newLocalExitRoot,
finalNewBatch
);
}
function checkStateRootInsidePrime(
uint256 newStateRoot
) public pure returns (bool) {
if (
((newStateRoot & _MAX_UINT_64) < _GOLDILOCKS_PRIME_FIELD) &&
(((newStateRoot >> 64) & _MAX_UINT_64) < _GOLDILOCKS_PRIME_FIELD) &&
(((newStateRoot >> 128) & _MAX_UINT_64) <
_GOLDILOCKS_PRIME_FIELD) &&
((newStateRoot >> 192) < _GOLDILOCKS_PRIME_FIELD)
) {
return true;
} else {
return false;
}
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "@openzeppelin/contracts/governance/TimelockController.sol";
import "./CDKValidium.sol";
/**
* @dev Contract module which acts as a timelocked controller.
* This gives time for users of the controlled contract to exit before a potentially dangerous maintenance operation is applied.
* If emergency mode of the cdkValidium contract system is active, this timelock have no delay.
*/
contract CDKValidiumTimelock is TimelockController {
// CDKValidium address. Will be used to check if it's on emergency state.
CDKValidium public immutable cdkValidium;
/**
* @notice Constructor of timelock
* @param minDelay initial minimum delay for operations
* @param proposers accounts to be granted proposer and canceller roles
* @param executors accounts to be granted executor role
* @param admin optional account to be granted admin role; disable with zero address
* @param _cdkValidium cdkValidium address
**/
constructor(
uint256 minDelay,
address[] memory proposers,
address[] memory executors,
address admin,
CDKValidium _cdkValidium
) TimelockController(minDelay, proposers, executors, admin) {
cdkValidium = _cdkValidium;
}
/**
* @dev Returns the minimum delay for an operation to become valid.
*
* This value can be changed by executing an operation that calls `updateDelay`.
* If CDKValidium is on emergency state the minDelay will be 0 instead.
*/
function getMinDelay() public view override returns (uint256 duration) {
if (address(cdkValidium) != address(0) && cdkValidium.isEmergencyState()) {
return 0;
} else {
return super.getMinDelay();
}
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "./lib/DepositContract.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import "./lib/TokenWrapped.sol";
import "./interfaces/IBasePolygonZkEVMGlobalExitRoot.sol";
import "./interfaces/IBridgeMessageReceiver.sol";
import "./interfaces/IPolygonZkEVMBridge.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol";
import "./lib/EmergencyManager.sol";
import "./lib/GlobalExitRootLib.sol";
/**
* PolygonZkEVMBridge that will be deployed on both networks Ethereum and Polygon zkEVM
* Contract responsible to manage the token interactions with other networks
*/
contract PolygonZkEVMBridge is
DepositContract,
EmergencyManager,
IPolygonZkEVMBridge
{
using SafeERC20Upgradeable for IERC20Upgradeable;
// Wrapped Token information struct
struct TokenInformation {
uint32 originNetwork;
address originTokenAddress;
}
// bytes4(keccak256(bytes("permit(address,address,uint256,uint256,uint8,bytes32,bytes32)")));
bytes4 private constant _PERMIT_SIGNATURE = 0xd505accf;
// bytes4(keccak256(bytes("permit(address,address,uint256,uint256,bool,uint8,bytes32,bytes32)")));
bytes4 private constant _PERMIT_SIGNATURE_DAI = 0x8fcbaf0c;
// Mainnet identifier
uint32 private constant _MAINNET_NETWORK_ID = 0;
// Number of networks supported by the bridge
uint32 private constant _CURRENT_SUPPORTED_NETWORKS = 2;
// Leaf type asset
uint8 private constant _LEAF_TYPE_ASSET = 0;
// Leaf type message
uint8 private constant _LEAF_TYPE_MESSAGE = 1;
// Network identifier
uint32 public networkID;
// Global Exit Root address
IBasePolygonZkEVMGlobalExitRoot public globalExitRootManager;
// Last updated deposit count to the global exit root manager
uint32 public lastUpdatedDepositCount;
// Leaf index --> claimed bit map
mapping(uint256 => uint256) public claimedBitMap;
// keccak256(OriginNetwork || tokenAddress) --> Wrapped token address
mapping(bytes32 => address) public tokenInfoToWrappedToken;
// Wrapped token Address --> Origin token information
mapping(address => TokenInformation) public wrappedTokenToTokenInfo;
// PolygonZkEVM address
address public polygonZkEVMaddress;
address public gasTokenAddress;
bytes public gasTokenMetadata;
/**
* @param _networkID networkID
* @param _globalExitRootManager global exit root manager address
* @param _polygonZkEVMaddress polygonZkEVM address
* @notice The value of `_polygonZkEVMaddress` on the L2 deployment of the contract will be address(0), so
* emergency state is not possible for the L2 deployment of the bridge, intentionally
*/
function initialize(
uint32 _networkID,
IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager,
address _polygonZkEVMaddress,
address _gasTokenAddress,
bytes memory _gasTokenMetadata
) external virtual initializer {
networkID = _networkID;
globalExitRootManager = _globalExitRootManager;
polygonZkEVMaddress = _polygonZkEVMaddress;
gasTokenAddress = _gasTokenAddress;
gasTokenMetadata = _gasTokenMetadata;
// Initialize OZ contracts
__ReentrancyGuard_init();
}
modifier onlyPolygonZkEVM() {
if (polygonZkEVMaddress != msg.sender) {
revert OnlyPolygonZkEVM();
}
_;
}
/**
* @dev Emitted when bridge assets or messages to another network
*/
event BridgeEvent(
uint8 leafType,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes metadata,
uint32 depositCount
);
/**
* @dev Emitted when a claim is done from another network
*/
event ClaimEvent(
uint32 index,
uint32 originNetwork,
address originAddress,
address destinationAddress,
uint256 amount
);
/**
* @dev Emitted when a new wrapped token is created
*/
event NewWrappedToken(
uint32 originNetwork,
address originTokenAddress,
address wrappedTokenAddress,
bytes metadata
);
/**
* @notice Deposit add a new leaf to the merkle tree
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param amount Amount of tokens
* @param token Token address, 0 address is reserved for ether
* @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
* @param permitData Raw data of the call `permit` of the token
*/
function bridgeAsset(
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
address token,
bool forceUpdateGlobalExitRoot,
bytes calldata permitData
) public payable virtual ifNotEmergencyState nonReentrant {
if (
destinationNetwork == networkID ||
destinationNetwork >= _CURRENT_SUPPORTED_NETWORKS
) {
revert DestinationNetworkInvalid();
}
address originTokenAddress;
uint32 originNetwork;
bytes memory metadata;
uint256 leafAmount = amount;
if (token == address(0)) {
// Ether transfer
if (msg.value != amount) {
revert AmountDoesNotMatchMsgValue();
}
// Ether is treated as ether from mainnet
originNetwork = _MAINNET_NETWORK_ID;
} else {
// Check msg.value is 0 if tokens are bridged
if (msg.value != 0) {
revert MsgValueNotZero();
}
TokenInformation memory tokenInfo = wrappedTokenToTokenInfo[token];
if (tokenInfo.originTokenAddress != address(0)) {
// The token is a wrapped token from another network
// Burn tokens
TokenWrapped(token).burn(msg.sender, amount);
originTokenAddress = tokenInfo.originTokenAddress;
originNetwork = tokenInfo.originNetwork;
} else {
// In order to support fee tokens check the amount received, not the transferred
uint256 balanceBefore = IERC20Upgradeable(token).balanceOf(
address(this)
);
IERC20Upgradeable(token).safeTransferFrom(
msg.sender,
address(this),
amount
);
uint256 balanceAfter = IERC20Upgradeable(token).balanceOf(
address(this)
);
// Override leafAmount with the received amount
leafAmount = balanceAfter - balanceBefore;
originTokenAddress = token;
originNetwork = networkID;
// Encode metadata
metadata = abi.encode(
_safeName(token),
_safeSymbol(token),
_safeDecimals(token)
);
}
}
if (gasTokenAddress != address (0)) { // is gas token
if (token == address(0)) {
originTokenAddress = gasTokenAddress;
metadata = gasTokenMetadata;
} else if (originTokenAddress == gasTokenAddress) {
originTokenAddress = address(0);
}
}
emit BridgeEvent(
_LEAF_TYPE_ASSET,
originNetwork,
originTokenAddress,
destinationNetwork,
destinationAddress,
leafAmount,
metadata,
uint32(depositCount)
);
_deposit(
getLeafValue(
_LEAF_TYPE_ASSET,
originNetwork,
originTokenAddress,
destinationNetwork,
destinationAddress,
leafAmount,
keccak256(metadata)
)
);
// Update the new root to the global exit root manager if set by the user
if (forceUpdateGlobalExitRoot) {
_updateGlobalExitRoot();
}
}
/**
* @notice Bridge message and send ETH value
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param forceUpdateGlobalExitRoot Indicates if the new global exit root is updated or not
* @param metadata Message metadata
*/
function bridgeMessage(
uint32 destinationNetwork,
address destinationAddress,
bool forceUpdateGlobalExitRoot,
bytes calldata metadata
) external payable ifNotEmergencyState {
if (
destinationNetwork == networkID ||
destinationNetwork >= _CURRENT_SUPPORTED_NETWORKS
) {
revert DestinationNetworkInvalid();
}
emit BridgeEvent(
_LEAF_TYPE_MESSAGE,
networkID,
msg.sender,
destinationNetwork,
destinationAddress,
msg.value,
metadata,
uint32(depositCount)
);
_deposit(
getLeafValue(
_LEAF_TYPE_MESSAGE,
networkID,
msg.sender,
destinationNetwork,
destinationAddress,
msg.value,
keccak256(metadata)
)
);
// Update the new root to the global exit root manager if set by the user
if (forceUpdateGlobalExitRoot) {
_updateGlobalExitRoot();
}
}
/**
* @notice Verify merkle proof and withdraw tokens/ether
* @param smtProof Smt proof
* @param index Index of the leaf
* @param mainnetExitRoot Mainnet exit root
* @param rollupExitRoot Rollup exit root
* @param originNetwork Origin network
* @param originTokenAddress Origin token address, 0 address is reserved for ether
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param amount Amount of tokens
* @param metadata Abi encoded metadata if any, empty otherwise
*/
function claimAsset(
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originTokenAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external ifNotEmergencyState {
// Verify leaf exist and it does not have been claimed
_verifyLeaf(
smtProof,
index,
mainnetExitRoot,
rollupExitRoot,
originNetwork,
originTokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
_LEAF_TYPE_ASSET
);
// Transfer funds
if (originTokenAddress == address(0)) {
// Transfer ether
/* solhint-disable avoid-low-level-calls */
(bool success, ) = destinationAddress.call{value: amount}(
new bytes(0)
);
if (!success) {
revert EtherTransferFailed();
}
} else {
// Transfer tokens
if (originNetwork == networkID) {
// The token is an ERC20 from this network
IERC20Upgradeable(originTokenAddress).safeTransfer(
destinationAddress,
amount
);
} else {
// The tokens is not from this network
// Create a wrapper for the token if not exist yet
bytes32 tokenInfoHash = keccak256(
abi.encodePacked(originNetwork, originTokenAddress)
);
address wrappedToken = tokenInfoToWrappedToken[tokenInfoHash];
if (wrappedToken == address(0)) {
// Get ERC20 metadata
(
string memory name,
string memory symbol,
uint8 decimals
) = abi.decode(metadata, (string, string, uint8));
// Create a new wrapped erc20 using create2
TokenWrapped newWrappedToken = (new TokenWrapped){
salt: tokenInfoHash
}(name, symbol, decimals);
// Mint tokens for the destination address
newWrappedToken.mint(destinationAddress, amount);
// Create mappings
tokenInfoToWrappedToken[tokenInfoHash] = address(
newWrappedToken
);
wrappedTokenToTokenInfo[
address(newWrappedToken)
] = TokenInformation(originNetwork, originTokenAddress);
emit NewWrappedToken(
originNetwork,
originTokenAddress,
address(newWrappedToken),
metadata
);
} else {
// Use the existing wrapped erc20
TokenWrapped(wrappedToken).mint(destinationAddress, amount);
}
}
}
emit ClaimEvent(
index,
originNetwork,
originTokenAddress,
destinationAddress,
amount
);
}
/**
* @notice Verify merkle proof and execute message
* If the receiving address is an EOA, the call will result as a success
* Which means that the amount of ether will be transferred correctly, but the message
* will not trigger any execution
* @param smtProof Smt proof
* @param index Index of the leaf
* @param mainnetExitRoot Mainnet exit root
* @param rollupExitRoot Rollup exit root
* @param originNetwork Origin network
* @param originAddress Origin address
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param amount message value
* @param metadata Abi encoded metadata if any, empty otherwise
*/
function claimMessage(
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external ifNotEmergencyState {
// Verify leaf exist and it does not have been claimed
_verifyLeaf(
smtProof,
index,
mainnetExitRoot,
rollupExitRoot,
originNetwork,
originAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
_LEAF_TYPE_MESSAGE
);
// Execute message
// Transfer ether
/* solhint-disable avoid-low-level-calls */
(bool success, ) = destinationAddress.call{value: amount}(
abi.encodeCall(
IBridgeMessageReceiver.onMessageReceived,
(originAddress, originNetwork, metadata)
)
);
if (!success) {
revert MessageFailed();
}
emit ClaimEvent(
index,
originNetwork,
originAddress,
destinationAddress,
amount
);
}
/**
* @notice Returns the precalculated address of a wrapper using the token information
* Note Updating the metadata of a token is not supported.
* Since the metadata has relevance in the address deployed, this function will not return a valid
* wrapped address if the metadata provided is not the original one.
* @param originNetwork Origin network
* @param originTokenAddress Origin token address, 0 address is reserved for ether
* @param name Name of the token
* @param symbol Symbol of the token
* @param decimals Decimals of the token
*/
function precalculatedWrapperAddress(
uint32 originNetwork,
address originTokenAddress,
string calldata name,
string calldata symbol,
uint8 decimals
) external view returns (address) {
bytes32 salt = keccak256(
abi.encodePacked(originNetwork, originTokenAddress)
);
bytes32 hashCreate2 = keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
abi.encodePacked(
type(TokenWrapped).creationCode,
abi.encode(name, symbol, decimals)
)
)
)
);
// last 20 bytes of hash to address
return address(uint160(uint256(hashCreate2)));
}
/**
* @notice Returns the address of a wrapper using the token information if already exist
* @param originNetwork Origin network
* @param originTokenAddress Origin token address, 0 address is reserved for ether
*/
function getTokenWrappedAddress(
uint32 originNetwork,
address originTokenAddress
) external view returns (address) {
return
tokenInfoToWrappedToken[
keccak256(abi.encodePacked(originNetwork, originTokenAddress))
];
}
/**
* @notice Function to activate the emergency state
" Only can be called by the Polygon ZK-EVM in extreme situations
*/
function activateEmergencyState() external onlyPolygonZkEVM {
_activateEmergencyState();
}
/**
* @notice Function to deactivate the emergency state
" Only can be called by the Polygon ZK-EVM
*/
function deactivateEmergencyState() external onlyPolygonZkEVM {
_deactivateEmergencyState();
}
/**
* @notice Verify leaf and checks that it has not been claimed
* @param smtProof Smt proof
* @param index Index of the leaf
* @param mainnetExitRoot Mainnet exit root
* @param rollupExitRoot Rollup exit root
* @param originNetwork Origin network
* @param originAddress Origin address
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param amount Amount of tokens
* @param metadata Abi encoded metadata if any, empty otherwise
* @param leafType Leaf type --> [0] transfer Ether / ERC20 tokens, [1] message
*/
function _verifyLeaf(
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata,
uint8 leafType
) internal {
// Set and check nullifier
_setAndCheckClaimed(index);
// Check timestamp where the global exit root was set
uint256 timestampGlobalExitRoot = globalExitRootManager
.globalExitRootMap(
GlobalExitRootLib.calculateGlobalExitRoot(
mainnetExitRoot,
rollupExitRoot
)
);
if (timestampGlobalExitRoot == 0) {
revert GlobalExitRootInvalid();
}
// Destination network must be networkID
if (destinationNetwork != networkID) {
revert DestinationNetworkInvalid();
}
bytes32 claimRoot;
if (networkID == _MAINNET_NETWORK_ID) {
// Verify merkle proof using rollup exit root
claimRoot = rollupExitRoot;
} else {
// Verify merkle proof using mainnet exit root
claimRoot = mainnetExitRoot;
}
if (
!verifyMerkleProof(
getLeafValue(
leafType,
originNetwork,
originAddress,
destinationNetwork,
destinationAddress,
amount,
keccak256(metadata)
),
smtProof,
index,
claimRoot
)
) {
revert InvalidSmtProof();
}
}
/**
* @notice Function to check if an index is claimed or not
* @param index Index
*/
function isClaimed(uint256 index) external view returns (bool) {
(uint256 wordPos, uint256 bitPos) = _bitmapPositions(index);
uint256 mask = (1 << bitPos);
return (claimedBitMap[wordPos] & mask) == mask;
}
/**
* @notice Function to check that an index is not claimed and set it as claimed
* @param index Index
*/
function _setAndCheckClaimed(uint256 index) private {
(uint256 wordPos, uint256 bitPos) = _bitmapPositions(index);
uint256 mask = 1 << bitPos;
uint256 flipped = claimedBitMap[wordPos] ^= mask;
if (flipped & mask == 0) {
revert AlreadyClaimed();
}
}
/**
* @notice Function to update the globalExitRoot if the last deposit is not submitted
*/
function updateGlobalExitRoot() external {
if (lastUpdatedDepositCount < depositCount) {
_updateGlobalExitRoot();
}
}
/**
* @notice Function to update the globalExitRoot
*/
function _updateGlobalExitRoot() internal {
lastUpdatedDepositCount = uint32(depositCount);
globalExitRootManager.updateExitRoot(getDepositRoot());
}
/**
* @notice Function decode an index into a wordPos and bitPos
* @param index Index
*/
function _bitmapPositions(
uint256 index
) private pure returns (uint256 wordPos, uint256 bitPos) {
wordPos = uint248(index >> 8);
bitPos = uint8(index);
}
/**
* @notice Function to call token permit method of extended ERC20
+ @param token ERC20 token address
* @param amount Quantity that is expected to be allowed
* @param permitData Raw data of the call `permit` of the token
*/
function _permit(
address token,
uint256 amount,
bytes calldata permitData
) internal {
bytes4 sig = bytes4(permitData[:4]);
if (sig == _PERMIT_SIGNATURE) {
(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) = abi.decode(
permitData[4:],
(
address,
address,
uint256,
uint256,
uint8,
bytes32,
bytes32
)
);
if (owner != msg.sender) {
revert NotValidOwner();
}
if (spender != address(this)) {
revert NotValidSpender();
}
if (value != amount) {
revert NotValidAmount();
}
// we call without checking the result, in case it fails and he doesn't have enough balance
// the following transferFrom should be fail. This prevents DoS attacks from using a signature
// before the smartcontract call
/* solhint-disable avoid-low-level-calls */
address(token).call(
abi.encodeWithSelector(
_PERMIT_SIGNATURE,
owner,
spender,
value,
deadline,
v,
r,
s
)
);
} else {
if (sig != _PERMIT_SIGNATURE_DAI) {
revert NotValidSignature();
}
(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) = abi.decode(
permitData[4:],
(
address,
address,
uint256,
uint256,
bool,
uint8,
bytes32,
bytes32
)
);
if (holder != msg.sender) {
revert NotValidOwner();
}
if (spender != address(this)) {
revert NotValidSpender();
}
// we call without checking the result, in case it fails and he doesn't have enough balance
// the following transferFrom should be fail. This prevents DoS attacks from using a signature
// before the smartcontract call
/* solhint-disable avoid-low-level-calls */
address(token).call(
abi.encodeWithSelector(
_PERMIT_SIGNATURE_DAI,
holder,
spender,
nonce,
expiry,
allowed,
v,
r,
s
)
);
}
}
// Helpers to safely get the metadata from a token, inspired by https://github.com/traderjoe-xyz/joe-core/blob/main/contracts/MasterChefJoeV3.sol#L55-L95
/**
* @notice Provides a safe ERC20.symbol version which returns 'NO_SYMBOL' as fallback string
* @param token The address of the ERC-20 token contract
*/
function _safeSymbol(address token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(
abi.encodeCall(IERC20MetadataUpgradeable.symbol, ())
);
return success ? _returnDataToString(data) : "NO_SYMBOL";
}
/**
* @notice Provides a safe ERC20.name version which returns 'NO_NAME' as fallback string.
* @param token The address of the ERC-20 token contract.
*/
function _safeName(address token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(
abi.encodeCall(IERC20MetadataUpgradeable.name, ())
);
return success ? _returnDataToString(data) : "NO_NAME";
}
/**
* @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
* Note Tokens with (decimals > 255) are not supported
* @param token The address of the ERC-20 token contract
*/
function _safeDecimals(address token) internal view returns (uint8) {
(bool success, bytes memory data) = address(token).staticcall(
abi.encodeCall(IERC20MetadataUpgradeable.decimals, ())
);
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
/**
* @notice Function to convert returned data to string
* returns 'NOT_VALID_ENCODING' as fallback value.
* @param data returned data
*/
function _returnDataToString(
bytes memory data
) internal pure returns (string memory) {
if (data.length >= 64) {
return abi.decode(data, (string));
} else if (data.length == 32) {
// Since the strings on bytes32 are encoded left-right, check the first zero in the data
uint256 nonZeroBytes;
while (nonZeroBytes < 32 && data[nonZeroBytes] != 0) {
nonZeroBytes++;
}
// If the first one is 0, we do not handle the encoding
if (nonZeroBytes == 0) {
return "NOT_VALID_ENCODING";
}
// Create a byte array with nonZeroBytes length
bytes memory bytesArray = new bytes(nonZeroBytes);
for (uint256 i = 0; i < nonZeroBytes; i++) {
bytesArray[i] = data[i];
}
return string(bytesArray);
} else {
return "NOT_VALID_ENCODING";
}
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "./interfaces/IPolygonZkEVMGlobalExitRoot.sol";
import "./lib/GlobalExitRootLib.sol";
/**
* Contract responsible for managing the exit roots across multiple networks
*/
contract PolygonZkEVMGlobalExitRoot is IPolygonZkEVMGlobalExitRoot {
// PolygonZkEVMBridge address
address public immutable bridgeAddress;
// Rollup contract address
address public immutable rollupAddress;
// Rollup exit root, this will be updated every time a batch is verified
bytes32 public lastRollupExitRoot;
// Mainnet exit root, this will be updated every time a deposit is made in mainnet
bytes32 public lastMainnetExitRoot;
// Store every global exit root: Root --> timestamp
mapping(bytes32 => uint256) public globalExitRootMap;
/**
* @dev Emitted when the global exit root is updated
*/
event UpdateGlobalExitRoot(
bytes32 indexed mainnetExitRoot,
bytes32 indexed rollupExitRoot
);
/**
* @param _rollupAddress Rollup contract address
* @param _bridgeAddress PolygonZkEVMBridge contract address
*/
constructor(address _rollupAddress, address _bridgeAddress) {
rollupAddress = _rollupAddress;
bridgeAddress = _bridgeAddress;
}
/**
* @notice Update the exit root of one of the networks and the global exit root
* @param newRoot new exit tree root
*/
function updateExitRoot(bytes32 newRoot) external {
// Store storage variables into temporal variables since will be used multiple times
bytes32 cacheLastRollupExitRoot = lastRollupExitRoot;
bytes32 cacheLastMainnetExitRoot = lastMainnetExitRoot;
if (msg.sender == bridgeAddress) {
lastMainnetExitRoot = newRoot;
cacheLastMainnetExitRoot = newRoot;
} else if (msg.sender == rollupAddress) {
lastRollupExitRoot = newRoot;
cacheLastRollupExitRoot = newRoot;
} else {
revert OnlyAllowedContracts();
}
bytes32 newGlobalExitRoot = GlobalExitRootLib.calculateGlobalExitRoot(
cacheLastMainnetExitRoot,
cacheLastRollupExitRoot
);
// If it already exists, do not modify the timestamp
if (globalExitRootMap[newGlobalExitRoot] == 0) {
globalExitRootMap[newGlobalExitRoot] = block.timestamp;
emit UpdateGlobalExitRoot(
cacheLastMainnetExitRoot,
cacheLastRollupExitRoot
);
}
}
/**
* @notice Return last global exit root
*/
function getLastGlobalExitRoot() public view returns (bytes32) {
return
GlobalExitRootLib.calculateGlobalExitRoot(
lastMainnetExitRoot,
lastRollupExitRoot
);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "./interfaces/IBasePolygonZkEVMGlobalExitRoot.sol";
/**
* Contract responsible for managing the exit roots for the L2 and global exit roots
* The special zkRom variables will be accessed and updated directly by the zkRom
*/
contract PolygonZkEVMGlobalExitRootL2 is IBasePolygonZkEVMGlobalExitRoot {
/////////////////////////////
// Special zkRom variables
////////////////////////////
// Store every global exit root: Root --> timestamp
// Note this variable is updated only by the zkRom
mapping(bytes32 => uint256) public globalExitRootMap;
// Rollup exit root will be updated for every PolygonZkEVMBridge call
// Note this variable will be readed by the zkRom
bytes32 public lastRollupExitRoot;
////////////////////
// Regular variables
///////////////////
// PolygonZkEVM Bridge address
address public immutable bridgeAddress;
/**
* @param _bridgeAddress PolygonZkEVMBridge contract address
*/
constructor(address _bridgeAddress) {
bridgeAddress = _bridgeAddress;
}
/**
* @notice Update the exit root of one of the networks and the global exit root
* @param newRoot new exit tree root
*/
function updateExitRoot(bytes32 newRoot) external {
if (msg.sender != bridgeAddress) {
revert OnlyAllowedContracts();
}
lastRollupExitRoot = newRoot;
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Create2.sol";
import "@openzeppelin/contracts/utils/Address.sol";
/**
* Contract responsible for deploying deterministic address contracts related with the CDKValidium
*/
contract CDKValidiumDeployer is Ownable {
/**
* @param _owner Owner
*/
constructor(address _owner) Ownable() {
_transferOwnership(_owner);
}
/**
* @dev Emitted when a contract is deployed
*/
event NewDeterministicDeployment(address newContractAddress);
/**
* @dev Emitted when a contract is called
*/
event FunctionCall();
/**
* @notice Allows to deploy a contract using create2
* @param amount Amount used in create2
* @param salt Salt used in create2
* @param initBytecode Init bytecode that will be use in create2
*/
function deployDeterministic(
uint256 amount,
bytes32 salt,
bytes memory initBytecode
) public payable onlyOwner {
address newContractAddress = Create2.deploy(amount, salt, initBytecode);
emit NewDeterministicDeployment(newContractAddress);
}
/**
* @notice Allows to deploy a contract using create2 and call it afterwards
* @param amount Amount used in create2
* @param salt Salt used in create2
* @param initBytecode Init bytecode that will be use in create2
* @param dataCall Data used in the call after deploying the smart contract
*/
function deployDeterministicAndCall(
uint256 amount,
bytes32 salt,
bytes memory initBytecode,
bytes memory dataCall
) public payable onlyOwner {
address newContractAddress = Create2.deploy(amount, salt, initBytecode);
Address.functionCall(newContractAddress, dataCall);
emit NewDeterministicDeployment(newContractAddress);
}
/**
* @param targetAddress Amount of contract deploy
* @param dataCall Data used to call the target smart contract
* @param amount Data used to call the target smart contract
*/
function functionCall(
address targetAddress,
bytes memory dataCall,
uint256 amount
) public payable onlyOwner {
Address.functionCallWithValue(targetAddress, dataCall, amount);
emit FunctionCall();
}
/**
* @param salt Salt used in create2
* @param bytecodeHash Init bytecode hashed, it contains the constructor parameters
*/
function predictDeterministicAddress(
bytes32 salt,
bytes32 bytecodeHash
) public view returns (address) {
return Create2.computeAddress(salt, bytecodeHash);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface IBasePolygonZkEVMGlobalExitRoot {
/**
* @dev Thrown when the caller is not the allowed contracts
*/
error OnlyAllowedContracts();
function updateExitRoot(bytes32 newRollupExitRoot) external;
function globalExitRootMap(
bytes32 globalExitRootNum
) external returns (uint256);
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
/**
* @dev Define interface for PolygonZkEVM Bridge message receiver
*/
interface IBridgeMessageReceiver {
function onMessageReceived(
address originAddress,
uint32 originNetwork,
bytes memory data
) external payable;
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
interface ICDKDataCommittee {
function verifySignatures(bytes32 hash, bytes memory signaturesAndAddrs) external view;
}
\ No newline at end of file
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface ICDKDataCommitteeErrors {
/**
* @dev Thrown when the addres bytes doesn't have the expected length
*/
error UnexpectedAddrsBytesLength();
/**
* @dev Thrown when the setup attempts to register a member with empty URL
*/
error EmptyURLNotAllowed();
/**
* @dev Thrown when the setup register doesn't order the members correctly
*/
error WrongAddrOrder();
/**
* @dev Thrown when the required amount of signatures is greater than the amount of members
*/
error TooManyRequiredSignatures();
/**
* @dev Thrown when the hash of the committee doesn't match with the provided addresses
*/
error UnexpectedCommitteeHash();
/**
* @dev Thrown when the signature of a DA hash doesn't belong to any member of the committee
*/
error CommitteeAddressDoesntExist();
/**
* @dev Thrown when the addresses and signatures byte array length has an unexpected size
*/
error UnexpectedAddrsAndSignaturesSize();
}
\ No newline at end of file
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface ICDKValidiumErrors {
/**
* @dev Thrown when the pending state timeout exceeds the _HALT_AGGREGATION_TIMEOUT
*/
error PendingStateTimeoutExceedHaltAggregationTimeout();
/**
* @dev Thrown when the trusted aggregator timeout exceeds the _HALT_AGGREGATION_TIMEOUT
*/
error TrustedAggregatorTimeoutExceedHaltAggregationTimeout();
/**
* @dev Thrown when the caller is not the admin
*/
error OnlyAdmin();
/**
* @dev Thrown when the caller is not the trusted sequencer
*/
error OnlyTrustedSequencer();
/**
* @dev Thrown when the caller is not the trusted aggregator
*/
error OnlyTrustedAggregator();
/**
* @dev Thrown when attempting to sequence 0 batches
*/
error SequenceZeroBatches();
/**
* @dev Thrown when attempting to sequence or verify more batches than _MAX_VERIFY_BATCHES
*/
error ExceedMaxVerifyBatches();
/**
* @dev Thrown when the forced data does not match
*/
error ForcedDataDoesNotMatch();
/**
* @dev Thrown when the sequenced timestamp is below the forced minimum timestamp
*/
error SequencedTimestampBelowForcedTimestamp();
/**
* @dev Thrown when a global exit root is not zero and does not exist
*/
error GlobalExitRootNotExist();
/**
* @dev Thrown when transactions array length is above _MAX_TRANSACTIONS_BYTE_LENGTH.
*/
error TransactionsLengthAboveMax();
/**
* @dev Thrown when a sequenced timestamp is not inside a correct range.
*/
error SequencedTimestampInvalid();
/**
* @dev Thrown when there are more sequenced force batches than were actually submitted, should be unreachable
*/
error ForceBatchesOverflow();
/**
* @dev Thrown when there are more sequenced force batches than were actually submitted
*/
error TrustedAggregatorTimeoutNotExpired();
/**
* @dev Thrown when attempting to access a pending state that does not exist
*/
error PendingStateDoesNotExist();
/**
* @dev Thrown when the init num batch does not match with the one in the pending state
*/
error InitNumBatchDoesNotMatchPendingState();
/**
* @dev Thrown when the old state root of a certain batch does not exist
*/
error OldStateRootDoesNotExist();
/**
* @dev Thrown when the init verification batch is above the last verification batch
*/
error InitNumBatchAboveLastVerifiedBatch();
/**
* @dev Thrown when the final verification batch is below or equal the last verification batch
*/
error FinalNumBatchBelowLastVerifiedBatch();
/**
* @dev Thrown when the zkproof is not valid
*/
error InvalidProof();
/**
* @dev Thrown when attempting to consolidate a pending state not yet consolidable
*/
error PendingStateNotConsolidable();
/**
* @dev Thrown when attempting to consolidate a pending state that is already consolidated or does not exist
*/
error PendingStateInvalid();
/**
* @dev Thrown when the matic amount is below the necessary matic fee
*/
error NotEnoughMaticAmount();
/**
* @dev Thrown when attempting to sequence a force batch using sequenceForceBatches and the
* force timeout did not expire
*/
error ForceBatchTimeoutNotExpired();
/**
* @dev Thrown when attempting to set a new trusted aggregator timeout equal or bigger than current one
*/
error NewTrustedAggregatorTimeoutMustBeLower();
/**
* @dev Thrown when attempting to set a new pending state timeout equal or bigger than current one
*/
error NewPendingStateTimeoutMustBeLower();
/**
* @dev Thrown when attempting to set a new multiplier batch fee in a invalid range of values
*/
error InvalidRangeMultiplierBatchFee();
/**
* @dev Thrown when attempting to set a batch time target in an invalid range of values
*/
error InvalidRangeBatchTimeTarget();
/**
* @dev Thrown when attempting to set a force batch timeout in an invalid range of values
*/
error InvalidRangeForceBatchTimeout();
/**
* @dev Thrown when the caller is not the pending admin
*/
error OnlyPendingAdmin();
/**
* @dev Thrown when the final pending state num is not in a valid range
*/
error FinalPendingStateNumInvalid();
/**
* @dev Thrown when the final num batch does not match with the one in the pending state
*/
error FinalNumBatchDoesNotMatchPendingState();
/**
* @dev Thrown when the stored root matches the new root proving a different state
*/
error StoredRootMustBeDifferentThanNewRoot();
/**
* @dev Thrown when the batch is already verified when attempting to activate the emergency state
*/
error BatchAlreadyVerified();
/**
* @dev Thrown when the batch is not sequenced or not at the end of a sequence when attempting to activate the emergency state
*/
error BatchNotSequencedOrNotSequenceEnd();
/**
* @dev Thrown when the halt timeout is not expired when attempting to activate the emergency state
*/
error HaltTimeoutNotExpired();
/**
* @dev Thrown when the old accumulate input hash does not exist
*/
error OldAccInputHashDoesNotExist();
/**
* @dev Thrown when the new accumulate input hash does not exist
*/
error NewAccInputHashDoesNotExist();
/**
* @dev Thrown when the new state root is not inside prime
*/
error NewStateRootNotInsidePrime();
/**
* @dev Thrown when force batch is not allowed
*/
error ForceBatchNotAllowed();
/**
* @dev Thrown when try to activate force batches when they are already active
*/
error ForceBatchesAlreadyActive();
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
interface IPolygonZkEVMBridge {
/**
* @dev Thrown when sender is not the PolygonZkEVM address
*/
error OnlyPolygonZkEVM();
/**
* @dev Thrown when the destination network is invalid
*/
error DestinationNetworkInvalid();
/**
* @dev Thrown when the amount does not match msg.value
*/
error AmountDoesNotMatchMsgValue();
/**
* @dev Thrown when user is bridging tokens and is also sending a value
*/
error MsgValueNotZero();
/**
* @dev Thrown when the Ether transfer on claimAsset fails
*/
error EtherTransferFailed();
/**
* @dev Thrown when the message transaction on claimMessage fails
*/
error MessageFailed();
/**
* @dev Thrown when the global exit root does not exist
*/
error GlobalExitRootInvalid();
/**
* @dev Thrown when the smt proof does not match
*/
error InvalidSmtProof();
/**
* @dev Thrown when an index is already claimed
*/
error AlreadyClaimed();
/**
* @dev Thrown when the owner of permit does not match the sender
*/
error NotValidOwner();
/**
* @dev Thrown when the spender of the permit does not match this contract address
*/
error NotValidSpender();
/**
* @dev Thrown when the amount of the permit does not match
*/
error NotValidAmount();
/**
* @dev Thrown when the permit data contains an invalid signature
*/
error NotValidSignature();
function bridgeAsset(
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
address token,
bool forceUpdateGlobalExitRoot,
bytes calldata permitData
) external payable;
function bridgeMessage(
uint32 destinationNetwork,
address destinationAddress,
bool forceUpdateGlobalExitRoot,
bytes calldata metadata
) external payable;
function claimAsset(
bytes32[32] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originTokenAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external;
function claimMessage(
bytes32[32] calldata smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes calldata metadata
) external;
function updateGlobalExitRoot() external;
function activateEmergencyState() external;
function deactivateEmergencyState() external;
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
import "./IBasePolygonZkEVMGlobalExitRoot.sol";
interface IPolygonZkEVMGlobalExitRoot is IBasePolygonZkEVMGlobalExitRoot {
function getLastGlobalExitRoot() external view returns (bytes32);
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
/**
* @dev Define interface verifier
*/
interface IVerifierRollup {
function verifyProof(
bytes32[24] calldata proof,
uint256[1] memory pubSignals
) external view returns (bool);
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
/**
* This contract will be used as a helper for all the sparse merkle tree related functions
* Based on the implementation of the deposit eth2.0 contract https://github.com/ethereum/consensus-specs/blob/dev/solidity_deposit_contract/deposit_contract.sol
*/
contract DepositContract is ReentrancyGuardUpgradeable {
/**
* @dev Thrown when the merkle tree is full
*/
error MerkleTreeFull();
// Merkle tree levels
uint256 internal constant _DEPOSIT_CONTRACT_TREE_DEPTH = 32;
// This ensures `depositCount` will fit into 32-bits
uint256 internal constant _MAX_DEPOSIT_COUNT =
2 ** _DEPOSIT_CONTRACT_TREE_DEPTH - 1;
// Branch array which contains the necessary sibilings to compute the next root when a new
// leaf is inserted
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] internal _branch;
// Counter of current deposits
uint256 public depositCount;
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
*/
uint256[10] private _gap;
/**
* @notice Computes and returns the merkle root
*/
function getDepositRoot() public view returns (bytes32) {
bytes32 node;
uint256 size = depositCount;
bytes32 currentZeroHashHeight = 0;
for (
uint256 height = 0;
height < _DEPOSIT_CONTRACT_TREE_DEPTH;
height++
) {
if (((size >> height) & 1) == 1)
node = keccak256(abi.encodePacked(_branch[height], node));
else
node = keccak256(abi.encodePacked(node, currentZeroHashHeight));
currentZeroHashHeight = keccak256(
abi.encodePacked(currentZeroHashHeight, currentZeroHashHeight)
);
}
return node;
}
/**
* @notice Add a new leaf to the merkle tree
* @param leafHash Leaf hash
*/
function _deposit(bytes32 leafHash) internal {
bytes32 node = leafHash;
// Avoid overflowing the Merkle tree (and prevent edge case in computing `_branch`)
if (depositCount >= _MAX_DEPOSIT_COUNT) {
revert MerkleTreeFull();
}
// Add deposit data root to Merkle tree (update a single `_branch` node)
uint256 size = ++depositCount;
for (
uint256 height = 0;
height < _DEPOSIT_CONTRACT_TREE_DEPTH;
height++
) {
if (((size >> height) & 1) == 1) {
_branch[height] = node;
return;
}
node = keccak256(abi.encodePacked(_branch[height], node));
}
// As the loop should always end prematurely with the `return` statement,
// this code should be unreachable. We assert `false` just to be safe.
assert(false);
}
/**
* @notice Verify merkle proof
* @param leafHash Leaf hash
* @param smtProof Smt proof
* @param index Index of the leaf
* @param root Merkle root
*/
function verifyMerkleProof(
bytes32 leafHash,
bytes32[_DEPOSIT_CONTRACT_TREE_DEPTH] calldata smtProof,
uint32 index,
bytes32 root
) public pure returns (bool) {
bytes32 node = leafHash;
// Check merkle proof
for (
uint256 height = 0;
height < _DEPOSIT_CONTRACT_TREE_DEPTH;
height++
) {
if (((index >> height) & 1) == 1)
node = keccak256(abi.encodePacked(smtProof[height], node));
else node = keccak256(abi.encodePacked(node, smtProof[height]));
}
return node == root;
}
/**
* @notice Given the leaf data returns the leaf value
* @param leafType Leaf type --> [0] transfer Ether / ERC20 tokens, [1] message
* @param originNetwork Origin Network
* @param originAddress [0] Origin token address, 0 address is reserved for ether, [1] msg.sender of the message
* @param destinationNetwork Destination network
* @param destinationAddress Destination address
* @param amount [0] Amount of tokens/ether, [1] Amount of ether
* @param metadataHash Hash of the metadata
*/
function getLeafValue(
uint8 leafType,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes32 metadataHash
) public pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
leafType,
originNetwork,
originAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash
)
);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.20;
/**
* @dev Contract helper responsible to manage the emergency state
*/
contract EmergencyManager {
/**
* @dev Thrown when emergency state is active, and the function requires otherwise
*/
error OnlyNotEmergencyState();
/**
* @dev Thrown when emergency state is not active, and the function requires otherwise
*/
error OnlyEmergencyState();
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
*/
uint256[10] private _gap;
// Indicates whether the emergency state is active or not
bool public isEmergencyState;
/**
* @dev Emitted when emergency state is activated
*/
event EmergencyStateActivated();
/**
* @dev Emitted when emergency state is deactivated
*/
event EmergencyStateDeactivated();
/**
* @notice Only allows a function to be callable if emergency state is unactive
*/
modifier ifNotEmergencyState() {
if (isEmergencyState) {
revert OnlyNotEmergencyState();
}
_;
}
/**
* @notice Only allows a function to be callable if emergency state is active
*/
modifier ifEmergencyState() {
if (!isEmergencyState) {
revert OnlyEmergencyState();
}
_;
}
/**
* @notice Activate emergency state
*/
function _activateEmergencyState() internal virtual ifNotEmergencyState {
isEmergencyState = true;
emit EmergencyStateActivated();
}
/**
* @notice Deactivate emergency state
*/
function _deactivateEmergencyState() internal virtual ifEmergencyState {
isEmergencyState = false;
emit EmergencyStateDeactivated();
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
/**
* @dev A library that provides the necessary calculations to calculate the global exit root
*/
library GlobalExitRootLib {
function calculateGlobalExitRoot(
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot
) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(mainnetExitRoot, rollupExitRoot));
}
}
// SPDX-License-Identifier: GPL-3.0
// Implementation of permit based on https://github.com/WETH10/WETH10/blob/main/contracts/WETH10.sol
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract TokenWrapped is ERC20 {
// Domain typehash
bytes32 public constant DOMAIN_TYPEHASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
// Permit typehash
bytes32 public constant PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
// Version
string public constant VERSION = "1";
// Chain id on deployment
uint256 public immutable deploymentChainId;
// Domain separator calculated on deployment
bytes32 private immutable _DEPLOYMENT_DOMAIN_SEPARATOR;
// PolygonZkEVM Bridge address
address public immutable bridgeAddress;
// Decimals
uint8 private immutable _decimals;
// Permit nonces
mapping(address => uint256) public nonces;
modifier onlyBridge() {
require(
msg.sender == bridgeAddress,
"TokenWrapped::onlyBridge: Not PolygonZkEVMBridge"
);
_;
}
constructor(
string memory name,
string memory symbol,
uint8 __decimals
) ERC20(name, symbol) {
bridgeAddress = msg.sender;
_decimals = __decimals;
deploymentChainId = block.chainid;
_DEPLOYMENT_DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid);
}
function mint(address to, uint256 value) external onlyBridge {
_mint(to, value);
}
// Notice that is not require to approve wrapped tokens to use the bridge
function burn(address account, uint256 value) external onlyBridge {
_burn(account, value);
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
// Permit relative functions
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(
block.timestamp <= deadline,
"TokenWrapped::permit: Expired permit"
);
bytes32 hashStruct = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
);
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), hashStruct)
);
address signer = ecrecover(digest, v, r, s);
require(
signer != address(0) && signer == owner,
"TokenWrapped::permit: Invalid signature"
);
_approve(owner, spender, value);
}
/**
* @notice Calculate domain separator, given a chainID.
* @param chainId Current chainID
*/
function _calculateDomainSeparator(
uint256 chainId
) private view returns (bytes32) {
return
keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name())),
keccak256(bytes(VERSION)),
chainId,
address(this)
)
);
}
/// @dev Return the DOMAIN_SEPARATOR.
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return
block.chainid == deploymentChainId
? _DEPLOYMENT_DOMAIN_SEPARATOR
: _calculateDomainSeparator(block.chainid);
}
}
/**
*Submitted for verification at Etherscan.io on 2019-11-14
*/
// hevm: flattened sources of /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
pragma solidity =0.5.12;
////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/lib.sol
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
/* pragma solidity 0.5.12; */
contract LibNote {
event LogNote(
bytes4 indexed sig,
address indexed usr,
bytes32 indexed arg1,
bytes32 indexed arg2,
bytes data
) anonymous;
modifier note() {
_;
assembly {
// log an 'anonymous' event with a constant 6 words of calldata
// and four indexed topics: selector, caller, arg1 and arg2
let mark := msize // end of memory ensures zero
mstore(0x40, add(mark, 288)) // update free memory pointer
mstore(mark, 0x20) // bytes type data offset
mstore(add(mark, 0x20), 224) // bytes size (padded)
calldatacopy(add(mark, 0x40), 0, 224) // bytes payload
log4(
mark,
288, // calldata
shl(224, shr(224, calldataload(0))), // msg.sig
caller, // msg.sender
calldataload(4), // arg1
calldataload(36) // arg2
)
}
}
}
////// /nix/store/8xb41r4qd0cjb63wcrxf1qmfg88p0961-dss-6fd7de0/src/dai.sol
// Copyright (C) 2017, 2018, 2019 dbrock, rain, mrchico
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
/* pragma solidity 0.5.12; */
/* import "./lib.sol"; */
contract Dai is LibNote {
// --- Auth ---
mapping(address => uint256) public wards;
function rely(address guy) external note auth {
wards[guy] = 1;
}
function deny(address guy) external note auth {
wards[guy] = 0;
}
modifier auth() {
require(wards[msg.sender] == 1, "Dai/not-authorized");
_;
}
// --- ERC20 Data ---
string public constant name = "Dai Stablecoin";
string public constant symbol = "DAI";
string public constant version = "1";
uint8 public constant decimals = 18;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(address => uint256) public nonces;
event Approval(address indexed src, address indexed guy, uint256 wad);
event Transfer(address indexed src, address indexed dst, uint256 wad);
// --- Math ---
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
// --- EIP712 niceties ---
bytes32 public DOMAIN_SEPARATOR;
// bytes32 public constant PERMIT_TYPEHASH = keccak256("Permit(address holder,address spender,uint256 nonce,uint256 expiry,bool allowed)");
bytes32 public constant PERMIT_TYPEHASH =
0xea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb;
constructor(uint256 chainId_) public {
wards[msg.sender] = 1;
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256(bytes(name)),
keccak256(bytes(version)),
chainId_,
address(this)
)
);
}
// --- Token ---
function transfer(address dst, uint256 wad) external returns (bool) {
return transferFrom(msg.sender, dst, wad);
}
function transferFrom(
address src,
address dst,
uint256 wad
) public returns (bool) {
require(balanceOf[src] >= wad, "Dai/insufficient-balance");
if (src != msg.sender && allowance[src][msg.sender] != uint256(-1)) {
require(
allowance[src][msg.sender] >= wad,
"Dai/insufficient-allowance"
);
allowance[src][msg.sender] = sub(allowance[src][msg.sender], wad);
}
balanceOf[src] = sub(balanceOf[src], wad);
balanceOf[dst] = add(balanceOf[dst], wad);
emit Transfer(src, dst, wad);
return true;
}
function mint(address usr, uint256 wad) external auth {
balanceOf[usr] = add(balanceOf[usr], wad);
totalSupply = add(totalSupply, wad);
emit Transfer(address(0), usr, wad);
}
function burn(address usr, uint256 wad) external {
require(balanceOf[usr] >= wad, "Dai/insufficient-balance");
if (usr != msg.sender && allowance[usr][msg.sender] != uint256(-1)) {
require(
allowance[usr][msg.sender] >= wad,
"Dai/insufficient-allowance"
);
allowance[usr][msg.sender] = sub(allowance[usr][msg.sender], wad);
}
balanceOf[usr] = sub(balanceOf[usr], wad);
totalSupply = sub(totalSupply, wad);
emit Transfer(usr, address(0), wad);
}
function approve(address usr, uint256 wad) external returns (bool) {
allowance[msg.sender][usr] = wad;
emit Approval(msg.sender, usr, wad);
return true;
}
// --- Alias ---
function push(address usr, uint256 wad) external {
transferFrom(msg.sender, usr, wad);
}
function pull(address usr, uint256 wad) external {
transferFrom(usr, msg.sender, wad);
}
function move(
address src,
address dst,
uint256 wad
) external {
transferFrom(src, dst, wad);
}
// --- Approve by signature ---
function permit(
address holder,
address spender,
uint256 nonce,
uint256 expiry,
bool allowed,
uint8 v,
bytes32 r,
bytes32 s
) external {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(
abi.encode(
PERMIT_TYPEHASH,
holder,
spender,
nonce,
expiry,
allowed
)
)
)
);
require(holder != address(0), "Dai/invalid-address-0");
require(holder == ecrecover(digest, v, r, s), "Dai/invalid-permit");
require(expiry == 0 || now <= expiry, "Dai/permit-expired");
require(nonce == nonces[holder]++, "Dai/invalid-nonce");
uint256 wad = allowed ? uint256(-1) : 0;
allowance[holder][spender] = wad;
emit Approval(holder, spender, wad);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../lib/DepositContract.sol";
import "hardhat/console.sol";
/**
* This contract will be used as a herlper for all the sparse merkle tree related functions
* Based on the implementation of the deposit eth2.0 contract https://github.com/ethereum/consensus-specs/blob/dev/solidity_deposit_contract/deposit_contract.sol
*/
contract DepositContractMock is DepositContract {
constructor() {
initialize();
}
function initialize() public initializer {}
/**
* @notice Given the leaf data returns the leaf value
* @param leafType Leaf type
* @param originNetwork Origin Network
* @param originTokenAddress Origin token address, 0 address is reserved for ether
* @param destinationNetwork Destination network
* @param destinationAddress Destination address
* @param amount Amount of tokens
* @param metadataHash Hash of the metadata
*/
function deposit(
uint8 leafType,
uint32 originNetwork,
address originTokenAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes32 metadataHash
) public {
_deposit(
getLeafValue(
leafType,
originNetwork,
originTokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash
)
);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract ERC20PermitMock is ERC20 {
constructor(
string memory name,
string memory symbol,
address initialAccount,
uint256 initialBalance
) payable ERC20(name, symbol) {
_mint(initialAccount, initialBalance);
NAME_HASH = keccak256(bytes(name));
}
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
function burn(uint256 amount) public {
_burn(msg.sender, amount);
}
function transferInternal(address from, address to, uint256 value) public {
_transfer(from, to, value);
}
function approveInternal(
address owner,
address spender,
uint256 value
) public {
_approve(owner, spender, value);
}
// erc20 permit
mapping(address => uint256) public nonces;
bytes32 public constant PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
bytes32 public NAME_HASH;
// bytes32 public constant VERSION_HASH =
// keccak256("1")
bytes32 public constant VERSION_HASH =
0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
// bytes32 public constant EIP712DOMAIN_HASH =
// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
bytes32 public constant EIP712DOMAIN_HASH =
0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
function _validateSignedData(
address signer,
bytes32 encodeData,
uint8 v,
bytes32 r,
bytes32 s
) internal view {
bytes32 domainSeparator = keccak256(
abi.encode(
EIP712DOMAIN_HASH,
NAME_HASH,
VERSION_HASH,
getChainId(),
address(this)
)
);
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", domainSeparator, encodeData)
);
address recoveredAddress = ecrecover(digest, v, r, s);
// Explicitly disallow authorizations for address(0) as ecrecover returns address(0) on malformed messages
require(
recoveredAddress != address(0) && recoveredAddress == signer,
"HEZ::_validateSignedData: INVALID_SIGNATURE"
);
}
function getChainId() public view returns (uint256 chainId) {
assembly {
chainId := chainid()
}
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(deadline >= block.timestamp, "HEZ::permit: AUTH_EXPIRED");
bytes32 encodeData = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
value,
nonces[owner]++,
deadline
)
);
_validateSignedData(owner, encodeData, v, r, s);
_approve(owner, spender, value);
}
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return
keccak256(
abi.encode(
EIP712DOMAIN_HASH,
NAME_HASH,
VERSION_HASH,
getChainId(),
address(this)
)
);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Implementation of the ERC20 with weird metadata.
*/
contract ERC20WeirdMetadata {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
bytes32 internal _name;
bytes internal _symbol;
uint256 internal _decimals;
bool public isRevert;
constructor(bytes32 name_, bytes memory symbol_, uint256 decimals_) {
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
}
function setDecimals(uint256 newDecimals) public {
_decimals = newDecimals;
}
function toggleIsRevert() public {
isRevert = !isRevert;
}
function name() public view virtual returns (bytes32) {
if(isRevert) {
require(false);
}
return _name;
}
function symbol() public view virtual returns (bytes memory) {
if(isRevert) {
require(false);
}
return _symbol;
}
function decimals() public view virtual returns (uint256) {
if(isRevert) {
require(false);
}
return _decimals;
}
function mint(address account, uint256 amount) public {
_mint(account, amount);
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
address owner = msg.sender;
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
address spender = msg.sender;
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = msg.sender;
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = msg.sender;
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../PolygonZkEVMBridge.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
/**
* PolygonZkEVMBridge that will be deployed on both networks Ethereum and Polygon zkEVM
* Contract responsible to manage the token interactions with other networks
*/
contract PolygonZkEVMBridgeMock is PolygonZkEVMBridge, OwnableUpgradeable {
uint256 public maxEtherBridge;
/**
* @param _networkID networkID
* @param _globalExitRootManager global exit root manager address
*/
function initialize(
uint32 _networkID,
IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager,
address _polygonZkEVMaddress,
address _a,
bytes memory _b
) public override initializer {
networkID = _networkID;
globalExitRootManager = _globalExitRootManager;
polygonZkEVMaddress = _polygonZkEVMaddress;
maxEtherBridge = 0.25 ether;
// Initialize OZ contracts
__Ownable_init_unchained();
}
function setNetworkID(uint32 _networkID) public onlyOwner {
networkID = _networkID;
}
function setMaxEtherBridge(uint256 _maxEtherBridge) public onlyOwner {
maxEtherBridge = _maxEtherBridge;
}
/**
* @notice Deposit add a new leaf to the merkle tree
* @param destinationNetwork Network destination
* @param destinationAddress Address destination
* @param amount Amount of tokens
* @param token Token address, 0 address is reserved for ether
* @param permitData Raw data of the call `permit` of the token
*/
function bridgeAsset(
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
address token,
bool forceUpdateGlobalExitRoot,
bytes calldata permitData
) public payable override {
require(
msg.value <= maxEtherBridge,
"PolygonZkEVMBridge::bridgeAsset: Cannot bridge more than maxEtherBridge"
);
super.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
token,
forceUpdateGlobalExitRoot,
permitData
);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../PolygonZkEVMGlobalExitRootL2.sol";
/**
* Contract responsible for managing the exit roots across multiple networks
*/
contract PolygonZkEVMGlobalExitRootL2Mock is PolygonZkEVMGlobalExitRootL2 {
/**
* @param _bridgeAddress PolygonZkEVM Bridge contract address
*/
constructor(
address _bridgeAddress
) PolygonZkEVMGlobalExitRootL2(_bridgeAddress) {}
/**
* @notice Set globalExitRoot
* @param globalExitRoot New global exit root
* @param blockNumber block number
*/
function setLastGlobalExitRoot(
bytes32 globalExitRoot,
uint256 blockNumber
) public {
globalExitRootMap[globalExitRoot] = blockNumber;
}
/**
* @notice Set rollup exit root
* @param newRoot New rollup exit root
*/
function setExitRoot(bytes32 newRoot) public {
lastRollupExitRoot = newRoot;
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../PolygonZkEVMGlobalExitRoot.sol";
/**
* Contract responsible for managing the exit roots across multiple networks
*/
contract PolygonZkEVMGlobalExitRootMock is PolygonZkEVMGlobalExitRoot {
/**
* @param _rollupAddress Rollup contract address
* @param _bridgeAddress PolygonZkEVM Bridge contract address
*/
constructor(
address _rollupAddress,
address _bridgeAddress
) PolygonZkEVMGlobalExitRoot(_rollupAddress, _bridgeAddress) {}
/**
* @notice Set last global exit root
* @param timestamp timestamp
*/
function setLastGlobalExitRoot(uint256 timestamp) public {
globalExitRootMap[getLastGlobalExitRoot()] = timestamp;
}
/**
* @notice Set last global exit root
* @param timestamp timestamp
*/
function setGlobalExitRoot(
bytes32 globalExitRoot,
uint256 timestamp
) public {
globalExitRootMap[globalExitRoot] = timestamp;
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../lib/DepositContract.sol";
import "hardhat/console.sol";
/**
* This contract will be used as a helper for PolygonZkEVM tests
*/
contract SendData {
/**
* @notice Send data to destination
* @param destination Destination
* @param data Data
*/
function sendData(address destination, bytes memory data) public {
destination.call(data);
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../CDKValidium.sol";
/**
* Contract responsible for managing the state and the updates of the L2 network
* There will be sequencer, which are able to send transactions. That transactions will be stored in the contract.
* The aggregators are forced to process and validate the sequencers transactions in the same order by using a verifier.
* To enter and exit of the L2 network will be used a PolygonZkEVM Bridge smart contract
*/
contract CDKValidiumMock is CDKValidium {
/**
* @param _globalExitRootManager Global exit root manager address
* @param _matic MATIC token address
* @param _rollupVerifier Rollup verifier address
* @param _bridgeAddress Bridge address
* @param _chainID L2 chainID
*/
constructor(
IPolygonZkEVMGlobalExitRoot _globalExitRootManager,
IERC20Upgradeable _matic,
IVerifierRollup _rollupVerifier,
IPolygonZkEVMBridge _bridgeAddress,
ICDKDataCommittee _dataComiteeAddress,
uint64 _chainID,
uint64 _forkID
)
CDKValidium(
_globalExitRootManager,
_matic,
_rollupVerifier,
_bridgeAddress,
_dataComiteeAddress,
_chainID,
_forkID
)
{}
/**
* @notice calculate accumulate input hash from parameters
* @param currentAccInputHash Accumulate input hash
* @param transactions Transactions
* @param globalExitRoot Global Exit Root
* @param timestamp Timestamp
* @param sequencerAddress Sequencer address
*/
function calculateAccInputHash(
bytes32 currentAccInputHash,
bytes memory transactions,
bytes32 globalExitRoot,
uint64 timestamp,
address sequencerAddress
) public pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
currentAccInputHash,
keccak256(transactions),
globalExitRoot,
timestamp,
sequencerAddress
)
);
}
/**
* @notice Return the next snark input
* @param pendingStateNum Pending state num
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
*/
function getNextSnarkInput(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot
) public view returns (uint256) {
bytes32 oldStateRoot;
uint64 currentLastVerifiedBatch = getLastVerifiedBatch();
// Use pending state if specified, otherwise use consolidated state
if (pendingStateNum != 0) {
// Check that pending state exist
// Already consolidated pending states can be used aswell
require(
pendingStateNum <= lastPendingState,
"CDKValidium::verifyBatches: pendingStateNum must be less or equal than lastPendingState"
);
// Check choosen pending state
PendingState storage currentPendingState = pendingStateTransitions[
pendingStateNum
];
// Get oldStateRoot from pending batch
oldStateRoot = currentPendingState.stateRoot;
// Check initNumBatch matches the pending state
require(
initNumBatch == currentPendingState.lastVerifiedBatch,
"CDKValidium::verifyBatches: initNumBatch must match the pending state batch"
);
} else {
// Use consolidated state
oldStateRoot = batchNumToStateRoot[initNumBatch];
require(
oldStateRoot != bytes32(0),
"CDKValidium::verifyBatches: initNumBatch state root does not exist"
);
// Check initNumBatch is inside the range
require(
initNumBatch <= currentLastVerifiedBatch,
"CDKValidium::verifyBatches: initNumBatch must be less or equal than currentLastVerifiedBatch"
);
}
// Check final batch
require(
finalNewBatch > currentLastVerifiedBatch,
"CDKValidium::verifyBatches: finalNewBatch must be bigger than currentLastVerifiedBatch"
);
// Get snark bytes
bytes memory snarkHashBytes = getInputSnarkBytes(
initNumBatch,
finalNewBatch,
newLocalExitRoot,
oldStateRoot,
newStateRoot
);
// Calulate the snark input
uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD;
return inputSnark;
}
/**
* @notice Set state root
* @param newStateRoot New State root ¡
*/
function setStateRoot(
bytes32 newStateRoot,
uint64 batchNum
) public onlyOwner {
batchNumToStateRoot[batchNum] = newStateRoot;
}
/**
* @notice Set Sequencer
* @param _numBatch New verifier
*/
function setVerifiedBatch(uint64 _numBatch) public onlyOwner {
lastVerifiedBatch = _numBatch;
}
/**
* @notice Set Sequencer
* @param _numBatch New verifier
*/
function setSequencedBatch(uint64 _numBatch) public onlyOwner {
lastBatchSequenced = _numBatch;
}
/**
* @notice Set network name
* @param _networkName New verifier
*/
function setNetworkName(string memory _networkName) public onlyOwner {
networkName = _networkName;
}
/**
* @notice Update fee mock function
* @param newLastVerifiedBatch New last verified batch
*/
function updateBatchFee(uint64 newLastVerifiedBatch) public onlyOwner {
_updateBatchFee(newLastVerifiedBatch);
}
/**
* @notice Set sequencedBatches
* @param batchNum bathc num
* @param accInputData accInputData
*/
function setSequencedBatches(
uint64 batchNum,
bytes32 accInputData,
uint64 timestamp,
uint64 lastPendingStateConsolidated
) public onlyOwner {
sequencedBatches[batchNum] = SequencedBatchData({
accInputHash: accInputData,
sequencedTimestamp: timestamp,
previousLastBatchSequenced: lastPendingStateConsolidated
});
}
/**
* @notice Allows an aggregator to verify multiple batches
* @param initNumBatch Batch which the aggregator starts the verification
* @param finalNewBatch Last batch aggregator intends to verify
* @param newLocalExitRoot New local exit root once the batch is processed
* @param newStateRoot New State root once the batch is processed
* @param proofA zk-snark input
* @param proofB zk-snark input
* @param proofC zk-snark input
*/
function trustedVerifyBatchesMock(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
uint256[2] calldata proofA,
uint256[2][2] calldata proofB,
uint256[2] calldata proofC
) public onlyOwner {
bytes32 oldStateRoot;
uint64 currentLastVerifiedBatch = getLastVerifiedBatch();
// Use pending state if specified, otherwise use consolidated state
if (pendingStateNum != 0) {
// Check that pending state exist
// Already consolidated pending states can be used aswell
require(
pendingStateNum <= lastPendingState,
"CDKValidium::verifyBatches: pendingStateNum must be less or equal than lastPendingState"
);
// Check choosen pending state
PendingState storage currentPendingState = pendingStateTransitions[
pendingStateNum
];
// Get oldStateRoot from pending batch
oldStateRoot = currentPendingState.stateRoot;
// Check initNumBatch matches the pending state
require(
initNumBatch == currentPendingState.lastVerifiedBatch,
"CDKValidium::verifyBatches: initNumBatch must match the pending state batch"
);
} else {
// Use consolidated state
oldStateRoot = batchNumToStateRoot[initNumBatch];
require(
oldStateRoot != bytes32(0),
"CDKValidium::verifyBatches: initNumBatch state root does not exist"
);
// Check initNumBatch is inside the range
require(
initNumBatch <= currentLastVerifiedBatch,
"CDKValidium::verifyBatches: initNumBatch must be less or equal than currentLastVerifiedBatch"
);
}
// Check final batch
require(
finalNewBatch > currentLastVerifiedBatch,
"CDKValidium::verifyBatches: finalNewBatch must be bigger than currentLastVerifiedBatch"
);
// Get snark bytes
bytes memory snarkHashBytes = getInputSnarkBytes(
initNumBatch,
finalNewBatch,
newLocalExitRoot,
oldStateRoot,
newStateRoot
);
// // Calulate the snark input
// uint256 inputSnark = uint256(sha256(snarkHashBytes)) % _RFIELD;
// // Verify proof
// require(
// rollupVerifier.verifyProof(proofA, proofB, proofC, [inputSnark]),
// "CDKValidium::verifyBatches: INVALID_PROOF"
// );
// // Get MATIC reward
// matic.safeTransfer(
// msg.sender,
// calculateRewardPerBatch() *
// (finalNewBatch - currentLastVerifiedBatch)
// );
// Consolidate state
lastVerifiedBatch = finalNewBatch;
batchNumToStateRoot[finalNewBatch] = newStateRoot;
// Clean pending state if any
if (lastPendingState > 0) {
lastPendingState = 0;
lastPendingStateConsolidated = 0;
}
// Interact with globalExitRootManager
globalExitRootManager.updateExitRoot(newLocalExitRoot);
emit VerifyBatchesTrustedAggregator(
finalNewBatch,
newStateRoot,
msg.sender
);
}
}
/**
*Submitted for verification at Etherscan.io on 2020-09-16
*/
/**
*Submitted for verification at Etherscan.io on 2020-09-15
*/
pragma solidity ^0.5.16;
pragma experimental ABIEncoderV2;
// From https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/Math.sol
// Subject to the MIT license.
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the addition of two unsigned integers, reverting with custom message on overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on underflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot underflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction underflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on underflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot underflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
/**
* @dev Returns the integer division of two unsigned integers.
* Reverts on division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers.
* Reverts with custom message on division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
contract Uni {
/// @notice EIP-20 token name for this token
string public constant name = "Uniswap";
/// @notice EIP-20 token symbol for this token
string public constant symbol = "UNI";
/// @notice EIP-20 token decimals for this token
uint8 public constant decimals = 18;
/// @notice Total number of tokens in circulation
uint256 public totalSupply = 1_000_000_000e18; // 1 billion Uni
/// @notice Address which may mint new tokens
address public minter;
/// @notice The timestamp after which minting may occur
uint256 public mintingAllowedAfter;
/// @notice Minimum time between mints
uint32 public constant minimumTimeBetweenMints = 1 days * 365;
/// @notice Cap on the percentage of totalSupply that can be minted at each mint
uint8 public constant mintCap = 2;
/// @notice Allowance amounts on behalf of others
mapping(address => mapping(address => uint96)) internal allowances;
/// @notice Official record of token balances for each account
mapping(address => uint96) internal balances;
/// @notice A record of each accounts delegate
mapping(address => address) public delegates;
/// @notice A checkpoint for marking number of votes from a given block
struct Checkpoint {
uint32 fromBlock;
uint96 votes;
}
/// @notice A record of votes checkpoints for each account, by index
mapping(address => mapping(uint32 => Checkpoint)) public checkpoints;
/// @notice The number of checkpoints for each account
mapping(address => uint32) public numCheckpoints;
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH =
keccak256(
"EIP712Domain(string name,uint256 chainId,address verifyingContract)"
);
/// @notice The EIP-712 typehash for the delegation struct used by the contract
bytes32 public constant DELEGATION_TYPEHASH =
keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
/// @notice The EIP-712 typehash for the permit struct used by the contract
bytes32 public constant PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
/// @notice A record of states for signing / validating signatures
mapping(address => uint256) public nonces;
/// @notice An event thats emitted when the minter address is changed
event MinterChanged(address minter, address newMinter);
/// @notice An event thats emitted when an account changes its delegate
event DelegateChanged(
address indexed delegator,
address indexed fromDelegate,
address indexed toDelegate
);
/// @notice An event thats emitted when a delegate account's vote balance changes
event DelegateVotesChanged(
address indexed delegate,
uint256 previousBalance,
uint256 newBalance
);
/// @notice The standard EIP-20 transfer event
event Transfer(address indexed from, address indexed to, uint256 amount);
/// @notice The standard EIP-20 approval event
event Approval(
address indexed owner,
address indexed spender,
uint256 amount
);
/**
* @notice Construct a new Uni token
* @param account The initial account to grant all the tokens
* @param minter_ The account with minting ability
* @param mintingAllowedAfter_ The timestamp after which minting may occur
*/
constructor(
address account,
address minter_,
uint256 mintingAllowedAfter_
) public {
require(
mintingAllowedAfter_ >= block.timestamp,
"Uni::constructor: minting can only begin after deployment"
);
balances[account] = uint96(totalSupply);
emit Transfer(address(0), account, totalSupply);
minter = minter_;
emit MinterChanged(address(0), minter);
mintingAllowedAfter = mintingAllowedAfter_;
}
/**
* @notice Change the minter address
* @param minter_ The address of the new minter
*/
function setMinter(address minter_) external {
require(
msg.sender == minter,
"Uni::setMinter: only the minter can change the minter address"
);
emit MinterChanged(minter, minter_);
minter = minter_;
}
/**
* @notice Mint new tokens
* @param dst The address of the destination account
* @param rawAmount The number of tokens to be minted
*/
function mint(address dst, uint256 rawAmount) external {
require(msg.sender == minter, "Uni::mint: only the minter can mint");
require(
block.timestamp >= mintingAllowedAfter,
"Uni::mint: minting not allowed yet"
);
require(
dst != address(0),
"Uni::mint: cannot transfer to the zero address"
);
// record the mint
mintingAllowedAfter = SafeMath.add(
block.timestamp,
minimumTimeBetweenMints
);
// mint the amount
uint96 amount = safe96(rawAmount, "Uni::mint: amount exceeds 96 bits");
require(
amount <= SafeMath.div(SafeMath.mul(totalSupply, mintCap), 100),
"Uni::mint: exceeded mint cap"
);
totalSupply = safe96(
SafeMath.add(totalSupply, amount),
"Uni::mint: totalSupply exceeds 96 bits"
);
// transfer the amount to the recipient
balances[dst] = add96(
balances[dst],
amount,
"Uni::mint: transfer amount overflows"
);
emit Transfer(address(0), dst, amount);
// move delegates
_moveDelegates(address(0), delegates[dst], amount);
}
/**
* @notice Get the number of tokens `spender` is approved to spend on behalf of `account`
* @param account The address of the account holding the funds
* @param spender The address of the account spending the funds
* @return The number of tokens approved
*/
function allowance(address account, address spender)
external
view
returns (uint256)
{
return allowances[account][spender];
}
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
* @return Whether or not the approval succeeded
*/
function approve(address spender, uint256 rawAmount)
external
returns (bool)
{
uint96 amount;
if (rawAmount == uint256(-1)) {
amount = uint96(-1);
} else {
amount = safe96(rawAmount, "Uni::approve: amount exceeds 96 bits");
}
allowances[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
/**
* @notice Triggers an approval from owner to spends
* @param owner The address to approve from
* @param spender The address to be approved
* @param rawAmount The number of tokens that are approved (2^256-1 means infinite)
* @param deadline The time at which to expire the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function permit(
address owner,
address spender,
uint256 rawAmount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
uint96 amount;
if (rawAmount == uint256(-1)) {
amount = uint96(-1);
} else {
amount = safe96(rawAmount, "Uni::permit: amount exceeds 96 bits");
}
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name)),
getChainId(),
address(this)
)
);
bytes32 structHash = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
rawAmount,
nonces[owner]++,
deadline
)
);
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", domainSeparator, structHash)
);
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), "Uni::permit: invalid signature");
require(signatory == owner, "Uni::permit: unauthorized");
require(now <= deadline, "Uni::permit: signature expired");
allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @notice Get the number of tokens held by the `account`
* @param account The address of the account to get the balance of
* @return The number of tokens held
*/
function balanceOf(address account) external view returns (uint256) {
return balances[account];
}
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param rawAmount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transfer(address dst, uint256 rawAmount) external returns (bool) {
uint96 amount = safe96(
rawAmount,
"Uni::transfer: amount exceeds 96 bits"
);
_transferTokens(msg.sender, dst, amount);
return true;
}
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param rawAmount The number of tokens to transfer
* @return Whether or not the transfer succeeded
*/
function transferFrom(
address src,
address dst,
uint256 rawAmount
) external returns (bool) {
address spender = msg.sender;
uint96 spenderAllowance = allowances[src][spender];
uint96 amount = safe96(
rawAmount,
"Uni::approve: amount exceeds 96 bits"
);
if (spender != src && spenderAllowance != uint96(-1)) {
uint96 newAllowance = sub96(
spenderAllowance,
amount,
"Uni::transferFrom: transfer amount exceeds spender allowance"
);
allowances[src][spender] = newAllowance;
emit Approval(src, spender, newAllowance);
}
_transferTokens(src, dst, amount);
return true;
}
/**
* @notice Delegate votes from `msg.sender` to `delegatee`
* @param delegatee The address to delegate votes to
*/
function delegate(address delegatee) public {
return _delegate(msg.sender, delegatee);
}
/**
* @notice Delegates votes from signatory to `delegatee`
* @param delegatee The address to delegate votes to
* @param nonce The contract state required to match the signature
* @param expiry The time at which to expire the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public {
bytes32 domainSeparator = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name)),
getChainId(),
address(this)
)
);
bytes32 structHash = keccak256(
abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry)
);
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", domainSeparator, structHash)
);
address signatory = ecrecover(digest, v, r, s);
require(
signatory != address(0),
"Uni::delegateBySig: invalid signature"
);
require(
nonce == nonces[signatory]++,
"Uni::delegateBySig: invalid nonce"
);
require(now <= expiry, "Uni::delegateBySig: signature expired");
return _delegate(signatory, delegatee);
}
/**
* @notice Gets the current votes balance for `account`
* @param account The address to get votes balance
* @return The number of current votes for `account`
*/
function getCurrentVotes(address account) external view returns (uint96) {
uint32 nCheckpoints = numCheckpoints[account];
return
nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
/**
* @notice Determine the prior number of votes for an account as of a block number
* @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
* @param account The address of the account to check
* @param blockNumber The block number to get the vote balance at
* @return The number of votes the account had as of the given block
*/
function getPriorVotes(address account, uint256 blockNumber)
public
view
returns (uint96)
{
require(
blockNumber < block.number,
"Uni::getPriorVotes: not yet determined"
);
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
// First check most recent balance
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
// Next check implicit zero balance
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
function _delegate(address delegator, address delegatee) internal {
address currentDelegate = delegates[delegator];
uint96 delegatorBalance = balances[delegator];
delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
function _transferTokens(
address src,
address dst,
uint96 amount
) internal {
require(
src != address(0),
"Uni::_transferTokens: cannot transfer from the zero address"
);
require(
dst != address(0),
"Uni::_transferTokens: cannot transfer to the zero address"
);
balances[src] = sub96(
balances[src],
amount,
"Uni::_transferTokens: transfer amount exceeds balance"
);
balances[dst] = add96(
balances[dst],
amount,
"Uni::_transferTokens: transfer amount overflows"
);
emit Transfer(src, dst, amount);
_moveDelegates(delegates[src], delegates[dst], amount);
}
function _moveDelegates(
address srcRep,
address dstRep,
uint96 amount
) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint96 srcRepOld = srcRepNum > 0
? checkpoints[srcRep][srcRepNum - 1].votes
: 0;
uint96 srcRepNew = sub96(
srcRepOld,
amount,
"Uni::_moveVotes: vote amount underflows"
);
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint96 dstRepOld = dstRepNum > 0
? checkpoints[dstRep][dstRepNum - 1].votes
: 0;
uint96 dstRepNew = add96(
dstRepOld,
amount,
"Uni::_moveVotes: vote amount overflows"
);
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
function _writeCheckpoint(
address delegatee,
uint32 nCheckpoints,
uint96 oldVotes,
uint96 newVotes
) internal {
uint32 blockNumber = safe32(
block.number,
"Uni::_writeCheckpoint: block number exceeds 32 bits"
);
if (
nCheckpoints > 0 &&
checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber
) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(
blockNumber,
newVotes
);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
function safe32(uint256 n, string memory errorMessage)
internal
pure
returns (uint32)
{
require(n < 2**32, errorMessage);
return uint32(n);
}
function safe96(uint256 n, string memory errorMessage)
internal
pure
returns (uint96)
{
require(n < 2**96, errorMessage);
return uint96(n);
}
function add96(
uint96 a,
uint96 b,
string memory errorMessage
) internal pure returns (uint96) {
uint96 c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub96(
uint96 a,
uint96 b,
string memory errorMessage
) internal pure returns (uint96) {
require(b <= a, errorMessage);
return a - b;
}
function getChainId() internal pure returns (uint256) {
uint256 chainId;
assembly {
chainId := chainid()
}
return chainId;
}
}
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.20;
import "../interfaces/IVerifierRollup.sol";
contract VerifierRollupHelperMock is IVerifierRollup {
function verifyProof(
bytes32[24] calldata proof,
uint256[1] memory pubSignals
) public view override returns (bool) {
return true;
}
}
// SPDX-License-Identifier: GPL-3.0
/*
Copyright 2021 0KIMS association.
This file is generated with [snarkJS](https://github.com/iden3/snarkjs).
snarkJS is a free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
snarkJS is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
License for more details.
You should have received a copy of the GNU General Public License
along with snarkJS. If not, see <https://www.gnu.org/licenses/>.
*/
pragma solidity >=0.7.0 <0.9.0;
contract FflonkVerifier {
uint32 constant n = 16777216; // Domain size
// Verification Key data
uint256 constant k1 = 2; // Plonk k1 multiplicative factor to force distinct cosets of H
uint256 constant k2 = 3; // Plonk k2 multiplicative factor to force distinct cosets of H
// OMEGAS
// Omega, Omega^{1/3}
uint256 constant w1 = 5709868443893258075976348696661355716898495876243883251619397131511003808859;
uint256 constant wr = 18200100796661656210024324131237448517259556535315737226009542456080026430510;
// Omega_3, Omega_3^2
uint256 constant w3 = 21888242871839275217838484774961031246154997185409878258781734729429964517155;
uint256 constant w3_2 = 4407920970296243842393367215006156084916469457145843978461;
// Omega_4, Omega_4^2, Omega_4^3
uint256 constant w4 = 21888242871839275217838484774961031246007050428528088939761107053157389710902;
uint256 constant w4_2 = 21888242871839275222246405745257275088548364400416034343698204186575808495616;
uint256 constant w4_3 = 4407920970296243842541313971887945403937097133418418784715;
// Omega_8, Omega_8^2, Omega_8^3, Omega_8^4, Omega_8^5, Omega_8^6, Omega_8^7
uint256 constant w8_1 = 19540430494807482326159819597004422086093766032135589407132600596362845576832;
uint256 constant w8_2 = 21888242871839275217838484774961031246007050428528088939761107053157389710902;
uint256 constant w8_3 = 13274704216607947843011480449124596415239537050559949017414504948711435969894;
uint256 constant w8_4 = 21888242871839275222246405745257275088548364400416034343698204186575808495616;
uint256 constant w8_5 = 2347812377031792896086586148252853002454598368280444936565603590212962918785;
uint256 constant w8_6 = 4407920970296243842541313971887945403937097133418418784715;
uint256 constant w8_7 = 8613538655231327379234925296132678673308827349856085326283699237864372525723;
// Verifier preprocessed input C_0(x)·[1]_1
uint256 constant C0x = 7436841426934271843999872946312645822871802402068881571108027575346498207286;
uint256 constant C0y = 18448034242258174646222819724328439025708531082946938915005051387020977719791;
// Verifier preprocessed input x·[1]_2
uint256 constant X2x1 = 21831381940315734285607113342023901060522397560371972897001948545212302161822;
uint256 constant X2x2 = 17231025384763736816414546592865244497437017442647097510447326538965263639101;
uint256 constant X2y1 = 2388026358213174446665280700919698872609886601280537296205114254867301080648;
uint256 constant X2y2 = 11507326595632554467052522095592665270651932854513688777769618397986436103170;
// Scalar field size
uint256 constant q = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
// Base field size
uint256 constant qf = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
// [1]_1
uint256 constant G1x = 1;
uint256 constant G1y = 2;
// [1]_2
uint256 constant G2x1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781;
uint256 constant G2x2 = 11559732032986387107991004021392285783925812861821192530917403151452391805634;
uint256 constant G2y1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930;
uint256 constant G2y2 = 4082367875863433681332203403145435568316851327593401208105741076214120093531;
// Proof calldata
// Byte offset of every parameter of the calldata
// Polynomial commitments
uint16 constant pC1 = 4 + 0; // [C1]_1
uint16 constant pC2 = 4 + 32*2; // [C2]_1
uint16 constant pW1 = 4 + 32*4; // [W]_1
uint16 constant pW2 = 4 + 32*6; // [W']_1
// Opening evaluations
uint16 constant pEval_ql = 4 + 32*8; // q_L(xi)
uint16 constant pEval_qr = 4 + 32*9; // q_R(xi)
uint16 constant pEval_qm = 4 + 32*10; // q_M(xi)
uint16 constant pEval_qo = 4 + 32*11; // q_O(xi)
uint16 constant pEval_qc = 4 + 32*12; // q_C(xi)
uint16 constant pEval_s1 = 4 + 32*13; // S_{sigma_1}(xi)
uint16 constant pEval_s2 = 4 + 32*14; // S_{sigma_2}(xi)
uint16 constant pEval_s3 = 4 + 32*15; // S_{sigma_3}(xi)
uint16 constant pEval_a = 4 + 32*16; // a(xi)
uint16 constant pEval_b = 4 + 32*17; // b(xi)
uint16 constant pEval_c = 4 + 32*18; // c(xi)
uint16 constant pEval_z = 4 + 32*19; // z(xi)
uint16 constant pEval_zw = 4 + 32*20; // z_omega(xi)
uint16 constant pEval_t1w = 4 + 32*21; // T_1(xi omega)
uint16 constant pEval_t2w = 4 + 32*22; // T_2(xi omega)
uint16 constant pEval_inv = 4 + 32*23; // inv(batch) sent by the prover to avoid any inverse calculation to save gas,
// we check the correctness of the inv(batch) by computing batch
// and checking inv(batch) * batch == 1
// Memory data
// Challenges
uint16 constant pAlpha = 0; // alpha challenge
uint16 constant pBeta = 32; // beta challenge
uint16 constant pGamma = 64; // gamma challenge
uint16 constant pY = 96; // y challenge
uint16 constant pXiSeed = 128; // xi seed, from this value we compute xi = xiSeed^24
uint16 constant pXiSeed2 = 160; // (xi seed)^2
uint16 constant pXi = 192; // xi challenge
// Roots
// S_0 = roots_8(xi) = { h_0, h_0w_8, h_0w_8^2, h_0w_8^3, h_0w_8^4, h_0w_8^5, h_0w_8^6, h_0w_8^7 }
uint16 constant pH0w8_0 = 224;
uint16 constant pH0w8_1 = 256;
uint16 constant pH0w8_2 = 288;
uint16 constant pH0w8_3 = 320;
uint16 constant pH0w8_4 = 352;
uint16 constant pH0w8_5 = 384;
uint16 constant pH0w8_6 = 416;
uint16 constant pH0w8_7 = 448;
// S_1 = roots_4(xi) = { h_1, h_1w_4, h_1w_4^2, h_1w_4^3 }
uint16 constant pH1w4_0 = 480;
uint16 constant pH1w4_1 = 512;
uint16 constant pH1w4_2 = 544;
uint16 constant pH1w4_3 = 576;
// S_2 = roots_3(xi) U roots_3(xi omega)
// roots_3(xi) = { h_2, h_2w_3, h_2w_3^2 }
uint16 constant pH2w3_0 = 608;
uint16 constant pH2w3_1 = 640;
uint16 constant pH2w3_2 = 672;
// roots_3(xi omega) = { h_3, h_3w_3, h_3w_3^2 }
uint16 constant pH3w3_0 = 704;
uint16 constant pH3w3_1 = 736;
uint16 constant pH3w3_2 = 768;
uint16 constant pPi = 800; // PI(xi)
uint16 constant pR0 = 832; // r0(y)
uint16 constant pR1 = 864; // r1(y)
uint16 constant pR2 = 896; // r2(y)
uint16 constant pF = 928; // [F]_1, 64 bytes
uint16 constant pE = 992; // [E]_1, 64 bytes
uint16 constant pJ = 1056; // [J]_1, 64 bytes
uint16 constant pZh = 1184; // Z_H(xi)
// From this point we write all the variables that must be computed using the Montgomery batch inversion
uint16 constant pZhInv = 1216; // 1/Z_H(xi)
uint16 constant pDenH1 = 1248; // 1/( (y-h_1w_4) (y-h_1w_4^2) (y-h_1w_4^3) (y-h_1w_4^4) )
uint16 constant pDenH2 = 1280; // 1/( (y-h_2w_3) (y-h_2w_3^2) (y-h_2w_3^3) (y-h_3w_3) (y-h_3w_3^2) (y-h_3w_3^3) )
uint16 constant pLiS0Inv = 1312; // Reserve 8 * 32 bytes to compute r_0(X)
uint16 constant pLiS1Inv = 1568; // Reserve 4 * 32 bytes to compute r_1(X)
uint16 constant pLiS2Inv = 1696; // Reserve 6 * 32 bytes to compute r_2(X)
// Lagrange evaluations
uint16 constant pEval_l1 = 1888;
uint16 constant lastMem = 1920;
function verifyProof(bytes32[24] calldata proof, uint256[1] calldata pubSignals) public view returns (bool) {
assembly {
// Computes the inverse of an array of values
// See https://vitalik.ca/general/2018/07/21/starks_part_3.html in section where explain fields operations
// To save the inverse to be computed on chain the prover sends the inverse as an evaluation in commits.eval_inv
function inverseArray(pMem) {
let pAux := mload(0x40) // Point to the next free position
let acc := mload(add(pMem,pZhInv)) // Read the first element
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pDenH1)), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pDenH2)), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pLiS0Inv)), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 32))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 64))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 96))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 128))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 160))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 192))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 224))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pLiS1Inv)), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 32))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 64))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 96))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pLiS2Inv)), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 32))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 64))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 96))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 128))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 160))), q)
mstore(pAux, acc)
pAux := add(pAux, 32)
acc := mulmod(acc, mload(add(pMem, pEval_l1)), q)
mstore(pAux, acc)
let inv := calldataload(pEval_inv)
// Before using the inverse sent by the prover the verifier checks inv(batch) * batch === 1
if iszero(eq(1, mulmod(acc, inv, q))) {
mstore(0, 0)
return(0,0x20)
}
acc := inv
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pEval_l1)), q)
mstore(add(pMem, pEval_l1), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 160))), q)
mstore(add(pMem, add(pLiS2Inv, 160)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 128))), q)
mstore(add(pMem, add(pLiS2Inv, 128)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 96))), q)
mstore(add(pMem, add(pLiS2Inv, 96)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 64))), q)
mstore(add(pMem, add(pLiS2Inv, 64)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS2Inv, 32))), q)
mstore(add(pMem, add(pLiS2Inv, 32)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pLiS2Inv)), q)
mstore(add(pMem, pLiS2Inv), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 96))), q)
mstore(add(pMem, add(pLiS1Inv, 96)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 64))), q)
mstore(add(pMem, add(pLiS1Inv, 64)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS1Inv, 32))), q)
mstore(add(pMem, add(pLiS1Inv, 32)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pLiS1Inv)), q)
mstore(add(pMem, pLiS1Inv), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 224))), q)
mstore(add(pMem, add(pLiS0Inv, 224)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 192))), q)
mstore(add(pMem, add(pLiS0Inv, 192)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 160))), q)
mstore(add(pMem, add(pLiS0Inv, 160)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 128))), q)
mstore(add(pMem, add(pLiS0Inv, 128)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 96))), q)
mstore(add(pMem, add(pLiS0Inv, 96)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 64))), q)
mstore(add(pMem, add(pLiS0Inv, 64)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, add(pLiS0Inv, 32))), q)
mstore(add(pMem, add(pLiS0Inv, 32)), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pLiS0Inv)), q)
mstore(add(pMem, pLiS0Inv), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pDenH2)), q)
mstore(add(pMem, pDenH2), inv)
pAux := sub(pAux, 32)
inv := mulmod(acc, mload(pAux), q)
acc := mulmod(acc, mload(add(pMem, pDenH1)), q)
mstore(add(pMem, pDenH1), inv)
mstore(add(pMem, pZhInv), acc)
}
function checkField(v) {
if iszero(lt(v, q)) {
mstore(0, 0)
return(0, 0x20)
}
}
function checkPointBelongsToBN128Curve(p) {
let x := calldataload(p)
let y := calldataload(add(p, 32))
// Check that the point is on the curve
// y^2 = x^3 + 3
let x3_3 := addmod(mulmod(x, mulmod(x, x, qf), qf), 3, qf)
let y2 := mulmod(y, y, qf)
if iszero(eq(x3_3, y2)) {
mstore(0, 0)
return(0, 0x20)
}
}
// Validate all the evaluations sent by the prover ∈ F
function checkInput() {
// Check proof commitments fullfill bn128 curve equation Y^2 = X^3 + 3
checkPointBelongsToBN128Curve(pC1)
checkPointBelongsToBN128Curve(pC2)
checkPointBelongsToBN128Curve(pW1)
checkPointBelongsToBN128Curve(pW2)
checkField(calldataload(pEval_ql))
checkField(calldataload(pEval_qr))
checkField(calldataload(pEval_qm))
checkField(calldataload(pEval_qo))
checkField(calldataload(pEval_qc))
checkField(calldataload(pEval_s1))
checkField(calldataload(pEval_s2))
checkField(calldataload(pEval_s3))
checkField(calldataload(pEval_a))
checkField(calldataload(pEval_b))
checkField(calldataload(pEval_c))
checkField(calldataload(pEval_z))
checkField(calldataload(pEval_zw))
checkField(calldataload(pEval_t1w))
checkField(calldataload(pEval_t2w))
checkField(calldataload(pEval_inv))
// Points are checked in the point operations precompiled smart contracts
}
function computeChallenges(pMem, pPublic) {
// Compute challenge.beta & challenge.gamma
mstore(add(pMem, 1920 ), C0x)
mstore(add(pMem, 1952 ), C0y)
mstore(add(pMem, 1984), calldataload(pPublic))
mstore(add(pMem, 2016 ), calldataload(pC1))
mstore(add(pMem, 2048 ), calldataload(add(pC1, 32)))
mstore(add(pMem, pBeta), mod(keccak256(add(pMem, lastMem), 160), q))
mstore(add(pMem, pGamma), mod(keccak256(add(pMem, pBeta), 32), q))
// Get xiSeed & xiSeed2
mstore(add(pMem, lastMem), mload(add(pMem, pGamma)))
mstore(add(pMem, 1952), calldataload(pC2))
mstore(add(pMem, 1984), calldataload(add(pC2, 32)))
let xiSeed := mod(keccak256(add(pMem, lastMem), 96), q)
mstore(add(pMem, pXiSeed), xiSeed)
mstore(add(pMem, pXiSeed2), mulmod(xiSeed, xiSeed, q))
// Compute roots.S0.h0w8
mstore(add(pMem, pH0w8_0), mulmod(mload(add(pMem, pXiSeed2)), mload(add(pMem, pXiSeed)), q))
mstore(add(pMem, pH0w8_1), mulmod(mload(add(pMem, pH0w8_0)), w8_1, q))
mstore(add(pMem, pH0w8_2), mulmod(mload(add(pMem, pH0w8_0)), w8_2, q))
mstore(add(pMem, pH0w8_3), mulmod(mload(add(pMem, pH0w8_0)), w8_3, q))
mstore(add(pMem, pH0w8_4), mulmod(mload(add(pMem, pH0w8_0)), w8_4, q))
mstore(add(pMem, pH0w8_5), mulmod(mload(add(pMem, pH0w8_0)), w8_5, q))
mstore(add(pMem, pH0w8_6), mulmod(mload(add(pMem, pH0w8_0)), w8_6, q))
mstore(add(pMem, pH0w8_7), mulmod(mload(add(pMem, pH0w8_0)), w8_7, q))
// Compute roots.S1.h1w4
mstore(add(pMem, pH1w4_0), mulmod(mload(add(pMem, pH0w8_0)), mload(add(pMem, pH0w8_0)), q))
mstore(add(pMem, pH1w4_1), mulmod(mload(add(pMem, pH1w4_0)), w4, q))
mstore(add(pMem, pH1w4_2), mulmod(mload(add(pMem, pH1w4_0)), w4_2, q))
mstore(add(pMem, pH1w4_3), mulmod(mload(add(pMem, pH1w4_0)), w4_3, q))
// Compute roots.S2.h2w3
mstore(add(pMem, pH2w3_0), mulmod(mload(add(pMem, pH1w4_0)), mload(add(pMem, pXiSeed2)), q))
mstore(add(pMem, pH2w3_1), mulmod(mload(add(pMem, pH2w3_0)), w3, q))
mstore(add(pMem, pH2w3_2), mulmod(mload(add(pMem, pH2w3_0)), w3_2, q))
// Compute roots.S2.h2w3
mstore(add(pMem, pH3w3_0), mulmod(mload(add(pMem, pH2w3_0)), wr, q))
mstore(add(pMem, pH3w3_1), mulmod(mload(add(pMem, pH3w3_0)), w3, q))
mstore(add(pMem, pH3w3_2), mulmod(mload(add(pMem, pH3w3_0)), w3_2, q))
let xin := mulmod(mulmod(mload(add(pMem, pH2w3_0)), mload(add(pMem, pH2w3_0)), q), mload(add(pMem, pH2w3_0)), q)
mstore(add(pMem, pXi), xin)
// Compute xi^n
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mulmod(xin, xin, q)
xin:= mod(add(sub(xin, 1), q), q)
mstore(add(pMem, pZh), xin)
mstore(add(pMem, pZhInv), xin) // We will invert later together with lagrange pols
// Compute challenge.alpha
mstore(add(pMem, lastMem), xiSeed)
calldatacopy(add(pMem, 1952), pEval_ql, 480)
mstore(add(pMem, pAlpha), mod(keccak256(add(pMem, lastMem), 512), q))
// Compute challenge.y
mstore(add(pMem, lastMem), mload(add(pMem, pAlpha)))
mstore(add(pMem, 1952 ), calldataload(pW1))
mstore(add(pMem, 1984 ), calldataload(add(pW1, 32)))
mstore(add(pMem, pY), mod(keccak256(add(pMem, lastMem), 96), q))
}
function computeLiS0(pMem) {
let root0 := mload(add(pMem, pH0w8_0))
let y := mload(add(pMem, pY))
let den1 := 1
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(8, den1, q)
let den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 0), 8), 32))))
let den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(0, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 0)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 1), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(1, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 32)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 2), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(2, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 64)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 3), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(3, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 96)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 4), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(4, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 128)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 5), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(5, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 160)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 6), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(6, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 192)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH0w8_0, mul(mod(mul(7, 7), 8), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH0w8_0, mul(7, 32))))), q), q)
mstore(add(pMem, add(pLiS0Inv, 224)), mulmod(den1, mulmod(den2, den3, q), q))
}
function computeLiS1(pMem) {
let root0 := mload(add(pMem, pH1w4_0))
let y := mload(add(pMem, pY))
let den1 := 1
den1 := mulmod(den1, root0, q)
den1 := mulmod(den1, root0, q)
den1 := mulmod(4, den1, q)
let den2 := mload(add(pMem, add(pH1w4_0, mul(mod(mul(3, 0), 4), 32))))
let den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH1w4_0, mul(0, 32))))), q), q)
mstore(add(pMem, add(pLiS1Inv, 0)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH1w4_0, mul(mod(mul(3, 1), 4), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH1w4_0, mul(1, 32))))), q), q)
mstore(add(pMem, add(pLiS1Inv, 32)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH1w4_0, mul(mod(mul(3, 2), 4), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH1w4_0, mul(2, 32))))), q), q)
mstore(add(pMem, add(pLiS1Inv, 64)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH1w4_0, mul(mod(mul(3, 3), 4), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH1w4_0, mul(3, 32))))), q), q)
mstore(add(pMem, add(pLiS1Inv, 96)), mulmod(den1, mulmod(den2, den3, q), q))
}
function computeLiS2(pMem) {
let y := mload(add(pMem, pY))
let den1 := mulmod(mulmod(3,mload(add(pMem, pH2w3_0)),q), addmod(mload(add(pMem, pXi)) ,mod(sub(q, mulmod(mload(add(pMem, pXi)), w1 ,q)), q), q), q)
let den2 := mload(add(pMem, add(pH2w3_0, mul(mod(mul(2, 0), 3), 32))))
let den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH2w3_0, mul(0, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 0)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH2w3_0, mul(mod(mul(2, 1), 3), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH2w3_0, mul(1, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 32)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH2w3_0, mul(mod(mul(2, 2), 3), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH2w3_0, mul(2, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 64)), mulmod(den1, mulmod(den2, den3, q), q))
den1 := mulmod(mulmod(3,mload(add(pMem, pH3w3_0)),q), addmod(mulmod(mload(add(pMem, pXi)), w1 ,q),mod(sub(q, mload(add(pMem, pXi))), q), q), q)
den2 := mload(add(pMem, add(pH3w3_0, mul(mod(mul(2, 0), 3), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH3w3_0, mul(0, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 96)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH3w3_0, mul(mod(mul(2, 1), 3), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH3w3_0, mul(1, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 128)), mulmod(den1, mulmod(den2, den3, q), q))
den2 := mload(add(pMem, add(pH3w3_0, mul(mod(mul(2, 2), 3), 32))))
den3 := addmod(y, mod(sub(q, mload(add(pMem, add(pH3w3_0, mul(2, 32))))), q), q)
mstore(add(pMem, add(pLiS2Inv, 160)), mulmod(den1, mulmod(den2, den3, q), q))
}
// Prepare all the denominators that must be inverted, placed them in consecutive memory addresses
function computeInversions(pMem) {
// 1/ZH(xi) used in steps 8 and 9 of the verifier to multiply by 1/Z_H(xi)
// Value computed during computeChallenges function and stores in pMem+pZhInv
// 1/((y - h1) (y - h1w4) (y - h1w4_2) (y - h1w4_3))
// used in steps 10 and 11 of the verifier
let y := mload(add(pMem, pY))
let w := addmod(y, mod(sub(q, mload(add(pMem, pH1w4_0))), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH1w4_1))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH1w4_2))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH1w4_3))), q), q), q)
mstore(add(pMem, pDenH1), w)
// 1/((y - h2) (y - h2w3) (y - h2w3_2) (y - h3) (y - h3w3) (y - h3w3_2))
w := addmod(y, mod(sub(q, mload(add(pMem, pH2w3_0))), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH2w3_1))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH2w3_2))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH3w3_0))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH3w3_1))), q), q), q)
w := mulmod(w, addmod(y, mod(sub(q, mload(add(pMem, pH3w3_2))), q), q), q)
mstore(add(pMem, pDenH2), w)
// Denominator needed in the verifier when computing L_i^{S0}(X)
computeLiS0(pMem)
// Denominator needed in the verifier when computing L_i^{S1}(X)
computeLiS1(pMem)
// Denominator needed in the verifier when computing L_i^{S2}(X)
computeLiS2(pMem)
// L_i where i from 1 to num public inputs, needed in step 6 and 7 of the verifier to compute L_1(xi) and PI(xi)
w := 1
let xi := mload(add(pMem, pXi))
mstore(add(pMem, pEval_l1), mulmod(n, mod(add(sub(xi, w), q), q), q))
// Execute Montgomery batched inversions of the previous prepared values
inverseArray(pMem) }
// Compute Lagrange polynomial evaluation L_i(xi)
function computeLagrange(pMem) {
let zh := mload(add(pMem, pZh))
let w := 1
mstore(add(pMem, pEval_l1 ), mulmod(mload(add(pMem, pEval_l1 )), zh, q))
}
// Compute public input polynomial evaluation PI(xi)
function computePi(pMem, pPub) {
let pi := 0
pi := mod(add(sub(pi, mulmod(mload(add(pMem, pEval_l1)), calldataload(pPub), q)), q), q)
mstore(add(pMem, pPi), pi)
}
// Compute r0(y) by interpolating the polynomial r0(X) using 8 points (x,y)
// where x = {h9, h0w8, h0w8^2, h0w8^3, h0w8^4, h0w8^5, h0w8^6, h0w8^7}
// and y = {C0(h0), C0(h0w8), C0(h0w8^2), C0(h0w8^3), C0(h0w8^4), C0(h0w8^5), C0(h0w8^6), C0(h0w8^7)}
// and computing C0(xi)
function computeR0(pMem) {
let num := 1
let y := mload(add(pMem, pY))
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := addmod(num, mod(sub(q, mload(add(pMem, pXi))), q), q)
let res
let h0w80
let c0Value
let h0w8i
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_0))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 0))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_1))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 32))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_2))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 64))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_3))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 96))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_4))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 128))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_5))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 160))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_6))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 192))), q), q), q)
// Compute c0Value = ql + (h0w8i) qr + (h0w8i)^2 qo + (h0w8i)^3 qm + (h0w8i)^4 qc +
// + (h0w8i)^5 S1 + (h0w8i)^6 S2 + (h0w8i)^7 S3
h0w80 := mload(add(pMem, pH0w8_7))
c0Value := addmod(calldataload(pEval_ql), mulmod(calldataload(pEval_qr), h0w80, q), q)
h0w8i := mulmod(h0w80, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qo), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qm), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_qc), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s1), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s2), h0w8i, q), q)
h0w8i := mulmod(h0w8i, h0w80, q)
c0Value := addmod(c0Value, mulmod(calldataload(pEval_s3), h0w8i, q), q)
res := addmod(res, mulmod(c0Value, mulmod(num, mload(add(pMem, add(pLiS0Inv, 224))), q), q), q)
mstore(add(pMem, pR0), res)
}
// Compute r1(y) by interpolating the polynomial r1(X) using 4 points (x,y)
// where x = {h1, h1w4, h1w4^2, h1w4^3}
// and y = {C1(h1), C1(h1w4), C1(h1w4^2), C1(h1w4^3)}
// and computing T0(xi)
function computeR1(pMem) {
let num := 1
let y := mload(add(pMem, pY))
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := mulmod(num, y, q)
num := addmod(num, mod(sub(q, mload(add(pMem, pXi))), q), q)
let t0
let evalA := calldataload(pEval_a)
let evalB := calldataload(pEval_b)
let evalC := calldataload(pEval_c)
t0 := mulmod(calldataload(pEval_ql), evalA, q)
t0 := addmod(t0, mulmod(calldataload(pEval_qr), evalB, q) ,q)
t0 := addmod(t0, mulmod(calldataload(pEval_qm), mulmod(evalA, evalB, q), q) ,q)
t0 := addmod(t0, mulmod(calldataload(pEval_qo), evalC, q) ,q)
t0 := addmod(t0, calldataload(pEval_qc) ,q)
t0 := addmod(t0, mload(add(pMem, pPi)), q)
t0 := mulmod(t0, mload(add(pMem, pZhInv)), q)
let res
let c1Value
let h1w4
let square
c1Value := evalA
h1w4 := mload(add(pMem, pH1w4_0))
c1Value := addmod(c1Value, mulmod(h1w4, evalB, q), q)
square := mulmod(h1w4, h1w4, q)
c1Value := addmod(c1Value, mulmod(square, evalC, q), q)
c1Value := addmod(c1Value, mulmod(mulmod(square, h1w4, q), t0, q), q)
res := addmod(res, mulmod(c1Value, mulmod(num, mload(add(pMem, add(pLiS1Inv, mul(0, 32)))), q), q), q)
c1Value := evalA
h1w4 := mload(add(pMem, pH1w4_1))
c1Value := addmod(c1Value, mulmod(h1w4, evalB, q), q)
square := mulmod(h1w4, h1w4, q)
c1Value := addmod(c1Value, mulmod(square, evalC, q), q)
c1Value := addmod(c1Value, mulmod(mulmod(square, h1w4, q), t0, q), q)
res := addmod(res, mulmod(c1Value, mulmod(num, mload(add(pMem, add(pLiS1Inv, mul(1, 32)))), q), q), q)
c1Value := evalA
h1w4 := mload(add(pMem, pH1w4_2))
c1Value := addmod(c1Value, mulmod(h1w4, evalB, q), q)
square := mulmod(h1w4, h1w4, q)
c1Value := addmod(c1Value, mulmod(square, evalC, q), q)
c1Value := addmod(c1Value, mulmod(mulmod(square, h1w4, q), t0, q), q)
res := addmod(res, mulmod(c1Value, mulmod(num, mload(add(pMem, add(pLiS1Inv, mul(2, 32)))), q), q), q)
c1Value := evalA
h1w4 := mload(add(pMem, pH1w4_3))
c1Value := addmod(c1Value, mulmod(h1w4, evalB, q), q)
square := mulmod(h1w4, h1w4, q)
c1Value := addmod(c1Value, mulmod(square, evalC, q), q)
c1Value := addmod(c1Value, mulmod(mulmod(square, h1w4, q), t0, q), q)
res := addmod(res, mulmod(c1Value, mulmod(num, mload(add(pMem, add(pLiS1Inv, mul(3, 32)))), q), q), q)
mstore(add(pMem, pR1), res)
}
// Compute r2(y) by interpolating the polynomial r2(X) using 6 points (x,y)
// where x = {[h2, h2w3, h2w3^2], [h3, h3w3, h3w3^2]}
// and y = {[C2(h2), C2(h2w3), C2(h2w3^2)], [C2(h3), C2(h3w3), C2(h3w3^2)]}
// and computing T1(xi) and T2(xi)
function computeR2(pMem) {
let y := mload(add(pMem, pY))
let num := 1
num := mulmod(y, num, q)
num := mulmod(y, num, q)
num := mulmod(y, num, q)
num := mulmod(y, num, q)
num := mulmod(y, num, q)
num := mulmod(y, num, q)
let num2 := 1
num2 := mulmod(y, num2, q)
num2 := mulmod(y, num2, q)
num2 := mulmod(y, num2, q)
num2 := mulmod(num2, addmod(mulmod(mload(add(pMem, pXi)), w1 ,q), mload(add(pMem, pXi)), q), q)
num := addmod(num, mod(sub(q, num2), q), q)
num2 := mulmod(mulmod(mload(add(pMem, pXi)), w1 ,q), mload(add(pMem, pXi)), q)
num := addmod(num, num2, q)
let t1
let t2
let betaXi := mulmod(mload(add(pMem, pBeta)), mload(add(pMem, pXi)), q)
let gamma := mload(add(pMem, pGamma))
t2 := addmod(calldataload( pEval_a), addmod(betaXi, gamma, q) ,q)
t2 := mulmod(t2,
addmod(calldataload( pEval_b),
addmod(mulmod(betaXi, k1, q), gamma, q) ,q), q)
t2 := mulmod(t2,
addmod(calldataload( pEval_c),
addmod(mulmod(betaXi, k2, q), gamma, q) ,q), q)
t2 := mulmod(t2, calldataload(pEval_z), q)
//Let's use t1 as a temporal variable to save one local
t1 := addmod(calldataload(pEval_a), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s1), q), gamma, q) ,q)
t1 := mulmod(t1,
addmod(calldataload(pEval_b), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s2), q), gamma, q) ,q), q)
t1 := mulmod(t1,
addmod(calldataload(pEval_c), addmod(mulmod(mload(add(pMem, pBeta)), calldataload(pEval_s3), q), gamma, q) ,q), q)
t1 := mulmod(t1, calldataload(pEval_zw), q)
t2:= addmod(t2, mod(sub(q, t1), q), q)
t2 := mulmod(t2, mload(add(pMem, pZhInv)), q)
// Compute T1(xi)
t1 := sub(calldataload(pEval_z), 1)
t1 := mulmod(t1, mload(add(pMem, pEval_l1)) ,q)
t1 := mulmod(t1, mload(add(pMem, pZhInv)) ,q)
// Let's use local variable gamma to save the result
gamma:=0
let hw
let c2Value
hw := mload(add(pMem, pH2w3_0))
c2Value := addmod(calldataload(pEval_z), mulmod(hw, t1, q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), t2, q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(0, 32)))), q), q), q)
hw := mload(add(pMem, pH2w3_1))
c2Value := addmod(calldataload(pEval_z), mulmod(hw, t1, q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), t2, q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(1, 32)))), q), q), q)
hw := mload(add(pMem, pH2w3_2))
c2Value := addmod(calldataload(pEval_z), mulmod(hw, t1, q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), t2, q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(2, 32)))), q), q), q)
hw := mload(add(pMem, pH3w3_0))
c2Value := addmod(calldataload(pEval_zw), mulmod(hw, calldataload(pEval_t1w), q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), calldataload(pEval_t2w), q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(3, 32)))), q), q), q)
hw := mload(add(pMem, pH3w3_1))
c2Value := addmod(calldataload(pEval_zw), mulmod(hw, calldataload(pEval_t1w), q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), calldataload(pEval_t2w), q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(4, 32)))), q), q), q)
hw := mload(add(pMem, pH3w3_2))
c2Value := addmod(calldataload(pEval_zw), mulmod(hw, calldataload(pEval_t1w), q), q)
c2Value := addmod(c2Value, mulmod(mulmod(hw, hw, q), calldataload(pEval_t2w), q), q)
gamma := addmod(gamma, mulmod(c2Value, mulmod(num, mload(add(pMem, add(pLiS2Inv, mul(5, 32)))), q), q), q)
mstore(add(pMem, pR2), gamma)
}
// G1 function to accumulate a G1 value to an address
function g1_acc(pR, pP) {
let mIn := mload(0x40)
mstore(mIn, mload(pR))
mstore(add(mIn, 32), mload(add(pR, 32)))
mstore(add(mIn, 64), mload(pP))
mstore(add(mIn, 96), mload(add(pP, 32)))
let success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
// G1 function to multiply a G1 value to value in an address
function g1_mulAcc(pR, pP, s) {
let success
let mIn := mload(0x40)
mstore(mIn, calldataload(pP))
mstore(add(mIn, 32), calldataload(add(pP, 32)))
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
// G1 function to multiply a G1 value(x,y) to value in an address
function g1_mulAccC(pR, x, y, s) {
let success
let mIn := mload(0x40)
mstore(mIn, x)
mstore(add(mIn, 32), y)
mstore(add(mIn, 64), s)
success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
mstore(add(mIn, 64), mload(pR))
mstore(add(mIn, 96), mload(add(pR, 32)))
success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64)
if iszero(success) {
mstore(0, 0)
return(0, 0x20)
}
}
function computeFEJ(pMem) {
// Prepare shared numerator between F, E and J to reuse it
let y := mload(add(pMem, pY))
let numerator := addmod(y, mod(sub(q, mload(add(pMem, pH0w8_0))), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_1))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_2))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_3))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_4))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_5))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_6))), q), q), q)
numerator := mulmod(numerator, addmod(y, mod(sub(q, mload(add(pMem, pH0w8_7))), q), q), q)
// Prepare shared quotient between F and E to reuse it
let quotient1 := mulmod(mload(add(pMem, pAlpha)), mulmod(numerator, mload(add(pMem, pDenH1)), q), q)
let quotient2 := mulmod(mulmod(mload(add(pMem, pAlpha)), mload(add(pMem, pAlpha)), q), mulmod(numerator, mload(add(pMem, pDenH2)), q), q)
// Compute full batched polynomial commitment [F]_1
mstore(add(pMem, pF), C0x)
mstore(add(pMem, add(pF, 32)), C0y)
g1_mulAcc(add(pMem, pF), pC1, quotient1)
g1_mulAcc(add(pMem, pF), pC2, quotient2)
// Compute group-encoded batch evaluation [E]_1
g1_mulAccC(add(pMem, pE), G1x, G1y, addmod(mload(add(pMem, pR0)), addmod(mulmod(quotient1, mload(add(pMem, pR1)),q), mulmod(quotient2, mload(add(pMem, pR2)),q), q), q))
// Compute the full difference [J]_1
g1_mulAcc(add(pMem, pJ), pW1, numerator)
}
// Validate all evaluations with a pairing checking that e([F]_1 - [E]_1 - [J]_1 + y[W2]_1, [1]_2) == e([W']_1, [x]_2)
function checkPairing(pMem) -> isOk {
let mIn := mload(0x40)
// First pairing value
// Compute -E
mstore(add(add(pMem, pE), 32), mod(sub(qf, mload(add(add(pMem, pE), 32))), qf))
// Compute -J
mstore(add(add(pMem, pJ), 32), mod(sub(qf, mload(add(add(pMem, pJ), 32))), qf))
// F = F - E - J + y·W2
g1_acc(add(pMem, pF), add(pMem, pE))
g1_acc(add(pMem, pF), add(pMem, pJ))
g1_mulAcc(add(pMem, pF), pW2, mload(add(pMem, pY)))
mstore(mIn, mload(add(pMem, pF)))
mstore(add(mIn, 32), mload(add(add(pMem, pF), 32)))
// Second pairing value
mstore(add(mIn, 64), G2x2)
mstore(add(mIn, 96), G2x1)
mstore(add(mIn, 128), G2y2)
mstore(add(mIn, 160), G2y1)
// Third pairing value
// Compute -W2
mstore(add(mIn, 192), calldataload(pW2))
let s := calldataload(add(pW2, 32))
s := mod(sub(qf, s), qf)
mstore(add(mIn, 224), s)
// Fourth pairing value
mstore(add(mIn, 256), X2x2)
mstore(add(mIn, 288), X2x1)
mstore(add(mIn, 320), X2y2)
mstore(add(mIn, 352), X2y1)
let success := staticcall(sub(gas(), 2000), 8, mIn, 384, mIn, 0x20)
isOk := and(success, mload(mIn))
}
let pMem := mload(0x40)
mstore(0x40, add(pMem, lastMem))
// Validate that all evaluations ∈ F
checkInput()
// Compute the challenges: beta, gamma, xi, alpha and y ∈ F, h1w4/h2w3/h3w3 roots, xiN and zh(xi)
computeChallenges(pMem, pubSignals)
// To divide prime fields the Extended Euclidean Algorithm for computing modular inverses is needed.
// The Montgomery batch inversion algorithm allow us to compute n inverses reducing to a single one inversion.
// More info: https://vitalik.ca/general/2018/07/21/starks_part_3.html
// To avoid this single inverse computation on-chain, it has been computed in proving time and send it to the verifier.
// Therefore, the verifier:
// 1) Prepare all the denominators to inverse
// 2) Check the inverse sent by the prover it is what it should be
// 3) Compute the others inverses using the Montgomery Batched Algorithm using the inverse sent to avoid the inversion operation it does.
computeInversions(pMem)
// Compute Lagrange polynomial evaluations Li(xi)
computeLagrange(pMem)
// Compute public input polynomial evaluation PI(xi) = \sum_i^l -public_input_i·L_i(xi)
computePi(pMem, pubSignals)
// Computes r1(y) and r2(y)
computeR0(pMem)
computeR1(pMem)
computeR2(pMem)
// Compute full batched polynomial commitment [F]_1, group-encoded batch evaluation [E]_1 and the full difference [J]_1
computeFEJ(pMem)
// Validate all evaluations
let isValid := checkPairing(pMem)
mstore(0, isValid)
return(0, 0x20)
}
}
}
\ No newline at end of file
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if, import/no-dynamic-require */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved, no-restricted-syntax */
const { expect } = require('chai');
const path = require('path');
const fs = require('fs');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const { argv } = require('yargs');
const DEFAULT_MNEMONIC = 'test test test test test test test test test test test junk';
process.env.HARDHAT_NETWORK = 'hardhat';
process.env.MNEMONIC = argv.test ? DEFAULT_MNEMONIC : process.env.MNEMONIC;
const { ethers, upgrades } = require('hardhat');
const {
MemDB, ZkEVMDB, getPoseidon, smtUtils,
} = require('@0xpolygonhermez/zkevm-commonjs');
const { deployCDKValidiumDeployer, create2Deployment } = require('./helpers/deployment-helpers');
const deployParametersPath = argv.input ? argv.input : './deploy_parameters.json';
const deployParameters = require(deployParametersPath);
const outPath = argv.out ? argv.out : './genesis.json';
const pathOutputJson = path.join(__dirname, outPath);
/*
* bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*/
const _ADMIN_SLOT = '0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103';
const _IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';
async function main() {
// Constant variables
const attemptsDeployProxy = 20;
const networkIDL2 = 1;
const globalExitRootL2Address = '0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa';
const cdkValidiumAddressL2 = ethers.constants.AddressZero;
// deploy parameters
const mandatoryDeploymentParameters = [
'timelockAddress',
'minDelayTimelock',
'salt',
'initialCDKValidiumDeployerOwner',
];
for (const parameterName of mandatoryDeploymentParameters) {
if (deployParameters[parameterName] === undefined || deployParameters[parameterName] === '') {
throw new Error(`Missing parameter: ${parameterName}`);
}
}
const {
timelockAddress,
minDelayTimelock,
salt,
initialCDKValidiumDeployerOwner,
} = deployParameters;
// Load deployer
await ethers.provider.send('hardhat_impersonateAccount', [initialCDKValidiumDeployerOwner]);
await ethers.provider.send('hardhat_setBalance', [initialCDKValidiumDeployerOwner, '0xffffffffffffffff']); // 18 ethers aprox
const deployer = await ethers.getSigner(initialCDKValidiumDeployerOwner);
// Deploy CDKValidiumDeployer if is not deployed already
const [cdkValidiumDeployerContract, keylessDeployer] = await deployCDKValidiumDeployer(initialCDKValidiumDeployerOwner, deployer);
/*
* Deploy Bridge
* Deploy admin --> implementation --> proxy
*/
// Deploy proxy admin:
const proxyAdminFactory = await ethers.getContractFactory('ProxyAdmin', deployer);
const deployTransactionAdmin = (proxyAdminFactory.getDeployTransaction()).data;
const dataCallAdmin = proxyAdminFactory.interface.encodeFunctionData('transferOwnership', [deployer.address]);
const [proxyAdminAddress] = await create2Deployment(cdkValidiumDeployerContract, salt, deployTransactionAdmin, dataCallAdmin, deployer);
// Deploy implementation PolygonZkEVMBridg
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge', deployer);
const deployTransactionBridge = (PolygonZkEVMBridgeFactory.getDeployTransaction()).data;
// Mandatory to override the gasLimit since the estimation with create are mess up D:
const overrideGasLimit = ethers.BigNumber.from(5500000);
const [bridgeImplementationAddress] = await create2Deployment(
cdkValidiumDeployerContract,
salt,
deployTransactionBridge,
null,
deployer,
overrideGasLimit,
);
/*
* deploy proxy
* Do not initialize directlythe proxy since we want to deploy the same code on L2 and this will alter the bytecode deployed of the proxy
*/
const transparentProxyFactory = await ethers.getContractFactory('TransparentUpgradeableProxy', deployer);
const initializeEmptyDataProxy = '0x';
const deployTransactionProxy = (transparentProxyFactory.getDeployTransaction(
bridgeImplementationAddress,
proxyAdminAddress,
initializeEmptyDataProxy,
)).data;
const gasTokenMetadata = ethers.utils.defaultAbiCoder.encode(['string', 'string', 'uint8'], ['AGICoin', 'AGC', 18]);
const dataCallProxy = PolygonZkEVMBridgeFactory.interface.encodeFunctionData(
'initialize',
[
networkIDL2,
globalExitRootL2Address,
cdkValidiumAddressL2,
process.env.GASTOKEN_ADDR,
gasTokenMetadata
],
);
const [proxyBridgeAddress] = await create2Deployment(
cdkValidiumDeployerContract,
salt,
deployTransactionProxy,
dataCallProxy,
deployer,
);
// Import OZ manifest the deployed contracts, its enough to import just the proyx, the rest are imported automatically ( admin/impl)
await upgrades.forceImport(proxyBridgeAddress, PolygonZkEVMBridgeFactory, 'transparent');
/*
*Deployment Global exit root manager
*/
const PolygonZkEVMGlobalExitRootL2Factory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootL2', deployer);
let PolygonZkEVMGlobalExitRootL2;
for (let i = 0; i < attemptsDeployProxy; i++) {
try {
PolygonZkEVMGlobalExitRootL2 = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootL2Factory, [], {
initializer: false,
constructorArgs: [proxyBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
break;
} catch (error) {
console.log(`attempt ${i}`);
console.log('upgrades.deployProxy of PolygonZkEVMGlobalExitRootL2 ', error.message);
}
// reach limits of attempts
if (i + 1 === attemptsDeployProxy) {
throw new Error('PolygonZkEVMGlobalExitRootL2 contract has not been deployed');
}
}
// Assert admin address
expect(await upgrades.erc1967.getAdminAddress(PolygonZkEVMGlobalExitRootL2.address)).to.be.equal(proxyAdminAddress);
expect(await upgrades.erc1967.getAdminAddress(proxyBridgeAddress)).to.be.equal(proxyAdminAddress);
const timelockContractFactory = await ethers.getContractFactory('CDKValidiumTimelock', deployer);
const timelockContract = await timelockContractFactory.deploy(
minDelayTimelock,
[timelockAddress],
[timelockAddress],
timelockAddress,
cdkValidiumAddressL2,
);
await timelockContract.deployed();
// Transfer ownership of the proxyAdmin to timelock
const proxyAdminInstance = proxyAdminFactory.attach(proxyAdminAddress);
await (await proxyAdminInstance.connect(deployer).transferOwnership(timelockContract.address)).wait();
// Recreate genesis with the current information:
const genesis = [];
// CDKValidiumDeployer
const cdkValidiumDeployerInfo = await getAddressInfo(cdkValidiumDeployerContract.address);
genesis.push({
contractName: 'CDKValidiumDeployer',
balance: '0',
nonce: cdkValidiumDeployerInfo.nonce.toString(),
address: cdkValidiumDeployerContract.address,
bytecode: cdkValidiumDeployerInfo.bytecode,
storage: cdkValidiumDeployerInfo.storage,
});
// Proxy Admin
const proxyAdminInfo = await getAddressInfo(proxyAdminAddress);
genesis.push({
contractName: 'ProxyAdmin',
balance: '0',
nonce: proxyAdminInfo.nonce.toString(),
address: proxyAdminAddress,
bytecode: proxyAdminInfo.bytecode,
storage: proxyAdminInfo.storage,
});
// Bridge implementation
const bridgeImplementationInfo = await getAddressInfo(bridgeImplementationAddress);
genesis.push({
contractName: 'PolygonZkEVMBridge implementation',
balance: '0',
nonce: bridgeImplementationInfo.nonce.toString(),
address: bridgeImplementationAddress,
bytecode: bridgeImplementationInfo.bytecode,
// storage: bridgeImplementationInfo.storage, implementation do not have storage
});
// Bridge proxy
const bridgeProxyInfo = await getAddressInfo(proxyBridgeAddress);
genesis.push({
contractName: 'PolygonZkEVMBridge proxy',
balance: '200000000000000000000000000',
nonce: bridgeProxyInfo.nonce.toString(),
address: proxyBridgeAddress,
bytecode: bridgeProxyInfo.bytecode,
storage: bridgeProxyInfo.storage,
});
// PolygonZkEVMGlobalExitRootL2 implementation
const implGlobalExitRootL2 = await upgrades.erc1967.getImplementationAddress(PolygonZkEVMGlobalExitRootL2.address);
const implGlobalExitRootL2Info = await getAddressInfo(implGlobalExitRootL2);
genesis.push({
contractName: 'PolygonZkEVMGlobalExitRootL2 implementation',
balance: '0',
nonce: implGlobalExitRootL2Info.nonce.toString(),
address: implGlobalExitRootL2,
bytecode: implGlobalExitRootL2Info.bytecode,
// storage: implGlobalExitRootL2Info.storage, , implementation do not have storage
});
// PolygonZkEVMGlobalExitRootL2 proxy
const proxyGlobalExitRootL2Info = await getAddressInfo(PolygonZkEVMGlobalExitRootL2.address);
genesis.push({
contractName: 'PolygonZkEVMGlobalExitRootL2 proxy',
balance: '0',
nonce: proxyGlobalExitRootL2Info.nonce.toString(),
address: globalExitRootL2Address, // Override address!
bytecode: proxyGlobalExitRootL2Info.bytecode,
storage: proxyGlobalExitRootL2Info.storage,
});
// Timelock
const timelockInfo = await getAddressInfo(timelockContract.address);
/*
* Since roles are used, most storage are writted in peusdoRandom storage slots
* bytes32 public constant TIMELOCK_ADMIN_ROLE = keccak256("TIMELOCK_ADMIN_ROLE");
* bytes32 public constant PROPOSER_ROLE = keccak256("PROPOSER_ROLE");
* bytes32 public constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
* bytes32 public constant CANCELLER_ROLE = keccak256("CANCELLER_ROLE");
*/
const timelockRolesHash = [
ethers.utils.id('TIMELOCK_ADMIN_ROLE'),
ethers.utils.id('PROPOSER_ROLE'),
ethers.utils.id('EXECUTOR_ROLE'),
ethers.utils.id('CANCELLER_ROLE'),
];
for (let i = 0; i < timelockRolesHash.length; i++) {
const rolesMappingStoragePositionStruct = 0;
const storagePosition = ethers.utils.solidityKeccak256(['uint256', 'uint256'], [timelockRolesHash[i], rolesMappingStoragePositionStruct]);
// check timelock address manager, and timelock address itself
const addressArray = [timelockAddress, timelockContract.address];
for (let j = 0; j < addressArray.length; j++) {
const storagePositionRole = ethers.utils.solidityKeccak256(['uint256', 'uint256'], [addressArray[j], storagePosition]);
const valueRole = await ethers.provider.getStorageAt(timelockContract.address, storagePositionRole);
if (valueRole !== '0x0000000000000000000000000000000000000000000000000000000000000000') {
timelockInfo.storage[storagePositionRole] = valueRole;
}
}
const roleAdminSlot = ethers.utils.hexZeroPad((ethers.BigNumber.from(storagePosition).add(1)).toHexString(), 32);
const valueRoleAdminSlot = await ethers.provider.getStorageAt(timelockContract.address, roleAdminSlot);
if (valueRoleAdminSlot !== '0x0000000000000000000000000000000000000000000000000000000000000000') {
timelockInfo.storage[roleAdminSlot] = valueRoleAdminSlot;
}
}
genesis.push({
contractName: 'CDKValidiumTimelock',
balance: '0',
nonce: timelockInfo.nonce.toString(),
address: timelockContract.address,
bytecode: timelockInfo.bytecode,
storage: timelockInfo.storage,
});
// Put nonces on deployers
// Keyless deployer
genesis.push({
accountName: 'keyless Deployer',
balance: '0',
nonce: '1',
address: keylessDeployer,
});
// deployer
const deployerInfo = await getAddressInfo(deployer.address);
genesis.push({
accountName: 'deployer',
balance: '10000000000000000000',
nonce: deployerInfo.nonce.toString(),
address: deployer.address,
});
if (argv.test) {
// Add tester account with ether
genesis[genesis.length - 1].balance = '100000000000000000000000';
}
// calculate root
const poseidon = await getPoseidon();
const { F } = poseidon;
const db = new MemDB(F);
const genesisRoot = [F.zero, F.zero, F.zero, F.zero];
const accHashInput = [F.zero, F.zero, F.zero, F.zero];
const defaultChainId = 1000;
const zkEVMDB = await ZkEVMDB.newZkEVM(
db,
poseidon,
genesisRoot,
accHashInput,
genesis,
null,
null,
defaultChainId,
);
fs.writeFileSync(pathOutputJson, JSON.stringify({
root: smtUtils.h4toString(zkEVMDB.stateRoot),
genesis,
}, null, 1));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
async function getAddressInfo(address) {
const nonce = await ethers.provider.getTransactionCount(address);
const bytecode = await ethers.provider.getCode(address);
const storage = {};
for (let i = 0; i < 120; i++) {
const storageValue = await ethers.provider.getStorageAt(address, i);
if (storageValue !== '0x0000000000000000000000000000000000000000000000000000000000000000') {
storage[ethers.utils.hexZeroPad(ethers.utils.hexlify(i), 32)] = storageValue;
}
}
const valueAdminSlot = await ethers.provider.getStorageAt(address, _ADMIN_SLOT);
if (valueAdminSlot !== '0x0000000000000000000000000000000000000000000000000000000000000000') {
storage[_ADMIN_SLOT] = valueAdminSlot;
}
const valuImplementationSlot = await ethers.provider.getStorageAt(address, _IMPLEMENTATION_SLOT);
if (valuImplementationSlot !== '0x0000000000000000000000000000000000000000000000000000000000000000') {
storage[_IMPLEMENTATION_SLOT] = valuImplementationSlot;
}
return { nonce, bytecode, storage };
}
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved */
const { ethers } = require('hardhat');
const path = require('path');
const fs = require('fs');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const { deployCDKValidiumDeployer } = require('./helpers/deployment-helpers');
const pathDeployParameters = path.join(__dirname, './deploy_parameters.json');
const deployParameters = require('./deploy_parameters.json');
async function main() {
// Load provider
let currentProvider = ethers.provider;
if (deployParameters.multiplierGas || deployParameters.maxFeePerGas) {
if (process.env.HARDHAT_NETWORK !== 'hardhat') {
currentProvider = new ethers.providers.JsonRpcProvider(`https://${process.env.HARDHAT_NETWORK}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
if (deployParameters.maxPriorityFeePerGas && deployParameters.maxFeePerGas) {
console.log(`Hardcoded gas used: MaxPriority${deployParameters.maxPriorityFeePerGas} gwei, MaxFee${deployParameters.maxFeePerGas} gwei`);
const FEE_DATA = {
maxFeePerGas: ethers.utils.parseUnits(deployParameters.maxFeePerGas, 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits(deployParameters.maxPriorityFeePerGas, 'gwei'),
};
currentProvider.getFeeData = async () => FEE_DATA;
} else {
console.log('Multiplier gas used: ', deployParameters.multiplierGas);
async function overrideFeeData() {
const feedata = await ethers.provider.getFeeData();
return {
maxFeePerGas: feedata.maxFeePerGas.mul(deployParameters.multiplierGas).div(1000),
maxPriorityFeePerGas: feedata.maxPriorityFeePerGas.mul(deployParameters.multiplierGas).div(1000),
};
}
currentProvider.getFeeData = overrideFeeData;
}
}
}
// Load deployer
let deployer;
if (deployParameters.deployerPvtKey) {
deployer = new ethers.Wallet(deployParameters.deployerPvtKey, currentProvider);
} else if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
} else {
[deployer] = (await ethers.getSigners());
}
// Load initialCDKValidiumDeployerOwner
const {
initialCDKValidiumDeployerOwner,
} = deployParameters;
if (initialCDKValidiumDeployerOwner === undefined || initialCDKValidiumDeployerOwner === '') {
throw new Error('Missing parameter: initialCDKValidiumDeployerOwner');
}
// Deploy CDKValidiumDeployer if is not deployed already using keyless deployment
const [cdkValidiumDeployerContract, keylessDeployer] = await deployCDKValidiumDeployer(initialCDKValidiumDeployerOwner, deployer);
if (keylessDeployer === ethers.constants.AddressZero) {
console.log('#######################\n');
console.log('cdkValidiumDeployer already deployed on: ', cdkValidiumDeployerContract.address);
} else {
console.log('#######################\n');
console.log('cdkValidiumDeployer deployed on: ', cdkValidiumDeployerContract.address);
}
deployParameters.cdkValidiumDeployerAddress = cdkValidiumDeployerContract.address;
fs.writeFileSync(pathDeployParameters, JSON.stringify(deployParameters, null, 1));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if, import/no-dynamic-require, global-require */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved, no-restricted-syntax */
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const path = require('path');
const fs = require('fs');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const { create2Deployment } = require('./helpers/deployment-helpers');
const pathOutputJson = path.join(__dirname, './deploy_output.json');
const pathOngoingDeploymentJson = path.join(__dirname, './deploy_ongoing.json');
const deployParameters = require('./deploy_parameters.json');
const genesis = require('./genesis.json');
const pathOZUpgradability = path.join(__dirname, `../.openzeppelin/${process.env.HARDHAT_NETWORK}.json`);
async function main() {
// Check that there's no previous OZ deployment
if (fs.existsSync(pathOZUpgradability)) {
throw new Error(`There's upgradability information from previous deployments, it's mandatory to erase them before start a new one, path: ${pathOZUpgradability}`);
}
// Check if there's an ongoing deployment
let ongoingDeployment = {};
if (fs.existsSync(pathOngoingDeploymentJson)) {
ongoingDeployment = require(pathOngoingDeploymentJson);
}
// Constant variables
const networkIDMainnet = 0;
const attemptsDeployProxy = 20;
/*
* Check deploy parameters
* Check that every necessary parameter is fullfilled
*/
const mandatoryDeploymentParameters = [
'realVerifier',
'trustedSequencerURL',
'networkName',
'version',
'trustedSequencer',
'chainID',
'admin',
'trustedAggregator',
'trustedAggregatorTimeout',
'pendingStateTimeout',
'forkID',
'cdkValidiumOwner',
'timelockAddress',
'minDelayTimelock',
'salt',
'cdkValidiumDeployerAddress',
'maticTokenAddress',
'setupEmptyCommittee',
'committeeTimelock',
];
for (const parameterName of mandatoryDeploymentParameters) {
if (deployParameters[parameterName] === undefined || deployParameters[parameterName] === '') {
throw new Error(`Missing parameter: ${parameterName}`);
}
}
const {
realVerifier,
trustedSequencerURL,
networkName,
version,
trustedSequencer,
chainID,
admin,
trustedAggregator,
trustedAggregatorTimeout,
pendingStateTimeout,
forkID,
cdkValidiumOwner,
timelockAddress,
minDelayTimelock,
salt,
cdkValidiumDeployerAddress,
maticTokenAddress,
setupEmptyCommittee,
committeeTimelock,
} = deployParameters;
// Load provider
let currentProvider = ethers.provider;
if (deployParameters.multiplierGas || deployParameters.maxFeePerGas) {
if (process.env.HARDHAT_NETWORK !== 'hardhat') {
currentProvider = new ethers.providers.JsonRpcProvider(`https://${process.env.HARDHAT_NETWORK}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
if (deployParameters.maxPriorityFeePerGas && deployParameters.maxFeePerGas) {
console.log(`Hardcoded gas used: MaxPriority${deployParameters.maxPriorityFeePerGas} gwei, MaxFee${deployParameters.maxFeePerGas} gwei`);
const FEE_DATA = {
maxFeePerGas: ethers.utils.parseUnits(deployParameters.maxFeePerGas, 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits(deployParameters.maxPriorityFeePerGas, 'gwei'),
};
currentProvider.getFeeData = async () => FEE_DATA;
} else {
console.log('Multiplier gas used: ', deployParameters.multiplierGas);
async function overrideFeeData() {
const feedata = await ethers.provider.getFeeData();
return {
maxFeePerGas: feedata.maxFeePerGas.mul(deployParameters.multiplierGas).div(1000),
maxPriorityFeePerGas: feedata.maxPriorityFeePerGas.mul(deployParameters.multiplierGas).div(1000),
};
}
currentProvider.getFeeData = overrideFeeData;
}
}
}
// Load deployer
let deployer;
if (deployParameters.deployerPvtKey) {
deployer = new ethers.Wallet(deployParameters.deployerPvtKey, currentProvider);
console.log('Using pvtKey deployer with address: ', deployer.address);
} else if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
console.log('Using MNEMONIC deployer with address: ', deployer.address);
} else {
[deployer] = (await ethers.getSigners());
}
// Load cdkValidium deployer
const CDKValidiumDeployerFactory = await ethers.getContractFactory('CDKValidiumDeployer', deployer);
const cdkValidiumDeployerContract = CDKValidiumDeployerFactory.attach(cdkValidiumDeployerAddress);
// check deployer is the owner of the deployer
if (await deployer.provider.getCode(cdkValidiumDeployerContract.address) === '0x') {
throw new Error('cdkValidium deployer contract is not deployed');
}
expect(deployer.address).to.be.equal(await cdkValidiumDeployerContract.owner());
let verifierContract;
if (!ongoingDeployment.verifierContract) {
if (realVerifier === true) {
const VerifierRollup = await ethers.getContractFactory('FflonkVerifier', deployer);
verifierContract = await VerifierRollup.deploy();
await verifierContract.deployed();
} else {
const VerifierRollupHelperFactory = await ethers.getContractFactory('VerifierRollupHelperMock', deployer);
verifierContract = await VerifierRollupHelperFactory.deploy();
await verifierContract.deployed();
}
console.log('#######################\n');
console.log('Verifier deployed to:', verifierContract.address);
// save an ongoing deployment
ongoingDeployment.verifierContract = verifierContract.address;
fs.writeFileSync(pathOngoingDeploymentJson, JSON.stringify(ongoingDeployment, null, 1));
} else {
console.log('Verifier already deployed on: ', ongoingDeployment.verifierContract);
const VerifierRollupFactory = await ethers.getContractFactory('FflonkVerifier', deployer);
verifierContract = VerifierRollupFactory.attach(ongoingDeployment.verifierContract);
}
/*
* Deploy Bridge
* Deploy admin --> implementation --> proxy
*/
// Deploy proxy admin:
const proxyAdminFactory = await ethers.getContractFactory('ProxyAdmin', deployer);
const deployTransactionAdmin = (proxyAdminFactory.getDeployTransaction()).data;
const dataCallAdmin = proxyAdminFactory.interface.encodeFunctionData('transferOwnership', [deployer.address]);
const [proxyAdminAddress, isProxyAdminDeployed] = await create2Deployment(
cdkValidiumDeployerContract,
salt,
deployTransactionAdmin,
dataCallAdmin,
deployer,
);
if (isProxyAdminDeployed) {
console.log('#######################\n');
console.log('Proxy admin deployed to:', proxyAdminAddress);
} else {
console.log('#######################\n');
console.log('Proxy admin was already deployed to:', proxyAdminAddress);
}
// Deploy implementation PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge', deployer);
const deployTransactionBridge = (PolygonZkEVMBridgeFactory.getDeployTransaction()).data;
const dataCallNull = null;
// Mandatory to override the gasLimit since the estimation with create are mess up D:
const overrideGasLimit = ethers.BigNumber.from(5500000);
const [bridgeImplementationAddress, isBridgeImplDeployed] = await create2Deployment(
cdkValidiumDeployerContract,
salt,
deployTransactionBridge,
dataCallNull,
deployer,
overrideGasLimit,
);
if (isBridgeImplDeployed) {
console.log('#######################\n');
console.log('bridge impl deployed to:', bridgeImplementationAddress);
} else {
console.log('#######################\n');
console.log('bridge impl was already deployed to:', bridgeImplementationAddress);
}
/*
* deploy proxy
* Do not initialize directly the proxy since we want to deploy the same code on L2 and this will alter the bytecode deployed of the proxy
*/
const transparentProxyFactory = await ethers.getContractFactory('TransparentUpgradeableProxy', deployer);
const initializeEmptyDataProxy = '0x';
const deployTransactionProxy = (transparentProxyFactory.getDeployTransaction(
bridgeImplementationAddress,
proxyAdminAddress,
initializeEmptyDataProxy,
)).data;
/*
* Nonce globalExitRoot: currentNonce + 1 (deploy bridge proxy) + 1(impl globalExitRoot
* + 1 (deploy data comittee proxy) + 1(impl data committee) + setupCommitte? = +4 or +5
*/
const nonceDelta = 4 + (setupEmptyCommittee ? 1 : 0);
const nonceProxyGlobalExitRoot = Number((await ethers.provider.getTransactionCount(deployer.address)))
+ nonceDelta;
// nonceProxyCDKValidium :Nonce globalExitRoot + 1 (proxy globalExitRoot) + 1 (impl cdk) = +2
const nonceProxyCDKValidium = nonceProxyGlobalExitRoot + 2;
let precalculateGLobalExitRootAddress; let
precalculateCDKValidiumAddress;
// Check if the contract is already deployed
if (ongoingDeployment.PolygonZkEVMGlobalExitRoot && ongoingDeployment.cdkValidiumContract) {
precalculateGLobalExitRootAddress = ongoingDeployment.PolygonZkEVMGlobalExitRoot;
precalculateCDKValidiumAddress = ongoingDeployment.cdkValidiumContract;
} else {
// If both are not deployed, it's better to deploy them both again
delete ongoingDeployment.PolygonZkEVMGlobalExitRoot;
delete ongoingDeployment.cdkValidiumContract;
fs.writeFileSync(pathOngoingDeploymentJson, JSON.stringify(ongoingDeployment, null, 1));
// Contracts are not deployed, normal deployment
precalculateGLobalExitRootAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyGlobalExitRoot });
precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
}
const gasTokenMetadata = ethers.utils.defaultAbiCoder.encode(['string', 'string', 'uint8'], ['Ether', 'ETH', 18]);
const dataCallProxy = PolygonZkEVMBridgeFactory.interface.encodeFunctionData(
'initialize',
[
networkIDMainnet,
precalculateGLobalExitRootAddress,
precalculateCDKValidiumAddress,
process.env.GASTOKEN_ADDR,
gasTokenMetadata
],
);
const [proxyBridgeAddress, isBridgeProxyDeployed] = await create2Deployment(
cdkValidiumDeployerContract,
salt,
deployTransactionProxy,
dataCallProxy,
deployer,
);
const PolygonZkEVMBridgeContract = PolygonZkEVMBridgeFactory.attach(proxyBridgeAddress);
if (isBridgeProxyDeployed) {
console.log('#######################\n');
console.log('PolygonZkEVMBridge deployed to:', PolygonZkEVMBridgeContract.address);
} else {
console.log('#######################\n');
console.log('PolygonZkEVMBridge was already deployed to:', PolygonZkEVMBridgeContract.address);
// If it was already deployed, check that the initialized calldata matches the actual deployment
expect(precalculateGLobalExitRootAddress).to.be.equal(await PolygonZkEVMBridgeContract.globalExitRootManager());
expect(precalculateCDKValidiumAddress).to.be.equal(await PolygonZkEVMBridgeContract.polygonZkEVMaddress());
}
console.log('\n#######################');
console.log('##### Checks PolygonZkEVMBridge #####');
console.log('#######################');
console.log('PolygonZkEVMGlobalExitRootAddress:', await PolygonZkEVMBridgeContract.globalExitRootManager());
console.log('networkID:', await PolygonZkEVMBridgeContract.networkID());
console.log('cdkValidiumaddress:', await PolygonZkEVMBridgeContract.polygonZkEVMaddress());
// Import OZ manifest the deployed contracts, its enough to import just the proxy, the rest are imported automatically (admin/impl)
await upgrades.forceImport(proxyBridgeAddress, PolygonZkEVMBridgeFactory, 'transparent');
/*
* Deployment Data Committee
*/
let cdkDataCommitteeContract;
const CDKDataCommitteeContractFactory = await ethers.getContractFactory('CDKDataCommittee', deployer);
for (let i = 0; i < attemptsDeployProxy; i++) {
try {
cdkDataCommitteeContract = await upgrades.deployProxy(
CDKDataCommitteeContractFactory,
[],
);
break;
} catch (error) {
console.log(`attempt ${i}`);
console.log('upgrades.deployProxy of cdkDataCommitteeContract ', error.message);
}
// reach limits of attempts
if (i + 1 === attemptsDeployProxy) {
throw new Error('cdkDataCommitteeContract contract has not been deployed');
}
}
console.log('#######################\n');
console.log('cdkDataCommittee deployed to:', cdkDataCommitteeContract.address);
if (setupEmptyCommittee) {
const expectedHash = ethers.utils.solidityKeccak256(['bytes'], [[]]);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(0, [], []))
.to.emit(cdkDataCommitteeContract, 'CommitteeUpdated')
.withArgs(expectedHash);
console.log('Empty committee seted up');
}
/*
*Deployment Global exit root manager
*/
let PolygonZkEVMGlobalExitRoot;
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot', deployer);
if (!ongoingDeployment.PolygonZkEVMGlobalExitRoot) {
for (let i = 0; i < attemptsDeployProxy; i++) {
try {
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, proxyBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
break;
} catch (error) {
console.log(`attempt ${i}`);
console.log('upgrades.deployProxy of PolygonZkEVMGlobalExitRoot ', error.message);
}
// reach limits of attempts
if (i + 1 === attemptsDeployProxy) {
throw new Error('PolygonZkEVMGlobalExitRoot contract has not been deployed');
}
}
expect(precalculateGLobalExitRootAddress).to.be.equal(PolygonZkEVMGlobalExitRoot.address);
console.log('#######################\n');
console.log('PolygonZkEVMGlobalExitRoot deployed to:', PolygonZkEVMGlobalExitRoot.address);
// save an ongoing deployment
ongoingDeployment.PolygonZkEVMGlobalExitRoot = PolygonZkEVMGlobalExitRoot.address;
fs.writeFileSync(pathOngoingDeploymentJson, JSON.stringify(ongoingDeployment, null, 1));
} else {
// sanity check
expect(precalculateGLobalExitRootAddress).to.be.equal(PolygonZkEVMGlobalExitRoot.address);
// Expect the precalculate address matches de onogin deployment
PolygonZkEVMGlobalExitRoot = PolygonZkEVMGlobalExitRootFactory.attach(ongoingDeployment.PolygonZkEVMGlobalExitRoot);
console.log('#######################\n');
console.log('PolygonZkEVMGlobalExitRoot already deployed on: ', ongoingDeployment.PolygonZkEVMGlobalExitRoot);
// Import OZ manifest the deployed contracts, its enough to import just the proyx, the rest are imported automatically (admin/impl)
await upgrades.forceImport(ongoingDeployment.PolygonZkEVMGlobalExitRoot, PolygonZkEVMGlobalExitRootFactory, 'transparent');
// Check against current deployment
expect(PolygonZkEVMBridgeContract.address).to.be.equal(await PolygonZkEVMBridgeContract.bridgeAddress());
expect(precalculateCDKValidiumAddress).to.be.equal(await PolygonZkEVMBridgeContract.rollupAddress());
}
// deploy CDKValidium
const genesisRootHex = genesis.root;
console.log('\n#######################');
console.log('##### Deployment CDKValidium #####');
console.log('#######################');
console.log('deployer:', deployer.address);
console.log('PolygonZkEVMGlobalExitRootAddress:', PolygonZkEVMGlobalExitRoot.address);
console.log('maticTokenAddress:', maticTokenAddress);
console.log('verifierAddress:', verifierContract.address);
console.log('PolygonZkEVMBridgeContract:', PolygonZkEVMBridgeContract.address);
console.log('admin:', admin);
console.log('chainID:', chainID);
console.log('trustedSequencer:', trustedSequencer);
console.log('pendingStateTimeout:', pendingStateTimeout);
console.log('trustedAggregator:', trustedAggregator);
console.log('trustedAggregatorTimeout:', trustedAggregatorTimeout);
console.log('genesisRoot:', genesisRootHex);
console.log('trustedSequencerURL:', trustedSequencerURL);
console.log('networkName:', networkName);
console.log('forkID:', forkID);
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidium', deployer);
let cdkValidiumContract;
let deploymentBlockNumber;
if (!ongoingDeployment.cdkValidiumContract) {
for (let i = 0; i < attemptsDeployProxy; i++) {
try {
cdkValidiumContract = await upgrades.deployProxy(
CDKValidiumFactory,
[
{
admin,
trustedSequencer,
pendingStateTimeout,
trustedAggregator,
trustedAggregatorTimeout,
},
genesisRootHex,
trustedSequencerURL,
networkName,
version,
],
{
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenAddress,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
forkID,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
},
);
break;
} catch (error) {
console.log(`attempt ${i}`);
console.log('upgrades.deployProxy of cdkValidiumContract ', error.message);
}
// reach limits of attempts
if (i + 1 === attemptsDeployProxy) {
throw new Error('CDKValidium contract has not been deployed');
}
}
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
console.log('#######################\n');
console.log('cdkValidiumContract deployed to:', cdkValidiumContract.address);
// save an ongoing deployment
ongoingDeployment.cdkValidiumContract = cdkValidiumContract.address;
fs.writeFileSync(pathOngoingDeploymentJson, JSON.stringify(ongoingDeployment, null, 1));
// Transfer ownership of cdkValidiumContract
if (cdkValidiumOwner !== deployer.address) {
await (await cdkValidiumContract.transferOwnership(cdkValidiumOwner)).wait();
}
deploymentBlockNumber = (await cdkValidiumContract.deployTransaction.wait()).blockNumber;
} else {
// Expect the precalculate address matches de onogin deployment, sanity check
expect(precalculateCDKValidiumAddress).to.be.equal(ongoingDeployment.cdkValidiumContract);
cdkValidiumContract = CDKValidiumFactory.attach(ongoingDeployment.cdkValidiumContract);
console.log('#######################\n');
console.log('cdkValidiumContract already deployed on: ', ongoingDeployment.cdkValidiumContract);
// Import OZ manifest the deployed contracts, its enough to import just the proyx, the rest are imported automatically ( admin/impl)
await upgrades.forceImport(ongoingDeployment.cdkValidiumContract, CDKValidiumFactory, 'transparent');
const cdkValidiumOwnerContract = await cdkValidiumContract.owner();
if (cdkValidiumOwnerContract === deployer.address) {
// Transfer ownership of cdkValidiumContract
if (cdkValidiumOwner !== deployer.address) {
await (await cdkValidiumContract.transferOwnership(cdkValidiumOwner)).wait();
}
} else {
expect(cdkValidiumOwner).to.be.equal(cdkValidiumOwnerContract);
}
deploymentBlockNumber = 0;
}
console.log('\n#######################');
console.log('##### Checks CDKValidium #####');
console.log('#######################');
console.log('PolygonZkEVMGlobalExitRootAddress:', await cdkValidiumContract.globalExitRootManager());
console.log('maticTokenAddress:', await cdkValidiumContract.matic());
console.log('verifierAddress:', await cdkValidiumContract.rollupVerifier());
console.log('PolygonZkEVMBridgeContract:', await cdkValidiumContract.bridgeAddress());
console.log('admin:', await cdkValidiumContract.admin());
console.log('chainID:', await cdkValidiumContract.chainID());
console.log('trustedSequencer:', await cdkValidiumContract.trustedSequencer());
console.log('pendingStateTimeout:', await cdkValidiumContract.pendingStateTimeout());
console.log('trustedAggregator:', await cdkValidiumContract.trustedAggregator());
console.log('trustedAggregatorTimeout:', await cdkValidiumContract.trustedAggregatorTimeout());
console.log('genesiRoot:', await cdkValidiumContract.batchNumToStateRoot(0));
console.log('trustedSequencerURL:', await cdkValidiumContract.trustedSequencerURL());
console.log('networkName:', await cdkValidiumContract.networkName());
console.log('owner:', await cdkValidiumContract.owner());
console.log('forkID:', await cdkValidiumContract.forkID());
// Assert admin address
expect(await upgrades.erc1967.getAdminAddress(precalculateCDKValidiumAddress)).to.be.equal(proxyAdminAddress);
expect(await upgrades.erc1967.getAdminAddress(precalculateGLobalExitRootAddress)).to.be.equal(proxyAdminAddress);
expect(await upgrades.erc1967.getAdminAddress(proxyBridgeAddress)).to.be.equal(proxyAdminAddress);
const proxyAdminInstance = proxyAdminFactory.attach(proxyAdminAddress);
const proxyAdminOwner = await proxyAdminInstance.owner();
const timelockContractFactory = await ethers.getContractFactory('CDKValidiumTimelock', deployer);
// TODO test stop here
let timelockContract;
if (proxyAdminOwner !== deployer.address) {
// Check if there's a timelock deployed there that match the current deployment
timelockContract = timelockContractFactory.attach(proxyAdminOwner);
expect(precalculateCDKValidiumAddress).to.be.equal(await timelockContract.cdkValidium());
console.log('#######################\n');
console.log(
'Polygon timelockContract already deployed to:',
timelockContract.address,
);
} else {
// deploy timelock
console.log('\n#######################');
console.log('##### Deployment TimelockContract #####');
console.log('#######################');
console.log('minDelayTimelock:', minDelayTimelock);
console.log('timelockAddress:', timelockAddress);
console.log('cdkValidiumAddress:', cdkValidiumContract.address);
timelockContract = await timelockContractFactory.deploy(
minDelayTimelock,
[timelockAddress],
[timelockAddress],
timelockAddress,
cdkValidiumContract.address,
);
await timelockContract.deployed();
console.log('#######################\n');
console.log(
'Polygon timelockContract deployed to:',
timelockContract.address,
);
// Transfer ownership of the proxyAdmin to timelock
await upgrades.admin.transferProxyAdminOwnership(timelockContract.address);
}
if (committeeTimelock) {
await (await cdkDataCommitteeContract.transferOwnership(timelockContract.address)).wait();
}
console.log('\n#######################');
console.log('##### Checks TimelockContract #####');
console.log('#######################');
console.log('minDelayTimelock:', await timelockContract.getMinDelay());
console.log('cdkValidium:', await timelockContract.cdkValidium());
const outputJson = {
cdkValidiumAddress: cdkValidiumContract.address,
polygonZkEVMBridgeAddress: PolygonZkEVMBridgeContract.address,
polygonZkEVMGlobalExitRootAddress: PolygonZkEVMGlobalExitRoot.address,
cdkDataCommitteeContract: cdkDataCommitteeContract.address,
maticTokenAddress,
verifierAddress: verifierContract.address,
cdkValidiumDeployerContract: cdkValidiumDeployerContract.address,
deployerAddress: deployer.address,
timelockContractAddress: timelockContract.address,
deploymentBlockNumber,
genesisRoot: genesisRootHex,
trustedSequencer,
trustedSequencerURL,
chainID,
networkName,
admin,
trustedAggregator,
proxyAdminAddress,
forkID,
salt,
version,
};
fs.writeFileSync(pathOutputJson, JSON.stringify(outputJson, null, 1));
// Remove ongoing deployment
fs.unlinkSync(pathOngoingDeploymentJson);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
## Requirements
- node version: 14.x
- npm version: 7.x
## Deployment
In project root execute:
```
npm i
cp .env.example .env
```
Fill `.env` with your `MNEMONIC` and `INFURA_PROJECT_ID`
If you want to verify the contracts also fill the `ETHERSCAN_API_KEY`
```
cd deployment
cp deploy_parameters.json.example deploy_parameters.json
```
Fill created `deploy_parameters.json` with appropiate parameters.
See below for more information about the `deploy_parameters.json`
The first step is deploying and verifying the `CDKValidiumDeployer`, this will be the factory for deterministic contracts, the address of the contracts will depend on the `salt` and the `initialCDKValidiumDeployerOwner`
This contrat is deployed using a keyless deployment, therefore the gasPrice is hardcoded.
The value is on `100 gweis`, if it's necessary to update it go to `helpers/deployment-helpers.js` and update the `gasPriceKeylessDeployment` constant.
Note that this operation will change all the deterministic address deployed.
```
npm run deploy:deployer:CDKValidium:sepolia
npm run verify:deployer:CDKValidium:sepolia
```
To deploy on testnet is necessary a token MATIC contract, therefore, there's another script that previously to the actual deployment, deploys a matic contracts and adds it automatically to the `deploy_parameters.json`
To deploy on testnet use:`deploy:testnet:CDKValidium:${network}`
In other cases use fullfill `maticTokenAddress` in the `deploy_parameters.json` and run `deploy:CDKValidium:${network}`
```
npm run deploy:testnet:CDKValidium:sepolia
```
To verify contracts use `npm run verify:CDKValidium:${network}`
```
npm run verify:CDKValidium:sepolia
```
A new folder will be created witth the following name `deployments/${network}_$(date +%s)` with all the output information and the OZ proxy information.
## deploy-parameters.json
- `realVerifier`: bool, Indicates whether deploy a real verifier or not
- `trustedSequencerURL`: string, trustedSequencer URL
- `networkName`: string, networkName
- `version`:string, will just be emitted at initialization of the contract, usefull just for synchronizer
- `trustedSequencer`: address, trusted sequencer addresss
- `chainID`: uint64, chainID of the CDKValidium
- `trustedAggregator`:address, Trusted aggregator address
- `trustedAggregatorTimeout`: uint64, If a sequence is not verified in this timeout everyone can verify it
- `pendingStateTimeout`: uint64, Once a pending state exceeds this timeout it can be consolidated
- `forkID`: uint64, Fork ID of the CDKValidium, indicates the prover (zkROM/executor) version
- `admin`:address, Admin address, can adjust CDKValidium parameters or stop the emergency state
- `cdkValidiumOwner`: address, Able to put the CDKValidium into emergency state (kill switch)
- `timelockAddress`: address, Timelock owner address, able to send start an upgradability process via timelock
- `minDelayTimelock`: number, Minimum timelock delay,
- `salt`: bytes32, Salt used in `CDKValidiumDeployer` to deploy deterministic contracts, such as the PolygonZkEVMBridge
- `initialCDKValidiumDeployerOwner`: address, Initial owner of the `CDKValidiumDeployer`
- `maticTokenAddress`: address, Matic token address, only if deploy on testnet can be left blank and will fullfilled by the scripts.
- `cdkValidiumDeployerAddress`: address, Address of the `CDKValidiumDeployer`. Can be left blank, will be fullfilled automatically with the `deploy:deployer:CDKValidium:sepolia` script.
### Optional Parameters
- `deployerPvtKey`: string, pvtKey of the deployer, overrides the address in `MNEMONIC` of `.env` if exist
- `maxFeePerGas`:string, Set `maxFeePerGas`, must define aswell `maxPriorityFeePerGas` to use it
- `maxPriorityFeePerGas`:string, Set `maxPriorityFeePerGas`, must define aswell `maxFeePerGas` to use it
- `multiplierGas`: number, Gas multiplier with 3 decimals. If `maxFeePerGas` and `maxPriorityFeePerGas` are set, this will not take effect
## Notes
- Since there are deterministic address you cannot deploy twice on the same network using the same `salt` and `initialCDKValidiumDeployerOwner`. Changing one of them is enough to make a new deployment.
- It's mandatory to delete the `.openzeppelin` upgradebility information in order to make a new deployment
- `genesis.json` has been generated using the tool: `1_createGenesis`, this script depends on the `deploy_parameters` aswell.
{
"realVerifier": false,
"trustedSequencerURL": "http://cdk-validium-json-rpc:8123",
"networkName": "cdk-validium",
"version":"0.0.1",
"trustedSequencer":"0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"chainID": 1001,
"trustedAggregator":"0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"trustedAggregatorTimeout": 604799,
"pendingStateTimeout": 604799,
"forkID": 1,
"admin":"0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"cdkValidiumOwner": "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"timelockAddress": "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"minDelayTimelock": 3600,
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"initialCDKValidiumDeployerOwner" :"0xaddress",
"maticTokenAddress":"0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"cdkValidiumDeployerAddress":"",
"deployerPvtKey": "",
"maxFeePerGas":"",
"maxPriorityFeePerGas":"",
"multiplierGas": "",
"setupEmptyCommittee": true,
"committeeTimelock": false
}
\ No newline at end of file
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if, import/no-dynamic-require */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved */
const { expect } = require('chai');
const { ethers } = require('hardhat');
const gasPriceKeylessDeployment = '100'; // 100 gweis
async function deployCDKValidiumDeployer(deployerAddress, signer) {
const CDKValidiumDeployerFactory = await ethers.getContractFactory('CDKValidiumDeployer', signer);
const deployTxCDKValidiumDeployer = (CDKValidiumDeployerFactory.getDeployTransaction(
deployerAddress,
)).data;
const gasLimit = ethers.BigNumber.from(1000000); // Put 1 Million, aprox 650k are necessary
const gasPrice = ethers.BigNumber.from(ethers.utils.parseUnits(gasPriceKeylessDeployment, 'gwei'));
const to = '0x'; // bc deployment transaction, "to" is "0x"
const tx = {
to,
nonce: 0,
value: 0,
gasLimit: gasLimit.toHexString(),
gasPrice: gasPrice.toHexString(),
data: deployTxCDKValidiumDeployer,
};
const signature = {
v: 27,
r: '0x5ca1ab1e0', // Equals 0x00000000000000000000000000000000000000000000000000000005ca1ab1e0
s: '0x5ca1ab1e', // Equals 0x000000000000000000000000000000000000000000000000000000005ca1ab1e
};
const serializedTransaction = ethers.utils.serializeTransaction(tx, signature);
const resultTransaction = ethers.utils.parseTransaction(serializedTransaction);
const totalEther = gasLimit.mul(gasPrice); // 0.1 ether
// Check if it's already deployed
const cdkValidiumDeployerAddress = ethers.utils.getContractAddress(resultTransaction);
if (await signer.provider.getCode(cdkValidiumDeployerAddress) !== '0x') {
const cdkValidiumDeployerContract = CDKValidiumDeployerFactory.attach(cdkValidiumDeployerAddress);
expect(await cdkValidiumDeployerContract.owner()).to.be.equal(signer.address);
return [cdkValidiumDeployerContract, ethers.constants.AddressZero];
}
// Fund keyless deployment
const params = {
to: resultTransaction.from,
value: totalEther.toHexString(),
};
await (await signer.sendTransaction(params)).wait();
// Deploy supernes2Deployer
await (await signer.provider.sendTransaction(serializedTransaction)).wait();
const cdkValidiumDeployerContract = await CDKValidiumDeployerFactory.attach(cdkValidiumDeployerAddress);
expect(await cdkValidiumDeployerContract.owner()).to.be.equal(deployerAddress);
return [cdkValidiumDeployerContract, resultTransaction.from];
}
async function create2Deployment(cdkValidiumDeployerContract, salt, deployTransaction, dataCall, deployer, hardcodedGasLimit) {
// Encode deploy transaction
const hashInitCode = ethers.utils.solidityKeccak256(['bytes'], [deployTransaction]);
// Precalculate create2 address
const precalculatedAddressDeployed = ethers.utils.getCreate2Address(cdkValidiumDeployerContract.address, salt, hashInitCode);
const amount = 0;
if (await deployer.provider.getCode(precalculatedAddressDeployed) !== '0x') {
return [precalculatedAddressDeployed, false];
}
if (dataCall) {
// Deploy using create2 and call
if (hardcodedGasLimit) {
const populatedTransaction = await cdkValidiumDeployerContract.populateTransaction.deployDeterministicAndCall(
amount,
salt,
deployTransaction,
dataCall,
);
populatedTransaction.gasLimit = ethers.BigNumber.from(hardcodedGasLimit);
await (await deployer.sendTransaction(populatedTransaction)).wait();
} else {
await (await cdkValidiumDeployerContract.deployDeterministicAndCall(amount, salt, deployTransaction, dataCall)).wait();
}
} else {
// Deploy using create2
if (hardcodedGasLimit) {
const populatedTransaction = await cdkValidiumDeployerContract.populateTransaction.deployDeterministic(
amount,
salt,
deployTransaction,
);
populatedTransaction.gasLimit = ethers.BigNumber.from(hardcodedGasLimit);
await (await deployer.sendTransaction(populatedTransaction)).wait();
} else {
await (await cdkValidiumDeployerContract.deployDeterministic(amount, salt, deployTransaction)).wait();
}
}
return [precalculatedAddressDeployed, true];
}
module.exports = {
deployCDKValidiumDeployer,
create2Deployment,
};
/* eslint-disable no-await-in-loop, no-use-before-define, no-lonely-if, no-restricted-syntax */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved */
const { ethers } = require('hardhat');
const path = require('path');
const fs = require('fs');
require('dotenv').config({ path: path.resolve(__dirname, '../../.env') });
const pathDeployParameters = path.join(__dirname, '../deploy_parameters.json');
const deployParameters = require('../deploy_parameters.json');
async function main() {
// Load provider
let currentProvider = ethers.provider;
if (deployParameters.multiplierGas || deployParameters.maxFeePerGas) {
if (process.env.HARDHAT_NETWORK !== 'hardhat') {
currentProvider = new ethers.providers.JsonRpcProvider(`https://${process.env.HARDHAT_NETWORK}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
if (deployParameters.maxPriorityFeePerGas && deployParameters.maxFeePerGas) {
console.log(`Hardcoded gas used: MaxPriority${deployParameters.maxPriorityFeePerGas} gwei, MaxFee${deployParameters.maxFeePerGas} gwei`);
const FEE_DATA = {
maxFeePerGas: ethers.utils.parseUnits(deployParameters.maxFeePerGas, 'gwei'),
maxPriorityFeePerGas: ethers.utils.parseUnits(deployParameters.maxPriorityFeePerGas, 'gwei'),
};
currentProvider.getFeeData = async () => FEE_DATA;
} else {
console.log('Multiplier gas used: ', deployParameters.multiplierGas);
async function overrideFeeData() {
const feedata = await ethers.provider.getFeeData();
return {
maxFeePerGas: feedata.maxFeePerGas.mul(deployParameters.multiplierGas),
maxPriorityFeePerGas: feedata.maxPriorityFeePerGas.mul(deployParameters.multiplierGas),
};
}
currentProvider.getFeeData = overrideFeeData;
}
}
}
// Load deployer
let deployer;
if (deployParameters.deployerPvtKey) {
deployer = new ethers.Wallet(deployParameters.deployerPvtKey, currentProvider);
console.log('Using pvtKey deployer with address: ', deployer.address);
} else if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
console.log('Using MNEMONIC deployer with address: ', deployer.address);
} else {
[deployer] = (await ethers.getSigners());
}
// Check trusted address from deploy parameters
const mandatoryDeploymentParameters = [
'trustedAggregator',
'trustedSequencer',
];
for (const parameterName of mandatoryDeploymentParameters) {
if (deployParameters[parameterName] === undefined || deployParameters[parameterName] === '') {
throw new Error(`Missing parameter: ${parameterName}`);
}
}
const {
trustedAggregator,
trustedSequencer,
} = deployParameters;
/*
*Deployment MATIC
*/
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock', deployer);
const maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
console.log('#######################\n');
console.log('Matic deployed to:', maticTokenContract.address);
// fund sequencer account with tokens and ether if it have less than 0.1 ether.
const balanceEther = await ethers.provider.getBalance(trustedSequencer);
const minEtherBalance = ethers.utils.parseEther('0.1');
if (balanceEther < minEtherBalance) {
const params = {
to: trustedSequencer,
value: minEtherBalance,
};
await deployer.sendTransaction(params);
}
const tokensBalance = ethers.utils.parseEther('100000');
await (await maticTokenContract.transfer(trustedSequencer, tokensBalance)).wait();
// fund aggregator account with ether if it have less than 0.1 ether.
const balanceEtherAggr = await ethers.provider.getBalance(trustedAggregator);
if (balanceEtherAggr < minEtherBalance) {
const params = {
to: trustedAggregator,
value: minEtherBalance,
};
await deployer.sendTransaction(params);
}
deployParameters.maticTokenAddress = maticTokenContract.address;
fs.writeFileSync(pathDeployParameters, JSON.stringify(deployParameters, null, 1));
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
/* eslint-disable import/no-dynamic-require, no-await-in-loop, no-restricted-syntax, guard-for-in */
require('dotenv').config();
const path = require('path');
const hre = require('hardhat');
const { expect } = require('chai');
const pathDeployParameters = path.join(__dirname, './deploy_parameters.json');
const deployParameters = require(pathDeployParameters);
async function main() {
// load deployer account
if (typeof process.env.ETHERSCAN_API_KEY === 'undefined') {
throw new Error('Etherscan API KEY has not been defined');
}
// verify cdkValidium deployer
try {
await hre.run(
'verify:verify',
{
address: deployParameters.cdkValidiumDeployerAddress,
constructorArguments: [
deployParameters.initialCDKValidiumDeployerOwner,
],
},
);
} catch (error) {
console.error(error);
expect(error.message.toLowerCase().includes('already verified')).to.be.equal(true);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
/* eslint-disable import/no-dynamic-require, no-await-in-loop, no-restricted-syntax, guard-for-in */
require('dotenv').config();
const path = require('path');
const hre = require('hardhat');
const { expect } = require('chai');
const { ethers } = require('hardhat');
const pathDeployOutputParameters = path.join(__dirname, './deploy_output.json');
const pathDeployParameters = path.join(__dirname, './deploy_parameters.json');
const deployOutputParameters = require(pathDeployOutputParameters);
const deployParameters = require(pathDeployParameters);
async function main() {
// load deployer account
if (typeof process.env.ETHERSCAN_API_KEY === 'undefined') {
throw new Error('Etherscan API KEY has not been defined');
}
// verify maticToken
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
try {
// verify governance
await hre.run(
'verify:verify',
{
address: deployOutputParameters.maticTokenAddress,
constructorArguments: [
maticTokenName,
maticTokenSymbol,
deployOutputParameters.deployerAddress,
maticTokenInitialBalance,
],
},
);
} catch (error) {
// expect(error.message.toLowerCase().includes('already verified')).to.be.equal(true);
}
// verify verifier
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.verifierAddress,
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('already verified')).to.be.equal(true);
}
const { minDelayTimelock } = deployParameters;
const { timelockAddress } = deployParameters;
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.timelockContractAddress,
constructorArguments: [
minDelayTimelock,
[timelockAddress],
[timelockAddress],
timelockAddress,
deployOutputParameters.cdkValidiumAddress,
],
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('already verified')).to.be.equal(true);
}
// verify proxy admin
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.proxyAdminAddress,
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('already verified')).to.be.equal(true);
}
// verify cdkValidium address
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.cdkValidiumAddress,
constructorArguments: [
deployOutputParameters.polygonZkEVMGlobalExitRootAddress,
deployOutputParameters.maticTokenAddress,
deployOutputParameters.verifierAddress,
deployOutputParameters.polygonZkEVMBridgeAddress,
deployOutputParameters.cdkDataCommitteeContract,
deployOutputParameters.chainID,
deployOutputParameters.forkID,
],
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('proxyadmin')).to.be.equal(true);
}
// verify global exit root address
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.polygonZkEVMGlobalExitRootAddress,
constructorArguments: [
deployOutputParameters.cdkValidiumAddress,
deployOutputParameters.polygonZkEVMBridgeAddress,
],
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('proxyadmin')).to.be.equal(true);
}
try {
await hre.run(
'verify:verify',
{
address: deployOutputParameters.polygonZkEVMBridgeAddress,
},
);
} catch (error) {
expect(error.message.toLowerCase().includes('proxyadmin')).to.be.equal(true);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
FROM ethereum/client-go:v1.12.0
EXPOSE 8545
COPY docker/gethData /
ENTRYPOINT ["geth"]
CMD ["--rpc.allow-unprotected-txs", "--http", "--http.addr", "0.0.0.0","--http.corsdomain", "*", "--http.vhosts" ,"*", "--ws", "--ws.origins", "*", "--ws.addr", "0.0.0.0", "--dev", "--dev.period", "1", "--datadir", "/geth_data"]
\ No newline at end of file
# Docker deployment
By default the following mnemonic will be used to deploy the smart contracts `MNEMONIC="test test test test test test test test test test test junk"`.
Also the first 20 accounts of this mnemonic will be funded with ether.
The first account of the mnemonic will be the deployer of the smart contracts and therefore the holder of all the MATIC test tokens, which are necessary to pay the `sendBatch` transactions.
You can change the deployment `mnemonic` creating a `.env` file in the project root with the following variable:
`MNEMONIC=<YOUR_MENMONIC>`
## Requirements
- node version: 14.x
- npm version: 7.x
- docker
- docker-compose
## Build dockers
In project root execute:
```
npm i
npm run docker:contracts
```
A new docker `hermeznetwork/geth-cdk-validium-contracts:latest` will be created
This docker will contain a geth node with the deployed contracts
The deployment output can be found in: `docker/deploymentOutput/deploy_output.json`
To run the docker you can use: `docker run -p 8545:8545 hermeznetwork/geth-cdk-validium-contracts:latest`
version: "3.3"
services:
geth:
image: ethereum/client-go:v1.12.0
environment:
- DEV_PERIOD
ports:
- "8545:8545"
volumes:
- ./gethData/geth_data:/geth_data
entrypoint:
- geth
- --rpc.allow-unprotected-txs
- --http
- --http.addr
- "0.0.0.0"
- --dev
- --dev.period
- $DEV_PERIOD
- --datadir
- /geth_data
#!/bin/bash
sudo rm -rf docker/gethData/geth_data
rm deployment/deploy_ongoing.json
DEV_PERIOD=1 docker-compose -f docker/docker-compose.geth.yml up -d geth
sleep 5
node docker/scripts/fund-accounts.js
cp docker/scripts/deploy_parameters_docker.json deployment/deploy_parameters.json
cp docker/scripts/genesis_docker.json deployment/genesis.json
npx hardhat run deployment/testnet/prepareTestnet.js --network localhost
npx hardhat run deployment/2_deployCDKValidiumDeployer.js --network localhost
npx hardhat run deployment/3_deployContracts.js --network localhost
mkdir docker/deploymentOutput
mv deployment/deploy_output.json docker/deploymentOutput
docker-compose -f docker/docker-compose.geth.yml down
sudo docker build -t hermeznetwork/geth-cdk-validium-contracts -f docker/Dockerfile.geth .
# Let it readable for the multiplatform build coming later!
sudo chmod -R go+rxw docker/gethData
{
"realVerifier": false,
"trustedSequencerURL": "http://zkevm-json-rpc:8123",
"networkName": "cdk-validium",
"version":"0.0.1",
"trustedSequencer":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"chainID": 1001,
"trustedAggregator":"0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"trustedAggregatorTimeout": 604799,
"pendingStateTimeout": 604799,
"forkID": 6,
"admin":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"cdkValidiumOwner": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"timelockAddress": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"minDelayTimelock": 10,
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
"cdkValidiumDeployerAddress":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"initialCDKValidiumDeployerOwner" :"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"maticTokenAddress":"0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"setupEmptyCommittee": true,
"committeeTimelock": false
}
\ No newline at end of file
/* eslint-disable no-await-in-loop */
const ethers = require('ethers');
require('dotenv').config();
const DEFAULT_MNEMONIC = 'test test test test test test test test test test test junk';
const DEFAULT_NUM_ACCOUNTS = 20;
async function main() {
const MNEMONIC = process.env.MNEMONIC || DEFAULT_MNEMONIC;
const currentProvider = new ethers.providers.JsonRpcProvider('http://localhost:8545');
const signerNode = await currentProvider.getSigner();
const numAccountsToFund = process.env.NUM_ACCOUNTS || DEFAULT_NUM_ACCOUNTS;
for (let i = 0; i < numAccountsToFund; i++) {
const pathWallet = `m/44'/60'/0'/0/${i}`;
const accountWallet = ethers.Wallet.fromMnemonic(MNEMONIC, pathWallet);
const params = [{
from: await signerNode.getAddress(),
to: accountWallet.address,
value: '0x3635C9ADC5DEA00000',
}];
const tx = await currentProvider.send('eth_sendTransaction', params);
if (i === numAccountsToFund - 1) {
await currentProvider.waitForTransaction(tx);
}
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
{
"root": "0xd88680f1b151dd67518f9aca85161424c0cac61df2f5424a3ddc04ea25adecc7",
"genesis": [
{
"contractName": "CDKValidiumDeployer",
"balance": "0",
"nonce": "4",
"address": "0x4b2700570f8426A24EA85e0324611E527BdD55B8",
"bytecode": "0x6080604052600436106100705760003560e01c8063715018a61161004e578063715018a6146100e65780638da5cb5b146100fb578063e11ae6cb14610126578063f2fde38b1461013957600080fd5b80632b79805a146100755780634a94d4871461008a5780636d07dbf81461009d575b600080fd5b610088610083366004610927565b610159565b005b6100886100983660046109c7565b6101cb565b3480156100a957600080fd5b506100bd6100b8366004610a1e565b61020d565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b50610088610220565b34801561010757600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100bd565b610088610134366004610a40565b610234565b34801561014557600080fd5b50610088610154366004610a90565b61029b565b610161610357565b600061016e8585856103d8565b905061017a8183610537565b5060405173ffffffffffffffffffffffffffffffffffffffff821681527fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a15050505050565b6101d3610357565b6101de83838361057b565b506040517f25adb19089b6a549831a273acdf7908cff8b7ee5f551f8d1d37996cf01c5df5b90600090a1505050565b600061021983836105a9565b9392505050565b610228610357565b61023260006105b6565b565b61023c610357565b60006102498484846103d8565b60405173ffffffffffffffffffffffffffffffffffffffff821681529091507fba82f25fed02cd2a23d9f5d11c2ef588d22af5437cbf23bfe61d87257c480e4c9060200160405180910390a150505050565b6102a3610357565b73ffffffffffffffffffffffffffffffffffffffff811661034b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610354816105b6565b50565b60005473ffffffffffffffffffffffffffffffffffffffff163314610232576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610342565b600083471015610444576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e63650000006044820152606401610342565b81516000036104af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f6044820152606401610342565b8282516020840186f5905073ffffffffffffffffffffffffffffffffffffffff8116610219576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f437265617465323a204661696c6564206f6e206465706c6f79000000000000006044820152606401610342565b6060610219838360006040518060400160405280601e81526020017f416464726573733a206c6f772d6c6576656c2063616c6c206661696c6564000081525061062b565b60606105a1848484604051806060016040528060298152602001610b3d6029913961062b565b949350505050565b6000610219838330610744565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6060824710156106bd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610342565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516106e69190610acf565b60006040518083038185875af1925050503d8060008114610723576040519150601f19603f3d011682016040523d82523d6000602084013e610728565b606091505b50915091506107398783838761076e565b979650505050505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b606083156108045782516000036107fd5773ffffffffffffffffffffffffffffffffffffffff85163b6107fd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610342565b50816105a1565b6105a183838151156108195781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103429190610aeb565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f83011261088d57600080fd5b813567ffffffffffffffff808211156108a8576108a861084d565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156108ee576108ee61084d565b8160405283815286602085880101111561090757600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561093d57600080fd5b8435935060208501359250604085013567ffffffffffffffff8082111561096357600080fd5b61096f8883890161087c565b9350606087013591508082111561098557600080fd5b506109928782880161087c565b91505092959194509250565b803573ffffffffffffffffffffffffffffffffffffffff811681146109c257600080fd5b919050565b6000806000606084860312156109dc57600080fd5b6109e58461099e565b9250602084013567ffffffffffffffff811115610a0157600080fd5b610a0d8682870161087c565b925050604084013590509250925092565b60008060408385031215610a3157600080fd5b50508035926020909101359150565b600080600060608486031215610a5557600080fd5b8335925060208401359150604084013567ffffffffffffffff811115610a7a57600080fd5b610a868682870161087c565b9150509250925092565b600060208284031215610aa257600080fd5b6102198261099e565b60005b83811015610ac6578181015183820152602001610aae565b50506000910152565b60008251610ae1818460208701610aab565b9190910192915050565b6020815260008251806020840152610b0a816040850160208701610aab565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2063616c6c20776974682076616c7565206661696c6564a26469706673582212203e70ce334e8ec9d8d03e87415afd36dce4e82633bd277b08937095a6bd66367764736f6c63430008110033",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266"
}
},
{
"contractName": "ProxyAdmin",
"balance": "0",
"nonce": "1",
"address": "0xf065BaE7C019ff5627E09ed48D4EeA317D211956",
"bytecode": "0x60806040526004361061007b5760003560e01c80639623609d1161004e5780639623609d1461012b57806399a88ec41461013e578063f2fde38b1461015e578063f3b7dead1461017e57600080fd5b8063204e1c7a14610080578063715018a6146100c95780637eff275e146100e05780638da5cb5b14610100575b600080fd5b34801561008c57600080fd5b506100a061009b366004610608565b61019e565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100d557600080fd5b506100de610255565b005b3480156100ec57600080fd5b506100de6100fb36600461062c565b610269565b34801561010c57600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166100a0565b6100de610139366004610694565b6102f7565b34801561014a57600080fd5b506100de61015936600461062c565b61038c565b34801561016a57600080fd5b506100de610179366004610608565b6103e8565b34801561018a57600080fd5b506100a0610199366004610608565b6104a4565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907f5c60da1b00000000000000000000000000000000000000000000000000000000815260040190565b600060405180830381855afa9150503d8060008114610225576040519150601f19603f3d011682016040523d82523d6000602084013e61022a565b606091505b50915091508161023957600080fd5b8080602001905181019061024d9190610788565b949350505050565b61025d6104f0565b6102676000610571565b565b6102716104f0565b6040517f8f28397000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690638f283970906024015b600060405180830381600087803b1580156102db57600080fd5b505af11580156102ef573d6000803e3d6000fd5b505050505050565b6102ff6104f0565b6040517f4f1ef28600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841690634f1ef28690349061035590869086906004016107a5565b6000604051808303818588803b15801561036e57600080fd5b505af1158015610382573d6000803e3d6000fd5b5050505050505050565b6103946104f0565b6040517f3659cfe600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8281166004830152831690633659cfe6906024016102c1565b6103f06104f0565b73ffffffffffffffffffffffffffffffffffffffff8116610498576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b6104a181610571565b50565b60008060008373ffffffffffffffffffffffffffffffffffffffff166040516101ea907ff851a44000000000000000000000000000000000000000000000000000000000815260040190565b60005473ffffffffffffffffffffffffffffffffffffffff163314610267576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161048f565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b73ffffffffffffffffffffffffffffffffffffffff811681146104a157600080fd5b60006020828403121561061a57600080fd5b8135610625816105e6565b9392505050565b6000806040838503121561063f57600080fd5b823561064a816105e6565b9150602083013561065a816105e6565b809150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000806000606084860312156106a957600080fd5b83356106b4816105e6565b925060208401356106c4816105e6565b9150604084013567ffffffffffffffff808211156106e157600080fd5b818601915086601f8301126106f557600080fd5b81358181111561070757610707610665565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561074d5761074d610665565b8160405282815289602084870101111561076657600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b60006020828403121561079a57600080fd5b8151610625816105e6565b73ffffffffffffffffffffffffffffffffffffffff8316815260006020604081840152835180604085015260005b818110156107ef578581018301518582016060015282016107d3565b5060006060828601015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010192505050939250505056fea2646970667358221220372a0e10eebea1b7fa43ae4c976994e6ed01d85eedc3637b83f01d3f06be442064736f6c63430008110033",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000165878a594ca255338adfa4d48449f69242eb8f"
}
},
{
"contractName": "PolygonZkEVMBridge implementation",
"balance": "0",
"nonce": "1",
"address": "0xf23919bb44BCa81aeAb4586BE71Ee3fd4E99B951",
"bytecode": "0x6080604052600436106200019f5760003560e01c8063647c576c11620000e7578063be5831c71162000089578063dbc169761162000060578063dbc169761462000639578063ee25560b1462000651578063fb570834146200068257600080fd5b8063be5831c714620005ae578063cd58657914620005ea578063d02103ca146200060157600080fd5b80639e34070f11620000be5780639e34070f146200050a578063aaa13cc2146200054f578063bab161bf146200057457600080fd5b8063647c576c146200048657806379e2cf9714620004ab57806381b1c17414620004c357600080fd5b80632d2c9d94116200015157806334ac9cf2116200012857806334ac9cf2146200034b5780633ae05047146200037a5780633e197043146200039257600080fd5b80632d2c9d9414620002765780632dfdf0b5146200029b578063318aee3d14620002c257600080fd5b806322e95f2c116200018657806322e95f2c14620001ef578063240ff378146200023a5780632cffd02e146200025157600080fd5b806315064c9614620001a45780632072f6c514620001d5575b600080fd5b348015620001b157600080fd5b50606854620001c09060ff1681565b60405190151581526020015b60405180910390f35b348015620001e257600080fd5b50620001ed620006a7565b005b348015620001fc57600080fd5b50620002146200020e366004620032db565b62000705565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001620001cc565b620001ed6200024b36600462003372565b620007a8565b3480156200025e57600080fd5b50620001ed6200027036600462003409565b620009d0565b3480156200028357600080fd5b50620001ed6200029536600462003409565b62000f74565b348015620002a857600080fd5b50620002b360535481565b604051908152602001620001cc565b348015620002cf57600080fd5b5062000319620002e1366004620034ef565b606b6020526000908152604090205463ffffffff811690640100000000900473ffffffffffffffffffffffffffffffffffffffff1682565b6040805163ffffffff909316835273ffffffffffffffffffffffffffffffffffffffff909116602083015201620001cc565b3480156200035857600080fd5b50606c54620002149073ffffffffffffffffffffffffffffffffffffffff1681565b3480156200038757600080fd5b50620002b362001178565b3480156200039f57600080fd5b50620002b3620003b136600462003526565b6040517fff0000000000000000000000000000000000000000000000000000000000000060f889901b1660208201527fffffffff0000000000000000000000000000000000000000000000000000000060e088811b821660218401527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606089811b821660258601529188901b909216603984015285901b16603d8201526051810183905260718101829052600090609101604051602081830303815290604052805190602001209050979650505050505050565b3480156200049357600080fd5b50620001ed620004a5366004620035b0565b6200125e565b348015620004b857600080fd5b50620001ed620014ad565b348015620004d057600080fd5b5062000214620004e236600462003600565b606a6020526000908152604090205473ffffffffffffffffffffffffffffffffffffffff1681565b3480156200051757600080fd5b50620001c06200052936600462003600565b600881901c600090815260696020526040902054600160ff9092169190911b9081161490565b3480156200055c57600080fd5b50620002146200056e3660046200361a565b620014e7565b3480156200058157600080fd5b506068546200059890610100900463ffffffff1681565b60405163ffffffff9091168152602001620001cc565b348015620005bb57600080fd5b506068546200059890790100000000000000000000000000000000000000000000000000900463ffffffff1681565b620001ed620005fb366004620036ce565b620016d3565b3480156200060e57600080fd5b50606854620002149065010000000000900473ffffffffffffffffffffffffffffffffffffffff1681565b3480156200064657600080fd5b50620001ed62001c37565b3480156200065e57600080fd5b50620002b36200067036600462003600565b60696020526000908152604090205481565b3480156200068f57600080fd5b50620001c0620006a136600462003770565b62001c93565b606c5473ffffffffffffffffffffffffffffffffffffffff163314620006f9576040517fe2e8106b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200070362001d7c565b565b6040805160e084901b7fffffffff0000000000000000000000000000000000000000000000000000000016602080830191909152606084901b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602483015282516018818403018152603890920183528151918101919091206000908152606a909152205473ffffffffffffffffffffffffffffffffffffffff165b92915050565b60685460ff1615620007e6576040517f2f0047fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60685463ffffffff8681166101009092041614806200080c5750600263ffffffff861610155b1562000844576040517f0595ea2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b6001606860019054906101000a900463ffffffff163388883488886053546040516200089a9998979695949392919062003806565b60405180910390a1620009b8620009b26001606860019054906101000a900463ffffffff16338989348989604051620008d592919062003881565b60405180910390206040517fff0000000000000000000000000000000000000000000000000000000000000060f889901b1660208201527fffffffff0000000000000000000000000000000000000000000000000000000060e088811b821660218401527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606089811b821660258601529188901b909216603984015285901b16603d8201526051810183905260718101829052600090609101604051602081830303815290604052805190602001209050979650505050505050565b62001e10565b8215620009c957620009c962001f27565b5050505050565b60685460ff161562000a0e576040517f2f0047fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000a258b8b8b8b8b8b8b8b8b8b8b600062001ffc565b73ffffffffffffffffffffffffffffffffffffffff861662000b01576040805160008082526020820190925273ffffffffffffffffffffffffffffffffffffffff861690859060405162000a7a9190620038e6565b60006040518083038185875af1925050503d806000811462000ab9576040519150601f19603f3d011682016040523d82523d6000602084013e62000abe565b606091505b505090508062000afa576040517f6747a28800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5062000efc565b60685463ffffffff61010090910481169088160362000b435762000b3d73ffffffffffffffffffffffffffffffffffffffff87168585620021ed565b62000efc565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1660208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b166024820152600090603801604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081840301815291815281516020928301206000818152606a90935291205490915073ffffffffffffffffffffffffffffffffffffffff168062000e6e576000808062000c1886880188620039fb565b92509250925060008584848460405162000c329062003292565b62000c409392919062003abd565b8190604051809103906000f590508015801562000c61573d6000803e3d6000fd5b506040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8c81166004830152602482018c9052919250908216906340c10f1990604401600060405180830381600087803b15801562000cd757600080fd5b505af115801562000cec573d6000803e3d6000fd5b5050505080606a600088815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060405180604001604052808e63ffffffff1681526020018d73ffffffffffffffffffffffffffffffffffffffff16815250606b60008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055509050507f490e59a1701b938786ac72570a1efeac994a3dbe96e2e883e19e902ace6e6a398d8d838b8b60405162000e5c95949392919062003afa565b60405180910390a15050505062000ef9565b6040517f40c10f1900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8781166004830152602482018790528216906340c10f1990604401600060405180830381600087803b15801562000edf57600080fd5b505af115801562000ef4573d6000803e3d6000fd5b505050505b50505b6040805163ffffffff8c811682528916602082015273ffffffffffffffffffffffffffffffffffffffff88811682840152861660608201526080810185905290517f25308c93ceeed162da955b3f7ce3e3f93606579e40fb92029faa9efe275459839181900360a00190a15050505050505050505050565b60685460ff161562000fb2576040517f2f0047fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b62000fc98b8b8b8b8b8b8b8b8b8b8b600162001ffc565b60008473ffffffffffffffffffffffffffffffffffffffff1684888a868660405160240162000ffc949392919062003b42565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529181526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f1806b5f200000000000000000000000000000000000000000000000000000000179052516200107f9190620038e6565b60006040518083038185875af1925050503d8060008114620010be576040519150601f19603f3d011682016040523d82523d6000602084013e620010c3565b606091505b5050905080620010ff576040517f37e391c300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805163ffffffff8d811682528a16602082015273ffffffffffffffffffffffffffffffffffffffff89811682840152871660608201526080810186905290517f25308c93ceeed162da955b3f7ce3e3f93606579e40fb92029faa9efe275459839181900360a00190a1505050505050505050505050565b605354600090819081805b602081101562001255578083901c600116600103620011e65760338160208110620011b257620011b262003b8a565b0154604080516020810192909252810185905260600160405160208183030381529060405280519060200120935062001213565b60408051602081018690529081018390526060016040516020818303038152906040528051906020012093505b604080516020810184905290810183905260600160405160208183030381529060405280519060200120915080806200124c9062003be8565b91505062001183565b50919392505050565b600054610100900460ff16158080156200127f5750600054600160ff909116105b806200129b5750303b1580156200129b575060005460ff166001145b6200132d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156200138c57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b606880547fffffffffffffff000000000000000000000000000000000000000000000000ff1661010063ffffffff8716027fffffffffffffff0000000000000000000000000000000000000000ffffffffff16176501000000000073ffffffffffffffffffffffffffffffffffffffff8681169190910291909117909155606c80547fffffffffffffffffffffffff00000000000000000000000000000000000000001691841691909117905562001443620022c3565b8015620014a757600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b605354606854790100000000000000000000000000000000000000000000000000900463ffffffff16101562000703576200070362001f27565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e089901b1660208201527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606088901b1660248201526000908190603801604051602081830303815290604052805190602001209050600060ff60f81b3083604051806020016200157d9062003292565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe082820381018352601f909101166040819052620015c8908d908d908d908d908d9060200162003c23565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529082905262001606929160200162003c64565b604051602081830303815290604052805190602001206040516020016200168f94939291907fff0000000000000000000000000000000000000000000000000000000000000094909416845260609290921b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660018401526015830152603582015260550190565b604080518083037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe001815291905280516020909101209a9950505050505050505050565b60685460ff161562001711576040517f2f0047fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200171b62002366565b60685463ffffffff888116610100909204161480620017415750600263ffffffff881610155b1562001779576040517f0595ea2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008060608773ffffffffffffffffffffffffffffffffffffffff8816620017df57883414620017d5576040517fb89240f500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000925062001ad9565b341562001818576040517f798ee6f100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8089166000908152606b602090815260409182902082518084019093525463ffffffff811683526401000000009004909216918101829052901562001908576040517f9dc29fac000000000000000000000000000000000000000000000000000000008152336004820152602481018b905273ffffffffffffffffffffffffffffffffffffffff8a1690639dc29fac90604401600060405180830381600087803b158015620018db57600080fd5b505af1158015620018f0573d6000803e3d6000fd5b50505050806020015194508060000151935062001ad7565b85156200191d576200191d898b8989620023db565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8b16906370a0823190602401602060405180830381865afa1580156200198b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620019b1919062003c97565b9050620019d773ffffffffffffffffffffffffffffffffffffffff8b1633308e620028f9565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260009073ffffffffffffffffffffffffffffffffffffffff8c16906370a0823190602401602060405180830381865afa15801562001a45573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062001a6b919062003c97565b905062001a79828262003cb1565b6068548c9850610100900463ffffffff169650935062001a998762002959565b62001aa48c62002a71565b62001aaf8d62002b7e565b60405160200162001ac39392919062003abd565b604051602081830303815290604052945050505b505b7f501781209a1f8899323b96b4ef08b168df93e0a90c673d1e4cce39366cb62f9b600084868e8e868860535460405162001b1b98979695949392919062003cc7565b60405180910390a162001c0f620009b2600085878f8f8789805190602001206040517fff0000000000000000000000000000000000000000000000000000000000000060f889901b1660208201527fffffffff0000000000000000000000000000000000000000000000000000000060e088811b821660218401527fffffffffffffffffffffffffffffffffffffffff000000000000000000000000606089811b821660258601529188901b909216603984015285901b16603d8201526051810183905260718101829052600090609101604051602081830303815290604052805190602001209050979650505050505050565b861562001c205762001c2062001f27565b5050505062001c2e60018055565b50505050505050565b606c5473ffffffffffffffffffffffffffffffffffffffff16331462001c89576040517fe2e8106b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6200070362002c80565b600084815b602081101562001d6e57600163ffffffff8616821c8116900362001d0a5785816020811062001ccb5762001ccb62003b8a565b60200201358260405160200162001cec929190918252602082015260400190565b60405160208183030381529060405280519060200120915062001d59565b8186826020811062001d205762001d2062003b8a565b602002013560405160200162001d40929190918252602082015260400190565b6040516020818303038152906040528051906020012091505b8062001d658162003be8565b91505062001c98565b50821490505b949350505050565b60685460ff161562001dba576040517f2f0047fc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606880547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556040517f2261efe5aef6fedc1fd1550b25facc9181745623049c7901287030b9ad1a549790600090a1565b80600162001e216020600262003e79565b62001e2d919062003cb1565b6053541062001e68576040517fef5ccf6600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600060536000815462001e7b9062003be8565b9182905550905060005b602081101562001f17578082901c60011660010362001ebd57826033826020811062001eb55762001eb562003b8a565b015550505050565b6033816020811062001ed35762001ed362003b8a565b01546040805160208101929092528101849052606001604051602081830303815290604052805190602001209250808062001f0e9062003be8565b91505062001e85565b5062001f2262003e87565b505050565b6053546068805463ffffffff909216790100000000000000000000000000000000000000000000000000027fffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffff909216919091179081905573ffffffffffffffffffffffffffffffffffffffff65010000000000909104166333d6247d62001fad62001178565b6040518263ffffffff1660e01b815260040162001fcc91815260200190565b600060405180830381600087803b15801562001fe757600080fd5b505af1158015620014a7573d6000803e3d6000fd5b6200200d8b63ffffffff1662002d10565b6068546040805160208082018e90528183018d9052825180830384018152606083019384905280519101207f257b363200000000000000000000000000000000000000000000000000000000909252606481019190915260009165010000000000900473ffffffffffffffffffffffffffffffffffffffff169063257b3632906084016020604051808303816000875af1158015620020b0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620020d6919062003c97565b90508060000362002112576040517e2f6fad00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60685463ffffffff88811661010090920416146200215c576040517f0595ea2e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606854600090610100900463ffffffff166200217a5750896200217d565b508a5b620021a66200219d848c8c8c8c8c8c8c604051620008d592919062003881565b8f8f8462001c93565b620021dd576040517fe0417cec00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050505050505050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff831660248201526044810182905262001f229084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915262002d75565b600054610100900460ff166200235c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840162001324565b6200070362002e88565b600260015403620023d4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604482015260640162001324565b6002600155565b6000620023ec600482848662003eb6565b620023f79162003ee2565b90507f2afa5331000000000000000000000000000000000000000000000000000000007fffffffff00000000000000000000000000000000000000000000000000000000821601620026765760008080808080806200245a896004818d62003eb6565b81019062002469919062003f2b565b96509650965096509650965096503373ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff1614620024dd576040517f912ecce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff861630146200252d576040517f750643af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8a851462002567576040517f03fffc4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff89811660248301528881166044830152606482018890526084820187905260ff861660a483015260c4820185905260e48083018590528351808403909101815261010490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd505accf000000000000000000000000000000000000000000000000000000001790529151918e1691620026229190620038e6565b6000604051808303816000865af19150503d806000811462002661576040519150601f19603f3d011682016040523d82523d6000602084013e62002666565b606091505b50505050505050505050620009c9565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f8fcbaf0c0000000000000000000000000000000000000000000000000000000014620026f2576040517fe282c0ba00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808080808080806200270a8a6004818e62003eb6565b81019062002719919062003f86565b975097509750975097509750975097503373ffffffffffffffffffffffffffffffffffffffff168873ffffffffffffffffffffffffffffffffffffffff16146200278f576040517f912ecce700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff87163014620027df576040517f750643af00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805173ffffffffffffffffffffffffffffffffffffffff8a811660248301528981166044830152606482018990526084820188905286151560a483015260ff861660c483015260e482018590526101048083018590528351808403909101815261012490920183526020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f8fcbaf0c000000000000000000000000000000000000000000000000000000001790529151918f1691620028a39190620038e6565b6000604051808303816000865af19150503d8060008114620028e2576040519150601f19603f3d011682016040523d82523d6000602084013e620028e7565b606091505b50505050505050505050505050505050565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052620014a79085907f23b872dd000000000000000000000000000000000000000000000000000000009060840162002240565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f06fdde03000000000000000000000000000000000000000000000000000000001790529051606091600091829173ffffffffffffffffffffffffffffffffffffffff861691620029dd9190620038e6565b600060405180830381855afa9150503d806000811462002a1a576040519150601f19603f3d011682016040523d82523d6000602084013e62002a1f565b606091505b50915091508162002a66576040518060400160405280600781526020017f4e4f5f4e414d450000000000000000000000000000000000000000000000000081525062001d74565b62001d748162002f21565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f95d89b41000000000000000000000000000000000000000000000000000000001790529051606091600091829173ffffffffffffffffffffffffffffffffffffffff86169162002af59190620038e6565b600060405180830381855afa9150503d806000811462002b32576040519150601f19603f3d011682016040523d82523d6000602084013e62002b37565b606091505b50915091508162002a66576040518060400160405280600981526020017f4e4f5f53594d424f4c000000000000000000000000000000000000000000000081525062001d74565b60408051600481526024810182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f313ce5670000000000000000000000000000000000000000000000000000000017905290516000918291829173ffffffffffffffffffffffffffffffffffffffff86169162002c019190620038e6565b600060405180830381855afa9150503d806000811462002c3e576040519150601f19603f3d011682016040523d82523d6000602084013e62002c43565b606091505b509150915081801562002c57575080516020145b62002c6457601262001d74565b8080602001905181019062001d74919062004012565b60018055565b60685460ff1662002cbd576040517f5386698100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b606880547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556040517f1e5e34eea33501aecf2ebec9fe0e884a40804275ea7fe10b2ba084c8374308b390600090a1565b600881901c60008181526069602052604081208054600160ff861690811b91821892839055929091908183169003620009c9576040517f646cf55800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600062002dd9826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff16620031119092919063ffffffff16565b80519091501562001f22578080602001905181019062002dfa919062004032565b62001f22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f74207375636365656400000000000000000000000000000000000000000000606482015260840162001324565b600054610100900460ff1662002c7a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e67000000000000000000000000000000000000000000606482015260840162001324565b6060604082511062002f435781806020019051810190620007a2919062004052565b8151602003620030d35760005b60208110801562002f9b575082818151811062002f715762002f7162003b8a565b01602001517fff000000000000000000000000000000000000000000000000000000000000001615155b1562002fb6578062002fad8162003be8565b91505062002f50565b8060000362002ffa57505060408051808201909152601281527f4e4f545f56414c49445f454e434f44494e4700000000000000000000000000006020820152919050565b60008167ffffffffffffffff81111562003018576200301862003891565b6040519080825280601f01601f19166020018201604052801562003043576020820181803683370190505b50905060005b82811015620030cb5784818151811062003067576200306762003b8a565b602001015160f81c60f81b82828151811062003087576200308762003b8a565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535080620030c28162003be8565b91505062003049565b509392505050565b505060408051808201909152601281527f4e4f545f56414c49445f454e434f44494e470000000000000000000000000000602082015290565b919050565b606062001d748484600085856000808673ffffffffffffffffffffffffffffffffffffffff168587604051620031489190620038e6565b60006040518083038185875af1925050503d806000811462003187576040519150601f19603f3d011682016040523d82523d6000602084013e6200318c565b606091505b50915091506200319f87838387620031aa565b979650505050505050565b60608315620032455782516000036200323d5773ffffffffffffffffffffffffffffffffffffffff85163b6200323d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640162001324565b508162001d74565b62001d7483838151156200325c5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620013249190620040d2565b611b6680620040e883390190565b803563ffffffff811681146200310c57600080fd5b73ffffffffffffffffffffffffffffffffffffffff81168114620032d857600080fd5b50565b60008060408385031215620032ef57600080fd5b620032fa83620032a0565b915060208301356200330c81620032b5565b809150509250929050565b8015158114620032d857600080fd5b60008083601f8401126200333957600080fd5b50813567ffffffffffffffff8111156200335257600080fd5b6020830191508360208285010111156200336b57600080fd5b9250929050565b6000806000806000608086880312156200338b57600080fd5b6200339686620032a0565b94506020860135620033a881620032b5565b93506040860135620033ba8162003317565b9250606086013567ffffffffffffffff811115620033d757600080fd5b620033e58882890162003326565b969995985093965092949392505050565b806104008101831015620007a257600080fd5b60008060008060008060008060008060006105208c8e0312156200342c57600080fd5b620034388d8d620033f6565b9a50620034496104008d01620032a0565b99506104208c013598506104408c013597506200346a6104608d01620032a0565b96506104808c01356200347d81620032b5565b95506200348e6104a08d01620032a0565b94506104c08c0135620034a181620032b5565b93506104e08c013592506105008c013567ffffffffffffffff811115620034c757600080fd5b620034d58e828f0162003326565b915080935050809150509295989b509295989b9093969950565b6000602082840312156200350257600080fd5b81356200350f81620032b5565b9392505050565b60ff81168114620032d857600080fd5b600080600080600080600060e0888a0312156200354257600080fd5b87356200354f8162003516565b96506200355f60208901620032a0565b955060408801356200357181620032b5565b94506200358160608901620032a0565b935060808801356200359381620032b5565b9699959850939692959460a0840135945060c09093013592915050565b600080600060608486031215620035c657600080fd5b620035d184620032a0565b92506020840135620035e381620032b5565b91506040840135620035f581620032b5565b809150509250925092565b6000602082840312156200361357600080fd5b5035919050565b600080600080600080600060a0888a0312156200363657600080fd5b6200364188620032a0565b965060208801356200365381620032b5565b9550604088013567ffffffffffffffff808211156200367157600080fd5b6200367f8b838c0162003326565b909750955060608a01359150808211156200369957600080fd5b50620036a88a828b0162003326565b9094509250506080880135620036be8162003516565b8091505092959891949750929550565b600080600080600080600060c0888a031215620036ea57600080fd5b620036f588620032a0565b965060208801356200370781620032b5565b95506040880135945060608801356200372081620032b5565b93506080880135620037328162003317565b925060a088013567ffffffffffffffff8111156200374f57600080fd5b6200375d8a828b0162003326565b989b979a50959850939692959293505050565b60008060008061046085870312156200378857600080fd5b843593506200379b8660208701620033f6565b9250620037ac6104208601620032a0565b939692955092936104400135925050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600061010060ff8c16835263ffffffff808c16602085015273ffffffffffffffffffffffffffffffffffffffff808c166040860152818b166060860152808a166080860152508760a08501528160c0850152620038678285018789620037bd565b925080851660e085015250509a9950505050505050505050565b8183823760009101908152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60005b83811015620038dd578181015183820152602001620038c3565b50506000910152565b60008251620038fa818460208701620038c0565b9190910192915050565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff811182821017156200394e576200394e62003891565b604052919050565b600067ffffffffffffffff82111562003973576200397362003891565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112620039b157600080fd5b8135620039c8620039c28262003956565b62003904565b818152846020838601011115620039de57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060006060848603121562003a1157600080fd5b833567ffffffffffffffff8082111562003a2a57600080fd5b62003a38878388016200399f565b9450602086013591508082111562003a4f57600080fd5b5062003a5e868287016200399f565b9250506040840135620035f58162003516565b6000815180845262003a8b816020860160208601620038c0565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60608152600062003ad2606083018662003a71565b828103602084015262003ae6818662003a71565b91505060ff83166040830152949350505050565b63ffffffff86168152600073ffffffffffffffffffffffffffffffffffffffff8087166020840152808616604084015250608060608301526200319f608083018486620037bd565b73ffffffffffffffffffffffffffffffffffffffff8516815263ffffffff8416602082015260606040820152600062003b80606083018486620037bd565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820362003c1c5762003c1c62003bb9565b5060010190565b60608152600062003c39606083018789620037bd565b828103602084015262003c4e818688620037bd565b91505060ff831660408301529695505050505050565b6000835162003c78818460208801620038c0565b83519083019062003c8e818360208801620038c0565b01949350505050565b60006020828403121562003caa57600080fd5b5051919050565b81810381811115620007a257620007a262003bb9565b600061010060ff8b16835263ffffffff808b16602085015273ffffffffffffffffffffffffffffffffffffffff808b166040860152818a1660608601528089166080860152508660a08501528160c085015262003d278285018762003a71565b925080851660e085015250509998505050505050505050565b600181815b8085111562003d9f57817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111562003d835762003d8362003bb9565b8085161562003d9157918102915b93841c939080029062003d45565b509250929050565b60008262003db857506001620007a2565b8162003dc757506000620007a2565b816001811462003de0576002811462003deb5762003e0b565b6001915050620007a2565b60ff84111562003dff5762003dff62003bb9565b50506001821b620007a2565b5060208310610133831016604e8410600b841016171562003e30575081810a620007a2565b62003e3c838362003d40565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111562003e715762003e7162003bb9565b029392505050565b60006200350f838362003da7565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b6000808585111562003ec757600080fd5b8386111562003ed557600080fd5b5050820193919092039150565b7fffffffff00000000000000000000000000000000000000000000000000000000813581811691600485101562003f235780818660040360031b1b83161692505b505092915050565b600080600080600080600060e0888a03121562003f4757600080fd5b873562003f5481620032b5565b9650602088013562003f6681620032b5565b955060408801359450606088013593506080880135620035938162003516565b600080600080600080600080610100898b03121562003fa457600080fd5b883562003fb181620032b5565b9750602089013562003fc381620032b5565b96506040890135955060608901359450608089013562003fe38162003317565b935060a089013562003ff58162003516565b979a969950949793969295929450505060c08201359160e0013590565b6000602082840312156200402557600080fd5b81516200350f8162003516565b6000602082840312156200404557600080fd5b81516200350f8162003317565b6000602082840312156200406557600080fd5b815167ffffffffffffffff8111156200407d57600080fd5b8201601f810184136200408f57600080fd5b8051620040a0620039c28262003956565b818152856020838501011115620040b657600080fd5b620040c9826020830160208601620038c0565b95945050505050565b6020815260006200350f602083018462003a7156fe6101006040523480156200001257600080fd5b5060405162001b6638038062001b6683398101604081905262000035916200028d565b82826003620000458382620003a1565b506004620000548282620003a1565b50503360c0525060ff811660e052466080819052620000739062000080565b60a052506200046d915050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f620000ad6200012e565b805160209182012060408051808201825260018152603160f81b90840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b6060600380546200013f9062000312565b80601f01602080910402602001604051908101604052809291908181526020018280546200016d9062000312565b8015620001be5780601f106200019257610100808354040283529160200191620001be565b820191906000526020600020905b815481529060010190602001808311620001a057829003601f168201915b5050505050905090565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001f057600080fd5b81516001600160401b03808211156200020d576200020d620001c8565b604051601f8301601f19908116603f01168101908282118183101715620002385762000238620001c8565b816040528381526020925086838588010111156200025557600080fd5b600091505b838210156200027957858201830151818301840152908201906200025a565b600093810190920192909252949350505050565b600080600060608486031215620002a357600080fd5b83516001600160401b0380821115620002bb57600080fd5b620002c987838801620001de565b94506020860151915080821115620002e057600080fd5b50620002ef86828701620001de565b925050604084015160ff811681146200030757600080fd5b809150509250925092565b600181811c908216806200032757607f821691505b6020821081036200034857634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200039c57600081815260208120601f850160051c81016020861015620003775750805b601f850160051c820191505b81811015620003985782815560010162000383565b5050505b505050565b81516001600160401b03811115620003bd57620003bd620001c8565b620003d581620003ce845462000312565b846200034e565b602080601f8311600181146200040d5760008415620003f45750858301515b600019600386901b1c1916600185901b17855562000398565b600085815260208120601f198616915b828110156200043e578886015182559484019460019091019084016200041d565b50858210156200045d5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a05160c05160e0516116aa620004bc6000396000610237015260008181610307015281816105c001526106a70152600061053a015260008181610379015261050401526116aa6000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c806370a08231116100d8578063a457c2d71161008c578063d505accf11610066578063d505accf1461039b578063dd62ed3e146103ae578063ffa1ad74146103f457600080fd5b8063a457c2d71461034e578063a9059cbb14610361578063cd0d00961461037457600080fd5b806395d89b41116100bd57806395d89b41146102e75780639dc29fac146102ef578063a3c573eb1461030257600080fd5b806370a08231146102915780637ecebe00146102c757600080fd5b806330adf81f1161012f5780633644e515116101145780633644e51514610261578063395093511461026957806340c10f191461027c57600080fd5b806330adf81f14610209578063313ce5671461023057600080fd5b806318160ddd1161016057806318160ddd146101bd57806320606b70146101cf57806323b872dd146101f657600080fd5b806306fdde031461017c578063095ea7b31461019a575b600080fd5b610184610430565b60405161019191906113e4565b60405180910390f35b6101ad6101a8366004611479565b6104c2565b6040519015158152602001610191565b6002545b604051908152602001610191565b6101c17f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81565b6101ad6102043660046114a3565b6104dc565b6101c17f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610191565b6101c1610500565b6101ad610277366004611479565b61055c565b61028f61028a366004611479565b6105a8565b005b6101c161029f3660046114df565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6101c16102d53660046114df565b60056020526000908152604090205481565b610184610680565b61028f6102fd366004611479565b61068f565b6103297f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610191565b6101ad61035c366004611479565b61075e565b6101ad61036f366004611479565b61082f565b6101c17f000000000000000000000000000000000000000000000000000000000000000081565b61028f6103a9366004611501565b61083d565b6101c16103bc366004611574565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6101846040518060400160405280600181526020017f310000000000000000000000000000000000000000000000000000000000000081525081565b60606003805461043f906115a7565b80601f016020809104026020016040519081016040528092919081815260200182805461046b906115a7565b80156104b85780601f1061048d576101008083540402835291602001916104b8565b820191906000526020600020905b81548152906001019060200180831161049b57829003601f168201915b5050505050905090565b6000336104d0818585610b73565b60019150505b92915050565b6000336104ea858285610d27565b6104f5858585610dfe565b506001949350505050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610537576105324661106d565b905090565b507f000000000000000000000000000000000000000000000000000000000000000090565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906104d090829086906105a3908790611629565b610b73565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610672576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d4272696467650000000000000000000000000000000060648201526084015b60405180910390fd5b61067c8282611135565b5050565b60606004805461043f906115a7565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610754576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603060248201527f546f6b656e577261707065643a3a6f6e6c794272696467653a204e6f7420506f60448201527f6c79676f6e5a6b45564d427269646765000000000000000000000000000000006064820152608401610669565b61067c8282611228565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610822576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610669565b6104f58286868403610b73565b6000336104d0818585610dfe565b834211156108cc576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f546f6b656e577261707065643a3a7065726d69743a204578706972656420706560448201527f726d6974000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8716600090815260056020526040812080547f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918a918a918a9190866109268361163c565b9091555060408051602081019690965273ffffffffffffffffffffffffffffffffffffffff94851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090506000610991610500565b6040517f19010000000000000000000000000000000000000000000000000000000000006020820152602281019190915260428101839052606201604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181528282528051602091820120600080855291840180845281905260ff89169284019290925260608301879052608083018690529092509060019060a0016020604051602081039080840390855afa158015610a55573d6000803e3d6000fd5b50506040517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015191505073ffffffffffffffffffffffffffffffffffffffff811615801590610ad057508973ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16145b610b5c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f546f6b656e577261707065643a3a7065726d69743a20496e76616c696420736960448201527f676e6174757265000000000000000000000000000000000000000000000000006064820152608401610669565b610b678a8a8a610b73565b50505050505050505050565b73ffffffffffffffffffffffffffffffffffffffff8316610c15576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610cb8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610df85781811015610deb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610669565b610df88484848403610b73565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8316610ea1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff8216610f44576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205481811015610ffa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610df8565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f611098610430565b8051602091820120604080518082018252600181527f310000000000000000000000000000000000000000000000000000000000000090840152805192830193909352918101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc66060820152608081018390523060a082015260c001604051602081830303815290604052805190602001209050919050565b73ffffffffffffffffffffffffffffffffffffffff82166111b2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610669565b80600260008282546111c49190611629565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b73ffffffffffffffffffffffffffffffffffffffff82166112cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff821660009081526020819052604090205481811015611381576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610669565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9101610d1a565b600060208083528351808285015260005b81811015611411578581018301518582016040015282016113f5565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461147457600080fd5b919050565b6000806040838503121561148c57600080fd5b61149583611450565b946020939093013593505050565b6000806000606084860312156114b857600080fd5b6114c184611450565b92506114cf60208501611450565b9150604084013590509250925092565b6000602082840312156114f157600080fd5b6114fa82611450565b9392505050565b600080600080600080600060e0888a03121561151c57600080fd5b61152588611450565b965061153360208901611450565b95506040880135945060608801359350608088013560ff8116811461155757600080fd5b9699959850939692959460a0840135945060c09093013592915050565b6000806040838503121561158757600080fd5b61159083611450565b915061159e60208401611450565b90509250929050565b600181811c908216806115bb57607f821691505b6020821081036115f4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b808201808211156104d6576104d66115fa565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361166d5761166d6115fa565b506001019056fea26469706673582212208d88fee561cff7120d381c345cfc534cef8229a272dc5809d4bbb685ad67141164736f6c63430008110033a2646970667358221220d9b3ca7b13ec80ac58634ddf0ecebe71e209a71f532614949b9e720413f50c8364736f6c63430008110033"
},
{
"contractName": "PolygonZkEVMBridge proxy",
"balance": "200000000000000000000000000",
"nonce": "1",
"address": "0xff0EE8ea08cEf5cb4322777F5CC3E8A584B8A4A0",
"bytecode": "0x60806040526004361061005e5760003560e01c80635c60da1b116100435780635c60da1b146100a85780638f283970146100e6578063f851a440146101065761006d565b80633659cfe6146100755780634f1ef286146100955761006d565b3661006d5761006b61011b565b005b61006b61011b565b34801561008157600080fd5b5061006b61009036600461088b565b610135565b61006b6100a33660046108a6565b61017f565b3480156100b457600080fd5b506100bd6101f3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b3480156100f257600080fd5b5061006b61010136600461088b565b610231565b34801561011257600080fd5b506100bd61025e565b6101236102d4565b61013361012e6103ab565b6103b5565b565b61013d6103d9565b73ffffffffffffffffffffffffffffffffffffffff1633036101775761017481604051806020016040528060008152506000610419565b50565b61017461011b565b6101876103d9565b73ffffffffffffffffffffffffffffffffffffffff1633036101eb576101e68383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610419915050565b505050565b6101e661011b565b60006101fd6103d9565b73ffffffffffffffffffffffffffffffffffffffff163303610226576102216103ab565b905090565b61022e61011b565b90565b6102396103d9565b73ffffffffffffffffffffffffffffffffffffffff1633036101775761017481610444565b60006102686103d9565b73ffffffffffffffffffffffffffffffffffffffff163303610226576102216103d9565b60606102b183836040518060600160405280602781526020016109bb602791396104a5565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b6102dc6103d9565b73ffffffffffffffffffffffffffffffffffffffff163303610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760648201527f6574000000000000000000000000000000000000000000000000000000000000608482015260a4015b60405180910390fd5b600061022161052a565b3660008037600080366000845af43d6000803e8080156103d4573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b5473ffffffffffffffffffffffffffffffffffffffff16919050565b61042283610552565b60008251118061042f5750805b156101e65761043e838361028c565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61046d6103d9565b6040805173ffffffffffffffffffffffffffffffffffffffff928316815291841660208301520160405180910390a16101748161059f565b60606000808573ffffffffffffffffffffffffffffffffffffffff16856040516104cf919061094d565b600060405180830381855af49150503d806000811461050a576040519150601f19603f3d011682016040523d82523d6000602084013e61050f565b606091505b5091509150610520868383876106ab565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6103fd565b61055b81610753565b60405173ffffffffffffffffffffffffffffffffffffffff8216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b73ffffffffffffffffffffffffffffffffffffffff8116610642576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201527f646472657373000000000000000000000000000000000000000000000000000060648201526084016103a2565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691909117905550565b6060831561074157825160000361073a5773ffffffffffffffffffffffffffffffffffffffff85163b61073a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103a2565b508161074b565b61074b838361081e565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff81163b6107f7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201527f6f74206120636f6e74726163740000000000000000000000000000000000000060648201526084016103a2565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610665565b81511561082e5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103a29190610969565b803573ffffffffffffffffffffffffffffffffffffffff8116811461088657600080fd5b919050565b60006020828403121561089d57600080fd5b6102b182610862565b6000806000604084860312156108bb57600080fd5b6108c484610862565b9250602084013567ffffffffffffffff808211156108e157600080fd5b818601915086601f8301126108f557600080fd5b81358181111561090457600080fd5b87602082850101111561091657600080fd5b6020830194508093505050509250925092565b60005b8381101561094457818101518382015260200161092c565b50506000910152565b6000825161095f818460208701610929565b9190910192915050565b6020815260008251806020840152610988816040850160208701610929565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220a1af0d6cb4f1e31496a4c5c1448913bce4bd6ad3a39e47c6f7190c114d6f9bf464736f6c63430008110033",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000001": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0x0000000000000000000000000000000000000000000000000000000000000068": "0x00000000000000a40d5f56745a118d0906a34e69aec8c0db1cb8fa0000000100",
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f065bae7c019ff5627e09ed48d4eea317d211956",
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000f23919bb44bca81aeab4586be71ee3fd4e99b951"
}
},
{
"contractName": "PolygonZkEVMGlobalExitRootL2 implementation",
"balance": "0",
"nonce": "1",
"address": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
"bytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c806301fd904414610051578063257b36321461006d57806333d6247d1461008d578063a3c573eb146100a2575b600080fd5b61005a60015481565b6040519081526020015b60405180910390f35b61005a61007b366004610162565b60006020819052908152604090205481565b6100a061009b366004610162565b6100ee565b005b6100c97f000000000000000000000000ff0ee8ea08cef5cb4322777f5cc3e8a584b8a4a081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610064565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ff0ee8ea08cef5cb4322777f5cc3e8a584b8a4a0161461015d576040517fb49365dd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600155565b60006020828403121561017457600080fd5b503591905056fea2646970667358221220a187fc278346c1b61c449ea3641002b6eac2bda3351a122a12c35099f933696864736f6c63430008110033"
},
{
"contractName": "PolygonZkEVMGlobalExitRootL2 proxy",
"balance": "0",
"nonce": "1",
"address": "0xa40d5f56745a118d0906a34e69aec8c0db1cb8fa",
"bytecode": "0x60806040526004361061004e5760003560e01c80633659cfe6146100655780634f1ef286146100855780635c60da1b146100985780638f283970146100c9578063f851a440146100e95761005d565b3661005d5761005b6100fe565b005b61005b6100fe565b34801561007157600080fd5b5061005b6100803660046106ca565b610118565b61005b6100933660046106e5565b61015f565b3480156100a457600080fd5b506100ad6101d0565b6040516001600160a01b03909116815260200160405180910390f35b3480156100d557600080fd5b5061005b6100e43660046106ca565b61020b565b3480156100f557600080fd5b506100ad610235565b610106610292565b610116610111610331565b61033b565b565b61012061035f565b6001600160a01b0316336001600160a01b031614156101575761015481604051806020016040528060008152506000610392565b50565b6101546100fe565b61016761035f565b6001600160a01b0316336001600160a01b031614156101c8576101c38383838080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525060019250610392915050565b505050565b6101c36100fe565b60006101da61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb610331565b905090565b6102086100fe565b90565b61021361035f565b6001600160a01b0316336001600160a01b0316141561015757610154816103f1565b600061023f61035f565b6001600160a01b0316336001600160a01b03161415610200576101fb61035f565b606061028583836040518060600160405280602781526020016107e460279139610445565b9392505050565b3b151590565b61029a61035f565b6001600160a01b0316336001600160a01b031614156101165760405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b60006101fb610519565b3660008037600080366000845af43d6000803e80801561035a573d6000f35b3d6000fd5b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b61039b83610541565b6040516001600160a01b038416907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a26000825111806103dc5750805b156101c3576103eb8383610260565b50505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f61041a61035f565b604080516001600160a01b03928316815291841660208301520160405180910390a1610154816105e9565b6060833b6104a45760405162461bcd60e51b815260206004820152602660248201527f416464726573733a2064656c65676174652063616c6c20746f206e6f6e2d636f6044820152651b9d1c9858dd60d21b6064820152608401610328565b600080856001600160a01b0316856040516104bf9190610794565b600060405180830381855af49150503d80600081146104fa576040519150601f19603f3d011682016040523d82523d6000602084013e6104ff565b606091505b509150915061050f828286610675565b9695505050505050565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc610383565b803b6105a55760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401610328565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b6001600160a01b03811661064e5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b6064820152608401610328565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61036105c8565b60608315610684575081610285565b8251156106945782518084602001fd5b8160405162461bcd60e51b815260040161032891906107b0565b80356001600160a01b03811681146106c557600080fd5b919050565b6000602082840312156106dc57600080fd5b610285826106ae565b6000806000604084860312156106fa57600080fd5b610703846106ae565b9250602084013567ffffffffffffffff8082111561072057600080fd5b818601915086601f83011261073457600080fd5b81358181111561074357600080fd5b87602082850101111561075557600080fd5b6020830194508093505050509250925092565b60005b8381101561078357818101518382015260200161076b565b838111156103eb5750506000910152565b600082516107a6818460208701610768565b9190910192915050565b60208152600082518060208401526107cf816040850160208701610768565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212204675187caf3a43285d9a2c1844a981e977bd52a85ff073e7fc649f73847d70a464736f6c63430008090033",
"storage": {
"0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103": "0x000000000000000000000000f065bae7c019ff5627e09ed48d4eea317d211956",
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc": "0x000000000000000000000000dc64a140aa3e981100a9beca4e685f962f0cf6c9"
}
},
{
"contractName": "CDKValidiumTimelock",
"balance": "0",
"nonce": "1",
"address": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"bytecode": "0x6080604052600436106101c65760003560e01c806364d62353116100f7578063b1c5f42711610095578063d547741f11610064578063d547741f14610661578063e38335e514610681578063f23a6e6114610694578063f27a0c92146106d957600080fd5b8063b1c5f427146105af578063bc197c81146105cf578063c4d252f514610614578063d45c44351461063457600080fd5b80638f61f4f5116100d15780638f61f4f5146104e157806391d1485414610515578063a217fddf14610566578063b08e51c01461057b57600080fd5b806364d62353146104815780638065657f146104a15780638f2a0bb0146104c157600080fd5b8063248a9ca31161016457806331d507501161013e57806331d50750146103c857806336568abe146103e85780633a6aae7214610408578063584b153e1461046157600080fd5b8063248a9ca3146103475780632ab0f529146103775780632f2ff15d146103a857600080fd5b80630d3cf6fc116101a05780630d3cf6fc1461026b578063134008d31461029f57806313bc9f20146102b2578063150b7a02146102d257600080fd5b806301d5062a146101d257806301ffc9a7146101f457806307bd02651461022957600080fd5b366101cd57005b600080fd5b3480156101de57600080fd5b506101f26101ed366004611c52565b6106ee565b005b34801561020057600080fd5b5061021461020f366004611cc7565b610783565b60405190151581526020015b60405180910390f35b34801561023557600080fd5b5061025d7fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e6381565b604051908152602001610220565b34801561027757600080fd5b5061025d7f5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca581565b6101f26102ad366004611d09565b6107df565b3480156102be57600080fd5b506102146102cd366004611d75565b6108d7565b3480156102de57600080fd5b506103166102ed366004611e9a565b7f150b7a0200000000000000000000000000000000000000000000000000000000949350505050565b6040517fffffffff000000000000000000000000000000000000000000000000000000009091168152602001610220565b34801561035357600080fd5b5061025d610362366004611d75565b60009081526020819052604090206001015490565b34801561038357600080fd5b50610214610392366004611d75565b6000908152600160208190526040909120541490565b3480156103b457600080fd5b506101f26103c3366004611f02565b6108fd565b3480156103d457600080fd5b506102146103e3366004611d75565b610927565b3480156103f457600080fd5b506101f2610403366004611f02565b610940565b34801561041457600080fd5b5061043c7f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610220565b34801561046d57600080fd5b5061021461047c366004611d75565b6109f8565b34801561048d57600080fd5b506101f261049c366004611d75565b610a0e565b3480156104ad57600080fd5b5061025d6104bc366004611d09565b610ade565b3480156104cd57600080fd5b506101f26104dc366004611f73565b610b1d565b3480156104ed57600080fd5b5061025d7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc181565b34801561052157600080fd5b50610214610530366004611f02565b60009182526020828152604080842073ffffffffffffffffffffffffffffffffffffffff93909316845291905290205460ff1690565b34801561057257600080fd5b5061025d600081565b34801561058757600080fd5b5061025d7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f78381565b3480156105bb57600080fd5b5061025d6105ca366004612025565b610d4f565b3480156105db57600080fd5b506103166105ea36600461214e565b7fbc197c810000000000000000000000000000000000000000000000000000000095945050505050565b34801561062057600080fd5b506101f261062f366004611d75565b610d94565b34801561064057600080fd5b5061025d61064f366004611d75565b60009081526001602052604090205490565b34801561066d57600080fd5b506101f261067c366004611f02565b610e8f565b6101f261068f366004612025565b610eb4565b3480156106a057600080fd5b506103166106af3660046121f8565b7ff23a6e610000000000000000000000000000000000000000000000000000000095945050505050565b3480156106e557600080fd5b5061025d611161565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc161071881611244565b6000610728898989898989610ade565b90506107348184611251565b6000817f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8b8b8b8b8b8a604051610770969594939291906122a6565b60405180910390a3505050505050505050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f4e2312e00000000000000000000000000000000000000000000000000000000014806107d957506107d98261139e565b92915050565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff1661085c5761085c8133611435565b600061086c888888888888610ade565b905061087881856114ed565b6108848888888861162a565b6000817fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588a8a8a8a6040516108bc94939291906122f1565b60405180910390a36108cd8161172e565b5050505050505050565b6000818152600160205260408120546001811180156108f65750428111155b9392505050565b60008281526020819052604090206001015461091881611244565b61092283836117d7565b505050565b60008181526001602052604081205481905b1192915050565b73ffffffffffffffffffffffffffffffffffffffff811633146109ea576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201527f20726f6c657320666f722073656c66000000000000000000000000000000000060648201526084015b60405180910390fd5b6109f482826118c7565b5050565b6000818152600160208190526040822054610939565b333014610a9d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f54696d656c6f636b436f6e74726f6c6c65723a2063616c6c6572206d7573742060448201527f62652074696d656c6f636b00000000000000000000000000000000000000000060648201526084016109e1565b60025460408051918252602082018390527f11c24f4ead16507c69ac467fbd5e4eed5fb5c699626d2cc6d66421df253886d5910160405180910390a1600255565b6000868686868686604051602001610afb969594939291906122a6565b6040516020818303038152906040528051906020012090509695505050505050565b7fb09aa5aeb3702cfd50b6b62bc4532604938f21248a27a1d5ca736082b6819cc1610b4781611244565b888714610bd6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b888514610c65576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b6000610c778b8b8b8b8b8b8b8b610d4f565b9050610c838184611251565b60005b8a811015610d415780827f4cf4410cc57040e44862ef0f45f3dd5a5e02db8eb8add648d4b0e236f1d07dca8e8e85818110610cc357610cc3612331565b9050602002016020810190610cd89190612360565b8d8d86818110610cea57610cea612331565b905060200201358c8c87818110610d0357610d03612331565b9050602002810190610d15919061237b565b8c8b604051610d29969594939291906122a6565b60405180910390a3610d3a8161240f565b9050610c86565b505050505050505050505050565b60008888888888888888604051602001610d709897969594939291906124f7565b60405160208183030381529060405280519060200120905098975050505050505050565b7ffd643c72710c63c0180259aba6b2d05451e3591a24e58b62239378085726f783610dbe81611244565b610dc7826109f8565b610e53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603160248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20636160448201527f6e6e6f742062652063616e63656c6c656400000000000000000000000000000060648201526084016109e1565b6000828152600160205260408082208290555183917fbaa1eb22f2a492ba1a5fea61b8df4d27c6c8b5f3971e63bb58fa14ff72eedb7091a25050565b600082815260208190526040902060010154610eaa81611244565b61092283836118c7565b600080527fdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d70696020527f5ba6852781629bcdcd4bdaa6de76d786f1c64b16acdac474e55bebc0ea157951547fd8aa0f3194971a2a116679f7c2090f6939c8d4e01a2a8d7e41d55e5351469e639060ff16610f3157610f318133611435565b878614610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b87841461104f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f54696d656c6f636b436f6e74726f6c6c65723a206c656e677468206d69736d6160448201527f746368000000000000000000000000000000000000000000000000000000000060648201526084016109e1565b60006110618a8a8a8a8a8a8a8a610d4f565b905061106d81856114ed565b60005b8981101561114b5760008b8b8381811061108c5761108c612331565b90506020020160208101906110a19190612360565b905060008a8a848181106110b7576110b7612331565b9050602002013590503660008a8a868181106110d5576110d5612331565b90506020028101906110e7919061237b565b915091506110f78484848461162a565b84867fc2617efa69bab66782fa219543714338489c4e9e178271560a91b82c3f612b588686868660405161112e94939291906122f1565b60405180910390a350505050806111449061240f565b9050611070565b506111558161172e565b50505050505050505050565b60007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff161580159061123257507f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff166315064c966040518163ffffffff1660e01b8152600401602060405180830381865afa15801561120e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061123291906125be565b1561123d5750600090565b5060025490565b61124e8133611435565b50565b61125a82610927565b156112e7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20616c60448201527f7265616479207363686564756c6564000000000000000000000000000000000060648201526084016109e1565b6112ef611161565b81101561137e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a20696e73756666696369656e7460448201527f2064656c6179000000000000000000000000000000000000000000000000000060648201526084016109e1565b61138881426125e0565b6000928352600160205260409092209190915550565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f7965db0b0000000000000000000000000000000000000000000000000000000014806107d957507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316146107d9565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f4576114738161197e565b61147e83602061199d565b60405160200161148f929190612617565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0818403018152908290527f08c379a00000000000000000000000000000000000000000000000000000000082526109e191600401612698565b6114f6826108d7565b611582576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b80158061159e5750600081815260016020819052604090912054145b6109f4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f54696d656c6f636b436f6e74726f6c6c65723a206d697373696e67206465706560448201527f6e64656e6379000000000000000000000000000000000000000000000000000060648201526084016109e1565b60008473ffffffffffffffffffffffffffffffffffffffff168484846040516116549291906126e9565b60006040518083038185875af1925050503d8060008114611691576040519150601f19603f3d011682016040523d82523d6000602084013e611696565b606091505b5050905080611727576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603360248201527f54696d656c6f636b436f6e74726f6c6c65723a20756e6465726c79696e67207460448201527f72616e73616374696f6e2072657665727465640000000000000000000000000060648201526084016109e1565b5050505050565b611737816108d7565b6117c3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f54696d656c6f636b436f6e74726f6c6c65723a206f7065726174696f6e20697360448201527f206e6f742072656164790000000000000000000000000000000000000000000060648201526084016109e1565b600090815260016020819052604090912055565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff166109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff85168452909152902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556118693390565b73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516845290915290205460ff16156109f45760008281526020818152604080832073ffffffffffffffffffffffffffffffffffffffff8516808552925280832080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b60606107d973ffffffffffffffffffffffffffffffffffffffff831660145b606060006119ac8360026126f9565b6119b79060026125e0565b67ffffffffffffffff8111156119cf576119cf611d8e565b6040519080825280601f01601f1916602001820160405280156119f9576020820181803683370190505b5090507f300000000000000000000000000000000000000000000000000000000000000081600081518110611a3057611a30612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053507f780000000000000000000000000000000000000000000000000000000000000081600181518110611a9357611a93612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506000611acf8460026126f9565b611ada9060016125e0565b90505b6001811115611b77577f303132333435363738396162636465660000000000000000000000000000000085600f1660108110611b1b57611b1b612331565b1a60f81b828281518110611b3157611b31612331565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a90535060049490941c93611b7081612710565b9050611add565b5083156108f6576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016109e1565b803573ffffffffffffffffffffffffffffffffffffffff81168114611c0457600080fd5b919050565b60008083601f840112611c1b57600080fd5b50813567ffffffffffffffff811115611c3357600080fd5b602083019150836020828501011115611c4b57600080fd5b9250929050565b600080600080600080600060c0888a031215611c6d57600080fd5b611c7688611be0565b965060208801359550604088013567ffffffffffffffff811115611c9957600080fd5b611ca58a828b01611c09565b989b979a50986060810135976080820135975060a09091013595509350505050565b600060208284031215611cd957600080fd5b81357fffffffff00000000000000000000000000000000000000000000000000000000811681146108f657600080fd5b60008060008060008060a08789031215611d2257600080fd5b611d2b87611be0565b955060208701359450604087013567ffffffffffffffff811115611d4e57600080fd5b611d5a89828a01611c09565b979a9699509760608101359660809091013595509350505050565b600060208284031215611d8757600080fd5b5035919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715611e0457611e04611d8e565b604052919050565b600082601f830112611e1d57600080fd5b813567ffffffffffffffff811115611e3757611e37611d8e565b611e6860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601611dbd565b818152846020838601011115611e7d57600080fd5b816020850160208301376000918101602001919091529392505050565b60008060008060808587031215611eb057600080fd5b611eb985611be0565b9350611ec760208601611be0565b925060408501359150606085013567ffffffffffffffff811115611eea57600080fd5b611ef687828801611e0c565b91505092959194509250565b60008060408385031215611f1557600080fd5b82359150611f2560208401611be0565b90509250929050565b60008083601f840112611f4057600080fd5b50813567ffffffffffffffff811115611f5857600080fd5b6020830191508360208260051b8501011115611c4b57600080fd5b600080600080600080600080600060c08a8c031215611f9157600080fd5b893567ffffffffffffffff80821115611fa957600080fd5b611fb58d838e01611f2e565b909b50995060208c0135915080821115611fce57600080fd5b611fda8d838e01611f2e565b909950975060408c0135915080821115611ff357600080fd5b506120008c828d01611f2e565b9a9d999c50979a969997986060880135976080810135975060a0013595509350505050565b60008060008060008060008060a0898b03121561204157600080fd5b883567ffffffffffffffff8082111561205957600080fd5b6120658c838d01611f2e565b909a50985060208b013591508082111561207e57600080fd5b61208a8c838d01611f2e565b909850965060408b01359150808211156120a357600080fd5b506120b08b828c01611f2e565b999c989b509699959896976060870135966080013595509350505050565b600082601f8301126120df57600080fd5b8135602067ffffffffffffffff8211156120fb576120fb611d8e565b8160051b61210a828201611dbd565b928352848101820192828101908785111561212457600080fd5b83870192505b848310156121435782358252918301919083019061212a565b979650505050505050565b600080600080600060a0868803121561216657600080fd5b61216f86611be0565b945061217d60208701611be0565b9350604086013567ffffffffffffffff8082111561219a57600080fd5b6121a689838a016120ce565b945060608801359150808211156121bc57600080fd5b6121c889838a016120ce565b935060808801359150808211156121de57600080fd5b506121eb88828901611e0c565b9150509295509295909350565b600080600080600060a0868803121561221057600080fd5b61221986611be0565b945061222760208701611be0565b93506040860135925060608601359150608086013567ffffffffffffffff81111561225157600080fd5b6121eb88828901611e0c565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a0604082015260006122dc60a08301868861225d565b60608301949094525060800152949350505050565b73ffffffffffffffffffffffffffffffffffffffff8516815283602082015260606040820152600061232760608301848661225d565b9695505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561237257600080fd5b6108f682611be0565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126123b057600080fd5b83018035915067ffffffffffffffff8211156123cb57600080fd5b602001915036819003821315611c4b57600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612440576124406123e0565b5060010190565b81835260006020808501808196508560051b810191508460005b878110156124ea57828403895281357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18836030181126124a057600080fd5b8701858101903567ffffffffffffffff8111156124bc57600080fd5b8036038213156124cb57600080fd5b6124d686828461225d565b9a87019a9550505090840190600101612461565b5091979650505050505050565b60a0808252810188905260008960c08301825b8b8110156125455773ffffffffffffffffffffffffffffffffffffffff61253084611be0565b1682526020928301929091019060010161250a565b5083810360208501528881527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff89111561257e57600080fd5b8860051b9150818a602083013701828103602090810160408501526125a69082018789612447565b60608401959095525050608001529695505050505050565b6000602082840312156125d057600080fd5b815180151581146108f657600080fd5b808201808211156107d9576107d96123e0565b60005b8381101561260e5781810151838201526020016125f6565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161264f8160178501602088016125f3565b7f206973206d697373696e6720726f6c6520000000000000000000000000000000601791840191820152835161268c8160288401602088016125f3565b01602801949350505050565b60208152600082518060208401526126b78160408501602087016125f3565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b8183823760009101908152919050565b80820281158282048414176107d9576107d96123e0565b60008161271f5761271f6123e0565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff019056fea26469706673582212206416c4e08f97752b4bb06159524dac058d3dccd8775e57ef1b01505751ebf7af64736f6c63430008110033",
"storage": {
"0x0000000000000000000000000000000000000000000000000000000000000002": "0x000000000000000000000000000000000000000000000000000000000000000a",
"0xaedcc9e7897c0d335bdc5d92fe3a8b4f23727fe558cd1c19f332b28716a30559": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0xf5e61edb9c9cc6bfbae4463e9a2b1dd6ac3b44ddef38f18016e56ba0363910d9": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0x64494413541ff93b31aa309254e3fed72a7456e9845988b915b4c7a7ceba8814": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5",
"0x60b9d94c75b7b3f721925089391e4644cd890cb5e6466f9596dfbd2c54e0b280": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0x3412d5605ac6cd444957cedb533e5dacad6378b4bc819ebe3652188a665066d6": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5",
"0x4b63b79f1e338a49559dcd3193ac9eecc50d0f275d24e97cc8c319e5a31a8bd0": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0xdae2aa361dfd1ca020a396615627d436107c35eff9fe7738a3512819782d706a": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5",
"0x800d5dfe4bba53eedee06cd4546a27da8de00f12db83f56062976d4493fda899": "0x0000000000000000000000000000000000000000000000000000000000000001",
"0xc3ad33e20b0c56a223ad5104fff154aa010f8715b9c981fd38fdc60a4d1a52fc": "0x5f58e3a2316349923ce3780f8d587db2d72378aed66a8261c916544fa6846ca5"
}
},
{
"accountName": "keyless Deployer",
"balance": "0",
"nonce": "1",
"address": "0x20E7077d25fe79C5F6c2D3ae4905E96aA7C89c13"
},
{
"accountName": "deployer",
"balance": "100000000000000000000000",
"nonce": "8",
"address": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
}
]
}
\ No newline at end of file
## Functions
### initialize
```solidity
function initialize(
) external
```
### setupCommittee
```solidity
function setupCommittee(
uint256 _requiredAmountOfSignatures,
string[] urls,
bytes addrsBytes
) external
```
Allows the admin to setup the members of the committee. Note that:
The system will require N / M signatures where N => _requiredAmountOfSignatures and M => urls.length
There must be the same amount of urls than addressess encoded in the addrsBytes
A member is represented by the url and the address contained in urls[i] and addrsBytes[i*_ADDR_SIZE : i*_ADDR_SIZE + _ADDR_SIZE]
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_requiredAmountOfSignatures` | uint256 | Required amount of signatures
|`urls` | string[] | List of urls of the members of the committee
|`addrsBytes` | bytes | Byte array that contains the addressess of the members of the committee
### getAmountOfMembers
```solidity
function getAmountOfMembers(
) public returns (uint256)
```
### verifySignatures
```solidity
function verifySignatures(
bytes32 signedHash,
bytes signaturesAndAddrs
) external
```
Verifies that the given signedHash has been signed by requiredAmountOfSignatures committee members
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`signedHash` | bytes32 | Hash that must have been signed by requiredAmountOfSignatures of committee members
|`signaturesAndAddrs` | bytes | Byte array containing the signatures and all the addresses of the committee in ascending order
[signature 0, ..., signature requiredAmountOfSignatures -1, address 0, ... address N]
note that each ECDSA signatures are used, therefore each one must be 65 bytes
## Events
### CommitteeUpdated
```solidity
event CommitteeUpdated(
bytes32 committeeHash
)
```
Emitted when the committee is updated
#### Parameters:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------- |
|`committeeHash`| bytes32 | hash of the addresses of the committee members
Contract responsible for managing the states and the updates of L2 network.
There will be a trusted sequencer, which is able to send transactions.
Any user can force some transaction and the sequencer will have a timeout to add them in the queue.
The sequenced state is deterministic and can be precalculated before it's actually verified by a zkProof.
The aggregators will be able to verify the sequenced state with zkProofs and therefore make available the withdrawals from L2 network.
To enter and exit of the L2 network will be used a PolygonZkEVMBridge smart contract that will be deployed in both networks.
## Functions
### constructor
```solidity
function constructor(
contract IPolygonZkEVMGlobalExitRoot _globalExitRootManager,
contract IERC20Upgradeable _matic,
contract IVerifierRollup _rollupVerifier,
contract IPolygonZkEVMBridge _bridgeAddress,
contract ICDKDataCommittee _dataCommitteeAddress,
uint64 _chainID,
uint64 _forkID
) public
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_globalExitRootManager` | contract IPolygonZkEVMGlobalExitRoot | Global exit root manager address
|`_matic` | contract IERC20Upgradeable | MATIC token address
|`_rollupVerifier` | contract IVerifierRollup | Rollup verifier address
|`_bridgeAddress` | contract IPolygonZkEVMBridge | Bridge address
|`_dataCommitteeAddress` | contract ICDKDataCommittee | Data committee address
|`_chainID` | uint64 | L2 chainID
|`_forkID` | uint64 | Fork Id
### initialize
```solidity
function initialize(
struct CDKValidium.InitializePackedParameters initializePackedParameters,
bytes32 genesisRoot,
string _trustedSequencerURL,
string _networkName
) external
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`initializePackedParameters` | struct CDKValidium.InitializePackedParameters | Struct to save gas and avoid stack too deep errors
|`genesisRoot` | bytes32 | Rollup genesis root
|`_trustedSequencerURL` | string | Trusted sequencer URL
|`_networkName` | string | L2 network name
### sequenceBatches
```solidity
function sequenceBatches(
struct CDKValidium.BatchData[] batches,
address l2Coinbase,
bytes signaturesAndAddrs
) external
```
Allows a sequencer to send multiple batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`batches` | struct CDKValidium.BatchData[] | Struct array which holds the necessary data to append new batches to the sequence
|`l2Coinbase` | address | Address that will receive the fees from L2
|`signaturesAndAddrs` | bytes | Byte array containing the signatures and all the addresses of the committee in ascending order
[signature 0, ..., signature requiredAmountOfSignatures -1, address 0, ... address N]
note that each ECDSA signatures are used, therefore each one must be 65 bytes
### verifyBatches
```solidity
function verifyBatches(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) external
```
Allows an aggregator to verify multiple batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`pendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### verifyBatchesTrustedAggregator
```solidity
function verifyBatchesTrustedAggregator(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) external
```
Allows an aggregator to verify multiple batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`pendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### _verifyAndRewardBatches
```solidity
function _verifyAndRewardBatches(
uint64 pendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) internal
```
Verify and reward batches internal function
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`pendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### _tryConsolidatePendingState
```solidity
function _tryConsolidatePendingState(
) internal
```
Internal function to consolidate the state automatically once sequence or verify batches are called
It tries to consolidate the first and the middle pending state in the queue
### consolidatePendingState
```solidity
function consolidatePendingState(
uint64 pendingStateNum
) external
```
Allows to consolidate any pending state that has already exceed the pendingStateTimeout
Can be called by the trusted aggregator, which can consolidate any state without the timeout restrictions
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`pendingStateNum` | uint64 | Pending state to consolidate
### _consolidatePendingState
```solidity
function _consolidatePendingState(
uint64 pendingStateNum
) internal
```
Internal function to consolidate any pending state that has already exceed the pendingStateTimeout
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`pendingStateNum` | uint64 | Pending state to consolidate
### _updateBatchFee
```solidity
function _updateBatchFee(
uint64 newLastVerifiedBatch
) internal
```
Function to update the batch fee based on the new verified batches
The batch fee will not be updated when the trusted aggregator verifies batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newLastVerifiedBatch` | uint64 | New last verified batch
### forceBatch
```solidity
function forceBatch(
bytes transactions,
uint256 maticAmount
) public
```
Allows a sequencer/user to force a batch of L2 transactions.
This should be used only in extreme cases where the trusted sequencer does not work as expected
Note The sequencer has certain degree of control on how non-forced and forced batches are ordered
In order to assure that users force transactions will be processed properly, user must not sign any other transaction
with the same nonce
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`transactions` | bytes | L2 ethereum transactions EIP-155 or pre-EIP-155 with signature:
|`maticAmount` | uint256 | Max amount of MATIC tokens that the sender is willing to pay
### sequenceForceBatches
```solidity
function sequenceForceBatches(
struct CDKValidium.ForcedBatchData[] batches
) external
```
Allows anyone to sequence forced Batches if the trusted sequencer has not done so in the timeout period
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`batches` | struct CDKValidium.ForcedBatchData[] | Struct array which holds the necessary data to append force batches
### setTrustedSequencer
```solidity
function setTrustedSequencer(
address newTrustedSequencer
) external
```
Allow the admin to set a new trusted sequencer
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newTrustedSequencer` | address | Address of the new trusted sequencer
### setTrustedSequencerURL
```solidity
function setTrustedSequencerURL(
string newTrustedSequencerURL
) external
```
Allow the admin to set the trusted sequencer URL
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newTrustedSequencerURL` | string | URL of trusted sequencer
### setTrustedAggregator
```solidity
function setTrustedAggregator(
address newTrustedAggregator
) external
```
Allow the admin to set a new trusted aggregator address
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newTrustedAggregator` | address | Address of the new trusted aggregator
### setTrustedAggregatorTimeout
```solidity
function setTrustedAggregatorTimeout(
uint64 newTrustedAggregatorTimeout
) external
```
Allow the admin to set a new pending state timeout
The timeout can only be lowered, except if emergency state is active
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newTrustedAggregatorTimeout` | uint64 | Trusted aggregator timeout
### setPendingStateTimeout
```solidity
function setPendingStateTimeout(
uint64 newPendingStateTimeout
) external
```
Allow the admin to set a new trusted aggregator timeout
The timeout can only be lowered, except if emergency state is active
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newPendingStateTimeout` | uint64 | Trusted aggregator timeout
### setMultiplierBatchFee
```solidity
function setMultiplierBatchFee(
uint16 newMultiplierBatchFee
) external
```
Allow the admin to set a new multiplier batch fee
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newMultiplierBatchFee` | uint16 | multiplier batch fee
### setVerifyBatchTimeTarget
```solidity
function setVerifyBatchTimeTarget(
uint64 newVerifyBatchTimeTarget
) external
```
Allow the admin to set a new verify batch time target
This value will only be relevant once the aggregation is decentralized, so
the trustedAggregatorTimeout should be zero or very close to zero
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newVerifyBatchTimeTarget` | uint64 | Verify batch time target
### setForceBatchTimeout
```solidity
function setForceBatchTimeout(
uint64 newforceBatchTimeout
) external
```
Allow the admin to set the forcedBatchTimeout
The new value can only be lower, except if emergency state is active
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newforceBatchTimeout` | uint64 | New force batch timeout
### activateForceBatches
```solidity
function activateForceBatches(
) external
```
Allow the admin to turn on the force batches
This action is not reversible
### transferAdminRole
```solidity
function transferAdminRole(
address newPendingAdmin
) external
```
Starts the admin role transfer
This is a two step process, the pending admin must accepted to finalize the process
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newPendingAdmin` | address | Address of the new pending admin
### acceptAdminRole
```solidity
function acceptAdminRole(
) external
```
Allow the current pending admin to accept the admin role
### overridePendingState
```solidity
function overridePendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) external
```
Allows the trusted aggregator to override the pending state
if it's possible to prove a different state root given the same batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`initPendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`finalPendingStateNum` | uint64 | Final pending state, that will be used to compare with the newStateRoot
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### proveNonDeterministicPendingState
```solidity
function proveNonDeterministicPendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) external
```
Allows to halt the CDKValidium if its possible to prove a different state root given the same batches
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`initPendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`finalPendingStateNum` | uint64 | Final pending state, that will be used to compare with the newStateRoot
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### _proveDistinctPendingState
```solidity
function _proveDistinctPendingState(
uint64 initPendingStateNum,
uint64 finalPendingStateNum,
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 newStateRoot,
bytes32[24] proof
) internal
```
Internal function that proves a different state root given the same batches to verify
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`initPendingStateNum` | uint64 | Init pending state, 0 if consolidated state is used
|`finalPendingStateNum` | uint64 | Final pending state, that will be used to compare with the newStateRoot
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
|`proof` | bytes32[24] | fflonk proof
### activateEmergencyState
```solidity
function activateEmergencyState(
uint64 sequencedBatchNum
) external
```
Function to activate emergency state, which also enables the emergency mode on both CDKValidium and PolygonZkEVMBridge contracts
If not called by the owner must be provided a batcnNum that does not have been aggregated in a _HALT_AGGREGATION_TIMEOUT period
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`sequencedBatchNum` | uint64 | Sequenced batch number that has not been aggreagated in _HALT_AGGREGATION_TIMEOUT
### deactivateEmergencyState
```solidity
function deactivateEmergencyState(
) external
```
Function to deactivate emergency state on both CDKValidium and PolygonZkEVMBridge contracts
### _activateEmergencyState
```solidity
function _activateEmergencyState(
) internal
```
Internal function to activate emergency state on both CDKValidium and PolygonZkEVMBridge contracts
### getForcedBatchFee
```solidity
function getForcedBatchFee(
) public returns (uint256)
```
Get forced batch fee
### getLastVerifiedBatch
```solidity
function getLastVerifiedBatch(
) public returns (uint64)
```
Get the last verified batch
### isPendingStateConsolidable
```solidity
function isPendingStateConsolidable(
) public returns (bool)
```
Returns a boolean that indicates if the pendingStateNum is or not consolidable
Note that his function does not check if the pending state currently exists, or if it's consolidated already
### calculateRewardPerBatch
```solidity
function calculateRewardPerBatch(
) public returns (uint256)
```
Function to calculate the reward to verify a single batch
### getInputSnarkBytes
```solidity
function getInputSnarkBytes(
uint64 initNumBatch,
uint64 finalNewBatch,
bytes32 newLocalExitRoot,
bytes32 oldStateRoot,
bytes32 newStateRoot
) public returns (bytes)
```
Function to calculate the input snark bytes
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`initNumBatch` | uint64 | Batch which the aggregator starts the verification
|`finalNewBatch` | uint64 | Last batch aggregator intends to verify
|`newLocalExitRoot` | bytes32 | New local exit root once the batch is processed
|`oldStateRoot` | bytes32 | State root before batch is processed
|`newStateRoot` | bytes32 | New State root once the batch is processed
### checkStateRootInsidePrime
```solidity
function checkStateRootInsidePrime(
) public returns (bool)
```
## Events
### SequenceBatches
```solidity
event SequenceBatches(
)
```
Emitted when the trusted sequencer sends a new batch of transactions
### ForceBatch
```solidity
event ForceBatch(
)
```
Emitted when a batch is forced
### SequenceForceBatches
```solidity
event SequenceForceBatches(
)
```
Emitted when forced batches are sequenced by not the trusted sequencer
### VerifyBatches
```solidity
event VerifyBatches(
)
```
Emitted when a aggregator verifies batches
### VerifyBatchesTrustedAggregator
```solidity
event VerifyBatchesTrustedAggregator(
)
```
Emitted when the trusted aggregator verifies batches
### ConsolidatePendingState
```solidity
event ConsolidatePendingState(
)
```
Emitted when pending state is consolidated
### SetTrustedSequencer
```solidity
event SetTrustedSequencer(
)
```
Emitted when the admin updates the trusted sequencer address
### SetTrustedSequencerURL
```solidity
event SetTrustedSequencerURL(
)
```
Emitted when the admin updates the sequencer URL
### SetTrustedAggregatorTimeout
```solidity
event SetTrustedAggregatorTimeout(
)
```
Emitted when the admin updates the trusted aggregator timeout
### SetPendingStateTimeout
```solidity
event SetPendingStateTimeout(
)
```
Emitted when the admin updates the pending state timeout
### SetTrustedAggregator
```solidity
event SetTrustedAggregator(
)
```
Emitted when the admin updates the trusted aggregator address
### SetMultiplierBatchFee
```solidity
event SetMultiplierBatchFee(
)
```
Emitted when the admin updates the multiplier batch fee
### SetVerifyBatchTimeTarget
```solidity
event SetVerifyBatchTimeTarget(
)
```
Emitted when the admin updates the verify batch timeout
### SetForceBatchTimeout
```solidity
event SetForceBatchTimeout(
)
```
Emitted when the admin update the force batch timeout
### ActivateForceBatches
```solidity
event ActivateForceBatches(
)
```
Emitted when activate force batches
### TransferAdminRole
```solidity
event TransferAdminRole(
)
```
Emitted when the admin starts the two-step transfer role setting a new pending admin
### AcceptAdminRole
```solidity
event AcceptAdminRole(
)
```
Emitted when the pending admin accepts the admin role
### ProveNonDeterministicPendingState
```solidity
event ProveNonDeterministicPendingState(
)
```
Emitted when is proved a different state given the same batches
### OverridePendingState
```solidity
event OverridePendingState(
)
```
Emitted when the trusted aggregator overrides pending state
### UpdateZkEVMVersion
```solidity
event UpdateZkEVMVersion(
)
```
Emitted everytime the forkID is updated, this includes the first initialization of the contract
This event is intended to be emitted for every upgrade of the contract with relevant changes for the nodes
Contract module which acts as a timelocked controller.
This gives time for users of the controlled contract to exit before a potentially dangerous maintenance operation is applied.
If emergency mode of the cdkValidium contract system is active, this timelock have no delay.
## Functions
### constructor
```solidity
function constructor(
uint256 minDelay,
address[] proposers,
address[] executors,
address admin,
contract CDKValidium _cdkValidium
) public
```
Constructor of timelock
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`minDelay` | uint256 | initial minimum delay for operations
|`proposers` | address[] | accounts to be granted proposer and canceller roles
|`executors` | address[] | accounts to be granted executor role
|`admin` | address | optional account to be granted admin role; disable with zero address
|`_cdkValidium` | contract CDKValidium | cdkValidium address
### getMinDelay
```solidity
function getMinDelay(
) public returns (uint256 duration)
```
Returns the minimum delay for an operation to become valid.
This value can be changed by executing an operation that calls `updateDelay`.
If CDKValidium is on emergency state the minDelay will be 0 instead.
PolygonZkEVMBridge that will be deployed on both networks Ethereum and Polygon zkEVM
Contract responsible to manage the token interactions with other networks
## Functions
### initialize
```solidity
function initialize(
uint32 _networkID,
contract IBasePolygonZkEVMGlobalExitRoot _globalExitRootManager,
address _polygonZkEVMaddress
) external
```
The value of `_polygonZkEVMaddress` on the L2 deployment of the contract will be address(0), so
emergency state is not possible for the L2 deployment of the bridge, intentionally
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_networkID` | uint32 | networkID
|`_globalExitRootManager` | contract IBasePolygonZkEVMGlobalExitRoot | global exit root manager address
|`_polygonZkEVMaddress` | address | polygonZkEVM address
### bridgeAsset
```solidity
function bridgeAsset(
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
address token,
bool forceUpdateGlobalExitRoot,
bytes permitData
) public
```
Deposit add a new leaf to the merkle tree
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`destinationNetwork` | uint32 | Network destination
|`destinationAddress` | address | Address destination
|`amount` | uint256 | Amount of tokens
|`token` | address | Token address, 0 address is reserved for ether
|`forceUpdateGlobalExitRoot` | bool | Indicates if the new global exit root is updated or not
|`permitData` | bytes | Raw data of the call `permit` of the token
### bridgeMessage
```solidity
function bridgeMessage(
uint32 destinationNetwork,
address destinationAddress,
bool forceUpdateGlobalExitRoot,
bytes metadata
) external
```
Bridge message and send ETH value
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`destinationNetwork` | uint32 | Network destination
|`destinationAddress` | address | Address destination
|`forceUpdateGlobalExitRoot` | bool | Indicates if the new global exit root is updated or not
|`metadata` | bytes | Message metadata
### claimAsset
```solidity
function claimAsset(
bytes32[32] smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originTokenAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes metadata
) external
```
Verify merkle proof and withdraw tokens/ether
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`smtProof` | bytes32[32] | Smt proof
|`index` | uint32 | Index of the leaf
|`mainnetExitRoot` | bytes32 | Mainnet exit root
|`rollupExitRoot` | bytes32 | Rollup exit root
|`originNetwork` | uint32 | Origin network
|`originTokenAddress` | address | Origin token address, 0 address is reserved for ether
|`destinationNetwork` | uint32 | Network destination
|`destinationAddress` | address | Address destination
|`amount` | uint256 | Amount of tokens
|`metadata` | bytes | Abi encoded metadata if any, empty otherwise
### claimMessage
```solidity
function claimMessage(
bytes32[32] smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes metadata
) external
```
Verify merkle proof and execute message
If the receiving address is an EOA, the call will result as a success
Which means that the amount of ether will be transferred correctly, but the message
will not trigger any execution
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`smtProof` | bytes32[32] | Smt proof
|`index` | uint32 | Index of the leaf
|`mainnetExitRoot` | bytes32 | Mainnet exit root
|`rollupExitRoot` | bytes32 | Rollup exit root
|`originNetwork` | uint32 | Origin network
|`originAddress` | address | Origin address
|`destinationNetwork` | uint32 | Network destination
|`destinationAddress` | address | Address destination
|`amount` | uint256 | message value
|`metadata` | bytes | Abi encoded metadata if any, empty otherwise
### precalculatedWrapperAddress
```solidity
function precalculatedWrapperAddress(
uint32 originNetwork,
address originTokenAddress,
string name,
string symbol,
uint8 decimals
) external returns (address)
```
Returns the precalculated address of a wrapper using the token information
Note Updating the metadata of a token is not supported.
Since the metadata has relevance in the address deployed, this function will not return a valid
wrapped address if the metadata provided is not the original one.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`originNetwork` | uint32 | Origin network
|`originTokenAddress` | address | Origin token address, 0 address is reserved for ether
|`name` | string | Name of the token
|`symbol` | string | Symbol of the token
|`decimals` | uint8 | Decimals of the token
### getTokenWrappedAddress
```solidity
function getTokenWrappedAddress(
uint32 originNetwork,
address originTokenAddress
) external returns (address)
```
Returns the address of a wrapper using the token information if already exist
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`originNetwork` | uint32 | Origin network
|`originTokenAddress` | address | Origin token address, 0 address is reserved for ether
### activateEmergencyState
```solidity
function activateEmergencyState(
) external
```
Function to activate the emergency state
" Only can be called by the Polygon ZK-EVM in extreme situations
### deactivateEmergencyState
```solidity
function deactivateEmergencyState(
) external
```
Function to deactivate the emergency state
" Only can be called by the Polygon ZK-EVM
### _verifyLeaf
```solidity
function _verifyLeaf(
bytes32[32] smtProof,
uint32 index,
bytes32 mainnetExitRoot,
bytes32 rollupExitRoot,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes metadata,
uint8 leafType
) internal
```
Verify leaf and checks that it has not been claimed
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`smtProof` | bytes32[32] | Smt proof
|`index` | uint32 | Index of the leaf
|`mainnetExitRoot` | bytes32 | Mainnet exit root
|`rollupExitRoot` | bytes32 | Rollup exit root
|`originNetwork` | uint32 | Origin network
|`originAddress` | address | Origin address
|`destinationNetwork` | uint32 | Network destination
|`destinationAddress` | address | Address destination
|`amount` | uint256 | Amount of tokens
|`metadata` | bytes | Abi encoded metadata if any, empty otherwise
|`leafType` | uint8 | Leaf type --> [0] transfer Ether / ERC20 tokens, [1] message
### isClaimed
```solidity
function isClaimed(
uint256 index
) external returns (bool)
```
Function to check if an index is claimed or not
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`index` | uint256 | Index
### updateGlobalExitRoot
```solidity
function updateGlobalExitRoot(
) external
```
Function to update the globalExitRoot if the last deposit is not submitted
### _updateGlobalExitRoot
```solidity
function _updateGlobalExitRoot(
) internal
```
Function to update the globalExitRoot
### _permit
```solidity
function _permit(
address amount,
uint256 permitData
) internal
```
Function to call token permit method of extended ERC20
+ @param token ERC20 token address
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`amount` | address | Quantity that is expected to be allowed
|`permitData` | uint256 | Raw data of the call `permit` of the token
### _safeSymbol
```solidity
function _safeSymbol(
address token
) internal returns (string)
```
Provides a safe ERC20.symbol version which returns 'NO_SYMBOL' as fallback string
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`token` | address | The address of the ERC-20 token contract
### _safeName
```solidity
function _safeName(
address token
) internal returns (string)
```
Provides a safe ERC20.name version which returns 'NO_NAME' as fallback string.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`token` | address | The address of the ERC-20 token contract.
### _safeDecimals
```solidity
function _safeDecimals(
address token
) internal returns (uint8)
```
Provides a safe ERC20.decimals version which returns '18' as fallback value.
Note Tokens with (decimals > 255) are not supported
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`token` | address | The address of the ERC-20 token contract
### _returnDataToString
```solidity
function _returnDataToString(
bytes data
) internal returns (string)
```
Function to convert returned data to string
returns 'NOT_VALID_ENCODING' as fallback value.
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`data` | bytes | returned data
## Events
### BridgeEvent
```solidity
event BridgeEvent(
)
```
Emitted when bridge assets or messages to another network
### ClaimEvent
```solidity
event ClaimEvent(
)
```
Emitted when a claim is done from another network
### NewWrappedToken
```solidity
event NewWrappedToken(
)
```
Emitted when a new wrapped token is created
Contract responsible for managing the exit roots across multiple networks
## Functions
### constructor
```solidity
function constructor(
address _rollupAddress,
address _bridgeAddress
) public
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_rollupAddress` | address | Rollup contract address
|`_bridgeAddress` | address | PolygonZkEVMBridge contract address
### updateExitRoot
```solidity
function updateExitRoot(
bytes32 newRoot
) external
```
Update the exit root of one of the networks and the global exit root
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newRoot` | bytes32 | new exit tree root
### getLastGlobalExitRoot
```solidity
function getLastGlobalExitRoot(
) public returns (bytes32)
```
Return last global exit root
## Events
### UpdateGlobalExitRoot
```solidity
event UpdateGlobalExitRoot(
)
```
Emitted when the global exit root is updated
Contract responsible for managing the exit roots for the L2 and global exit roots
The special zkRom variables will be accessed and updated directly by the zkRom
## Functions
### constructor
```solidity
function constructor(
address _bridgeAddress
) public
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_bridgeAddress` | address | PolygonZkEVMBridge contract address
### updateExitRoot
```solidity
function updateExitRoot(
bytes32 newRoot
) external
```
Update the exit root of one of the networks and the global exit root
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`newRoot` | bytes32 | new exit tree root
Contract responsible for deploying deterministic address contracts related with the CDKValidium
## Functions
### constructor
```solidity
function constructor(
address _owner
) public
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`_owner` | address | Owner
### deployDeterministic
```solidity
function deployDeterministic(
uint256 amount,
bytes32 salt,
bytes initBytecode
) public
```
Allows to deploy a contract using create2
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`amount` | uint256 | Amount used in create2
|`salt` | bytes32 | Salt used in create2
|`initBytecode` | bytes | Init bytecode that will be use in create2
### deployDeterministicAndCall
```solidity
function deployDeterministicAndCall(
uint256 amount,
bytes32 salt,
bytes initBytecode,
bytes dataCall
) public
```
Allows to deploy a contract using create2 and call it afterwards
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`amount` | uint256 | Amount used in create2
|`salt` | bytes32 | Salt used in create2
|`initBytecode` | bytes | Init bytecode that will be use in create2
|`dataCall` | bytes | Data used in the call after deploying the smart contract
### functionCall
```solidity
function functionCall(
address targetAddress,
bytes dataCall,
uint256 amount
) public
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`targetAddress` | address | Amount of contract deploy
|`dataCall` | bytes | Data used to call the target smart contract
|`amount` | uint256 | Data used to call the target smart contract
### predictDeterministicAddress
```solidity
function predictDeterministicAddress(
bytes32 salt,
bytes32 bytecodeHash
) public returns (address)
```
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`salt` | bytes32 | Salt used in create2
|`bytecodeHash` | bytes32 | Init bytecode hashed, it contains the constructor parameters
## Events
### NewDeterministicDeployment
```solidity
event NewDeterministicDeployment(
)
```
Emitted when a contract is deployed
### FunctionCall
```solidity
event FunctionCall(
)
```
Emitted when a contract is called
This contract will be used as a helper for all the sparse merkle tree related functions
Based on the implementation of the deposit eth2.0 contract https://github.com/ethereum/consensus-specs/blob/dev/solidity_deposit_contract/deposit_contract.sol
## Functions
### getDepositRoot
```solidity
function getDepositRoot(
) public returns (bytes32)
```
Computes and returns the merkle root
### _deposit
```solidity
function _deposit(
bytes32 leafHash
) internal
```
Add a new leaf to the merkle tree
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`leafHash` | bytes32 | Leaf hash
### verifyMerkleProof
```solidity
function verifyMerkleProof(
bytes32 leafHash,
bytes32[32] smtProof,
uint32 index,
bytes32 root
) public returns (bool)
```
Verify merkle proof
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`leafHash` | bytes32 | Leaf hash
|`smtProof` | bytes32[32] | Smt proof
|`index` | uint32 | Index of the leaf
|`root` | bytes32 | Merkle root
### getLeafValue
```solidity
function getLeafValue(
uint8 leafType,
uint32 originNetwork,
address originAddress,
uint32 destinationNetwork,
address destinationAddress,
uint256 amount,
bytes32 metadataHash
) public returns (bytes32)
```
Given the leaf data returns the leaf value
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
|`leafType` | uint8 | Leaf type --> [0] transfer Ether / ERC20 tokens, [1] message
|`originNetwork` | uint32 | Origin Network
|`originAddress` | address | [0] Origin token address, 0 address is reserved for ether, [1] msg.sender of the message
|`destinationNetwork` | uint32 | Destination network
|`destinationAddress` | address | Destination address
|`amount` | uint256 | [0] Amount of tokens/ether, [1] Amount of ether
|`metadataHash` | bytes32 | Hash of the metadata
Contract helper responsible to manage the emergency state
## Functions
### _activateEmergencyState
```solidity
function _activateEmergencyState(
) internal
```
Activate emergency state
### _deactivateEmergencyState
```solidity
function _deactivateEmergencyState(
) internal
```
Deactivate emergency state
## Events
### EmergencyStateActivated
```solidity
event EmergencyStateActivated(
)
```
Emitted when emergency state is activated
### EmergencyStateDeactivated
```solidity
event EmergencyStateDeactivated(
)
```
Emitted when emergency state is deactivated
A library that provides the necessary calculations to calculate the global exit root
## Functions
### calculateGlobalExitRoot
```solidity
function calculateGlobalExitRoot(
) internal returns (bytes32)
```
## Functions
### constructor
```solidity
function constructor(
) public
```
### mint
```solidity
function mint(
) external
```
### burn
```solidity
function burn(
) external
```
### decimals
```solidity
function decimals(
) public returns (uint8)
```
### permit
```solidity
function permit(
) external
```
### DOMAIN_SEPARATOR
```solidity
function DOMAIN_SEPARATOR(
) public returns (bytes32)
```
Return the DOMAIN_SEPARATOR.
{{{natspec.userdoc}}}
{{{natspec.devdoc}}}
{{#if ownFunctions}}
## Functions
{{/if}}
{{#ownFunctions}}
### {{name}}
```solidity
function {{name}}(
{{#natspec.params}}
{{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
{{/natspec.params}}
) {{visibility}}{{#if outputs}} returns ({{outputs}}){{/if}}
```
{{#if natspec.userdoc}}{{natspec.userdoc}}{{/if}}
{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
{{#if natspec.params}}
#### Parameters:
| Name | Type | Description |
| :--- | :--- | :------------------------------------------------------------------- |
{{#natspec.params}}
|`{{param}}` | {{#lookup ../args.types @index}}{{/lookup}} | {{ description }}{{/natspec.params}}{{/if}}
{{#if natspec.returns}}
#### Return Values:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------------------------------------- |
{{#natspec.returns}}
|`{{param}}`| {{#lookup ../args.types @index}}{{/lookup}} | {{{description}}}{{/natspec.returns}}{{/if}}
{{/ownFunctions}}
{{#if ownEvents}}
## Events
{{/if}}
{{#ownEvents}}
### {{name}}
```solidity
event {{name}}(
{{#natspec.params}}
{{#lookup ../args.types @index}}{{/lookup}} {{param}}{{#if @last}}{{else}},{{/if}}
{{/natspec.params}}
)
```
{{#if natspec.userdoc}}{{natspec.userdoc}}{{/if}}
{{#if natspec.devdoc}}{{natspec.devdoc}}{{/if}}
{{#if natspec.params}}
#### Parameters:
| Name | Type | Description |
| :----------------------------- | :------------ | :--------------------------------------------- |
{{#natspec.params}}
|`{{param}}`| {{#lookup ../args.types @index}}{{/lookup}} | {{{description}}}{{/natspec.params}}{{/if}}
{{/ownEvents}}
\ No newline at end of file
require('dotenv').config();
require('@nomiclabs/hardhat-waffle');
require('hardhat-gas-reporter');
require('solidity-coverage');
require('@nomiclabs/hardhat-etherscan');
require('@openzeppelin/hardhat-upgrades');
require('hardhat-dependency-compiler');
const DEFAULT_MNEMONIC = 'test test test test test test test test test test test junk';
/*
* You need to export an object to set up your config
* Go to https://hardhat.org/config/ to learn more
*/
/**
* @type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
dependencyCompiler: {
paths: [
'@openzeppelin/contracts/token/ERC20/presets/ERC20PresetFixedSupply.sol',
'@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol',
'@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol'
]//,
//keep: true
},
solidity: {
compilers: [
{
version: "0.8.20",
settings: {
evmVersion: 'paris',
optimizer: {
enabled: true,
runs: 99
}
}
},
{
version: "0.6.11",
settings: {
optimizer: {
enabled: true,
runs: 999999
}
}
},
{
version: "0.5.12",
settings: {
optimizer: {
enabled: true,
runs: 999999
}
}
},
{
version: "0.5.16",
settings: {
optimizer: {
enabled: true,
runs: 999999
}
}
}
]
},
networks: {
mainnet: {
url: `https://mainnet.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
ropsten: {
url: `https://ropsten.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
goerli: {
url: `https://goerli.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
sepolia: {
url: `https://sepolia.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
rinkeby: {
url: `https://rinkeby.infura.io/v3/${process.env.INFURA_PROJECT_ID}`,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
localhost: {
url: 'http://127.0.0.1:8545',
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
hardhat: {
initialDate: '0',
allowUnlimitedContractSize: true,
accounts: {
mnemonic: process.env.MNEMONIC || DEFAULT_MNEMONIC,
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
},
},
},
gasReporter: {
enabled: !!process.env.REPORT_GAS,
outputFile: process.env.REPORT_GAS_FILE ? "./gas_report.md" : null,
noColors: process.env.REPORT_GAS_FILE ? true : false
},
etherscan: {
apiKey: {
goerli: `${process.env.ETHERSCAN_API_KEY}`,
sepolia: `${process.env.ETHERSCAN_API_KEY}`,
mainnet: `${process.env.ETHERSCAN_API_KEY}`
},
},
};
module.exports.PolygonZkEVMBridge = require('./compiled-contracts/PolygonZkEVMBridge.json');
module.exports.PolygonZkEVMGlobalExitRoot = require('./compiled-contracts/PolygonZkEVMGlobalExitRoot.json');
module.exports.PolygonZkEVMGlobalExitRootL2 = require('./compiled-contracts/PolygonZkEVMGlobalExitRootL2.json');
module.exports.CDKValidium = require('./compiled-contracts/CDKValidium.json');
module.exports.TokenWrapped = require('./compiled-contracts/TokenWrapped.json');
module.exports.FflonkVerifier = require('./compiled-contracts/FflonkVerifier.json');
module.exports.PolygonZkEVMBridgeMock = require('./compiled-contracts/PolygonZkEVMBridgeMock.json');
module.exports.ERC20PermitMock = require('./compiled-contracts/ERC20PermitMock.json');
module.exports.PolygonZkEVMGlobalExitRootL2Mock = require('./compiled-contracts/PolygonZkEVMGlobalExitRootL2Mock.json');
module.exports.PolygonZkEVMGlobalExitRootMock = require('./compiled-contracts/PolygonZkEVMGlobalExitRootMock.json');
module.exports.CDKValidiumMock = require('./compiled-contracts/CDKValidiumMock.json');
module.exports.VerifierRollupHelperMock = require('./compiled-contracts/VerifierRollupHelperMock.json');
module.exports.PermitHelper = require('./src/permit-helper');
module.exports.ProxyAdmin = require('./compiled-contracts/ProxyAdmin.json');
module.exports.TransparentUpgradeableProxy = require('./compiled-contracts/TransparentUpgradeableProxy.json');
module.exports.CDKValidiumDeployer = require('./compiled-contracts/CDKValidiumDeployer.json');
module.exports.CDKValidiumTimelock = require('./compiled-contracts/CDKValidiumTimelock.json');
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "cdk-validium-contracts",
"description": "Core contracts for the Polygon CDK Validium",
"version": "0.0.1",
"repository": {
"type": "git",
"url": "git+https://github.com/0xPolygon/cdk-validium-contracts.git"
},
"main": "index.js",
"keywords": [
"zkevm",
"snark",
"polygon",
"stark",
"validium",
"EVM",
"ethereum",
"blockchain"
],
"author": "0xPolygon",
"files": [
"contracts",
"index.js",
"compiled-contracts",
"src"
],
"bugs": {
"url": "https://github.com/0xPolygon/cdk-validium-contracts/issues"
},
"homepage": "https://github.com/0xPolygon/CDKValidium-contracts#readme",
"license": "pending",
"dependencies": {
"chai": "^4.3.7",
"ethers": "^5.7.2"
},
"devDependencies": {
"@0xpolygonhermez/zkevm-commonjs": "github:0xPolygonHermez/zkevm-commonjs#develop",
"@nomiclabs/hardhat-ethers": "^2.2.2",
"@nomiclabs/hardhat-etherscan": "^3.1.7",
"@nomiclabs/hardhat-waffle": "^2.0.5",
"@openzeppelin/contracts": "4.8.2",
"@openzeppelin/contracts-upgradeable": "4.8.2",
"@openzeppelin/hardhat-upgrades": "1.22.1",
"@openzeppelin/test-helpers": "0.5.16",
"circomlibjs": "0.1.1",
"dotenv": "^8.6.0",
"eslint": "^8.36.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-mocha": "^9.0.0",
"ethereum-waffle": "^3.4.4",
"ffjavascript": "^0.2.57",
"hardhat": "^2.16.1",
"hardhat-dependency-compiler": "^1.1.3",
"hardhat-gas-reporter": "^1.0.9",
"prettier": "^2.8.4",
"prettier-plugin-solidity": "^1.1.3",
"solc-0.8": "npm:solc@0.8.20",
"solidity-coverage": "^0.7.22",
"solidity-docgen": "^0.5.17"
},
"scripts": {
"saveDeployment:sepolia": "mkdir -p deployments/sepolia_$(date +%s) && cp -r deployment/deploy_*.json deployments/sepolia_$(date +%s) && cp .openzeppelin/sepolia.json deployments/sepolia_$(date +%s) && cp deployment/genesis.json deployments/sepolia_$(date +%s)",
"saveDeployment:mainnet": "mkdir -p deployments/mainnet_$(date +%s) && cp -r deployment/deploy_*.json deployments/mainnet_$(date +%s) && cp .openzeppelin/mainnet.json deployments/mainnet_$(date +%s) && cp deployment/genesis.json deployments/mainnet_$(date +%s)",
"test": "npx hardhat test test/contracts/**.test.js",
"docgen": "npx solidity-docgen --solc-module solc-0.8 -t ./docs/templates -e ./contracts/verifiers,./contracts/mocks",
"prepare:testnet:CDKValidium:localhost": "npx hardhat run deployment/testnet/prepareTestnet.js --network localhost",
"deploy:CDKValidium:localhost": "rm -f .openzeppelin/unknown-31337.json && node deployment/1_createGenesis.js && npx hardhat run deployment/2_deployCDKValidiumDeployer.js --network localhost && npx hardhat run deployment/3_deployContracts.js --network localhost",
"deploy:testnet:CDKValidium:localhost": "npm run prepare:testnet:CDKValidium:localhost && npm run deploy:CDKValidium:localhost",
"prepare:testnet:CDKValidium:sepolia": "npx hardhat run deployment/testnet/prepareTestnet.js --network sepolia",
"deploy:CDKValidium:sepolia": "node deployment/1_createGenesis.js && npx hardhat run deployment/3_deployContracts.js --network sepolia && npm run saveDeployment:sepolia",
"deploy:deployer:CDKValidium:sepolia": "npx hardhat run deployment/2_deployCDKValidiumDeployer.js --network sepolia",
"verify:deployer:CDKValidium:sepolia": "npx hardhat run deployment/verifyCDKValidiumDeployer.js --network sepolia",
"deploy:testnet:CDKValidium:sepolia": "npm run prepare:testnet:CDKValidium:sepolia && npm run deploy:CDKValidium:sepolia",
"upgrade:timelock:sepolia": "npx hardhat run upgrade/timeLockUpgrade.js --network sepolia",
"verify:CDKValidium:sepolia": "npx hardhat run deployment/verifyContracts.js --network sepolia",
"deploy:deployer:CDKValidium:mainnet": "npx hardhat run deployment/2_deployCDKValidiumDeployer.js --network mainnet",
"verify:deployer:CDKValidium:mainnet": "npx hardhat run deployment/verifyCDKValidiumDeployer.js --network mainnet",
"deploy:CDKValidium:mainnet": "node deployment/1_createGenesis.js && npx hardhat run deployment/3_deployContracts.js --network mainnet && npm run saveDeployment:mainnet",
"upgrade:timelock:mainnet": "npx hardhat run upgrade/timeLockUpgrade.js --network mainnet",
"verify:CDKValidium:mainnet": "npx hardhat run deployment/verifyContracts.js --network mainnet",
"lint": "npx eslint ./test && npx eslint ./docker/scripts && npx eslint ./deployment && npx eslint ./src",
"lint:fix": "npx eslint ./test --fix && npx eslint ./docker/scripts --fix && npx eslint ./deployment --fix && npx eslint ./src --fix",
"compile": "npx hardhat compile",
"docker:contracts": "./docker/scripts/deploy-docker.sh",
"push:docker:contracts": "docker push hermeznetwork/geth-cdk-validium-contracts",
"update:genesis": "node deployment/1_createGenesis.js && node deployment/1_createGenesis.js --test --input ../docker/scripts/deploy_parameters_docker.json --out ../docker/scripts/genesis_docker.json",
"coverage": "npx hardhat coverage",
"gas:report": "REPORT_GAS=true npx hardhat test",
"gas:report:file": "rm -f .openzeppelin/unknown-31337.json && REPORT_GAS=true REPORT_GAS_FILE=true npx hardhat test"
}
}
\ No newline at end of file
/* eslint-disable global-require */
/* eslint-disable import/no-dynamic-require */
/* eslint-disable no-unused-expressions */
/* eslint-disable no-console */
/* eslint-disable multiline-comment-style */
/* eslint-disable no-restricted-syntax */
/* eslint-disable no-await-in-loop */
/* eslint-disable guard-for-in */
/* eslint-disable import/no-extraneous-dependencies */
const { Scalar } = require('ffjavascript');
const fs = require('fs');
const ethers = require('ethers');
const {
Address,
} = require('ethereumjs-util');
const { defaultAbiCoder } = require('@ethersproject/abi');
const path = require('path');
const { argv } = require('yargs');
const {
MemDB, ZkEVMDB, getPoseidon, processorUtils, smtUtils, Constants,
} = require('@0xpolygonhermez/zkevm-commonjs');
const contractsPolygonHermez = require('../index');
// Example of use: node create-genesis.js --gen genesis-gen.json --out genesis.json
async function main() {
// load generator
const inputPath = (typeof argv.gen === 'undefined') ? undefined : argv.gen;
if (inputPath === undefined) { throw Error('Input genesis must be provided'); }
// load output file
const outPath = (typeof argv.out === 'undefined') ? undefined : argv.out;
if (outPath === undefined) { throw Error('Output file must be specified'); }
const genesisGenerator = require(path.join(__dirname, inputPath));
const genesisOutput = {};
const poseidon = await getPoseidon();
const { F } = poseidon;
const genesisRoot = [F.zero, F.zero, F.zero, F.zero];
const accHashInput = [F.zero, F.zero, F.zero, F.zero];
const globalExitRoot = ethers.constants.HashZero;
const {
genesis,
txs,
sequencerAddress,
timestamp,
defaultChainId,
} = genesisGenerator;
const db = new MemDB(F);
// create a zkEVMDB and build a batch
const zkEVMDB = await ZkEVMDB.newZkEVM(
db,
poseidon,
genesisRoot,
accHashInput,
genesis,
null,
null,
defaultChainId,
);
/*
* build, sign transaction and generate rawTxs
* rawTxs would be the calldata inserted in the contract
*/
const addressToContractName = {};
const rawTxs = [];
for (let j = 0; j < txs.length; j++) {
const currentTx = txs[j];
// if (currentTx.contractName.CDKValidiumDeployer) {
// }
const tx = {
to: currentTx.to || '0x',
nonce: currentTx.nonce,
value: processorUtils.toHexStringRlp(ethers.utils.parseUnits(currentTx.value, 'wei')),
gasLimit: currentTx.gasLimit,
gasPrice: processorUtils.toHexStringRlp(ethers.utils.parseUnits(currentTx.gasPrice, 'wei')),
chainId: currentTx.chainId,
data: currentTx.data || '0x',
};
// Contract deployment from tx
let bytecode; let
abi;
if (contractsPolygonHermez[currentTx.contractName]) {
({ bytecode, abi } = contractsPolygonHermez[currentTx.contractName]);
}
if (currentTx.function) {
const interfaceContract = new ethers.utils.Interface(abi);
tx.data = interfaceContract.encodeFunctionData(currentTx.function, currentTx.paramsFunction);
} else {
if (currentTx.paramsDeploy) {
const params = defaultAbiCoder.encode(currentTx.paramsDeploy.types, currentTx.paramsDeploy.values);
tx.data = bytecode + params.slice(2);
} else {
tx.data = bytecode;
}
const addressContract = await ethers.utils.getContractAddress(
{ from: currentTx.from, nonce: currentTx.nonce },
);
addressToContractName[addressContract.toLowerCase()] = currentTx.contractName;
}
let customRawTx;
const address = genesis.find((o) => o.address === currentTx.from);
const wallet = new ethers.Wallet(address.pvtKey);
if (tx.chainId === 0 || tx.chainId === undefined) {
const signData = ethers.utils.RLP.encode([
processorUtils.toHexStringRlp(Scalar.e(tx.nonce)),
processorUtils.toHexStringRlp(tx.gasPrice),
processorUtils.toHexStringRlp(tx.gasLimit),
processorUtils.toHexStringRlp(tx.to),
processorUtils.toHexStringRlp(tx.value),
processorUtils.toHexStringRlp(tx.data),
]);
const digest = ethers.utils.keccak256(signData);
const signingKey = new ethers.utils.SigningKey(address.pvtKey);
const signature = signingKey.signDigest(digest);
const r = signature.r.slice(2).padStart(64, '0'); // 32 bytes
const s = signature.s.slice(2).padStart(64, '0'); // 32 bytes
const v = (signature.v).toString(16).padStart(2, '0'); // 1 bytes
customRawTx = signData.concat(r).concat(s).concat(v);
} else {
const rawTxEthers = await wallet.signTransaction(tx);
customRawTx = processorUtils.rawTxToCustomRawTx(rawTxEthers);
}
rawTxs.push(customRawTx);
}
const options = { skipUpdateSystemStorage: true };
const batch = await zkEVMDB.buildBatch(
timestamp,
sequencerAddress,
smtUtils.stringToH4(globalExitRoot),
undefined,
options,
);
for (let j = 0; j < rawTxs.length; j++) {
batch.addRawTx(rawTxs[j]);
}
// execute the transactions added to the batch
await batch.executeTxs();
// consolidate state
await zkEVMDB.consolidate(batch);
// clean address 0 batch, clean globalExitRoot
const updatedAccounts = batch.getUpdatedAccountsBatch();
const currentVM = batch.vm;
const accountsOutput = [];
for (const item in updatedAccounts) {
const address = item;
const account = updatedAccounts[address];
const currentAccountOutput = {};
currentAccountOutput.balance = account.balance.toString();
currentAccountOutput.nonce = account.nonce.toString();
currentAccountOutput.address = address;
// If account is a contract, update storage and bytecode
if (account.isContract()) {
const addressInstance = Address.fromString(address);
const smCode = await currentVM.stateManager.getContractCode(addressInstance);
const sto = await currentVM.stateManager.dumpStorage(addressInstance);
const storage = {};
const keys = Object.keys(sto).map((v) => `0x${v}`);
const values = Object.values(sto).map((v) => `0x${v}`);
for (let k = 0; k < keys.length; k++) {
storage[keys[k]] = ethers.utils.RLP.decode(values[k]);
}
currentAccountOutput.bytecode = `0x${smCode.toString('hex')}`;
currentAccountOutput.storage = storage;
currentAccountOutput.contractName = addressToContractName[address];
} else if (address !== Constants.ADDRESS_SYSTEM
&& address.toLowerCase() !== Constants.ADDRESS_GLOBAL_EXIT_ROOT_MANAGER_L2.toLowerCase()) {
currentAccountOutput.pvtKey = (genesis.find((o) => o.address.toLowerCase() === address.toLowerCase())).pvtKey;
}
accountsOutput.push(currentAccountOutput);
}
// add accounts that has not been used
for (let i = 0; i < genesis.length; i++) {
const item = genesis[i];
if (typeof updatedAccounts[item.address.toLowerCase()] === 'undefined') {
accountsOutput.push(item);
}
}
genesisOutput.root = smtUtils.h4toString(batch.currentStateRoot);
genesisOutput.genesis = accountsOutput;
const decodedTxs = await batch.getDecodedTxs();
genesisOutput.transactions = rawTxs.map((rawTx, index) => {
if (decodedTxs[index].receipt) {
const receipt = {
status: decodedTxs[index].receipt.status,
gasUsed: `0x${decodedTxs[index].receipt.gasUsed.toString('hex')}`,
logs: decodedTxs[index].receipt.logs ? decodedTxs[index].receipt.logs.map((log) => log.map((infoLogs) => {
if (Array.isArray(infoLogs)) {
return infoLogs.map((buffer) => `0x${buffer.toString('hex')}`);
}
return `0x${infoLogs.toString('hex')}`;
})) : [],
};
return {
rawTx,
receipt,
createAddress: decodedTxs[index].createdAddress ? `${decodedTxs[index].createdAddress.toString('hex')}` : null,
};
}
return {
rawTx,
};
});
const genesisOutputPath = path.join(__dirname, outPath);
await fs.writeFileSync(genesisOutputPath, JSON.stringify(genesisOutput, null, 2));
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
const { ethers } = require('ethers');
const { expect } = require('chai');
/**
* Create a permit signature with the EIP-2612 standard
* @param {Object} tokenContractInstance - EthersJS contract instance of the token
* @param {Object} wallet - EthersJs wallet instance that will sign the permit
* @param {String} spenderAddress - Spender address, usually the contract that the permit will interact with
* @param {String} value - Value of the permit
* @param {String} nonce - Nonce of the permit
* @param {String} deadline - Deadline of the permit
* @returns {Object} - Signature obejct, { v, r, s}
*/
async function createPermitSignature(tokenContractInstance, wallet, spenderAddress, value, nonce, deadline, chainId) {
const name = await tokenContractInstance.name();
// The domain
const domain = {
name,
version: '1',
chainId,
verifyingContract: tokenContractInstance.address,
};
// The named list of all type definitions
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
// The data to sign
const values = {
owner: wallet.address,
spender: spenderAddress,
value,
nonce,
deadline,
};
const rawSignature = await wallet._signTypedData(domain, types, values);
const signature = ethers.utils.splitSignature(rawSignature);
const recoveredAddressTyped = ethers.utils.verifyTypedData(domain, types, values, rawSignature);
expect(recoveredAddressTyped).to.be.equal(wallet.address);
return signature;
}
/**
* Create a permit signature with the DAi approach
* @param {Object} tokenContractInstance - EthersJS contract instance of the token
* @param {Object} wallet - EthersJs wallet instance that will sign the permit
* @param {String} spenderAddress - Spender address, usually the contract that the permit will interact with
* @param {String} value - Value of the permit
* @param {String} nonce - Nonce of the permit
* @param {String} expiry - expiry of the permit
* @param {Number} chainId - expiry of the permit
* @returns {Object} - Signature obejct, { v, r, s}
*/
async function createPermitSignatureDaiType(tokenContractInstance, wallet, spenderAddress, nonce, expiry, chainId) {
const name = await tokenContractInstance.name();
const version = await tokenContractInstance.version();
// The domain
const domain = {
name,
version,
chainId,
verifyingContract: tokenContractInstance.address,
};
// The named list of all type definitions
const types = {
Permit: [
{ name: 'holder', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'nonce', type: 'uint256' },
{ name: 'expiry', type: 'uint256' },
{ name: 'allowed', type: 'bool' },
],
};
// The data to sign
const values = {
holder: wallet.address,
spender: spenderAddress,
nonce,
expiry,
allowed: true,
};
const rawSignature = await wallet._signTypedData(domain, types, values);
const signature = ethers.utils.splitSignature(rawSignature);
const recoveredAddressTyped = ethers.utils.verifyTypedData(domain, types, values, rawSignature);
expect(recoveredAddressTyped).to.be.equal(wallet.address);
return signature;
}
/**
* Create a permit signature with the UNI approach
* @param {Object} tokenContractInstance - EthersJS contract instance of the token
* @param {Object} wallet - EthersJs wallet instance that will sign the permit
* @param {String} spenderAddress - Spender address, usually the contract that the permit will interact with
* @param {String} value - Value of the permit
* @param {String} nonce - Nonce of the permit
* @param {String} deadline - Deadline of the permit
* @param {Number} chainId - expiry of the permit
* @returns {Object} - Signature obejct, { v, r, s}
*/
async function createPermitSignatureUniType(tokenContractInstance, wallet, spenderAddress, value, nonce, deadline, chainId) {
const name = await tokenContractInstance.name();
// The domain
const domain = {
name,
chainId,
verifyingContract: tokenContractInstance.address,
};
// The named list of all type definitions
const types = {
Permit: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
{ name: 'value', type: 'uint256' },
{ name: 'nonce', type: 'uint256' },
{ name: 'deadline', type: 'uint256' },
],
};
// The data to sign
const values = {
owner: wallet.address,
spender: spenderAddress,
value,
nonce,
deadline,
};
const rawSignature = await wallet._signTypedData(domain, types, values);
const signature = ethers.utils.splitSignature(rawSignature);
const recoveredAddressTyped = ethers.utils.verifyTypedData(domain, types, values, rawSignature);
expect(recoveredAddressTyped).to.be.equal(wallet.address);
return signature;
}
/**
* Permit interface
*/
const ifacePermit = new ethers.utils.Interface(['function permit(address,address,uint256,uint256,uint8,bytes32,bytes32)']);
/**
* Permit interface DAI
*/
const ifacePermitDAI = new ethers.utils.Interface(['function permit(address,address,uint256,uint256,bool,uint8,bytes32,bytes32)']);
module.exports = {
createPermitSignature,
createPermitSignatureDaiType,
ifacePermit,
ifacePermitDAI,
createPermitSignatureUniType,
};
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const MerkleTreeBridge = require('@0xpolygonhermez/zkevm-commonjs').MTBridge;
const {
verifyMerkleProof,
getLeafValue,
} = require('@0xpolygonhermez/zkevm-commonjs').mtBridgeUtils;
function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) {
return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]);
}
describe('PolygonZkEVMBridge Contract', () => {
let deployer;
let rollup;
let acc1;
let polygonZkEVMGlobalExitRoot;
let polygonZkEVMBridgeContract;
let tokenContract;
const tokenName = 'Matic Token';
const tokenSymbol = 'MATIC';
const decimals = 18;
const tokenInitialBalance = ethers.utils.parseEther('20000000');
const metadataToken = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[tokenName, tokenSymbol, decimals],
);
const networkIDMainnet = 0;
const networkIDRollup = 1;
const LEAF_TYPE_ASSET = 0;
const LEAF_TYPE_MESSAGE = 1;
const polygonZkEVMAddress = ethers.constants.AddressZero;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, rollup, acc1] = await ethers.getSigners();
// deploy PolygonZkEVMBridge
const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false });
// deploy global exit root manager
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address);
await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress);
// deploy token
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
tokenContract = await maticTokenFactory.deploy(
tokenName,
tokenSymbol,
deployer.address,
tokenInitialBalance,
);
await tokenContract.deployed();
});
it('should check the constructor parameters', async () => {
expect(await polygonZkEVMBridgeContract.globalExitRootManager()).to.be.equal(polygonZkEVMGlobalExitRoot.address);
expect(await polygonZkEVMBridgeContract.networkID()).to.be.equal(networkIDMainnet);
expect(await polygonZkEVMBridgeContract.polygonZkEVMaddress()).to.be.equal(polygonZkEVMAddress);
});
it('should PolygonZkEVM bridge asset and verify merkle proof', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await tokenContract.balanceOf(deployer.address);
const balanceBridge = await tokenContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// create a new deposit
await expect(tokenContract.approve(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Approval')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
// check requires
await expect(polygonZkEVMBridgeContract.bridgeAsset(
2,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
)).to.be.revertedWith('DestinationNetworkInvalid');
await expect(polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: 1 },
)).to.be.revertedWith('MsgValueNotZero');
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(LEAF_TYPE_ASSET, originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
expect(await tokenContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await tokenContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(1);
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('should PolygonZkEVMBridge message and verify merkle proof', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const originAddress = deployer.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_MESSAGE,
originNetwork,
originAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeMessage(destinationNetwork, destinationAddress, true, metadata, { value: amount }))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(
LEAF_TYPE_MESSAGE,
originNetwork,
originAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
depositCount,
);
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('should PolygonZkEVM bridge asset and message to check global exit root updates', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await tokenContract.balanceOf(deployer.address);
const balanceBridge = await tokenContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// create a new deposit
await expect(tokenContract.approve(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Approval')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, false, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(LEAF_TYPE_ASSET, originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount);
expect(await tokenContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await tokenContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(0);
expect(await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.be.equal(ethers.constants.HashZero);
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// Update global exit root
await expect(polygonZkEVMBridgeContract.updateGlobalExitRoot())
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
// no state changes since there are not any deposit pending to be updated
await polygonZkEVMBridgeContract.updateGlobalExitRoot();
expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(1);
expect(await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.be.equal(rootJSMainnet);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// bridge message
await expect(polygonZkEVMBridgeContract.bridgeMessage(destinationNetwork, destinationAddress, false, metadata, { value: amount }))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(
LEAF_TYPE_MESSAGE,
originNetwork,
deployer.address,
destinationNetwork,
destinationAddress,
amount,
metadata,
1,
);
expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(1);
expect(await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.be.equal(rootJSMainnet);
// Update global exit root
await expect(polygonZkEVMBridgeContract.updateGlobalExitRoot())
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot');
expect(await polygonZkEVMBridgeContract.lastUpdatedDepositCount()).to.be.equal(2);
expect(await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.not.be.equal(rootJSMainnet);
// Just to have the metric of a low cost bridge Asset
const tokenAddress2 = ethers.constants.AddressZero; // Ether
const amount2 = ethers.utils.parseEther('10');
await polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount2, tokenAddress2, false, '0x', { value: amount2 });
});
it('should claim tokens from Mainnet to Mainnet', async () => {
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDMainnet;
const destinationAddress = acc1.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot();
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
// check merkle root with SC
const rootJSRollup = merkleTree.getRoot();
// check only rollup account with update rollup exit root
await expect(polygonZkEVMGlobalExitRoot.updateExitRoot(rootJSRollup))
.to.be.revertedWith('OnlyAllowedContracts');
// add rollup Merkle root
await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup))
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(mainnetExitRoot, rootJSRollup);
// check roots
const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
expect(rollupExitRootSC).to.be.equal(rootJSRollup);
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootJSRollup,
)).to.be.equal(true);
/*
* claim
* Can't claim without tokens
*/
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('ERC20: transfer amount exceeds balance');
// transfer tokens, then claim
await expect(tokenContract.transfer(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Transfer')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
expect(false).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index));
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
).to.emit(tokenContract, 'Transfer')
.withArgs(polygonZkEVMBridgeContract.address, acc1.address, amount);
// Can't claim because nullifier
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
expect(true).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index));
});
it('should claim tokens from Rollup to Mainnet', async () => {
const originNetwork = networkIDRollup;
const tokenAddress = ethers.utils.getAddress(ethers.utils.hexlify(ethers.utils.randomBytes(20)));
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDMainnet;
const destinationAddress = deployer.address;
const metadata = metadataToken; // since we are inserting in the exit root can be anything
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot();
// compute root merkle tree in Js
const height = 32;
const merkleTreeRollup = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// Add 2 leafs
merkleTreeRollup.add(leafValue);
merkleTreeRollup.add(leafValue);
// check merkle root with SC
const rootJSRollup = merkleTreeRollup.getRoot();
// check only rollup account with update rollup exit root
await expect(polygonZkEVMGlobalExitRoot.updateExitRoot(rootJSRollup))
.to.be.revertedWith('OnlyAllowedContracts');
// add rollup Merkle root
await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup))
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(mainnetExitRoot, rootJSRollup);
// check roots
const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
expect(rollupExitRootSC).to.be.equal(rootJSRollup);
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// check merkle proof
const proof = merkleTreeRollup.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootJSRollup,
)).to.be.equal(true);
// claim
// precalculate wrapped erc20 address
const tokenWrappedFactory = await ethers.getContractFactory('TokenWrapped');
// create2 parameters
const salt = ethers.utils.solidityKeccak256(['uint32', 'address'], [networkIDRollup, tokenAddress]);
const minimalBytecodeProxy = tokenWrappedFactory.bytecode;
const hashInitCode = ethers.utils.solidityKeccak256(['bytes', 'bytes'], [minimalBytecodeProxy, metadataToken]);
const precalculateWrappedErc20 = await ethers.utils.getCreate2Address(polygonZkEVMBridgeContract.address, salt, hashInitCode);
const newWrappedToken = tokenWrappedFactory.attach(precalculateWrappedErc20);
// Use precalculatedWrapperAddress and check if matches
expect(await polygonZkEVMBridgeContract.precalculatedWrapperAddress(
networkIDRollup,
tokenAddress,
tokenName,
tokenSymbol,
decimals,
)).to.be.equal(precalculateWrappedErc20);
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
).to.emit(polygonZkEVMBridgeContract, 'NewWrappedToken')
.withArgs(originNetwork, tokenAddress, precalculateWrappedErc20, metadata)
.to.emit(newWrappedToken, 'Transfer')
.withArgs(ethers.constants.AddressZero, deployer.address, amount);
// Assert maps created
const newTokenInfo = await polygonZkEVMBridgeContract.wrappedTokenToTokenInfo(precalculateWrappedErc20);
expect(newTokenInfo.originNetwork).to.be.equal(networkIDRollup);
expect(newTokenInfo.originTokenAddress).to.be.equal(tokenAddress);
expect(await polygonZkEVMBridgeContract.getTokenWrappedAddress(
networkIDRollup,
tokenAddress,
)).to.be.equal(precalculateWrappedErc20);
expect(await polygonZkEVMBridgeContract.getTokenWrappedAddress(
networkIDRollup,
tokenAddress,
)).to.be.equal(precalculateWrappedErc20);
expect(await polygonZkEVMBridgeContract.tokenInfoToWrappedToken(salt)).to.be.equal(precalculateWrappedErc20);
// Check the wrapper info
expect(await newWrappedToken.name()).to.be.equal(tokenName);
expect(await newWrappedToken.symbol()).to.be.equal(tokenSymbol);
expect(await newWrappedToken.decimals()).to.be.equal(decimals);
// Can't claim because nullifier
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
// Check new token
expect(await newWrappedToken.totalSupply()).to.be.equal(amount);
// Claim again the other leaf to mint tokens
const index2 = 1;
const proof2 = merkleTreeRollup.getProofTreeByIndex(index2);
await expect(polygonZkEVMBridgeContract.claimAsset(
proof2,
index2,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
).to.emit(newWrappedToken, 'Transfer')
.withArgs(ethers.constants.AddressZero, deployer.address, amount);
// Burn Tokens
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const wrappedTokenAddress = newWrappedToken.address;
const newDestinationNetwork = networkIDRollup;
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// create a new deposit
await expect(newWrappedToken.approve(polygonZkEVMBridgeContract.address, amount))
.to.emit(newWrappedToken, 'Approval')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
/*
* pre compute root merkle tree in Js
* const height = 32;
*/
const merkleTreeMainnet = new MerkleTreeBridge(height);
// Imporant calcualte leaf with origin token address no wrapped token address
const originTokenAddress = tokenAddress;
const metadataMainnet = '0x'; // since the token does not belong to this network
const metadataHashMainnet = ethers.utils.solidityKeccak256(['bytes'], [metadataMainnet]);
const leafValueMainnet = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
originTokenAddress,
newDestinationNetwork,
destinationAddress,
amount,
metadataHashMainnet,
);
const leafValueMainnetSC = await polygonZkEVMBridgeContract.getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
originTokenAddress,
newDestinationNetwork,
destinationAddress,
amount,
metadataHashMainnet,
);
expect(leafValueMainnet).to.be.equal(leafValueMainnetSC);
merkleTreeMainnet.add(leafValueMainnet);
const rootJSMainnet = merkleTreeMainnet.getRoot();
// Tokens are burnt
expect(await newWrappedToken.totalSupply()).to.be.equal(ethers.BigNumber.from(amount).mul(2));
expect(await newWrappedToken.balanceOf(deployer.address)).to.be.equal(ethers.BigNumber.from(amount).mul(2));
await expect(polygonZkEVMBridgeContract.bridgeAsset(newDestinationNetwork, destinationAddress, amount, wrappedTokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(
LEAF_TYPE_ASSET,
originNetwork,
originTokenAddress,
newDestinationNetwork,
destinationAddress,
amount,
metadataMainnet,
depositCount,
)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot)
.to.emit(newWrappedToken, 'Transfer')
.withArgs(deployer.address, ethers.constants.AddressZero, amount);
expect(await newWrappedToken.totalSupply()).to.be.equal(amount);
expect(await newWrappedToken.balanceOf(deployer.address)).to.be.equal(amount);
expect(await newWrappedToken.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(0);
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proofMainnet = merkleTreeMainnet.getProofTreeByIndex(0);
const indexMainnet = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValueMainnet, proofMainnet, indexMainnet, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValueMainnet,
proofMainnet,
indexMainnet,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot2 = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot2).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('should PolygonZkEVMBridge and sync the current root with events', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = ethers.constants.AddressZero; // Ether
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = '0x';// since is ether does not have metadata
// create 3 new deposit
await expect(polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
))
.to.emit(
polygonZkEVMBridgeContract,
'BridgeEvent',
)
.withArgs(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
depositCount,
);
await expect(polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
))
.to.emit(
polygonZkEVMBridgeContract,
'BridgeEvent',
)
.withArgs(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
depositCount.add(1),
);
await expect(polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
))
.to.emit(
polygonZkEVMBridgeContract,
'BridgeEvent',
)
.withArgs(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
depositCount.add(2),
);
// Prepare merkle tree
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
// Get the deposit's events
const filter = polygonZkEVMBridgeContract.filters.BridgeEvent(
null,
null,
null,
null,
null,
);
const events = await polygonZkEVMBridgeContract.queryFilter(filter, 0, 'latest');
events.forEach((e) => {
const { args } = e;
const leafValue = getLeafValue(
args.leafType,
args.originNetwork,
args.originAddress,
args.destinationNetwork,
args.destinationAddress,
args.amount,
ethers.utils.solidityKeccak256(['bytes'], [args.metadata]),
);
merkleTree.add(leafValue);
});
// Check merkle root with SC
const rootSC = await polygonZkEVMBridgeContract.getDepositRoot();
const rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
});
it('should claim testing all the asserts', async () => {
// Add a claim leaf to rollup exit tree
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDMainnet;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot();
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
// check merkle root with SC
const rootJSRollup = merkleTree.getRoot();
// add rollup Merkle root
await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup))
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(mainnetExitRoot, rootJSRollup);
// check roots
const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
expect(rollupExitRootSC).to.be.equal(rootJSRollup);
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootJSRollup,
)).to.be.equal(true);
// Can't claim without tokens
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('ERC20: transfer amount exceeds balance');
// transfer tokens, then claim
await expect(tokenContract.transfer(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Transfer')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
// Check Destination network does not match assert
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
networkIDRollup, // Wrong destination network
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('DestinationNetworkInvalid');
// Check GlobalExitRoot invalid assert
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
mainnetExitRoot, // Wrong rollup Root
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('GlobalExitRootInvalid');
// Check Invalid smt proof assert
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index + 1, // Wrong index
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('InvalidSmtProof');
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
).to.emit(tokenContract, 'Transfer')
.withArgs(polygonZkEVMBridgeContract.address, deployer.address, amount);
// Check Already claimed_claim
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
});
it('should claim ether', async () => {
// Add a claim leaf to rollup exit tree
const originNetwork = networkIDMainnet;
const tokenAddress = ethers.constants.AddressZero; // ether
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDMainnet;
const destinationAddress = deployer.address;
const metadata = '0x'; // since is ether does not have metadata
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot();
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
// check merkle root with SC
const rootJSRollup = merkleTree.getRoot();
// add rollup Merkle root
await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup))
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(mainnetExitRoot, rootJSRollup);
// check roots
const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
expect(rollupExitRootSC).to.be.equal(rootJSRollup);
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootJSRollup,
)).to.be.equal(true);
/*
* claim
* Can't claim without ether
*/
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('EtherTransferFailed');
const balanceDeployer = await ethers.provider.getBalance(deployer.address);
/*
* Create a deposit to add ether to the PolygonZkEVMBridge
* Check deposit amount ether asserts
*/
await expect(polygonZkEVMBridgeContract.bridgeAsset(
networkIDRollup,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: ethers.utils.parseEther('100') },
)).to.be.revertedWith('AmountDoesNotMatchMsgValue');
// Check mainnet destination assert
await expect(polygonZkEVMBridgeContract.bridgeAsset(
networkIDMainnet,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
)).to.be.revertedWith('DestinationNetworkInvalid');
// This is used just to pay ether to the PolygonZkEVMBridge smart contract and be able to claim it afterwards.
expect(await polygonZkEVMBridgeContract.bridgeAsset(
networkIDRollup,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
));
// Check balances before claim
expect(await ethers.provider.getBalance(polygonZkEVMBridgeContract.address)).to.be.equal(amount);
expect(await ethers.provider.getBalance(deployer.address)).to.be.lte(balanceDeployer.sub(amount));
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
);
// Check balances after claim
expect(await ethers.provider.getBalance(polygonZkEVMBridgeContract.address)).to.be.equal(ethers.utils.parseEther('0'));
expect(await ethers.provider.getBalance(deployer.address)).to.be.lte(balanceDeployer);
// Can't claim because nullifier
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
});
it('should claim message', async () => {
// Add a claim leaf to rollup exit tree
const originNetwork = networkIDMainnet;
const tokenAddress = ethers.constants.AddressZero; // ether
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDMainnet;
const destinationAddress = deployer.address;
const metadata = '0x176923791298713271763697869132'; // since is ether does not have metadata
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const mainnetExitRoot = await polygonZkEVMGlobalExitRoot.lastMainnetExitRoot();
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_MESSAGE,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
// check merkle root with SC
const rootJSRollup = merkleTree.getRoot();
// add rollup Merkle root
await expect(polygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(rootJSRollup))
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(mainnetExitRoot, rootJSRollup);
// check roots
const rollupExitRootSC = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
expect(rollupExitRootSC).to.be.equal(rootJSRollup);
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRootSC);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootJSRollup)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootJSRollup,
)).to.be.equal(true);
/*
* claim
* Can't claim a message as an assets
*/
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('InvalidSmtProof');
/*
* claim
* Can't claim without ether
*/
await expect(polygonZkEVMBridgeContract.claimMessage(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('MessageFailed');
const balanceDeployer = await ethers.provider.getBalance(deployer.address);
/*
* Create a deposit to add ether to the PolygonZkEVMBridge
* Check deposit amount ether asserts
*/
await expect(polygonZkEVMBridgeContract.bridgeAsset(
networkIDRollup,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: ethers.utils.parseEther('100') },
)).to.be.revertedWith('AmountDoesNotMatchMsgValue');
// Check mainnet destination assert
await expect(polygonZkEVMBridgeContract.bridgeAsset(
networkIDMainnet,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
)).to.be.revertedWith('DestinationNetworkInvalid');
// This is used just to pay ether to the PolygonZkEVMBridge smart contract and be able to claim it afterwards.
expect(await polygonZkEVMBridgeContract.bridgeAsset(
networkIDRollup,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: amount },
));
// Check balances before claim
expect(await ethers.provider.getBalance(polygonZkEVMBridgeContract.address)).to.be.equal(amount);
expect(await ethers.provider.getBalance(deployer.address)).to.be.lte(balanceDeployer.sub(amount));
// Check mainnet destination assert
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('InvalidSmtProof');
await expect(polygonZkEVMBridgeContract.claimMessage(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
);
// Check balances after claim
expect(await ethers.provider.getBalance(polygonZkEVMBridgeContract.address)).to.be.equal(ethers.utils.parseEther('0'));
expect(await ethers.provider.getBalance(deployer.address)).to.be.lte(balanceDeployer);
// Can't claim because nullifier
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRootSC,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
});
});
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const MerkleTreeBridge = require('@0xpolygonhermez/zkevm-commonjs').MTBridge;
const {
verifyMerkleProof,
getLeafValue,
} = require('@0xpolygonhermez/zkevm-commonjs').mtBridgeUtils;
function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) {
return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]);
}
describe('PolygonZkEVMBridge Mock Contract', () => {
let deployer;
let rollup;
let acc1;
let polygonZkEVMGlobalExitRoot;
let polygonZkEVMBridgeContract;
let tokenContract;
const tokenName = 'Matic Token';
const tokenSymbol = 'MATIC';
const decimals = 18;
const tokenInitialBalance = ethers.utils.parseEther('20000000');
const metadataToken = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[tokenName, tokenSymbol, decimals],
);
const networkIDMainnet = 0;
const networkIDRollup = 1;
const LEAF_TYPE_ASSET = 0;
const polygonZkEVMAddress = ethers.constants.AddressZero;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, rollup, acc1] = await ethers.getSigners();
// deploy global exit root manager
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootMock');
// deploy PolygonZkEVMBridge
const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridgeMock');
polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false });
polygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address);
await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress);
// deploy token
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
tokenContract = await maticTokenFactory.deploy(
tokenName,
tokenSymbol,
deployer.address,
tokenInitialBalance,
);
await tokenContract.deployed();
});
it('should check the constructor parameters', async () => {
expect(await polygonZkEVMBridgeContract.globalExitRootManager()).to.be.equal(polygonZkEVMGlobalExitRoot.address);
expect(await polygonZkEVMBridgeContract.networkID()).to.be.equal(networkIDMainnet);
});
it('should PolygonZkEVMBridge and verify merkle proof', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await tokenContract.balanceOf(deployer.address);
const balanceBridge = await tokenContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// create a new deposit
await expect(tokenContract.approve(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Approval')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
expect(await tokenContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await tokenContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('shouldnt be able to PolygonZkEVMBridge more thna 0.25e ehters', async () => {
// Add a claim leaf to rollup exit tree
const tokenAddress = ethers.constants.AddressZero; // ether
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
await expect(polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
{ value: ethers.utils.parseEther('10') },
)).to.be.revertedWith('PolygonZkEVMBridge::bridgeAsset: Cannot bridge more than maxEtherBridge');
await polygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
ethers.utils.parseEther('0.25'),
tokenAddress,
true,
'0x',
{ value: ethers.utils.parseEther('0.25') },
);
});
it('should claim tokens from Rollup to Rollup', async () => {
const originNetwork = networkIDRollup;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = acc1.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
// Set network to Rollup
await polygonZkEVMBridgeContract.setNetworkID(1);
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
// check merkle root with SC
const mainnetExitRoot = merkleTree.getRoot();
const rollupExitRoot = ethers.constants.HashZero;
const computedGlobalExitRoot = calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot);
// set globalExitRoot
await polygonZkEVMGlobalExitRoot.setGlobalExitRoot(computedGlobalExitRoot, 1);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, mainnetExitRoot)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
mainnetExitRoot,
)).to.be.equal(true);
// transfer tokens, then claim
await expect(tokenContract.transfer(polygonZkEVMBridgeContract.address, amount))
.to.emit(tokenContract, 'Transfer')
.withArgs(deployer.address, polygonZkEVMBridgeContract.address, amount);
expect(false).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index));
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRoot,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
))
.to.emit(polygonZkEVMBridgeContract, 'ClaimEvent')
.withArgs(
index,
originNetwork,
tokenAddress,
destinationAddress,
amount,
).to.emit(tokenContract, 'Transfer')
.withArgs(polygonZkEVMBridgeContract.address, acc1.address, amount);
// Can't claim because nullifier
await expect(polygonZkEVMBridgeContract.claimAsset(
proof,
index,
mainnetExitRoot,
rollupExitRoot,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadata,
)).to.be.revertedWith('AlreadyClaimed');
expect(true).to.be.equal(await polygonZkEVMBridgeContract.isClaimed(index));
});
});
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const MerkleTreeBridge = require('@0xpolygonhermez/zkevm-commonjs').MTBridge;
const {
getLeafValue,
} = require('@0xpolygonhermez/zkevm-commonjs').mtBridgeUtils;
describe('PolygonZkEVMBridge Contract werid metadata', () => {
let deployer;
let rollup;
let polygonZkEVMGlobalExitRoot;
let polygonZkEVMBridgeContract;
let tokenContract;
const tokenName = 'Matic Token';
const tokenSymbol = 'MATIC';
const decimals = 18;
const tokenInitialBalance = ethers.utils.parseEther('20000000');
const networkIDMainnet = 0;
const networkIDRollup = 1;
const LEAF_TYPE_ASSET = 0;
const polygonZkEVMAddress = ethers.constants.AddressZero;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, rollup] = await ethers.getSigners();
// deploy PolygonZkEVMBridge
const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false });
// deploy global exit root manager
const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address);
await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress);
// deploy token
const maticTokenFactory = await ethers.getContractFactory('TokenWrapped');
tokenContract = await maticTokenFactory.deploy(
tokenName,
tokenSymbol,
decimals,
);
await tokenContract.deployed();
await tokenContract.mint(deployer.address, tokenInitialBalance);
});
it('should PolygonZkEVMBridge with weird token metadata', async () => {
const weirdErc20Metadata = await ethers.getContractFactory('ERC20WeirdMetadata');
const nameWeird = 'nameToken';
const symbolWeird = 'NTK';
const nameWeirdBytes32 = ethers.utils.formatBytes32String(nameWeird);
const symbolWeirdBytes = ethers.utils.toUtf8Bytes(symbolWeird);
const decimalsWeird = 14;
const weirdTokenContract = await weirdErc20Metadata.deploy(
nameWeirdBytes32, // bytes32
symbolWeirdBytes, // bytes
decimalsWeird,
);
await weirdTokenContract.deployed();
// mint and approve tokens
await weirdTokenContract.mint(deployer.address, tokenInitialBalance);
await weirdTokenContract.approve(polygonZkEVMBridgeContract.address, tokenInitialBalance);
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = weirdTokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[nameWeird, symbolWeird, decimalsWeird],
);
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(LEAF_TYPE_ASSET, originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount);
expect(await polygonZkEVMBridgeContract.getDepositRoot()).to.be.equal(rootJSMainnet);
});
it('should PolygonZkEVMBridge with weird token metadata with reverts', async () => {
const weirdErc20Metadata = await ethers.getContractFactory('ERC20WeirdMetadata');
const nameWeird = 'nameToken';
const symbolWeird = 'NTK';
const nameWeirdBytes32 = ethers.utils.formatBytes32String(nameWeird);
const symbolWeirdBytes = ethers.utils.toUtf8Bytes(symbolWeird);
const decimalsWeird = ethers.constants.MaxUint256;
const weirdTokenContract = await weirdErc20Metadata.deploy(
nameWeirdBytes32, // bytes32
symbolWeirdBytes, // bytes
decimalsWeird,
);
await weirdTokenContract.deployed();
// mint and approve tokens
await weirdTokenContract.mint(deployer.address, tokenInitialBalance);
await weirdTokenContract.approve(polygonZkEVMBridgeContract.address, tokenInitialBalance);
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = weirdTokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
// Since cannot decode decimals
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x')).to.be.reverted;
// toogle revert
await weirdTokenContract.toggleIsRevert();
// Use revert strings
const nameRevert = 'NO_NAME';
const symbolRevert = 'NO_SYMBOL';
const decimalsTooRevert = 18;
const metadata = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[nameRevert, symbolRevert, decimalsTooRevert],
);
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(LEAF_TYPE_ASSET, originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount);
expect(await polygonZkEVMBridgeContract.getDepositRoot()).to.be.equal(rootJSMainnet);
});
it('should PolygonZkEVMBridge with weird token metadata with empty data', async () => {
const weirdErc20Metadata = await ethers.getContractFactory('ERC20WeirdMetadata');
const nameWeird = '';
const symbolWeird = '';
const nameWeirdBytes32 = ethers.utils.formatBytes32String(nameWeird);
const symbolWeirdBytes = ethers.utils.toUtf8Bytes(symbolWeird);
const decimalsWeird = 255;
const weirdTokenContract = await weirdErc20Metadata.deploy(
nameWeirdBytes32, // bytes32
symbolWeirdBytes, // bytes
decimalsWeird,
);
await weirdTokenContract.deployed();
// mint and approve tokens
await weirdTokenContract.mint(deployer.address, tokenInitialBalance);
await weirdTokenContract.approve(polygonZkEVMBridgeContract.address, tokenInitialBalance);
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = weirdTokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
// Empty bytes32 is a not valid encoding
const nameEmpty = 'NOT_VALID_ENCODING'; // bytes32 empty
const symbolEmpty = '';
const metadata = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[nameEmpty, symbolEmpty, decimalsWeird],
);
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(LEAF_TYPE_ASSET, originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount);
expect(await polygonZkEVMBridgeContract.getDepositRoot()).to.be.equal(rootJSMainnet);
});
});
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const MerkleTreeBridge = require('@0xpolygonhermez/zkevm-commonjs').MTBridge;
const {
verifyMerkleProof,
getLeafValue,
} = require('@0xpolygonhermez/zkevm-commonjs').mtBridgeUtils;
const {
createPermitSignature,
ifacePermit,
createPermitSignatureDaiType,
ifacePermitDAI,
createPermitSignatureUniType,
} = require('../../src/permit-helper');
function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) {
return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]);
}
describe('PolygonZkEVMBridge Contract Permit tests', () => {
let deployer;
let rollup;
let polygonZkEVMGlobalExitRoot;
let polygonZkEVMBridgeContract;
let tokenContract;
const tokenName = 'Matic Token';
const tokenSymbol = 'MATIC';
const decimals = 18;
const tokenInitialBalance = ethers.utils.parseEther('20000000');
const metadataToken = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[tokenName, tokenSymbol, decimals],
);
const networkIDMainnet = 0;
const networkIDRollup = 1;
const LEAF_TYPE_ASSET = 0;
const polygonZkEVMAddress = ethers.constants.AddressZero;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, rollup] = await ethers.getSigners();
// deploy PolygonZkEVMBridge
const polygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
polygonZkEVMBridgeContract = await upgrades.deployProxy(polygonZkEVMBridgeFactory, [], { initializer: false });
// deploy global exit root manager
const polygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
polygonZkEVMGlobalExitRoot = await polygonZkEVMGlobalExitRootFactory.deploy(rollup.address, polygonZkEVMBridgeContract.address);
await polygonZkEVMBridgeContract.initialize(networkIDMainnet, polygonZkEVMGlobalExitRoot.address, polygonZkEVMAddress);
// deploy token
const maticTokenFactory = await ethers.getContractFactory('TokenWrapped');
tokenContract = await maticTokenFactory.deploy(
tokenName,
tokenSymbol,
decimals,
);
await tokenContract.deployed();
await tokenContract.mint(deployer.address, tokenInitialBalance);
});
it('should PolygonZkEVMBridge and with permit eip-2612 compilant', async () => {
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = tokenContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = metadataToken;
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await tokenContract.balanceOf(deployer.address);
const balanceBridge = await tokenContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.be.revertedWith('ERC20: insufficient allowance');
// user permit
const nonce = await tokenContract.nonces(deployer.address);
const deadline = ethers.constants.MaxUint256;
const { chainId } = await ethers.provider.getNetwork();
const { v, r, s } = await createPermitSignature(
tokenContract,
deployer,
polygonZkEVMBridgeContract.address,
amount,
nonce,
deadline,
chainId,
);
const dataPermit = ifacePermit.encodeFunctionData('permit', [
deployer.address,
polygonZkEVMBridgeContract.address,
amount,
deadline,
v,
r,
s,
]);
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, dataPermit))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
expect(await tokenContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await tokenContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('should PolygonZkEVMBridge with permit DAI type contracts', async () => {
const { chainId } = await ethers.provider.getNetwork();
const daiTokenFactory = await ethers.getContractFactory('Dai');
const daiContract = await daiTokenFactory.deploy(
chainId,
);
await daiContract.deployed();
await daiContract.mint(deployer.address, ethers.utils.parseEther('100'));
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = daiContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[await daiContract.name(), await daiContract.symbol(), await daiContract.decimals()],
);
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await daiContract.balanceOf(deployer.address);
const balanceBridge = await daiContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.be.revertedWith('Dai/insufficient-allowance');
// user permit
const nonce = await daiContract.nonces(deployer.address);
const deadline = ethers.constants.MaxUint256;
const { v, r, s } = await createPermitSignatureDaiType(
daiContract,
deployer,
polygonZkEVMBridgeContract.address,
nonce,
deadline,
chainId,
);
const dataPermit = ifacePermitDAI.encodeFunctionData('permit', [
deployer.address,
polygonZkEVMBridgeContract.address,
nonce,
deadline,
true,
v,
r,
s,
]);
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, dataPermit))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
expect(await daiContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await daiContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
it('should PolygonZkEVMBridge with permit UNI type contracts', async () => {
const uniTokenFactory = await ethers.getContractFactory('Uni');
const uniContract = await uniTokenFactory.deploy(
deployer.address,
deployer.address,
(await ethers.provider.getBlock()).timestamp + 1,
);
await uniContract.deployed();
await uniContract.mint(deployer.address, ethers.utils.parseEther('100'));
const depositCount = await polygonZkEVMBridgeContract.depositCount();
const originNetwork = networkIDMainnet;
const tokenAddress = uniContract.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = networkIDRollup;
const destinationAddress = deployer.address;
const metadata = ethers.utils.defaultAbiCoder.encode(
['string', 'string', 'uint8'],
[await uniContract.name(), await uniContract.symbol(), await uniContract.decimals()],
);
const metadataHash = ethers.utils.solidityKeccak256(['bytes'], [metadata]);
const balanceDeployer = await uniContract.balanceOf(deployer.address);
const balanceBridge = await uniContract.balanceOf(polygonZkEVMBridgeContract.address);
const rollupExitRoot = await polygonZkEVMGlobalExitRoot.lastRollupExitRoot();
// pre compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
const rootJSMainnet = merkleTree.getRoot();
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, '0x'))
.to.be.revertedWith('Uni::transferFrom: transfer amount exceeds spender allowance');
// user permit
const nonce = await uniContract.nonces(deployer.address);
const deadline = ethers.constants.MaxUint256;
const { chainId } = await ethers.provider.getNetwork();
const { v, r, s } = await createPermitSignatureUniType(
uniContract,
deployer,
polygonZkEVMBridgeContract.address,
amount,
nonce,
deadline,
chainId,
);
const dataPermit = ifacePermit.encodeFunctionData('permit', [
deployer.address,
polygonZkEVMBridgeContract.address,
amount,
deadline,
v,
r,
s,
]);
await expect(polygonZkEVMBridgeContract.bridgeAsset(destinationNetwork, destinationAddress, amount, tokenAddress, true, dataPermit))
.to.emit(polygonZkEVMBridgeContract, 'BridgeEvent')
.withArgs(originNetwork, tokenAddress, destinationNetwork, destinationAddress, amount, metadata, depositCount)
.to.emit(polygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(rootJSMainnet, rollupExitRoot);
expect(await uniContract.balanceOf(deployer.address)).to.be.equal(balanceDeployer.sub(amount));
expect(await uniContract.balanceOf(polygonZkEVMBridgeContract.address)).to.be.equal(balanceBridge.add(amount));
// check merkle root with SC
const rootSCMainnet = await polygonZkEVMBridgeContract.getDepositRoot();
expect(rootSCMainnet).to.be.equal(rootJSMainnet);
// check merkle proof
const proof = merkleTree.getProofTreeByIndex(0);
const index = 0;
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSCMainnet)).to.be.equal(true);
expect(await polygonZkEVMBridgeContract.verifyMerkleProof(
leafValue,
proof,
index,
rootSCMainnet,
)).to.be.equal(true);
const computedGlobalExitRoot = calculateGlobalExitRoot(rootJSMainnet, rollupExitRoot);
expect(computedGlobalExitRoot).to.be.equal(await polygonZkEVMGlobalExitRoot.getLastGlobalExitRoot());
});
});
/* eslint-disable no-plusplus, no-await-in-loop */
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
const { calculateAccInputHash, calculateBatchHashData } = contractUtils;
describe('Polygon Data Committee', () => {
let deployer;
let trustedAggregator;
let trustedSequencer;
let admin;
let verifierContract;
let PolygonZkEVMBridgeContract;
let cdkValidiumContract;
let cdkDataCommitteeContract;
let maticTokenContract;
let PolygonZkEVMGlobalExitRoot;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const networkIDMainnet = 0;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const chainID = 1000;
const networkName = 'cdk-validium';
const version = '0.0.1';
const forkID = 0;
const pendingStateTimeoutDefault = 100;
const trustedAggregatorTimeoutDefault = 10;
// Committe parameters
const requiredAmountOfSignatures = 3;
const nMembers = 4;
let addrs = [];
let committeeMembers;
function membersToURLsAndAddrsBytes(members) {
const urls = [];
let addrsBytes = '0x';
for (let i = 0; i < members.length; i++) {
urls.push(members[i].url);
addrsBytes += members[i].addr.slice(2);
}
return { urls, addrsBytes };
}
function addreessToDerivationPath(address) {
const wallets = addrs.slice(0, nMembers);
for (let i = 0; i < nMembers; i++) {
if (wallets[i].address === address) {
return i;
}
}
throw Error('address not found');
}
function getSignatures(hashToSign) {
let signatures = '0x';
for (let i = 0; i < requiredAmountOfSignatures; i++) {
const derivationPath = addreessToDerivationPath(committeeMembers[i].addr);
const wallet = ethers.Wallet.fromMnemonic(
// eslint-disable-next-line no-undef
config.networks.hardhat.accounts.mnemonic,
// eslint-disable-next-line no-undef
`${config.networks.hardhat.accounts.path}/${derivationPath}`,
);
const signatureRsv = wallet._signingKey().signDigest(hashToSign);
const signature = ethers.utils.joinSignature(signatureRsv);
signatures += signature.slice(2);
}
return signatures;
}
function genSignaturesAndAddrs(hashToSign) {
const signatures = getSignatures(hashToSign);
let encodedAssresses = '';
for (let i = 0; i < nMembers; i++) {
encodedAssresses += committeeMembers[i].addr.slice(2);
}
return signatures + encodedAssresses;
}
async function calculateLastAccInputHash(sequences) {
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
let currentAccInputHash = (await cdkValidiumContract.sequencedBatches(lastBatchSequenced)).accInputHash;
for (let i = 0; i < sequences.length; i++) {
currentAccInputHash = calculateAccInputHash(
currentAccInputHash,
sequences[i].transactionsHash,
sequences[i].globalExitRoot,
sequences[i].timestamp,
deployer.address,
);
}
return currentAccInputHash;
}
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, trustedAggregator, trustedSequencer, admin] = await ethers.getSigners();
committeeMembers = [];
addrs = await ethers.getSigners();
const committeeAddrs = addrs.slice(0, nMembers)
.sort((a, b) => a.address - b.address);
for (let i = 0; i < nMembers; i++) {
committeeMembers.push({
url: `foo-${i}`,
addr: committeeAddrs[i].address,
});
}
// deploy mock verifier
const VerifierRollupHelperFactory = await ethers.getContractFactory(
'VerifierRollupHelperMock',
);
verifierContract = await VerifierRollupHelperFactory.deploy();
// deploy MATIC
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
// deploy CDKDataCommittee
const cdkDataCommitteeFactory = await ethers.getContractFactory('CDKDataCommittee');
cdkDataCommitteeContract = await upgrades.deployProxy(
cdkDataCommitteeFactory,
[],
{ initializer: false },
);
const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + 2;
// Always have to redeploy impl since the PolygonZkEVMGlobalExitRoot address changes
const nonceProxyCDKValidium = nonceProxyBridge + 2;
const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge });
const precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, precalculateBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
// deploy PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
PolygonZkEVMBridgeContract = await upgrades.deployProxy(PolygonZkEVMBridgeFactory, [], { initializer: false });
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
forkID,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
expect(precalculateBridgeAddress).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
await PolygonZkEVMBridgeContract.initialize(networkIDMainnet, PolygonZkEVMGlobalExitRoot.address, cdkValidiumContract.address);
await cdkValidiumContract.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
// fund sequencer address with Matic tokens
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('1000'));
// setup committee
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(committeeMembers);
const expectedHash = ethers.utils.solidityKeccak256(['bytes'], [addrsBytes]);
await cdkDataCommitteeContract.initialize();
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(requiredAmountOfSignatures, urls, addrsBytes))
.to.emit(cdkDataCommitteeContract, 'CommitteeUpdated')
.withArgs(expectedHash);
const actualAmountOfmembers = await cdkDataCommitteeContract.getAmountOfMembers();
expect(actualAmountOfmembers).to.be.equal(committeeMembers.length);
});
// SETUP COMMITTEE tests
it('fail because required amount of signatures is greater than members', async () => {
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(committeeMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(nMembers + 1, urls, addrsBytes))
.to.be.revertedWith('TooManyRequiredSignatures');
});
it('fail because wrong address order', async () => {
const wrongAddressOrderMembers = [{
url: 'foo',
addr: '0x341f33e89ec1f28b9d5618413c223f973426140b',
}, {
url: 'bar',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}];
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(wrongAddressOrderMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, addrsBytes))
.to.be.revertedWith('WrongAddrOrder');
});
it('fail because repeated address', async () => {
const wrongAddressOrderMembers = [{
url: 'foo',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}, {
url: 'bar',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}];
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(wrongAddressOrderMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, addrsBytes))
.to.be.revertedWith('WrongAddrOrder');
});
it('fail because zero address', async () => {
const wrongAddressOrderMembers = [{
url: 'foo',
addr: '0x0000000000000000000000000000000000000000',
}, {
url: 'bar',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}];
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(wrongAddressOrderMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, addrsBytes))
.to.be.revertedWith('WrongAddrOrder');
});
it('fail because empty URL', async () => {
const wrongAddressOrderMembers = [{
url: 'foo',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}, {
url: '',
addr: '0x341f33e89ec1f28b9d5618413c223f973426140b',
}];
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(wrongAddressOrderMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, addrsBytes))
.to.be.revertedWith('EmptyURLNotAllowed');
});
it('fail because unexpected addrsBytes length', async () => {
const wrongAddressOrderMembers = [{
url: 'foo',
addr: '0x2d630a3ac2b39472958507d73e3e450acde3431c',
}, {
url: 'bar',
addr: '0x341f33e89ec1f28b9d5618413c223f973426140b',
}];
const { urls, addrsBytes } = membersToURLsAndAddrsBytes(wrongAddressOrderMembers);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, addrsBytes.substring(0, addrsBytes.length - 2)))
.to.be.revertedWith('UnexpectedAddrsBytesLength');
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(1, urls, `${addrsBytes}ff`))
.to.be.revertedWith('UnexpectedAddrsBytesLength');
});
// VERIFY SIGNATURES tests
it('fails because signature and addrs byte array has wrong size', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Remove last byte
const withMissingByte = signaturesAndAddrs.substring(0, signaturesAndAddrs.length - 2);
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withMissingByte))
.to.be.revertedWith('UnexpectedAddrsAndSignaturesSize');
// Add extra byte
const withExtraByte = `${signaturesAndAddrs.substring(0, signaturesAndAddrs.length)}11`;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withExtraByte))
.to.be.revertedWith('UnexpectedAddrsAndSignaturesSize');
// Add extra bytes that matches with address length (20 bytes) will fail to match the hash
const extra20Bytes = '690b9a9e9aa1c9db991c7721a92d351db4fac990';
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, signaturesAndAddrs + extra20Bytes))
.to.be.revertedWith('UnexpectedCommitteeHash');
});
it('fails because the hash of the addresses doesnt match', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Change half byte of the address list, so the hash doesn't match
const withLastHalfByteSwapped = `${signaturesAndAddrs.substring(0, signaturesAndAddrs.length - 1)}a`;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withLastHalfByteSwapped))
.to.be.revertedWith('UnexpectedCommitteeHash');
});
it('fails because there is an address in the list that is not part of the committe', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Replace last address
const addressNotFromTheCommittee = '690b9a9e9aa1c9db991c7721a92d351db4fac990';
const withWrongAddr = signaturesAndAddrs.substring(0, signaturesAndAddrs.length - 40)
+ addressNotFromTheCommittee;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withWrongAddr))
.to.be.revertedWith('UnexpectedCommitteeHash');
});
it('fails because there is a repeated address in the list', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Replace last address
const repeatedAddr = signaturesAndAddrs.substring(
signaturesAndAddrs.length - 40,
signaturesAndAddrs.length,
);
const withWrongAddr = signaturesAndAddrs.substring(0, signaturesAndAddrs.length - 80)
+ repeatedAddr + repeatedAddr;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withWrongAddr))
.to.be.revertedWith('UnexpectedCommitteeHash');
});
it('fails because there is a signing address that is not part of the committe', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Replace last address
const withWrongSignature = `0x1${signaturesAndAddrs.slice(3)}`;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withWrongSignature))
.to.be.revertedWith('CommitteeAddressDoesntExist');
});
it('fails because there is a repeated signature', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Replace last address
const signatureSize = 65 * 2;
const zeroXSize = 2;
const repeatedSignature = signaturesAndAddrs.substring(zeroXSize, zeroXSize + signatureSize);
const withoutZeroXAndTwoFirstSignatures = signaturesAndAddrs.substring(zeroXSize + 2 * signatureSize, signaturesAndAddrs.length);
const withRepeatedSignature = `0x${repeatedSignature}${repeatedSignature
}${withoutZeroXAndTwoFirstSignatures}`;
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, withRepeatedSignature))
.to.be.revertedWith('CommitteeAddressDoesntExist');
});
it('success single batch', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Send sequence successfully
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence], deployer.address, signaturesAndAddrs))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
});
it('success multiple batches', async () => {
const l2txData1 = '0x123456';
const transactionsHash1 = calculateBatchHashData(l2txData1);
const timestamp1 = (await ethers.provider.getBlock()).timestamp;
const sequence1 = {
transactionsHash: transactionsHash1,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(timestamp1),
minForcedTimestamp: 0,
};
const l2txData2 = '0x042069';
const transactionsHash2 = calculateBatchHashData(l2txData2);
const timestamp2 = (await ethers.provider.getBlock()).timestamp;
const sequence2 = {
transactionsHash: transactionsHash2,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(timestamp2),
minForcedTimestamp: 0,
};
// Approve tokens
const maticAmount = (await cdkValidiumContract.batchFee()).mul(2);
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Sign committee data
const hashToSign = await calculateLastAccInputHash([sequence1, sequence2]);
const signaturesAndAddrs = genSignaturesAndAddrs(hashToSign);
// Send sequence successfully
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence1, sequence2], deployer.address, signaturesAndAddrs))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 2);
});
it('success forced batch', async () => {
const l2txDataForceBatch = '0x123456';
const transactionsHashForceBatch = calculateBatchHashData(l2txDataForceBatch);
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()) + 1;
// Activate forced batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
// Force batch
await expect(cdkValidiumContract.forceBatch(l2txDataForceBatch, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, deployer.address, '0x');
// sequence 2 batches
const l2txData = '0x1234';
const transactionsHash2 = calculateBatchHashData(l2txData);
const maticAmountSequence = (await cdkValidiumContract.batchFee()).mul(1);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence1 = {
transactionsHash: transactionsHashForceBatch,
globalExitRoot: lastGlobalExitRoot,
timestamp: currentTimestamp,
minForcedTimestamp: currentTimestamp,
};
const sequence2 = {
transactionsHash: transactionsHash2,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const initialOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmountSequence),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Assert that the timestamp requirements must accomplish with force batches too
sequence1.minForcedTimestamp += 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence1, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('ForcedDataDoesNotMatch');
sequence1.minForcedTimestamp -= 1;
sequence1.timestamp -= 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence1, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampBelowForcedTimestamp');
sequence1.timestamp += 1;
sequence1.timestamp = currentTimestamp + 10;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence1, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
sequence1.timestamp = currentTimestamp;
sequence2.timestamp -= 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence1, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
sequence2.timestamp += 1;
// Sequence Bathces
let batchAccInputHashJs = calculateAccInputHash(
ethers.constants.HashZero,
sequence1.transactionsHash,
sequence1.globalExitRoot,
sequence1.timestamp,
trustedSequencer.address,
);
// Calcultate input Hahs for batch 2
batchAccInputHashJs = calculateAccInputHash(
batchAccInputHashJs,
sequence2.transactionsHash,
sequence2.globalExitRoot,
sequence2.timestamp,
trustedSequencer.address,
);
const signaturesAndAddrs = genSignaturesAndAddrs(batchAccInputHashJs);
await expect(cdkValidiumContract.connect(trustedSequencer)
.sequenceBatches([sequence1, sequence2], trustedSequencer.address, signaturesAndAddrs))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(Number(lastBatchSequenced) + 2);
const sequencedTimestamp = (await ethers.provider.getBlock()).timestamp;
const finalOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmountSequence)),
);
// Check batch mapping
const batchAccInputHash = (await cdkValidiumContract.sequencedBatches(1)).accInputHash;
// Only last batch is added to the mapping
expect(batchAccInputHash).to.be.equal(ethers.constants.HashZero);
const batchData2 = await cdkValidiumContract.sequencedBatches(2);
expect(batchData2.accInputHash).to.be.equal(batchAccInputHashJs);
expect(batchData2.sequencedTimestamp).to.be.equal(sequencedTimestamp);
expect(batchData2.previousLastBatchSequenced).to.be.equal(0);
});
});
const { expect } = require('chai');
const { ethers } = require('hardhat');
const MerkleTreeBridge = require('@0xpolygonhermez/zkevm-commonjs').MTBridge;
const {
verifyMerkleProof,
getLeafValue,
} = require('@0xpolygonhermez/zkevm-commonjs').mtBridgeUtils;
describe('Deposit Contract', () => {
let deployer;
let acc2;
let depositContractMock;
const LEAF_TYPE_ASSET = 0;
const MESSAGE_TYPE_ASSET = 1;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, acc2] = await ethers.getSigners();
// deploy deposit contract mock
const depositFactory = await ethers.getContractFactory('DepositContractMock');
depositContractMock = await depositFactory.deploy();
await depositContractMock.deployed();
});
it('should deposit and verify merkle proof', async () => {
const originNetwork = 0;
const tokenAddress = deployer.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = 1;
const destinationAddress = deployer.address;
const metadataHash = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await depositContractMock.deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValueJs = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const leafValueSC = await depositContractMock.getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
expect(leafValueJs).to.be.equal(leafValueSC);
merkleTree.add(leafValueJs);
const rootSC = await depositContractMock.getDepositRoot();
const rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
// check merkle proof
const index = 0;
const proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(leafValueJs, proof, index, rootSC)).to.be.equal(true);
expect(await depositContractMock.verifyMerkleProof(
leafValueJs,
proof,
index,
rootSC,
)).to.be.equal(true);
});
it('should deposit and verify merkle proof with 2 leafs', async () => {
const originNetwork = 0;
const tokenAddress = deployer.address;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = 1;
const destinationAddress = deployer.address;
const metadataHash = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await depositContractMock.deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
await depositContractMock.deposit(
MESSAGE_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
const leafValueJs = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const leafValueJs2 = getLeafValue(
MESSAGE_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const leafValueSC = await depositContractMock.getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const leafValueSC2 = await depositContractMock.getLeafValue(
MESSAGE_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
expect(leafValueJs).to.be.equal(leafValueSC);
expect(leafValueJs2).to.be.equal(leafValueSC2);
merkleTree.add(leafValueJs);
merkleTree.add(leafValueJs2);
const rootSC = await depositContractMock.getDepositRoot();
const rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
// check merkle proof
const index = 0;
const proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(leafValueJs, proof, index, rootSC)).to.be.equal(true);
expect(await depositContractMock.verifyMerkleProof(
leafValueJs,
proof,
index,
rootSC,
)).to.be.equal(true);
});
it('should create a more exhaustive merkle tree test', async () => {
/*
* Different deposits will be created and verified one by one
* Deposit 1
*/
let originNetwork = 0; // mainnet
let tokenAddress = deployer.address;
let amount = ethers.utils.parseEther('10');
let destinationNetwork = 1;
let destinationAddress = deployer.address;
let metadataHash = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await depositContractMock.deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// compute root merkle tree in Js
const height = 32;
const merkleTree = new MerkleTreeBridge(height);
let leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const leafValueSC = await depositContractMock.getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
expect(leafValue).to.be.equal(leafValueSC);
merkleTree.add(leafValue);
let rootSC = await depositContractMock.getDepositRoot();
let rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
// check merkle proof
let index = 0;
let proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(
leafValue,
proof,
index,
rootSC,
)).to.be.equal(true);
expect(await depositContractMock.verifyMerkleProof(
leafValue,
proof,
index,
rootSC,
)).to.be.equal(true);
// Deposit 2 - different address and amount
originNetwork = 0;
tokenAddress = deployer.address;
amount = ethers.utils.parseEther('1');
destinationNetwork = 1;
destinationAddress = acc2.address;
metadataHash = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await depositContractMock.connect(acc2).deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// compute root merkle tree in Js
leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
rootSC = await depositContractMock.getDepositRoot();
rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
// check merkle proof
index += 1;
proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSC)).to.be.equal(true);
expect(await depositContractMock.verifyMerkleProof(
leafValue,
proof,
index,
rootSC,
)).to.be.equal(true);
// Deposit 3 - deposit ether
originNetwork = 0;
tokenAddress = ethers.constants.AddressZero; // ether
amount = ethers.utils.parseEther('100');
destinationNetwork = 1;
destinationAddress = acc2.address;
metadataHash = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await depositContractMock.connect(acc2).deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
// compute root merkle tree in Js
leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
merkleTree.add(leafValue);
rootSC = await depositContractMock.getDepositRoot();
rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
// check merkle proof
index += 1;
proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSC)).to.be.equal(true);
expect(await depositContractMock.verifyMerkleProof(
leafValue,
proof,
index,
rootSC,
)).to.be.equal(true);
// Deposit lots of transactions
const txCount = 100;
const depositCount = Number(await depositContractMock.depositCount());
amount = ethers.utils.parseEther('0.01');
leafValue = getLeafValue(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
);
const results = [];
for (let i = 0; i < txCount; i++) {
const p = depositContractMock.connect(acc2).deposit(
LEAF_TYPE_ASSET,
originNetwork,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
metadataHash,
).then(() => {
merkleTree.add(leafValue);
});
results.push(p);
}
await Promise.all(results);
// Check roots
rootSC = await depositContractMock.getDepositRoot();
rootJS = merkleTree.getRoot();
expect(rootSC).to.be.equal(rootJS);
/*
* Check merkle proof
* Check random index from the ones generated in the loop
*/
const promises = [];
for (let i = 0; i < 10; i++) {
index = Math.floor(Math.random() * (txCount - depositCount) + depositCount);
proof = merkleTree.getProofTreeByIndex(index);
// verify merkle proof
expect(verifyMerkleProof(leafValue, proof, index, rootSC)).to.be.equal(true);
const p = depositContractMock.verifyMerkleProof(
leafValue,
proof,
index,
rootSC,
).then((res) => {
expect(res).to.be.equal(true);
});
promises.push(p);
}
await Promise.all(promises);
});
});
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
const { calculateBatchHashData } = contractUtils;
describe('Emergency mode test', () => {
let deployer;
let trustedAggregator;
let trustedSequencer;
let admin;
let verifierContract;
let PolygonZkEVMBridgeContract;
let cdkValidiumContract;
let cdkDataCommitteeContract;
let maticTokenContract;
let PolygonZkEVMGlobalExitRoot;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const networkIDMainnet = 0;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const chainID = 1000;
const networkName = 'cdk-validium';
const version = '0.0.1';
const pendingStateTimeoutDefault = 10;
const trustedAggregatorTimeoutDefault = 10;
let firstDeployment = true;
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, trustedAggregator, trustedSequencer, admin] = await ethers.getSigners();
// deploy mock verifier
const VerifierRollupHelperFactory = await ethers.getContractFactory(
'VerifierRollupHelperMock',
);
verifierContract = await VerifierRollupHelperFactory.deploy();
// deploy MATIC
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
/*
* deploy global exit root manager
* In order to not have trouble with nonce deploy first proxy admin
*/
await upgrades.deployProxyAdmin();
if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') {
firstDeployment = false;
}
const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2);
const nonceProxyCommittee = nonceProxyBridge + 1;
// Always have to redeploy impl since the PolygonZkEVMGlobalExitRoot address changes
const nonceProxyCDKValidium = nonceProxyCommittee + 2;
const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge });
const precalculateCommitteeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCommittee });
const precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
firstDeployment = false;
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, precalculateBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
// deploy PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
PolygonZkEVMBridgeContract = await upgrades.deployProxy(PolygonZkEVMBridgeFactory, [], { initializer: false });
// deploy CDKDataCommittee
const cdkDataCommitteeFactory = await ethers.getContractFactory('CDKDataCommittee');
cdkDataCommitteeContract = await upgrades.deployProxy(
cdkDataCommitteeFactory,
[],
{ initializer: false },
);
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
0,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
expect(precalculateBridgeAddress).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(precalculateCommitteeAddress).to.be.equal(cdkDataCommitteeContract.address);
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
await PolygonZkEVMBridgeContract.initialize(networkIDMainnet, PolygonZkEVMGlobalExitRoot.address, cdkValidiumContract.address);
await cdkValidiumContract.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
// fund sequencer address with Matic tokens
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('1000'));
// init data committee
await cdkDataCommitteeContract.initialize();
const expectedHash = ethers.utils.solidityKeccak256(['bytes'], [[]]);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(0, [], []))
.to.emit(cdkDataCommitteeContract, 'CommitteeUpdated')
.withArgs(expectedHash);
// Activate force batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
});
it('should activate emergency mode', async () => {
// Check isEmergencyState
expect(await cdkValidiumContract.isEmergencyState()).to.be.equal(false);
expect(await PolygonZkEVMBridgeContract.isEmergencyState()).to.be.equal(false);
await expect(cdkValidiumContract.connect(admin).deactivateEmergencyState())
.to.be.revertedWith('OnlyEmergencyState');
// Set isEmergencyState
await expect(cdkValidiumContract.connect(admin).activateEmergencyState(1))
.to.be.revertedWith('BatchNotSequencedOrNotSequenceEnd');
await expect(PolygonZkEVMBridgeContract.connect(deployer).activateEmergencyState())
.to.be.revertedWith('OnlyPolygonZkEVM');
await expect(cdkValidiumContract.activateEmergencyState(0))
.to.emit(cdkValidiumContract, 'EmergencyStateActivated')
.to.emit(PolygonZkEVMBridgeContract, 'EmergencyStateActivated');
expect(await cdkValidiumContract.isEmergencyState()).to.be.equal(true);
expect(await PolygonZkEVMBridgeContract.isEmergencyState()).to.be.equal(true);
// Once in emergency state no sequenceBatches/forceBatches can be done
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
const forcedSequence = {
transactions: l2txData,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// revert because emergency state
await expect(cdkValidiumContract.sequenceBatches([sequence], deployer.address, []))
.to.be.revertedWith('OnlyNotEmergencyState');
// revert because emergency state
await expect(cdkValidiumContract.sequenceForceBatches([forcedSequence]))
.to.be.revertedWith('OnlyNotEmergencyState');
// revert because emergency state
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.be.revertedWith('OnlyNotEmergencyState');
// revert because emergency state
await expect(cdkValidiumContract.consolidatePendingState(0))
.to.be.revertedWith('OnlyNotEmergencyState');
// trustedAggregator forge the batch
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()).toNumber() + 1;
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
const pendingStateNum = 0;
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatches(
pendingStateNum,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OnlyNotEmergencyState');
// Check PolygonZkEVMBridge no PolygonZkEVMBridge is in emergency state also
const tokenAddress = ethers.constants.AddressZero;
const amount = ethers.utils.parseEther('10');
const destinationNetwork = 1;
const destinationAddress = deployer.address;
await expect(PolygonZkEVMBridgeContract.bridgeAsset(
destinationNetwork,
destinationAddress,
amount,
tokenAddress,
true,
'0x',
)).to.be.revertedWith('OnlyNotEmergencyState');
await expect(PolygonZkEVMBridgeContract.bridgeMessage(
destinationNetwork,
destinationAddress,
true,
'0x',
)).to.be.revertedWith('OnlyNotEmergencyState');
const proof = Array(32).fill(ethers.constants.HashZero);
const index = 0;
const root = ethers.constants.HashZero;
await expect(PolygonZkEVMBridgeContract.claimAsset(
proof,
index,
root,
root,
0,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
'0x',
)).to.be.revertedWith('OnlyNotEmergencyState');
await expect(PolygonZkEVMBridgeContract.claimMessage(
proof,
index,
root,
root,
0,
tokenAddress,
destinationNetwork,
destinationAddress,
amount,
'0x',
)).to.be.revertedWith('OnlyNotEmergencyState');
// Emergency council should deactivate emergency mode
await expect(cdkValidiumContract.activateEmergencyState(0))
.to.be.revertedWith('OnlyNotEmergencyState');
await expect(PolygonZkEVMBridgeContract.connect(deployer).deactivateEmergencyState())
.to.be.revertedWith('OnlyPolygonZkEVM');
await expect(cdkValidiumContract.deactivateEmergencyState())
.to.be.revertedWith('OnlyAdmin');
await expect(cdkValidiumContract.connect(admin).deactivateEmergencyState())
.to.emit(cdkValidiumContract, 'EmergencyStateDeactivated')
.to.emit(PolygonZkEVMBridgeContract, 'EmergencyStateDeactivated');
// Check isEmergencyState
expect(await cdkValidiumContract.isEmergencyState()).to.be.equal(false);
expect(await PolygonZkEVMBridgeContract.isEmergencyState()).to.be.equal(false);
/*
* Continue normal flow
* Approve tokens
*/
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Sequence Batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
// trustedAggregator forge the batch
const initialAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
await ethers.provider.send('evm_increaseTime', [trustedAggregatorTimeoutDefault]); // evm_setNextBlockTimestamp
// Verify batch
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatches(
pendingStateNum,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(numBatch, newStateRoot, trustedAggregator.address);
const finalAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
expect(finalAggregatorMatic).to.equal(
ethers.BigNumber.from(initialAggregatorMatic).add(ethers.BigNumber.from(maticAmount)),
);
// Finally enter in emergency mode again proving distinc state
const finalPendingStateNum = 1;
await expect(
cdkValidiumContract.connect(trustedAggregator).proveNonDeterministicPendingState(
pendingStateNum,
finalPendingStateNum,
numBatch - 1,
numBatch - 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchDoesNotMatchPendingState');
await expect(
cdkValidiumContract.connect(trustedAggregator).proveNonDeterministicPendingState(
pendingStateNum,
finalPendingStateNum,
numBatch - 1,
numBatch + 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchDoesNotMatchPendingState');
const newStateRootDistinct = '0x0000000000000000000000000000000000000000000000000000000000000002';
await expect(
cdkValidiumContract.proveNonDeterministicPendingState(
pendingStateNum,
finalPendingStateNum,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRootDistinct,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'ProveNonDeterministicPendingState').withArgs(newStateRoot, newStateRootDistinct)
.to.emit(cdkValidiumContract, 'EmergencyStateActivated')
.to.emit(PolygonZkEVMBridgeContract, 'EmergencyStateActivated');
// Check emergency state is active
expect(await cdkValidiumContract.isEmergencyState()).to.be.equal(true);
expect(await PolygonZkEVMBridgeContract.isEmergencyState()).to.be.equal(true);
});
});
const { expect } = require('chai');
const { ethers } = require('hardhat');
function calculateGlobalExitRoot(mainnetExitRoot, rollupExitRoot) {
return ethers.utils.solidityKeccak256(['bytes32', 'bytes32'], [mainnetExitRoot, rollupExitRoot]);
}
const zero32bytes = '0x0000000000000000000000000000000000000000000000000000000000000000';
describe('Global Exit Root', () => {
let rollup;
let PolygonZkEVMBridge;
let PolygonZkEVMGlobalExitRoot;
beforeEach('Deploy contracts', async () => {
// load signers
[, rollup, PolygonZkEVMBridge] = await ethers.getSigners();
// deploy global exit root manager
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
PolygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(
rollup.address,
PolygonZkEVMBridge.address,
);
await PolygonZkEVMGlobalExitRoot.deployed();
});
it('should check the constructor parameters', async () => {
expect(await PolygonZkEVMGlobalExitRoot.rollupAddress()).to.be.equal(rollup.address);
expect(await PolygonZkEVMGlobalExitRoot.bridgeAddress()).to.be.equal(PolygonZkEVMBridge.address);
expect(await PolygonZkEVMGlobalExitRoot.lastRollupExitRoot()).to.be.equal(zero32bytes);
expect(await PolygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.be.equal(zero32bytes);
});
it('should update root and check global exit root', async () => {
const newRootRollup = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await expect(PolygonZkEVMGlobalExitRoot.updateExitRoot(newRootRollup))
.to.be.revertedWith('OnlyAllowedContracts');
// Update root from the rollup
await expect(PolygonZkEVMGlobalExitRoot.connect(rollup).updateExitRoot(newRootRollup))
.to.emit(PolygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(zero32bytes, newRootRollup);
expect(await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot())
.to.be.equal(calculateGlobalExitRoot(zero32bytes, newRootRollup));
// Update root from the PolygonZkEVMBridge
const newRootBridge = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await expect(PolygonZkEVMGlobalExitRoot.connect(PolygonZkEVMBridge).updateExitRoot(newRootBridge))
.to.emit(PolygonZkEVMGlobalExitRoot, 'UpdateGlobalExitRoot')
.withArgs(newRootBridge, newRootRollup);
expect(await PolygonZkEVMGlobalExitRoot.lastMainnetExitRoot()).to.be.equal(newRootBridge);
expect(await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot())
.to.be.equal(calculateGlobalExitRoot(newRootBridge, newRootRollup));
});
});
const { expect } = require('chai');
const { ethers } = require('hardhat');
const zero32bytes = '0x0000000000000000000000000000000000000000000000000000000000000000';
describe('Global Exit Root L2', () => {
let PolygonZkEVMBridge;
let PolygonZkEVMGlobalExitRoot;
let deployer;
beforeEach('Deploy contracts', async () => {
// load signers
[deployer, PolygonZkEVMBridge] = await ethers.getSigners();
// deploy global exit root manager
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootL2Mock', deployer);
PolygonZkEVMGlobalExitRoot = await PolygonZkEVMGlobalExitRootFactory.deploy(PolygonZkEVMBridge.address);
});
it('should check the constructor parameters', async () => {
expect(await PolygonZkEVMGlobalExitRoot.bridgeAddress()).to.be.equal(PolygonZkEVMBridge.address);
expect(await PolygonZkEVMGlobalExitRoot.lastRollupExitRoot()).to.be.equal(zero32bytes);
});
it('should update root and check global exit root', async () => {
const newRootRollup = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await expect(PolygonZkEVMGlobalExitRoot.updateExitRoot(newRootRollup))
.to.be.revertedWith('OnlyAllowedContracts');
// Update root from the rollup
await PolygonZkEVMGlobalExitRoot.connect(PolygonZkEVMBridge).updateExitRoot(newRootRollup);
expect(await PolygonZkEVMGlobalExitRoot.lastRollupExitRoot()).to.be.equal(newRootRollup);
});
it('should update root and check the storage position matches', async () => {
// Check global exit root
const newRoot = ethers.utils.hexlify(ethers.utils.randomBytes(32));
const blockNumber = 1;
await PolygonZkEVMGlobalExitRoot.setLastGlobalExitRoot(newRoot, blockNumber);
expect(await PolygonZkEVMGlobalExitRoot.globalExitRootMap(newRoot)).to.be.equal(blockNumber);
const mapStoragePosition = 0;
const key = newRoot;
const storagePosition = ethers.utils.solidityKeccak256(['uint256', 'uint256'], [key, mapStoragePosition]);
const storageValue = await ethers.provider.getStorageAt(PolygonZkEVMGlobalExitRoot.address, storagePosition);
expect(blockNumber).to.be.equal(ethers.BigNumber.from(storageValue).toNumber());
// Check rollup exit root
const newRootRollupExitRoot = ethers.utils.hexlify(ethers.utils.randomBytes(32));
await PolygonZkEVMGlobalExitRoot.setExitRoot(newRootRollupExitRoot);
expect(await PolygonZkEVMGlobalExitRoot.lastRollupExitRoot()).to.be.equal(newRootRollupExitRoot);
const storagePositionExitRoot = 1;
const storageValueExitRoot = await ethers.provider.getStorageAt(PolygonZkEVMGlobalExitRoot.address, storagePositionExitRoot);
expect(newRootRollupExitRoot, storageValueExitRoot);
});
});
/* eslint-disable no-await-in-loop */
const { stateUtils } = require('@0xpolygonhermez/zkevm-commonjs');
async function setGenesisBlock(addressArray, amountArray, nonceArray, smt) {
let currentRoot = smt.empty;
for (let i = 0; i < addressArray.length; i++) {
currentRoot = await stateUtils.setAccountState(addressArray[i], smt, currentRoot, amountArray[i], nonceArray[i]);
}
return currentRoot;
}
module.exports = {
setGenesisBlock,
};
/* eslint-disable no-await-in-loop */
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const { Scalar } = require('ffjavascript');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
const { generateSolidityInputs } = contractUtils;
const { calculateSnarkInput, calculateBatchHashData, calculateAccInputHash } = contractUtils;
const proofJson = require('./test-inputs/proof.json');
const input = require('./test-inputs/public.json');
const inputJson = require('./test-inputs/input.json');
describe('Real flow test', () => {
let verifierContract;
let maticTokenContract;
let PolygonZkEVMBridgeContract;
let cdkValidiumContract;
let PolygonZkEVMGlobalExitRoot;
let cdkDataCommitteeContract;
let deployer;
let trustedSequencer;
let trustedAggregator;
let admin;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const genesisRoot = inputJson.oldStateRoot;
const networkIDMainnet = 0;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const { chainID } = inputJson;
const networkName = 'cdk-validium';
const version = '0.0.1';
const forkID = 0;
const pendingStateTimeoutDefault = 10;
const trustedAggregatorTimeoutDefault = 10;
let firstDeployment = true;
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, trustedAggregator, admin] = await ethers.getSigners();
// Could be different address teorically but for now it's fine
const trustedSequencerAddress = inputJson.singleBatchData[0].sequencerAddr;
await ethers.provider.send('hardhat_impersonateAccount', [trustedSequencerAddress]);
trustedSequencer = await ethers.getSigner(trustedSequencerAddress);
await deployer.sendTransaction({
to: trustedSequencerAddress,
value: ethers.utils.parseEther('4'),
});
// deploy mock verifier
const VerifierRollupHelperFactory = await ethers.getContractFactory(
'FflonkVerifier',
);
verifierContract = await VerifierRollupHelperFactory.deploy();
// deploy MATIC
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
/*
* deploy global exit root manager
* In order to not have trouble with nonce deploy first proxy admin
*/
await upgrades.deployProxyAdmin();
if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') {
firstDeployment = false;
}
const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2);
const nonceProxyCommittee = nonceProxyBridge + (firstDeployment ? 2 : 1);
// Always have to redeploy impl since the PolygonZkEVMGlobalExitRoot address changes
const nonceProxyCDKValidium = nonceProxyCommittee + 2;
const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge });
const precalculateCommitteeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCommittee });
const precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
firstDeployment = false;
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRootMock');
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, precalculateBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
// deploy PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
PolygonZkEVMBridgeContract = await upgrades.deployProxy(PolygonZkEVMBridgeFactory, [], { initializer: false });
// deploy CDKDataCommittee
const cdkDataCommitteeFactory = await ethers.getContractFactory('CDKDataCommittee');
cdkDataCommitteeContract = await upgrades.deployProxy(
cdkDataCommitteeFactory,
[],
{ initializer: false },
);
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
0,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
expect(precalculateBridgeAddress).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(precalculateCommitteeAddress).to.be.equal(cdkDataCommitteeContract.address);
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
await PolygonZkEVMBridgeContract.initialize(networkIDMainnet, PolygonZkEVMGlobalExitRoot.address, cdkValidiumContract.address);
await cdkValidiumContract.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
// fund sequencer address with Matic tokens
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('1000'));
});
it('Test real prover', async () => {
const batchesData = inputJson.singleBatchData;
const batchesNum = batchesData.length;
// Approve tokens
const maticAmount = await cdkValidiumContract.batchFee();
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount.mul(batchesNum)),
).to.emit(maticTokenContract, 'Approval');
// prepare CDKValidiumMock
await cdkValidiumContract.setVerifiedBatch(inputJson.oldNumBatch);
await cdkValidiumContract.setSequencedBatch(inputJson.oldNumBatch);
const lastTimestamp = batchesData[batchesNum - 1].timestamp;
await ethers.provider.send('evm_setNextBlockTimestamp', [lastTimestamp]);
for (let i = 0; i < batchesNum; i++) {
// set timestamp for the sendBatch call
const currentBatchData = batchesData[i];
const currentSequence = {
transactions: currentBatchData.batchL2Data,
globalExitRoot: currentBatchData.globalExitRoot,
timestamp: currentBatchData.timestamp,
minForcedTimestamp: 0,
};
const batchAccInputHashJs = calculateAccInputHash(
currentBatchData.oldAccInputHash,
calculateBatchHashData(currentBatchData.batchL2Data),
currentBatchData.globalExitRoot,
currentBatchData.timestamp,
currentBatchData.sequencerAddr, // fix
);
expect(batchAccInputHashJs).to.be.eq(currentBatchData.newAccInputHash);
// prapare globalExitRoot
const randomTimestamp = 1001;
const { globalExitRoot } = batchesData[0];
await PolygonZkEVMGlobalExitRoot.setGlobalExitRoot(globalExitRoot, randomTimestamp);
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// check trusted sequencer
const trustedSequencerAddress = inputJson.singleBatchData[i].sequencerAddr;
if (trustedSequencer.address !== trustedSequencerAddress) {
await cdkValidiumContract.connect(admin).setTrustedSequencer(trustedSequencerAddress);
await ethers.provider.send('hardhat_impersonateAccount', [trustedSequencerAddress]);
trustedSequencer = await ethers.getSigner(trustedSequencerAddress);
await deployer.sendTransaction({
to: trustedSequencerAddress,
value: ethers.utils.parseEther('4'),
});
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount.mul(batchesNum)),
).to.emit(maticTokenContract, 'Approval');
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('100'));
}
// Sequence Batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([currentSequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(Number(lastBatchSequenced) + 1);
}
// Set state and exit root
await cdkValidiumContract.setStateRoot(inputJson.oldStateRoot, inputJson.oldNumBatch);
const { aggregatorAddress } = inputJson;
await ethers.provider.send('hardhat_impersonateAccount', [aggregatorAddress]);
const aggregator = await ethers.getSigner(aggregatorAddress);
await deployer.sendTransaction({
to: aggregatorAddress,
value: ethers.utils.parseEther('4'),
});
await cdkValidiumContract.connect(admin).setTrustedAggregator(aggregatorAddress);
const batchAccInputHash = (await cdkValidiumContract.sequencedBatches(inputJson.newNumBatch)).accInputHash;
expect(batchAccInputHash).to.be.equal(inputJson.newAccInputHash);
const proof = generateSolidityInputs(proofJson);
// Verify snark input
const circuitInputStarkJS = await calculateSnarkInput(
inputJson.oldStateRoot,
inputJson.newStateRoot,
inputJson.newLocalExitRoot,
inputJson.oldAccInputHash,
inputJson.newAccInputHash,
inputJson.oldNumBatch,
inputJson.newNumBatch,
inputJson.chainID,
inputJson.aggregatorAddress,
forkID,
);
expect(circuitInputStarkJS).to.be.eq(Scalar.e(input[0]));
// aggregator forge the batch
const { newLocalExitRoot } = inputJson;
const { newStateRoot } = inputJson;
const { oldNumBatch } = inputJson;
const { newNumBatch } = inputJson;
const pendingStateNum = 0;
// Verify batch
await expect(
cdkValidiumContract.connect(aggregator).verifyBatchesTrustedAggregator(
pendingStateNum,
oldNumBatch,
newNumBatch,
newLocalExitRoot,
newStateRoot,
'0x',
),
).to.be.revertedWith('InvalidProof');
await expect(
cdkValidiumContract.connect(aggregator).verifyBatchesTrustedAggregator(
pendingStateNum,
oldNumBatch,
newNumBatch,
newLocalExitRoot,
newStateRoot,
proof,
),
).to.emit(cdkValidiumContract, 'VerifyBatchesTrustedAggregator')
.withArgs(newNumBatch, newStateRoot, aggregator.address);
});
});
const { expect } = require('chai');
const { ethers } = require('hardhat');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
const { generateSolidityInputs } = contractUtils;
const proofJson = require('./test-inputs/proof.json');
const input = require('./test-inputs/public.json');
describe('Real prover inputs test', () => {
let verifierContract;
beforeEach('Deploy contract', async () => {
// deploy mock verifier
const VerifierFactory = await ethers.getContractFactory(
'FflonkVerifier',
);
verifierContract = await VerifierFactory.deploy();
});
it('Test real prover', async () => {
const proof = generateSolidityInputs(proofJson);
expect(await verifierContract.verifyProof(
proof,
input,
)).to.be.equal(true);
});
});
{
"singleBatchData": [
{
"oldStateRoot": "0x3ca39a7b5b419d1c50c89a8d15d1234f6cbc8baadb465efb609832bbc19f9026",
"newStateRoot": "0xf96e5fada3068287b9c78ce46c31cf3c03e817bb764be203daf61ddcac6d73e7",
"oldAccInputHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"newAccInputHash": "0x070c0c1abe776249fe4011d71b63a0cce202069584af3801d5df2dccf64a2d9a",
"newLocalExitRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"oldNumBatch": 0,
"newNumBatch": 1,
"chainID": 1000,
"forkID": 3,
"batchL2Data": "0xef80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff8901314fb37062980000808203e88080f9ea8e90ae323e360a22bd0b1a972d15cb33df6ccbfbada4a0d49792d1164ea56029d84d5093a7186fb5bf2f1b7258d57e9c09ac89c4cb8eb44a3c961c4dd89b1b",
"globalExitRoot": "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9",
"timestamp": 1944498031,
"sequencerAddr": "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"batchHashData": "0x0e8a7e57fe270ab7780cb10ca4b4a39792432701268b0d3a64e6870729e506d1",
"contractsBytecode": {},
"db": {
"0x3ca39a7b5b419d1c50c89a8d15d1234f6cbc8baadb465efb609832bbc19f9026": [
"cddc57c0d0fdd4ed",
"d24df1950f2d8f15",
"4c2f3e938869b82d",
"649e63bfe1247ba4",
"b69b044f5e694795",
"f57d81efba5d4445",
"339438195426ad0a",
"3efad1dd58c2259d",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x3efad1dd58c2259d339438195426ad0af57d81efba5d4445b69b044f5e694795": [
"00000000dea00000",
"0000000035c9adc5",
"0000000000000036",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
]
}
},
{
"oldStateRoot": "0xf96e5fada3068287b9c78ce46c31cf3c03e817bb764be203daf61ddcac6d73e7",
"newStateRoot": "0x0e22fa9f137193d182e0373420efb5cf4b137cb6626f5446edda37817889b3de",
"oldAccInputHash": "0x070c0c1abe776249fe4011d71b63a0cce202069584af3801d5df2dccf64a2d9a",
"newAccInputHash": "0x389bc63239f455750717b4efb5e9440d7b08751241b546e70ffe6075509d6c32",
"newLocalExitRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"oldNumBatch": 1,
"newNumBatch": 2,
"chainID": 1000,
"forkID": 3,
"batchL2Data": "0xee80843b9aca00830186a094617b3a3528f9cdd6630fd3301b9c8911f7bf063d881bc16d674ec80000808203e880804902102bdd9b91ae389102098dd7d8cb6e732410851223a1153cbe78b0c54d1e63bd7740451f2efcf88bbebdcc334ff3b3e3c332fbbf5703e4699124454663ba1b",
"globalExitRoot": "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9",
"timestamp": 1944498032,
"sequencerAddr": "0x4d5Cf5032B2a844602278b01199ED191A86c93ff",
"batchHashData": "0xe2058260b8a93dc906fd253c2af22d4cb6136e6833b110a2655f6e0637952e07",
"contractsBytecode": {},
"db": {
"0xf96e5fada3068287b9c78ce46c31cf3c03e817bb764be203daf61ddcac6d73e7": [
"66ca5c224339ab7a",
"b931414b0c3e62b2",
"9eed1433f6be7864",
"3c744e84dd0f82a2",
"3eb5df14d4bdc47c",
"ef2191d6cdc0bf45",
"8f7fb26dd51e6103",
"55e7e82ae2a7af55",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x3c744e84dd0f82a29eed1433f6be7864b931414b0c3e62b266ca5c224339ab7a": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"56f2387fd51bc750",
"e6fc6a2bdffea4b5",
"65467a41c64ac89f",
"cd68610e704d4336",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xcd68610e704d433665467a41c64ac89fe6fc6a2bdffea4b556f2387fd51bc750": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"db36156dfba0dd2b",
"9f34e3bb9658a94e",
"4b9baac1433cd083",
"20d3677c95277497",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x20d3677c952774974b9baac1433cd0839f34e3bb9658a94edb36156dfba0dd2b": [
"361826bb3d6f667d",
"f3d8c954de079f6a",
"51ac461137d9f3f7",
"3416ae986e8ebfa8",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x3416ae986e8ebfa851ac461137d9f3f7f3d8c954de079f6a361826bb3d6f667d": [
"a9cc526a347bc157",
"125a290166bdae30",
"1ae9909ea83d5545",
"2cd4ccf12490921c",
"00b3ff071dd86d04",
"844eb4eb42b16fb3",
"5faccb68fe41c19c",
"e5fe202e040d3002",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x2cd4ccf12490921c1ae9909ea83d5545125a290166bdae30a9cc526a347bc157": [
"2da3fe1fe83f2616",
"48e96847312823e8",
"258649e03dbf05e4",
"404c4132b4f35a14",
"0e27e92c28029aec",
"98e0c826ec17d37a",
"65597af607e1af8b",
"ace107db9858f320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xace107db9858f32065597af607e1af8b98e0c826ec17d37a0e27e92c28029aec": [
"0000000073e6af6f",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xe5fe202e040d30025faccb68fe41c19c844eb4eb42b16fb300b3ff071dd86d04": [
"20284081553655e3",
"501c2393cf10d5fd",
"083dff83bd06d987",
"77c2e46005223573",
"c4fe80ece2496840",
"247e4a8208f142c3",
"1ab5e319efbcada7",
"51d2faa706019320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x51d2faa7060193201ab5e319efbcada7247e4a8208f142c3c4fe80ece2496840": [
"00000000d806bada",
"00000000d3bcbbe4",
"00000000b8ec425c",
"0000000027b4eda5",
"00000000904a0265",
"000000009c0a7847",
"000000002c4c83f4",
"00000000b34368aa"
],
"0x55e7e82ae2a7af558f7fb26dd51e6103ef2191d6cdc0bf453eb5df14d4bdc47c": [
"1c4f1ba045cf5b95",
"bc713c1bbad32c2e",
"4281764be58d6a22",
"9e3757e8ec1272dd",
"bd7c49e380253fa5",
"e372e59a3b60703c",
"a451273cbbb1e1e5",
"98918ae937e2cf3e",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x9e3757e8ec1272dd4281764be58d6a22bc713c1bbad32c2e1c4f1ba045cf5b95": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"95ab0f1313448263",
"014f76d48abc1a5a",
"abac7bde3231c7b3",
"725f5f7511034537",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x725f5f7511034537abac7bde3231c7b3014f76d48abc1a5a95ab0f1313448263": [
"f9afb01623f15c79",
"308cdd5740bc023c",
"322e20e4c0dcef74",
"87cca53735cec736",
"7fe9e067044258cd",
"c1c7a23630d74438",
"f6d76a253178385f",
"617c2a11f77ff536",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x87cca53735cec736322e20e4c0dcef74308cdd5740bc023cf9afb01623f15c79": [
"16dde42596b907f0",
"49015d7e991a1528",
"4a6eced6e8304885",
"305a6af4d7a8a00c",
"85b8fc8024db5e5c",
"cb9fc9e8676fe5f9",
"900609fbcf391183",
"41e5632337836bc5",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x41e5632337836bc5900609fbcf391183cb9fc9e8676fe5f985b8fc8024db5e5c": [
"0000000062980000",
"00000000314fb370",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x617c2a11f77ff536f6d76a253178385fc1c7a23630d744387fe9e067044258cd": [
"11c8725c9dfdb98b",
"7ffbf50ba07b42f8",
"7c18430af73c190c",
"1c3e76134671e490",
"d074b8cee5dcf415",
"2346a1b4c0f390e8",
"47969c1f5a6a25b1",
"da62fdf84a21108e",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xda62fdf84a21108e47969c1f5a6a25b12346a1b4c0f390e8d074b8cee5dcf415": [
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x98918ae937e2cf3ea451273cbbb1e1e5e372e59a3b60703cbd7c49e380253fa5": [
"d43f2781cecb9cea",
"a8ef821b2d1a19c4",
"8d39c881d94606f2",
"571c41938781a282",
"c4066ba8c32e8a54",
"99ccbd6826c25549",
"1bbff6a9558c13d8",
"5c34e8b7e09eb9d2",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x571c41938781a2828d39c881d94606f2a8ef821b2d1a19c4d43f2781cecb9cea": [
"442f7573a4097cf2",
"3520bc74612a2ea9",
"143ce4e61bf52251",
"da69a3c4a8007a5a",
"d074b8cee5dcf415",
"2346a1b4c0f390e8",
"47969c1f5a6a25b1",
"da62fdf84a21108e",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x5c34e8b7e09eb9d21bbff6a9558c13d899ccbd6826c25549c4066ba8c32e8a54": [
"66ee2be0687eea76",
"6926f8ca8796c78a",
"26179f49c434dc16",
"649e63bfe1247ba4",
"8d6f4bb1f77e3231",
"e8588d537a7fd215",
"06643ffe76ca3417",
"ac6fb937ba074e4f",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xac6fb937ba074e4f06643ffe76ca3417e8588d537a7fd2158d6f4bb1f77e3231": [
"000000007c080000",
"000000000479fa55",
"0000000000000035",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
]
}
},
{
"oldStateRoot": "0x0e22fa9f137193d182e0373420efb5cf4b137cb6626f5446edda37817889b3de",
"newStateRoot": "0x5375f9ef9ce7262aedf64e236b8754f17fc99144f72b3113ef3030ee859fdf42",
"oldAccInputHash": "0x389bc63239f455750717b4efb5e9440d7b08751241b546e70ffe6075509d6c32",
"newAccInputHash": "0xad1915669aaf61a83bbfad1f4360c1a32245e38ffb67b1afa68c825135890fc9",
"newLocalExitRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"oldNumBatch": 2,
"newNumBatch": 3,
"chainID": 1000,
"forkID": 3,
"batchL2Data": "0xee01843b9aca00830186a094617b3a3528f9cdd6630fd3301b9c8911f7bf063d880de0b6b3a7640000808203e88080d71f728226f6e692289eab9f2c9565036ffda72650c88e2b9c001b95a5f1a2e647612249728261ab90977a2c50b22b8fd486f88b356a921f4487327e5d2775651cee02843b9aca00830186a094617b3a3528f9cdd6630fd3301b9c8911f7bf063d880de0b6b3a7640000808203e880808ab4d93e176f0762b48dfd8382f97200e134eb1e8e6423951d70af8f99e53c805b61ee8721981423a592c0c1af55c482bed43a45e2e3c1ecc70d350e2083ebc11b",
"globalExitRoot": "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9",
"timestamp": 1944498032,
"sequencerAddr": "0x4d5Cf5032B2a844602278b01199ED191A86c93ff",
"batchHashData": "0xe0b9d084dae9d015aaf65bc9b013b37c12b3cc31ae4e9590296c50eda518d8a1",
"contractsBytecode": {},
"db": {
"0x0e22fa9f137193d182e0373420efb5cf4b137cb6626f5446edda37817889b3de": [
"348d782834851457",
"63b29f6e4d064c43",
"641b2973999516cb",
"d50d4234f0bb97f9",
"8ba0a723d57968ca",
"ead72c942a94db4e",
"e4525c05d3bd7f35",
"e427a38da73c9212",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xd50d4234f0bb97f9641b2973999516cb63b29f6e4d064c43348d782834851457": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"8b1180f4a4d31d84",
"90ff0d43283a0245",
"4a8fc38939056e50",
"9e0d49dce0a60169",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x9e0d49dce0a601694a8fc38939056e5090ff0d43283a02458b1180f4a4d31d84": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"91ac7edb1f162c08",
"a3c00a3edb3c0791",
"0c365de202a6f59d",
"5e27481ad9dc427d",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x5e27481ad9dc427d0c365de202a6f59da3c00a3edb3c079191ac7edb1f162c08": [
"a83b71cfeaad532e",
"3555f42200329adf",
"414579599c3c34bd",
"4998de09a9bd6c3d",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x4998de09a9bd6c3d414579599c3c34bd3555f42200329adfa83b71cfeaad532e": [
"a9cc526a347bc157",
"125a290166bdae30",
"1ae9909ea83d5545",
"2cd4ccf12490921c",
"696de345126302de",
"07d788644c80b2d2",
"67d64069175f6d0c",
"fd993c2b31f6e62e",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x2cd4ccf12490921c1ae9909ea83d5545125a290166bdae30a9cc526a347bc157": [
"2da3fe1fe83f2616",
"48e96847312823e8",
"258649e03dbf05e4",
"404c4132b4f35a14",
"0e27e92c28029aec",
"98e0c826ec17d37a",
"65597af607e1af8b",
"ace107db9858f320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xace107db9858f32065597af607e1af8b98e0c826ec17d37a0e27e92c28029aec": [
"0000000073e6af6f",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xfd993c2b31f6e62e67d64069175f6d0c07d788644c80b2d2696de345126302de": [
"0913ff64639f31f1",
"5f9f9c7ae3c1a51b",
"6ce95a01c39f7eb4",
"98fd5d75b176ec04",
"e8c5ee51a8a70872",
"4d0f7cadff43d8b2",
"186417a11c584386",
"190ce122ca70915e",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x98fd5d75b176ec046ce95a01c39f7eb45f9f9c7ae3c1a51b0913ff64639f31f1": [
"37866cdbe5877c88",
"2d62534cb92ee170",
"6bf2c9fd6a3aa16c",
"325ba19f4ab866aa",
"d074b8cee5dcf415",
"2346a1b4c0f390e8",
"47969c1f5a6a25b1",
"da62fdf84a21108e",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xda62fdf84a21108e47969c1f5a6a25b12346a1b4c0f390e8d074b8cee5dcf415": [
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x190ce122ca70915e186417a11c5843864d0f7cadff43d8b2e8c5ee51a8a70872": [
"20284081553655e3",
"280e11c9e7886afe",
"083dff83bd06d987",
"77c2e46005223573",
"c4fe80ece2496840",
"247e4a8208f142c3",
"1ab5e319efbcada7",
"51d2faa706019320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x51d2faa7060193201ab5e319efbcada7247e4a8208f142c3c4fe80ece2496840": [
"00000000d806bada",
"00000000d3bcbbe4",
"00000000b8ec425c",
"0000000027b4eda5",
"00000000904a0265",
"000000009c0a7847",
"000000002c4c83f4",
"00000000b34368aa"
],
"0xe427a38da73c9212e4525c05d3bd7f35ead72c942a94db4e8ba0a723d57968ca": [
"a79f72adf8dd62fa",
"70063394d1df00c5",
"5f821a2304f84d60",
"1934fff4c5941ee1",
"f0f28fed86c13bbb",
"8699d6784fa0f3bc",
"1ded40c7f360855f",
"0797f696d912493b",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x1934fff4c5941ee15f821a2304f84d6070063394d1df00c5a79f72adf8dd62fa": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"8c9dd4d6dd9556e2",
"1f3ffcfcc152941f",
"cc6de1a34247b76a",
"20cb4f0747788c63",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x20cb4f0747788c63cc6de1a34247b76a1f3ffcfcc152941f8c9dd4d6dd9556e2": [
"f7edfb994664d84d",
"161450b849bf3951",
"3e9f9bb44d29def1",
"436c8be3776a6612",
"8f641912be821d66",
"9c74208e5f23cc3d",
"dff4e79c0aa1dd7a",
"a56cb315200b6a24",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x436c8be3776a66123e9f9bb44d29def1161450b849bf3951f7edfb994664d84d": [
"16dde42596b907f0",
"49015d7e991a1528",
"4a6eced6e8304885",
"305a6af4d7a8a00c",
"18c9513b1e7d9904",
"0b6835f79f991d71",
"a8751b2d01a9def9",
"5bc67a31279947d0",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x5bc67a31279947d0a8751b2d01a9def90b6835f79f991d7118c9513b1e7d9904": [
"0000000013d00000",
"00000000158e4609",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xa56cb315200b6a24dff4e79c0aa1dd7a9c74208e5f23cc3d8f641912be821d66": [
"11c8725c9dfdb98b",
"7ffbf50ba07b42f8",
"7c18430af73c190c",
"1c3e76134671e490",
"7615b40971dc29f2",
"303a082109d64fe0",
"54f2216c0b37148d",
"adb5787a1f8676b5",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xadb5787a1f8676b554f2216c0b37148d303a082109d64fe07615b40971dc29f2": [
"0000000000000002",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x0797f696d912493b1ded40c7f360855f8699d6784fa0f3bcf0f28fed86c13bbb": [
"110a54b57866d84a",
"f0ca6c839cc72757",
"49bf28cc906f15c9",
"ebe723970b832294",
"88de93befbb5aaa4",
"0daef15f24ea3e20",
"02ef2f6176cbbf97",
"69d5f78b6a46aa1d",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xebe723970b83229449bf28cc906f15c9f0ca6c839cc72757110a54b57866d84a": [
"205c77d6b9f5eefc",
"28f8d6d672c9506b",
"4eb97b136c3768b6",
"1a621134ad618159",
"087b91ec357a7455",
"d754bee55da47c70",
"6a70963d5350497d",
"fa59dd534edc21a1",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x1a621134ad6181594eb97b136c3768b628f8d6d672c9506b205c77d6b9f5eefc": [
"442f7573a4097cf2",
"3520bc74612a2ea9",
"143ce4e61bf52251",
"6d34d1e254003d2d",
"d074b8cee5dcf415",
"2346a1b4c0f390e8",
"47969c1f5a6a25b1",
"da62fdf84a21108e",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xfa59dd534edc21a16a70963d5350497dd754bee55da47c70087b91ec357a7455": [
"7160cca03ed221b2",
"347a16f174192c56",
"2a3e1067115745c2",
"5c330b93d4700b22",
"d0f98c871ca6c8bb",
"9d2c6e47bb45226f",
"a0f420d37173bb3b",
"c0d0ea06ca63701d",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xc0d0ea06ca63701da0f420d37173bb3b9d2c6e47bb45226fd0f98c871ca6c8bb": [
"00000000ddab1eff",
"00000000344276a2",
"00000000b13cfb74",
"0000000055e2ef36",
"000000008a16d30a",
"000000003066cbc5",
"00000000adb5b2e4",
"00000000fa3fc29b"
],
"0x69d5f78b6a46aa1d02ef2f6176cbbf970daef15f24ea3e2088de93befbb5aaa4": [
"66ee2be0687eea76",
"6926f8ca8796c78a",
"26179f49c434dc16",
"649e63bfe1247ba4",
"5e83cc15f9f05fa9",
"4098e44c091c5d81",
"1a9584e1365c3ec9",
"021fc6d3425cd2f6",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x021fc6d3425cd2f61a9584e1365c3ec94098e44c091c5d815e83cc15f9f05fa9": [
"00000000cad00000",
"00000000203b67bc",
"0000000000000035",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
]
}
},
{
"oldStateRoot": "0x5375f9ef9ce7262aedf64e236b8754f17fc99144f72b3113ef3030ee859fdf42",
"newStateRoot": "0x5efb129d7896f694f081751b6ebadf12fd3e5ecd72c5c4750a0fc6ffd3040fe2",
"oldAccInputHash": "0xad1915669aaf61a83bbfad1f4360c1a32245e38ffb67b1afa68c825135890fc9",
"newAccInputHash": "0xbd71644a8d0eff3d5ae771310c7f15c097dddffe7199e6db738479b01729abfe",
"newLocalExitRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"oldNumBatch": 3,
"newNumBatch": 4,
"chainID": 1000,
"forkID": 3,
"batchL2Data": "0xee01843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff883782dace9d900000808203e88080d7d8b23bdec805a0c758de3e2289077b1e4c8401ddff73c7e59e2f9351d14f5f6f04e7bbb69548737581cd5a6e3bac8695776bfd41dc1c8fc9921c4b08e008031bee02843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff8829a2241af62c0000808203e88080ee8448de88596fff6a98af2e603c9a10ccafa114815e844c8b7e8c52cb3fd1470d81101bb5a4a0aa5f459f29da0f6e95cd9e178774f0f3f1c48733618d7bd2231c",
"globalExitRoot": "0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9",
"timestamp": 1944498032,
"sequencerAddr": "0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D",
"batchHashData": "0x763d3272e66b10bef69106ea07750823c580d20802fb1141b69b5ec56790a449",
"contractsBytecode": {},
"db": {
"0x5375f9ef9ce7262aedf64e236b8754f17fc99144f72b3113ef3030ee859fdf42": [
"5f2a012357061710",
"68797b96da18cb04",
"b2be25aa971ed9c2",
"40db9304d462bb59",
"ff9698720b1fefee",
"5e28588717ba4839",
"c57cbbd60b9aa7df",
"cb0771fbcc3f3b08",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x40db9304d462bb59b2be25aa971ed9c268797b96da18cb045f2a012357061710": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"142fc01a956c3463",
"be69ad1a253d3495",
"db729746cae0ab3f",
"63a5f19fe61eb681",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x63a5f19fe61eb681db729746cae0ab3fbe69ad1a253d3495142fc01a956c3463": [
"3d61dea9d955d14f",
"418f230b0eca6a26",
"2dc6e0d935073bc7",
"6dc8f1680d75b260",
"b30dcf0715a61eb6",
"9c8fc0955512deb6",
"eb14d58ffdc22f3d",
"7440e84e360f5f54",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x6dc8f1680d75b2602dc6e0d935073bc7418f230b0eca6a263d61dea9d955d14f": [
"02aa707693b30b9c",
"6cfec68811f9460f",
"5180f56a58386690",
"bcc3f4d2c3de39da",
"dcbf582c45d40023",
"3d624ea98acbd28d",
"389a62ede52eb5f2",
"df13ab17f1953de6",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xdf13ab17f1953de6389a62ede52eb5f23d624ea98acbd28ddcbf582c45d40023": [
"00000000cfd09878",
"0000000099f9ccaa",
"00000000362076ff",
"00000000b8d0520f",
"00000000a032c705",
"00000000adc062b4",
"0000000018b835c8",
"00000000436dfa33"
],
"0x7440e84e360f5f54eb14d58ffdc22f3d9c8fc0955512deb6b30dcf0715a61eb6": [
"0015516f5bc5291b",
"eb1c9bd0ad5b63de",
"82f90a2e3119c366",
"4fc91d2689836a49",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x4fc91d2689836a4982f90a2e3119c366eb1c9bd0ad5b63de0015516f5bc5291b": [
"a9cc526a347bc157",
"125a290166bdae30",
"1ae9909ea83d5545",
"2cd4ccf12490921c",
"35053cb6d7619674",
"c4efbb809df37e47",
"cc441aea8abebc59",
"6c6f302a0411f8ef",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x2cd4ccf12490921c1ae9909ea83d5545125a290166bdae30a9cc526a347bc157": [
"2da3fe1fe83f2616",
"48e96847312823e8",
"258649e03dbf05e4",
"404c4132b4f35a14",
"0e27e92c28029aec",
"98e0c826ec17d37a",
"65597af607e1af8b",
"ace107db9858f320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xace107db9858f32065597af607e1af8b98e0c826ec17d37a0e27e92c28029aec": [
"0000000073e6af6f",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x6c6f302a0411f8efcc441aea8abebc59c4efbb809df37e4735053cb6d7619674": [
"94c9b6c977fd6b62",
"63d51a718f1d4595",
"0af1d54b83576182",
"e7ead4cf47b5f6a7",
"e8c5ee51a8a70872",
"4d0f7cadff43d8b2",
"186417a11c584386",
"190ce122ca70915e",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xe7ead4cf47b5f6a70af1d54b8357618263d51a718f1d459594c9b6c977fd6b62": [
"37866cdbe5877c88",
"2d62534cb92ee170",
"6bf2c9fd6a3aa16c",
"325ba19f4ab866aa",
"cad6cdbfa198be91",
"cd0e941b466a39b4",
"a7e6b3f9d3f197d5",
"a2135065f1605059",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xa2135065f1605059a7e6b3f9d3f197d5cd0e941b466a39b4cad6cdbfa198be91": [
"0000000000000003",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x190ce122ca70915e186417a11c5843864d0f7cadff43d8b2e8c5ee51a8a70872": [
"20284081553655e3",
"280e11c9e7886afe",
"083dff83bd06d987",
"77c2e46005223573",
"c4fe80ece2496840",
"247e4a8208f142c3",
"1ab5e319efbcada7",
"51d2faa706019320",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x51d2faa7060193201ab5e319efbcada7247e4a8208f142c3c4fe80ece2496840": [
"00000000d806bada",
"00000000d3bcbbe4",
"00000000b8ec425c",
"0000000027b4eda5",
"00000000904a0265",
"000000009c0a7847",
"000000002c4c83f4",
"00000000b34368aa"
],
"0xcb0771fbcc3f3b08c57cbbd60b9aa7df5e28588717ba4839ff9698720b1fefee": [
"34da81428536c512",
"c3ea5274443c61e4",
"3ad6e4f97fb5b40e",
"ba9231c064de477a",
"bdf0772825a32f6b",
"ce5ad8db3ee53d9a",
"71442de10bf6aec5",
"7292bc7097e10bb9",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xba9231c064de477a3ad6e4f97fb5b40ec3ea5274443c61e434da81428536c512": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"05f1b648dd3089bc",
"55d95f629ea0bda4",
"91d548725e9b7683",
"8825f28fdf088760",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x8825f28fdf08876091d548725e9b768355d95f629ea0bda405f1b648dd3089bc": [
"9e0f700ca62c18b6",
"ad28cf8357819cbe",
"5755654f95ffb169",
"080d700756d33b65",
"7cd0f89258c6847c",
"1506ba587b488b41",
"985116650c6343c9",
"25fbaa55e7423f1a",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x080d700756d33b655755654f95ffb169ad28cf8357819cbe9e0f700ca62c18b6": [
"16dde42596b907f0",
"49015d7e991a1528",
"4a6eced6e8304885",
"305a6af4d7a8a00c",
"90e98ea2a6fee2b3",
"09b4104e4b0f1f42",
"3c656749691cf3af",
"4b1d11a97ceff72f",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x4b1d11a97ceff72f3c656749691cf3af09b4104e4b0f1f4290e98ea2a6fee2b3": [
"00000000c5080000",
"00000000f9ccd8a1",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x25fbaa55e7423f1a985116650c6343c91506ba587b488b417cd0f89258c6847c": [
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"bf709ad680979c29",
"11a664db1b55ed37",
"f487fc81d1981265",
"ddd2839df8cf0325",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xddd2839df8cf0325f487fc81d198126511a664db1b55ed37bf709ad680979c29": [
"e37ad7988d2734e1",
"b48ff1a2d485bf8b",
"b8479aa634e0301f",
"02299c9ad4460ab2",
"7bda68490ad6845b",
"3f2ea5c54d354ff8",
"ed9bf6f50bfbd963",
"af04d8fb37d93812",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x02299c9ad4460ab2b8479aa634e0301fb48ff1a2d485bf8be37ad7988d2734e1": [
"08e4392e4efedcc5",
"3ffdfa85d03da17c",
"7c18430af73c190c",
"1c3e76134671e490",
"a27c610b929c5373",
"d822841a75cff991",
"d5fd2275e32107a8",
"eebb7a70544ab6b3",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xeebb7a70544ab6b3d5fd2275e32107a8d822841a75cff991a27c610b929c5373": [
"0000000000000004",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xaf04d8fb37d93812ed9bf6f50bfbd9633f2ea5c54d354ff87bda68490ad6845b": [
"25fd1dddf4354e7c",
"2b1a0e19c11639a0",
"4b2ece4042b09d92",
"58a82e472c5b400b",
"c079c081a5d59f62",
"97ef46ab04cbcc81",
"d1fd675ca9eccdbd",
"1189d2b093196c75",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x1189d2b093196c75d1fd675ca9eccdbd97ef46ab04cbcc81c079c081a5d59f62": [
"00000000f3c5a36e",
"00000000f31e3261",
"00000000a7a6e780",
"0000000075934b04",
"00000000eed08254",
"00000000145e173b",
"00000000b2d4951a",
"00000000ffb440e0"
],
"0x7292bc7097e10bb971442de10bf6aec5ce5ad8db3ee53d9abdf0772825a32f6b": [
"110a54b57866d84a",
"f0ca6c839cc72757",
"49bf28cc906f15c9",
"ebe723970b832294",
"2b6429747753e992",
"8de937d8391bc07c",
"0ba2467e6b2a03b8",
"43ce72d79779e851",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xebe723970b83229449bf28cc906f15c9f0ca6c839cc72757110a54b57866d84a": [
"205c77d6b9f5eefc",
"28f8d6d672c9506b",
"4eb97b136c3768b6",
"1a621134ad618159",
"087b91ec357a7455",
"d754bee55da47c70",
"6a70963d5350497d",
"fa59dd534edc21a1",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x1a621134ad6181594eb97b136c3768b628f8d6d672c9506b205c77d6b9f5eefc": [
"442f7573a4097cf2",
"3520bc74612a2ea9",
"143ce4e61bf52251",
"6d34d1e254003d2d",
"d074b8cee5dcf415",
"2346a1b4c0f390e8",
"47969c1f5a6a25b1",
"da62fdf84a21108e",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xda62fdf84a21108e47969c1f5a6a25b12346a1b4c0f390e8d074b8cee5dcf415": [
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xfa59dd534edc21a16a70963d5350497dd754bee55da47c70087b91ec357a7455": [
"7160cca03ed221b2",
"347a16f174192c56",
"2a3e1067115745c2",
"5c330b93d4700b22",
"d0f98c871ca6c8bb",
"9d2c6e47bb45226f",
"a0f420d37173bb3b",
"c0d0ea06ca63701d",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0xc0d0ea06ca63701da0f420d37173bb3b9d2c6e47bb45226fd0f98c871ca6c8bb": [
"00000000ddab1eff",
"00000000344276a2",
"00000000b13cfb74",
"0000000055e2ef36",
"000000008a16d30a",
"000000003066cbc5",
"00000000adb5b2e4",
"00000000fa3fc29b"
],
"0x43ce72d79779e8510ba2467e6b2a03b88de937d8391bc07c2b6429747753e992": [
"66ee2be0687eea76",
"6926f8ca8796c78a",
"26179f49c434dc16",
"649e63bfe1247ba4",
"67bbfffd0982dfa4",
"93c661c88448b5ed",
"cec2687622ccc942",
"4e709c8e17913749",
"0000000000000001",
"0000000000000000",
"0000000000000000",
"0000000000000000"
],
"0x4e709c8e17913749cec2687622ccc94293c661c88448b5ed67bbfffd0982dfa4": [
"0000000019980000",
"000000003bfcd524",
"0000000000000035",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000",
"0000000000000000"
]
}
}
],
"oldStateRoot": "0x3ca39a7b5b419d1c50c89a8d15d1234f6cbc8baadb465efb609832bbc19f9026",
"oldAccInputHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"oldNumBatch": 0,
"newStateRoot": "0x5efb129d7896f694f081751b6ebadf12fd3e5ecd72c5c4750a0fc6ffd3040fe2",
"newAccInputHash": "0xbd71644a8d0eff3d5ae771310c7f15c097dddffe7199e6db738479b01729abfe",
"newLocalExitRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"newNumBatch": 4,
"chainID": 1000,
"forkID": 3,
"aggregatorAddress": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"inputSnark": "0x2b4cee8b70441373144781576ba4d09df1c259eef85f73585b373d7337621a5a"
}
\ No newline at end of file
{
"curve": "bn128",
"evaluations": {
"a": "9702969745918705481839805950921709589337567271136858610573534676949744842267",
"b": "341902451305098325588274536638232982500645316303847667311699639028281007546",
"c": "10089136329707562787095186991940399671235868281107556829251231305232728679682",
"inv": "1955317095964997532096759377382751305143448868916056378222243512469054113842",
"qc": "10782279106031138244634232032817369339714783360582741873805912278948357157586",
"ql": "21251808300522045537904185777972611020866727049420529442210109963857906214889",
"qm": "17471054534374682175982394929866179474101758026949454352441573981958345011791",
"qo": "593458529869580986585382253403246015645108630200332957766491007433366200564",
"qr": "14857681067819006905475333449198913926111918296903865133508595828620835725428",
"s1": "3807827801268525577391775311558443473982829962972149818537946317394078789278",
"s2": "4397320204995723396306594522728505487798639077199002369395380144177013324052",
"s3": "18332001246779002778528499238127440740157760469206669940377231761041608865451",
"t1w": "3783166700504360214709754445949688701744393960851118042830956743923382253816",
"t2w": "13996969915734692472195789214718626509129048070536208858437857877922069792469",
"z": "8541406622232730765144154114534482432327357919465646008346907097038763678566",
"zw": "1832633582965391321323432315063604337854235240731281923564364630154302008895"
},
"polynomials": {
"C1": [
"5320383136707118150961589735727057893618792443592325268991891786568049186597",
"3429053040128136166640053209131079617911025054412502995806068449238317388495",
"1"
],
"C2": [
"13110027018027162687869061215276171550037210425611115274025292428135814498280",
"19744765739594594951498703331610475091676541995387229955171858316234990841770",
"1"
],
"W1": [
"19953864813013383811791292897136363698413701896013021750839490990469478344731",
"12225282668357773750977460317101465492913810465324122457193920924888880034763",
"1"
],
"W2": [
"9044609927797983462416846529925883781292057530242351839556328732192193714074",
"11917256847008706478816281118166319202875650041166749915284162189072588726279",
"1"
]
},
"protocol": "fflonk"
}
\ No newline at end of file
[
"5105835634000876263592828603732612898759495378504810447268373845312293495935"
]
\ No newline at end of file
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
describe('Polygon ZK-EVM snark stark input test', () => {
let cdkValidiumContract;
const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
let randomSigner;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const chainID = 1000;
const networkName = 'cdk-validium';
const version = '0.0.1';
const forkID = 0;
const batchL2Data = '0xee80843b9aca00830186a0944d5cf5032b2a844602278b01199ed191a86c93ff88016345785d8a0000808203e880801cee7e01dc62f69a12c3510c6d64de04ee6346d84b6a017f3e786c7d87f963e75d8cc91fa983cd6d9cf55fff80d73bd26cd333b0f098acc1e58edb1fd484ad731b';
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[randomSigner] = await ethers.getSigners();
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
randomSigner.address,
randomSigner.address,
randomSigner.address,
randomSigner.address,
randomSigner.address,
chainID,
0,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
await cdkValidiumContract.initialize(
{
admin: randomSigner.address,
trustedSequencer: randomSigner.address,
pendingStateTimeout: 0,
trustedAggregator: randomSigner.address,
trustedAggregatorTimeout: 0,
requiredAmountOfMembers: 0,
requiredAmountOfSignatures: 0,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
await cdkValidiumContract.deployed();
});
it('Check Accumualte input Hash', async () => {
const oldAccInputHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
const globalExitRoot = '0x090bcaf734c4f06c93954a827b45a6e8c67b8e0fd1e0a35a1c5982d6961828f9';
const timestamp = 1944498031;
const sequencerAddr = '0x617b3a3528F9cDd6630fd3301B9c8911F7Bf063D';
const expectedStarkHashExecutor = '0x704d5cfd3e44b82028f7f8cae31168267a7422c5a447b90a65134116da5a8432';
const batchL2DataHash = contractUtils.calculateBatchHashData(batchL2Data);
const accumulateInputHashJs = await contractUtils.calculateAccInputHash(
oldAccInputHash,
batchL2DataHash,
globalExitRoot,
timestamp,
sequencerAddr,
);
const accumulateInputHashSC = await cdkValidiumContract.calculateAccInputHash(
oldAccInputHash,
batchL2Data,
globalExitRoot,
timestamp,
sequencerAddr,
);
expect(accumulateInputHashJs).to.be.equal(accumulateInputHashSC);
expect(accumulateInputHashSC).to.be.equal(expectedStarkHashExecutor);
});
it('Check commonjs unit test', async () => {
// Unit test taken from https://github.com/0xPolygonHermez/zkevm-commonjs/blob/main/test/contract-utils.test.js#L16
const oldStateRoot = '0x2dc4db4293af236cb329700be43f08ace740a05088f8c7654736871709687e90';
const newStateRoot = '0xbff23fc2c168c033aaac77503ce18f958e9689d5cdaebb88c5524ce5c0319de3';
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000000';
const oldAccInputHash = '0x0000000000000000000000000000000000000000000000000000000000000000';
const newAccInputHash = '0x2c9d2c1b2ed8e4be0719f443235c3483f8d6288c6d057859e7210fe39acce682';
const oldNumBatch = 0;
const newNumBatch = 1;
const aggregatorAddress = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266';
const expectedSnarkInputHash = '14018390222040434090025131340942647213193155362626068451264286945439322107665';
const lastPendingStateConsolidated = 0;
const sequencedTimestamp = 999;
// set smart contract with correct parameters
await cdkValidiumContract.setStateRoot(oldStateRoot, oldNumBatch);
await cdkValidiumContract.setSequencedBatches(newNumBatch, newAccInputHash, sequencedTimestamp, lastPendingStateConsolidated);
await cdkValidiumContract.setSequencedBatch(1);
await ethers.provider.send('hardhat_impersonateAccount', [aggregatorAddress]);
const aggregator = await ethers.getSigner(aggregatorAddress);
await randomSigner.sendTransaction({
to: aggregatorAddress,
value: ethers.utils.parseEther('4'),
});
// Compute SC input
const pendingStateNum = 0;
const inputSnarkSC = await cdkValidiumContract.connect(aggregator).getNextSnarkInput(
pendingStateNum,
oldNumBatch,
newNumBatch,
newLocalExitRoot,
newStateRoot,
);
// Compute Js input
const inputSnarkJS = await contractUtils.calculateSnarkInput(
oldStateRoot,
newStateRoot,
newLocalExitRoot,
oldAccInputHash,
newAccInputHash,
oldNumBatch,
newNumBatch,
chainID,
aggregatorAddress,
forkID,
);
expect(inputSnarkSC).to.be.equal(inputSnarkJS);
expect(inputSnarkSC).to.be.equal(expectedSnarkInputHash);
});
});
/* eslint-disable no-plusplus, no-await-in-loop */
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
const { contractUtils } = require('@0xpolygonhermez/zkevm-commonjs');
const { calculateSnarkInput, calculateAccInputHash, calculateBatchHashData } = contractUtils;
describe('CDKValidium', () => {
let deployer;
let trustedAggregator;
let trustedSequencer;
let admin;
let aggregator1;
let verifierContract;
let PolygonZkEVMBridgeContract;
let cdkValidiumContract;
let cdkDataCommitteeContract;
let maticTokenContract;
let PolygonZkEVMGlobalExitRoot;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const networkIDMainnet = 0;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const chainID = 1000;
const networkName = 'cdk-validium';
const version = '0.0.1';
const forkID = 0;
const pendingStateTimeoutDefault = 100;
const trustedAggregatorTimeoutDefault = 10;
let firstDeployment = true;
// CDKValidium Constants
const FORCE_BATCH_TIMEOUT = 60 * 60 * 24 * 5; // 5 days
const MAX_BATCH_MULTIPLIER = 12;
const HALT_AGGREGATION_TIMEOUT = 60 * 60 * 24 * 7; // 7 days
const _MAX_VERIFY_BATCHES = 1000;
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, trustedAggregator, trustedSequencer, admin, aggregator1] = await ethers.getSigners();
// deploy mock verifier
const VerifierRollupHelperFactory = await ethers.getContractFactory(
'VerifierRollupHelperMock',
);
verifierContract = await VerifierRollupHelperFactory.deploy();
// deploy MATIC
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
/*
* deploy global exit root manager
* In order to not have trouble with nonce deploy first proxy admin
*/
await upgrades.deployProxyAdmin();
if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') {
firstDeployment = false;
}
const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2);
const nonceProxyCommittee = nonceProxyBridge + (firstDeployment ? 2 : 1);
// Always have to redeploy impl since the PolygonZkEVMGlobalExitRoot address changes
const nonceProxyCDKValidium = nonceProxyCommittee + 2;
const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge });
const precalculateCommitteeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCommittee });
const precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
firstDeployment = false;
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, precalculateBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
// deploy PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
PolygonZkEVMBridgeContract = await upgrades.deployProxy(PolygonZkEVMBridgeFactory, [], { initializer: false });
// deploy CDKDataCommittee
const cdkDataCommitteeFactory = await ethers.getContractFactory('CDKDataCommittee');
cdkDataCommitteeContract = await upgrades.deployProxy(
cdkDataCommitteeFactory,
[],
{ initializer: false },
);
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
forkID,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
expect(precalculateBridgeAddress).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(precalculateCommitteeAddress).to.be.equal(cdkDataCommitteeContract.address);
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
await PolygonZkEVMBridgeContract.initialize(networkIDMainnet, PolygonZkEVMGlobalExitRoot.address, cdkValidiumContract.address);
await cdkValidiumContract.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
await cdkDataCommitteeContract.initialize();
const expectedHash = ethers.utils.solidityKeccak256(['bytes'], [[]]);
await expect(cdkDataCommitteeContract.connect(deployer)
.setupCommittee(0, [], []))
.to.emit(cdkDataCommitteeContract, 'CommitteeUpdated')
.withArgs(expectedHash);
// fund sequencer address with Matic tokens
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('1000'));
});
it('should check the constructor parameters', async () => {
expect(await cdkValidiumContract.globalExitRootManager()).to.be.equal(PolygonZkEVMGlobalExitRoot.address);
expect(await cdkValidiumContract.matic()).to.be.equal(maticTokenContract.address);
expect(await cdkValidiumContract.rollupVerifier()).to.be.equal(verifierContract.address);
expect(await cdkValidiumContract.bridgeAddress()).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(await cdkValidiumContract.owner()).to.be.equal(deployer.address);
expect(await cdkValidiumContract.admin()).to.be.equal(admin.address);
expect(await cdkValidiumContract.chainID()).to.be.equal(chainID);
expect(await cdkValidiumContract.trustedSequencer()).to.be.equal(trustedSequencer.address);
expect(await cdkValidiumContract.pendingStateTimeout()).to.be.equal(pendingStateTimeoutDefault);
expect(await cdkValidiumContract.trustedAggregator()).to.be.equal(trustedAggregator.address);
expect(await cdkValidiumContract.trustedAggregatorTimeout()).to.be.equal(trustedAggregatorTimeoutDefault);
expect(await cdkValidiumContract.batchNumToStateRoot(0)).to.be.equal(genesisRoot);
expect(await cdkValidiumContract.trustedSequencerURL()).to.be.equal(urlSequencer);
expect(await cdkValidiumContract.networkName()).to.be.equal(networkName);
expect(await cdkValidiumContract.batchFee()).to.be.equal(ethers.utils.parseEther('0.1'));
expect(await cdkValidiumContract.batchFee()).to.be.equal(ethers.utils.parseEther('0.1'));
expect(await cdkValidiumContract.getForcedBatchFee()).to.be.equal(ethers.utils.parseEther('10'));
expect(await cdkValidiumContract.forceBatchTimeout()).to.be.equal(FORCE_BATCH_TIMEOUT);
expect(await cdkValidiumContract.isForcedBatchDisallowed()).to.be.equal(true);
});
it('should check initialize function', async () => {
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
const cdkValidiumContractInitialize = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
forkID,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
await expect(cdkValidiumContractInitialize.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: HALT_AGGREGATION_TIMEOUT + 1,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
)).to.be.revertedWith('PendingStateTimeoutExceedHaltAggregationTimeout');
await expect(cdkValidiumContractInitialize.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: HALT_AGGREGATION_TIMEOUT + 1,
},
genesisRoot,
urlSequencer,
networkName,
version,
)).to.be.revertedWith('TrustedAggregatorTimeoutExceedHaltAggregationTimeout');
await expect(
cdkValidiumContractInitialize.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
),
).to.emit(cdkValidiumContractInitialize, 'UpdateZkEVMVersion').withArgs(0, forkID, version);
});
it('should check setters of admin', async () => {
expect(await cdkValidiumContract.trustedSequencer()).to.be.equal(trustedSequencer.address);
expect(await cdkValidiumContract.trustedSequencerURL()).to.be.equal(urlSequencer);
expect(await cdkValidiumContract.trustedAggregator()).to.be.equal(trustedAggregator.address);
expect(await cdkValidiumContract.trustedAggregatorTimeout()).to.be.equal(trustedAggregatorTimeoutDefault);
expect(await cdkValidiumContract.pendingStateTimeout()).to.be.equal(pendingStateTimeoutDefault);
expect(await cdkValidiumContract.admin()).to.be.equal(admin.address);
// setTrustedSequencer
await expect(cdkValidiumContract.setTrustedSequencer(deployer.address))
.to.be.revertedWith('OnlyAdmin');
await expect(
cdkValidiumContract.connect(admin).setTrustedSequencer(deployer.address),
).to.emit(cdkValidiumContract, 'SetTrustedSequencer').withArgs(deployer.address);
expect(await cdkValidiumContract.trustedSequencer()).to.be.equal(deployer.address);
// setTrustedSequencerURL
const url = 'https://test';
await expect(cdkValidiumContract.setTrustedSequencerURL(url))
.to.be.revertedWith('OnlyAdmin');
await expect(
cdkValidiumContract.connect(admin).setTrustedSequencerURL(url),
).to.emit(cdkValidiumContract, 'SetTrustedSequencerURL').withArgs(url);
expect(await cdkValidiumContract.trustedSequencerURL()).to.be.equal(url);
// setTrustedAggregator
const newTrustedAggregator = deployer.address;
await expect(cdkValidiumContract.setTrustedAggregator(newTrustedAggregator))
.to.be.revertedWith('OnlyAdmin');
await expect(
cdkValidiumContract.connect(admin).setTrustedAggregator(newTrustedAggregator),
).to.emit(cdkValidiumContract, 'SetTrustedAggregator').withArgs(newTrustedAggregator);
expect(await cdkValidiumContract.trustedAggregator()).to.be.equal(newTrustedAggregator);
// setTrustedAggregatorTimeout
await expect(cdkValidiumContract.setTrustedAggregatorTimeout(trustedAggregatorTimeoutDefault))
.to.be.revertedWith('OnlyAdmin');
await expect(cdkValidiumContract.connect(admin).setTrustedAggregatorTimeout(HALT_AGGREGATION_TIMEOUT + 1))
.to.be.revertedWith('TrustedAggregatorTimeoutExceedHaltAggregationTimeout');
await expect(cdkValidiumContract.connect(admin).setTrustedAggregatorTimeout(trustedAggregatorTimeoutDefault))
.to.be.revertedWith('NewTrustedAggregatorTimeoutMustBeLower');
const newTrustedAggregatorTimeout = trustedAggregatorTimeoutDefault - 1;
await expect(
cdkValidiumContract.connect(admin).setTrustedAggregatorTimeout(newTrustedAggregatorTimeout),
).to.emit(cdkValidiumContract, 'SetTrustedAggregatorTimeout').withArgs(newTrustedAggregatorTimeout);
expect(await cdkValidiumContract.trustedAggregatorTimeout()).to.be.equal(newTrustedAggregatorTimeout);
// setPendingStateTimeoutDefault
await expect(cdkValidiumContract.setPendingStateTimeout(pendingStateTimeoutDefault))
.to.be.revertedWith('OnlyAdmin');
await expect(cdkValidiumContract.connect(admin).setPendingStateTimeout(HALT_AGGREGATION_TIMEOUT + 1))
.to.be.revertedWith('PendingStateTimeoutExceedHaltAggregationTimeout');
await expect(cdkValidiumContract.connect(admin).setPendingStateTimeout(pendingStateTimeoutDefault))
.to.be.revertedWith('NewPendingStateTimeoutMustBeLower');
const newPendingStateTimeoutDefault = pendingStateTimeoutDefault - 1;
await expect(
cdkValidiumContract.connect(admin).setPendingStateTimeout(newPendingStateTimeoutDefault),
).to.emit(cdkValidiumContract, 'SetPendingStateTimeout').withArgs(newPendingStateTimeoutDefault);
expect(await cdkValidiumContract.pendingStateTimeout()).to.be.equal(newPendingStateTimeoutDefault);
// setMultiplierBatchFee
const newMultiplierBatchFee = 1023;
await expect(cdkValidiumContract.connect(admin).setMultiplierBatchFee(newMultiplierBatchFee + 1))
.to.be.revertedWith('InvalidRangeMultiplierBatchFee');
await expect(
cdkValidiumContract.connect(admin).setMultiplierBatchFee(newMultiplierBatchFee),
).to.emit(cdkValidiumContract, 'SetMultiplierBatchFee').withArgs(newMultiplierBatchFee);
expect(await cdkValidiumContract.multiplierBatchFee()).to.be.equal(newMultiplierBatchFee);
// setVerifyBatchTimeTarget
const newVerifyBatchTimeTarget = 100;
await expect(cdkValidiumContract.connect(admin).setVerifyBatchTimeTarget(60 * 60 * 24 + 1)) // more than 1 day
.to.be.revertedWith('InvalidRangeBatchTimeTarget');
await expect(
cdkValidiumContract.connect(admin).setVerifyBatchTimeTarget(newVerifyBatchTimeTarget),
).to.emit(cdkValidiumContract, 'SetVerifyBatchTimeTarget').withArgs(newVerifyBatchTimeTarget);
expect(await cdkValidiumContract.verifyBatchTimeTarget()).to.be.equal(newVerifyBatchTimeTarget);
// setPendingStateTimeoutDefault
const newForceBatchTimeout = 0;
await expect(cdkValidiumContract.setForceBatchTimeout(newForceBatchTimeout))
.to.be.revertedWith('OnlyAdmin');
await expect(cdkValidiumContract.connect(admin).setForceBatchTimeout(HALT_AGGREGATION_TIMEOUT + 1))
.to.be.revertedWith('InvalidRangeForceBatchTimeout');
await expect(cdkValidiumContract.connect(admin).setForceBatchTimeout(FORCE_BATCH_TIMEOUT))
.to.be.revertedWith('InvalidRangeForceBatchTimeout');
await expect(
cdkValidiumContract.connect(admin).setForceBatchTimeout(newForceBatchTimeout),
).to.emit(cdkValidiumContract, 'SetForceBatchTimeout').withArgs(newForceBatchTimeout);
expect(await cdkValidiumContract.forceBatchTimeout()).to.be.equal(newForceBatchTimeout);
// Activate force batches
await expect(cdkValidiumContract.activateForceBatches())
.to.be.revertedWith('OnlyAdmin');
// Check force batches are unactive
await expect(cdkValidiumContract.forceBatch('0x', 0))
.to.be.revertedWith('ForceBatchNotAllowed');
await expect(cdkValidiumContract.sequenceForceBatches([]))
.to.be.revertedWith('ForceBatchNotAllowed');
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
await expect(cdkValidiumContract.connect(admin).activateForceBatches())
.to.be.revertedWith('ForceBatchesAlreadyActive');
expect(await cdkValidiumContract.isForcedBatchDisallowed()).to.be.equal(false);
// Transfer admin role
// First set pending Admin
expect(await cdkValidiumContract.pendingAdmin()).to.be.equal(ethers.constants.AddressZero);
await expect(cdkValidiumContract.transferAdminRole(deployer.address))
.to.be.revertedWith('OnlyAdmin');
await expect(
cdkValidiumContract.connect(admin).transferAdminRole(deployer.address),
).to.emit(cdkValidiumContract, 'TransferAdminRole').withArgs(deployer.address);
expect(await cdkValidiumContract.pendingAdmin()).to.be.equal(deployer.address);
// Accept transfer admin
expect(await cdkValidiumContract.admin()).to.be.equal(admin.address);
await expect(cdkValidiumContract.connect(admin).acceptAdminRole())
.to.be.revertedWith('OnlyPendingAdmin');
await expect(
cdkValidiumContract.connect(deployer).acceptAdminRole(),
).to.emit(cdkValidiumContract, 'AcceptAdminRole').withArgs(deployer.address);
expect(await cdkValidiumContract.admin()).to.be.equal(deployer.address);
});
it('should check state roots inside prime', async () => {
const validRoots = [
'0x02959FFA45214AF690A3730806D4F59F7056CCC449373BBE42C20765D3996CA1',
'0x7E680781BF155C4682C7D431E86DAFD1DD986BE664A7256879CB2B33391E2DAC',
'0xD710F3A64598F4C6C94E2A5F3F6193B4FBADBF5A5DBAFEBD0A75277E27E2BCD3',
'0x048F3F2D4430DAF38E3CC891853C9BB102E5880E1ADA799554C7ED392B4BD7F3',
'0x7E680781BF155C4682C7D431E86DAFD1DD986BE664A7256879CB2B33391E2DAC',
'0xFFFFFFFE45214AF690A3730806D4F59F7056CCC449373BBE42C20765D3996CA1',
'0x7E680781BF155C4FFFFFFFF1E86DAFD1DD986BE664A7256879CB2B33391E2DAC',
'0xFFFFFFFF00000000C94E2A5F3F6193B4FBADBF5A5DBAFEBD0A75277E27E2BCD3',
'0x048F3F2D4430DAF3FFFFFFFF0000000002E5880E1ADA799554C7ED392B4BD7F3',
'0x7E680781BF155C4682C7D431E86DAFD1FFFFFFFF0000000079CB2B33391E2DAC',
'0x02959FFA45214AF690A3730806D4F59F7056CCC449373BBEFFFFFFFF00000000',
'0xFFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000',
];
const aliasInvalidRoots = ['0xFFFFFFFF45214AF690A3730806D4F59F7056CCC449373BBE42C20765D3996CA1',
'0x7E680781BF155C46FFFFFFFFE86DAFD1DD986BE664A7256879CB2B33391E2DAC',
'0xD710F3A64598F4C6C94E2A5F3F6193B4FFFFFFFF5DBAFEBD0A75277E27E2BCD3',
'0x048F3F2D4430DAF38E3CC891853C9BB102E5880E1ADA7995FFFFFFFF2B4BD7F3',
'0xFFFFFFFFBF155C4682C7D431E86DAFD1FFFFFFFF64A7256879CB2B33391E2DAC',
];
for (let i = 0; i < validRoots.length; i++) {
expect(await cdkValidiumContract.checkStateRootInsidePrime(validRoots[i])).to.be.equal(true);
}
for (let i = 0; i < aliasInvalidRoots.length; i++) {
expect(await cdkValidiumContract.checkStateRootInsidePrime(aliasInvalidRoots[i])).to.be.equal(false);
}
});
it('should sequence a batch as trusted sequencer', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// revert because sender is not truested sequencer
await expect(cdkValidiumContract.sequenceBatches([sequence], trustedSequencer.address, []))
.to.be.revertedWith('OnlyTrustedSequencer');
// revert because tokens were not approved
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.be.revertedWith('ERC20: insufficient allowance');
const initialOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Test sequence batches errors
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([], trustedSequencer.address, []))
.to.be.revertedWith('SequenceZeroBatches');
sequence.globalExitRoot = ethers.constants.MaxUint256;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.be.revertedWith('GlobalExitRootNotExist');
sequence.globalExitRoot = ethers.constants.HashZero;
// Sequence batch
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], deployer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
const sequencedTimestamp = (await ethers.provider.getBlock()).timestamp;
const finalOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmount)),
);
// Check batch mapping
const sequencedBatchData = await cdkValidiumContract.sequencedBatches(1);
const batchAccInputHash = sequencedBatchData.accInputHash;
const batchAccInputHashJs = calculateAccInputHash(
(await cdkValidiumContract.sequencedBatches(0)).accInputHash,
transactionsHash,
sequence.globalExitRoot,
sequence.timestamp,
deployer.address,
);
expect(batchAccInputHash).to.be.equal(batchAccInputHashJs);
expect(sequencedBatchData.sequencedTimestamp).to.be.equal(sequencedTimestamp);
expect(sequencedBatchData.previousLastBatchSequenced).to.be.equal(0);
});
it('sequenceBatches should sequence multiple batches', async () => {
const l2txData = '0x1234';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = (await cdkValidiumContract.batchFee()).mul(2);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const sequence2 = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const initialOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Sequence batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 2);
const finalOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmount)),
);
// Check batch mapping
const sequencedBatchData = await cdkValidiumContract.sequencedBatches(1);
const batchAccInputHash = sequencedBatchData.accInputHash;
// Only last batch is added to the mapping
expect(batchAccInputHash).to.be.equal(ethers.constants.HashZero);
const sequencedBatchData2 = await cdkValidiumContract.sequencedBatches(2);
const batchAccInputHash2 = sequencedBatchData2.accInputHash;
// Calcultate input Hahs for batch 1
let batchAccInputHashJs = calculateAccInputHash(
ethers.constants.HashZero,
sequence.transactionsHash,
sequence.globalExitRoot,
sequence.timestamp,
trustedSequencer.address,
);
// Calcultate input Hahs for batch 2
batchAccInputHashJs = calculateAccInputHash(
batchAccInputHashJs,
sequence2.transactionsHash,
sequence2.globalExitRoot,
sequence2.timestamp,
trustedSequencer.address,
);
expect(batchAccInputHash2).to.be.equal(batchAccInputHashJs);
});
it('force batches through smart contract', async () => {
const l2txDataForceBatch = '0x123456';
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
// deploy sender SC
const sendDataFactory = await ethers.getContractFactory('SendData');
const sendDataContract = await sendDataFactory.deploy();
await sendDataContract.deployed();
// transfer matic
await maticTokenContract.transfer(sendDataContract.address, ethers.utils.parseEther('1000'));
// Approve matic
const approveTx = await maticTokenContract.populateTransaction.approve(cdkValidiumContract.address, maticAmount);
await sendDataContract.sendData(approveTx.to, approveTx.data);
// Activate forced batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
// Force batch
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()) + 1;
const forceBatchTx = await cdkValidiumContract.populateTransaction.forceBatch(l2txDataForceBatch, maticAmount);
await expect(sendDataContract.sendData(forceBatchTx.to, forceBatchTx.data))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, sendDataContract.address, l2txDataForceBatch);
});
it('sequenceBatches should sequence multiple batches and force batches', async () => {
const l2txDataForceBatch = '0x123456';
const transactionsHashForceBatch = calculateBatchHashData(l2txDataForceBatch);
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()) + 1;
// Activate forced batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
// Force batch
await expect(cdkValidiumContract.forceBatch(l2txDataForceBatch, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, deployer.address, '0x');
// sequence 2 batches
const l2txData = '0x1234';
const transactionsHash2 = calculateBatchHashData(l2txData);
const maticAmountSequence = (await cdkValidiumContract.batchFee()).mul(1);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash: transactionsHashForceBatch,
globalExitRoot: lastGlobalExitRoot,
timestamp: currentTimestamp,
minForcedTimestamp: currentTimestamp,
};
const sequence2 = {
transactionsHash: transactionsHash2,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const initialOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmountSequence),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Assert that the timestamp requirements must accomplish with force batches too
sequence.minForcedTimestamp += 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('ForcedDataDoesNotMatch');
sequence.minForcedTimestamp -= 1;
sequence.timestamp -= 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampBelowForcedTimestamp');
sequence.timestamp += 1;
sequence.timestamp = currentTimestamp + 10;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
sequence.timestamp = currentTimestamp;
sequence2.timestamp -= 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
sequence2.timestamp += 1;
// Sequence Bathces
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(Number(lastBatchSequenced) + 2);
const sequencedTimestamp = (await ethers.provider.getBlock()).timestamp;
const finalOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmountSequence)),
);
// Check batch mapping
const batchAccInputHash = (await cdkValidiumContract.sequencedBatches(1)).accInputHash;
// Only last batch is added to the mapping
expect(batchAccInputHash).to.be.equal(ethers.constants.HashZero);
/*
* Check batch mapping
* Calcultate input Hahs for batch 1
*/
let batchAccInputHashJs = calculateAccInputHash(
ethers.constants.HashZero,
sequence.transactionsHash,
sequence.globalExitRoot,
sequence.timestamp,
trustedSequencer.address,
);
// Calcultate input Hahs for batch 2
batchAccInputHashJs = calculateAccInputHash(
batchAccInputHashJs,
sequence2.transactionsHash,
sequence2.globalExitRoot,
sequence2.timestamp,
trustedSequencer.address,
);
const batchData2 = await cdkValidiumContract.sequencedBatches(2);
expect(batchData2.accInputHash).to.be.equal(batchAccInputHashJs);
expect(batchData2.sequencedTimestamp).to.be.equal(sequencedTimestamp);
expect(batchData2.previousLastBatchSequenced).to.be.equal(0);
});
it('sequenceBatches should check the timestamp correctly', async () => {
const l2txData = '0x';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = (await cdkValidiumContract.batchFee()).mul(2);
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: 0,
minForcedTimestamp: 0,
};
const sequence2 = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: 0,
minForcedTimestamp: 0,
};
const initialOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
let currentTimestamp = (await ethers.provider.getBlock()).timestamp;
await ethers.provider.send('evm_increaseTime', [1]); // evm_setNextBlockTimestamp
sequence.timestamp = currentTimestamp + 2; // bigger than current block tiemstamp
// revert because timestamp is more than the current one
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
currentTimestamp = (await ethers.provider.getBlock()).timestamp;
await ethers.provider.send('evm_increaseTime', [1]);
sequence.timestamp = currentTimestamp;
sequence2.timestamp = currentTimestamp - 1;
// revert because the second sequence has less timestamp than the previous batch
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.be.revertedWith('SequencedTimestampInvalid');
currentTimestamp = (await ethers.provider.getBlock()).timestamp;
await ethers.provider.send('evm_increaseTime', [1]);
sequence.timestamp = currentTimestamp + 1; // edge case, same timestamp as the block
sequence2.timestamp = currentTimestamp + 1;
// Sequence Batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence, sequence2], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 2);
const finalOwnerBalance = await maticTokenContract.balanceOf(
await trustedSequencer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmount)),
);
});
it('should force a batch of transactions', async () => {
const l2txData = '0x123456';
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
expect(maticAmount.toString()).to.be.equal((await cdkValidiumContract.getForcedBatchFee()).toString());
// Activate force batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
// revert because the maxMatic amount is less than the necessary to pay
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount.sub(1)))
.to.be.revertedWith('NotEnoughMaticAmount');
// revert because tokens were not approved
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.be.revertedWith('ERC20: insufficient allowance');
const initialOwnerBalance = await maticTokenContract.balanceOf(
await deployer.address,
);
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastForceBatch = await cdkValidiumContract.lastForceBatch();
// Force batch
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForceBatch + 1, lastGlobalExitRoot, deployer.address, '0x');
const finalOwnerBalance = await maticTokenContract.balanceOf(
await deployer.address,
);
expect(finalOwnerBalance).to.equal(
ethers.BigNumber.from(initialOwnerBalance).sub(ethers.BigNumber.from(maticAmount)),
);
// Check force batches struct
const batchHash = await cdkValidiumContract.forcedBatches(1);
const timestampForceBatch = (await ethers.provider.getBlock()).timestamp;
const batchHashJs = ethers.utils.solidityKeccak256(
['bytes32', 'bytes32', 'uint64'],
[
calculateBatchHashData(l2txData),
lastGlobalExitRoot,
timestampForceBatch,
],
);
expect(batchHashJs).to.be.equal(batchHash);
});
it('should sequence force batches using sequenceForceBatches', async () => {
const l2txData = '0x123456';
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Activate force batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()) + 1;
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, deployer.address, '0x');
const timestampForceBatch = (await ethers.provider.getBlock()).timestamp;
const forceBatchHash = await cdkValidiumContract.forcedBatches(1);
const batchHashJs = ethers.utils.solidityKeccak256(
['bytes32', 'bytes32', 'uint64'],
[
calculateBatchHashData(l2txData),
lastGlobalExitRoot,
timestampForceBatch,
],
);
expect(batchHashJs).to.be.equal(forceBatchHash);
// Check storage variables before call
expect(await cdkValidiumContract.lastForceBatchSequenced()).to.be.equal(0);
expect(await cdkValidiumContract.lastForceBatch()).to.be.equal(1);
expect(await cdkValidiumContract.lastBatchSequenced()).to.be.equal(0);
const forceBatchStruct = {
transactions: l2txData,
globalExitRoot: lastGlobalExitRoot,
minForcedTimestamp: timestampForceBatch,
};
// revert because the timeout is not expired
await expect(cdkValidiumContract.sequenceForceBatches([]))
.to.be.revertedWith('SequenceZeroBatches');
// revert because does not exist that many forced Batches
await expect(cdkValidiumContract.sequenceForceBatches(Array(2).fill(forceBatchStruct)))
.to.be.revertedWith('ForceBatchesOverflow');
// revert because the timeout is not expired
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStruct]))
.to.be.revertedWith('ForceBatchTimeoutNotExpired');
const forceBatchStructBad = {
transactions: l2txData,
globalExitRoot: lastGlobalExitRoot,
minForcedTimestamp: timestampForceBatch,
};
forceBatchStructBad.minForcedTimestamp += 1;
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStructBad]))
.to.be.revertedWith('ForcedDataDoesNotMatch');
forceBatchStructBad.minForcedTimestamp -= 1;
forceBatchStructBad.globalExitRoot = ethers.constants.HashZero;
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStructBad]))
.to.be.revertedWith('ForcedDataDoesNotMatch');
forceBatchStructBad.globalExitRoot = lastGlobalExitRoot;
forceBatchStructBad.transactions = '0x1111';
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStructBad]))
.to.be.revertedWith('ForcedDataDoesNotMatch');
forceBatchStructBad.transactions = l2txData;
// Increment timestamp
await ethers.provider.send('evm_setNextBlockTimestamp', [timestampForceBatch + FORCE_BATCH_TIMEOUT]);
// sequence force batch
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStruct]))
.to.emit(cdkValidiumContract, 'SequenceForceBatches')
.withArgs(1);
const timestampSequenceBatch = (await ethers.provider.getBlock()).timestamp;
expect(await cdkValidiumContract.lastForceBatchSequenced()).to.be.equal(1);
expect(await cdkValidiumContract.lastForceBatch()).to.be.equal(1);
expect(await cdkValidiumContract.lastBatchSequenced()).to.be.equal(1);
// Check force batches struct
const batchAccInputHash = (await cdkValidiumContract.sequencedBatches(1)).accInputHash;
const batchAccInputHashJs = calculateAccInputHash(
ethers.constants.HashZero,
calculateBatchHashData(l2txData),
lastGlobalExitRoot,
timestampSequenceBatch,
deployer.address,
);
expect(batchAccInputHash).to.be.equal(batchAccInputHashJs);
});
it('should verify a sequenced batch using verifyBatchesTrustedAggregator', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Sequence Batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
// trustedAggregator forge the batch
const pendingState = 0;
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000000';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000000';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()) + 1;
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
const initialAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
await expect(
cdkValidiumContract.connect(deployer).verifyBatchesTrustedAggregator(
pendingState,
numBatch - 1,
numBatch - 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OnlyTrustedAggregator');
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
pendingState,
numBatch - 1,
numBatch - 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchBelowLastVerifiedBatch');
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
pendingState,
numBatch - 1,
numBatch + 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('NewAccInputHashDoesNotExist');
// Verify batch
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
pendingState,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatchesTrustedAggregator')
.withArgs(numBatch, newStateRoot, trustedAggregator.address);
const finalAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
expect(finalAggregatorMatic).to.equal(
ethers.BigNumber.from(initialAggregatorMatic).add(ethers.BigNumber.from(maticAmount)),
);
});
it('should verify forced sequenced batch using verifyBatchesTrustedAggregator', async () => {
const l2txData = '0x123456';
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Activate force batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()) + 1;
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, deployer.address, '0x');
const timestampForceBatch = (await ethers.provider.getBlock()).timestamp;
// Increment timestamp
await ethers.provider.send('evm_setNextBlockTimestamp', [timestampForceBatch + FORCE_BATCH_TIMEOUT]);
const forceBatchStruct = {
transactions: l2txData,
globalExitRoot: lastGlobalExitRoot,
minForcedTimestamp: timestampForceBatch,
};
// sequence force batch
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStruct]))
.to.emit(cdkValidiumContract, 'SequenceForceBatches')
.withArgs(lastForcedBatch);
// trustedAggregator forge the batch
const pendingState = 0;
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000000';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000000';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()) + 1;
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
const initialAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
// Verify batch
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
pendingState,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatch')
.withArgs(numBatch, trustedAggregator.address)
.to.emit(maticTokenContract, 'Transfer')
.withArgs(cdkValidiumContract.address, trustedAggregator.address, maticAmount);
const finalAggregatorMatic = await maticTokenContract.balanceOf(
trustedAggregator.address,
);
expect(finalAggregatorMatic).to.equal(
ethers.BigNumber.from(initialAggregatorMatic).add(ethers.BigNumber.from(maticAmount)),
);
});
it('should match the computed SC input with the Js input', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Sequence
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
const sentBatchHash = (await cdkValidiumContract.sequencedBatches(lastBatchSequenced + 1)).accInputHash;
const oldAccInputHash = (await cdkValidiumContract.sequencedBatches(0)).accInputHash;
const batchAccInputHashJs = calculateAccInputHash(
oldAccInputHash,
sequence.transactionsHash,
sequence.globalExitRoot,
sequence.timestamp,
trustedSequencer.address,
);
expect(sentBatchHash).to.be.equal(batchAccInputHashJs);
// Compute circuit input with the SC function
const currentStateRoot = await cdkValidiumContract.batchNumToStateRoot(0);
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000001234';
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000456';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()) + 1;
// Compute Js input
const inputSnarkJS = await calculateSnarkInput(
currentStateRoot,
newStateRoot,
newLocalExitRoot,
oldAccInputHash,
batchAccInputHashJs,
numBatch - 1,
numBatch,
chainID,
deployer.address,
forkID,
);
// Compute Js input
const pendingStateNum = 0;
const circuitInpuSnarkSC = await cdkValidiumContract.getNextSnarkInput(
pendingStateNum,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
);
expect(circuitInpuSnarkSC).to.be.equal(inputSnarkJS);
});
it('should match the computed SC input with the Js input in force batches', async () => {
const l2txData = '0x123456';
const maticAmount = await cdkValidiumContract.getForcedBatchFee();
const lastGlobalExitRoot = await PolygonZkEVMGlobalExitRoot.getLastGlobalExitRoot();
await expect(
maticTokenContract.approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Activate force batches
await expect(
cdkValidiumContract.connect(admin).activateForceBatches(),
).to.emit(cdkValidiumContract, 'ActivateForceBatches');
const lastForcedBatch = (await cdkValidiumContract.lastForceBatch()).toNumber() + 1;
await expect(cdkValidiumContract.forceBatch(l2txData, maticAmount))
.to.emit(cdkValidiumContract, 'ForceBatch')
.withArgs(lastForcedBatch, lastGlobalExitRoot, deployer.address, '0x');
const timestampForceBatch = (await ethers.provider.getBlock()).timestamp;
// Increment timestamp
await ethers.provider.send('evm_setNextBlockTimestamp', [timestampForceBatch + FORCE_BATCH_TIMEOUT]);
const forceBatchStruct = {
transactions: l2txData,
globalExitRoot: lastGlobalExitRoot,
minForcedTimestamp: timestampForceBatch,
};
// sequence force batch
await expect(cdkValidiumContract.sequenceForceBatches([forceBatchStruct]))
.to.emit(cdkValidiumContract, 'SequenceForceBatches')
.withArgs(lastForcedBatch);
const sequencedTimestmap = (await ethers.provider.getBlock()).timestamp;
const oldAccInputHash = (await cdkValidiumContract.sequencedBatches(0)).accInputHash;
const batchAccInputHash = (await cdkValidiumContract.sequencedBatches(1)).accInputHash;
const batchAccInputHashJs = calculateAccInputHash(
oldAccInputHash,
calculateBatchHashData(l2txData),
lastGlobalExitRoot,
sequencedTimestmap,
deployer.address,
);
expect(batchAccInputHash).to.be.equal(batchAccInputHashJs);
// Compute circuit input with the SC function
const currentStateRoot = await cdkValidiumContract.batchNumToStateRoot(0);
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000001234';
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000456';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()) + 1;
// Compute Js input
const inputSnarkJS = await calculateSnarkInput(
currentStateRoot,
newStateRoot,
newLocalExitRoot,
oldAccInputHash,
batchAccInputHashJs,
numBatch - 1,
numBatch,
chainID,
deployer.address,
forkID,
);
// Compute Js input
const pendingStateNum = 0;
const circuitInpuSnarkSC = await cdkValidiumContract.getNextSnarkInput(
pendingStateNum,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
);
expect(circuitInpuSnarkSC).to.be.equal(inputSnarkJS);
});
it('should verify a sequenced batch using verifyBatches', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
const lastBatchSequenced = await cdkValidiumContract.lastBatchSequenced();
// Sequence Batches
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced + 1);
// aggregator forge the batch
const pendingState = 0;
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000002';
const numBatch = (await cdkValidiumContract.lastVerifiedBatch()) + 1;
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
const initialAggregatorMatic = await maticTokenContract.balanceOf(
aggregator1.address,
);
const sequencedBatchData = await cdkValidiumContract.sequencedBatches(1);
const { sequencedTimestamp } = sequencedBatchData;
const currentBatchFee = await cdkValidiumContract.batchFee();
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
pendingState,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('TrustedAggregatorTimeoutNotExpired');
await ethers.provider.send('evm_setNextBlockTimestamp', [sequencedTimestamp.toNumber() + trustedAggregatorTimeoutDefault - 1]);
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
pendingState,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('TrustedAggregatorTimeoutNotExpired');
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
pendingState,
numBatch - 1,
numBatch + 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('NewAccInputHashDoesNotExist');
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
pendingState,
numBatch - 1,
numBatch + _MAX_VERIFY_BATCHES,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('ExceedMaxVerifyBatches');
// Verify batch
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
pendingState,
numBatch - 1,
numBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(numBatch, newStateRoot, aggregator1.address);
const verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
const finalAggregatorMatic = await maticTokenContract.balanceOf(
aggregator1.address,
);
expect(finalAggregatorMatic).to.equal(
ethers.BigNumber.from(initialAggregatorMatic).add(ethers.BigNumber.from(maticAmount)),
);
// Check pending state
const lastPendingstate = 1;
expect(lastPendingstate).to.be.equal(await cdkValidiumContract.lastPendingState());
const pendingStateData = await cdkValidiumContract.pendingStateTransitions(lastPendingstate);
expect(verifyTimestamp).to.be.equal(pendingStateData.timestamp);
expect(numBatch).to.be.equal(pendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(pendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(pendingStateData.stateRoot);
// Try consolidate state
expect(0).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
// Pending state can't be 0
await expect(
cdkValidiumContract.consolidatePendingState(0),
).to.be.revertedWith('PendingStateInvalid');
// Pending state does not exist
await expect(
cdkValidiumContract.consolidatePendingState(2),
).to.be.revertedWith('PendingStateInvalid');
// Not ready to be consolidated
await expect(
cdkValidiumContract.consolidatePendingState(lastPendingstate),
).to.be.revertedWith('PendingStateNotConsolidable');
await ethers.provider.send('evm_setNextBlockTimestamp', [verifyTimestamp + pendingStateTimeoutDefault - 1]);
await expect(
cdkValidiumContract.consolidatePendingState(lastPendingstate),
).to.be.revertedWith('PendingStateNotConsolidable');
await expect(
cdkValidiumContract.consolidatePendingState(lastPendingstate),
).to.emit(cdkValidiumContract, 'ConsolidatePendingState')
.withArgs(numBatch, newStateRoot, lastPendingstate);
// Pending state already consolidated
await expect(
cdkValidiumContract.consolidatePendingState(1),
).to.be.revertedWith('PendingStateInvalid');
// Fee es divided because is was fast verified
const multiplierFee = await cdkValidiumContract.multiplierBatchFee();
expect((currentBatchFee.mul(1000)).div(multiplierFee)).to.be.equal(await cdkValidiumContract.batchFee());
// Check pending state variables
expect(1).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(1));
expect(1).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
});
it('should test the pending state properly', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const batchesForSequence = 5;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const sequencesArray = Array(batchesForSequence).fill(sequence);
// Array(5).fill("girl", 0);
// Approve lots of tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticTokenInitialBalance),
).to.emit(maticTokenContract, 'Approval');
// Make 20 sequences of 5 batches, with 1 minut timestamp difference
for (let i = 0; i < 20; i++) {
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches(sequencesArray, trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches');
}
await ethers.provider.send('evm_increaseTime', [60]);
// Forge first sequence with verifyBAtches
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000002';
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
let currentPendingState = 0;
let currentNumBatch = 0;
let newBatch = currentNumBatch + batchesForSequence;
// Verify batch
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
let verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
// Check pending state
currentPendingState++;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
let currentPendingStateData = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
expect(verifyTimestamp).to.be.equal(currentPendingStateData.timestamp);
expect(newBatch).to.be.equal(currentPendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(currentPendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(currentPendingStateData.stateRoot);
// Try to verify Batches that does not go beyond the last pending state
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
0,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchBelowLastVerifiedBatch');
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
10,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('PendingStateDoesNotExist');
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('InitNumBatchDoesNotMatchPendingState');
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatchesTrustedAggregator')
.withArgs(newBatch, newStateRoot, trustedAggregator.address);
// Check pending state is clear
currentPendingState = 0;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(0).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
// Check consolidated state
let currentVerifiedBatch = newBatch;
expect(currentVerifiedBatch).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentVerifiedBatch));
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
1,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('PendingStateDoesNotExist');
// Since this pending state was not consolidated, the currentNumBatch does not have stored root
expect(ethers.constants.HashZero).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentNumBatch));
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OldStateRootDoesNotExist');
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
currentPendingState,
0,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchBelowLastVerifiedBatch');
// Again use verifyBatches
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
// Check pending state
verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
currentPendingState++;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
currentPendingStateData = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
expect(verifyTimestamp).to.be.equal(currentPendingStateData.timestamp);
expect(newBatch).to.be.equal(currentPendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(currentPendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(currentPendingStateData.stateRoot);
// Verify another sequence from batch 0
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
0,
1,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OldStateRootDoesNotExist');
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
0,
0,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
// Check pending state
verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
currentPendingState++;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
currentPendingStateData = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
expect(verifyTimestamp).to.be.equal(currentPendingStateData.timestamp);
expect(newBatch).to.be.equal(currentPendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(currentPendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(currentPendingStateData.stateRoot);
// Verify batches using old pending state
currentNumBatch = newBatch;
newBatch += batchesForSequence;
// Must specify pending state num while is not consolidated
await expect(
cdkValidiumContract.connect(trustedAggregator).verifyBatchesTrustedAggregator(
0,
currentNumBatch - 5,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OldStateRootDoesNotExist');
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState - 1,
currentNumBatch - 5,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
currentPendingState++;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
currentPendingStateData = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
expect(verifyTimestamp).to.be.equal(currentPendingStateData.timestamp);
expect(newBatch).to.be.equal(currentPendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(currentPendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(currentPendingStateData.stateRoot);
// Consolidate using verifyBatches
const firstPendingState = await cdkValidiumContract.pendingStateTransitions(1);
await ethers.provider.send('evm_setNextBlockTimestamp', [firstPendingState.timestamp.toNumber() + pendingStateTimeoutDefault]);
let currentPendingConsolidated = 0;
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address)
.to.emit(cdkValidiumContract, 'ConsolidatePendingState')
.withArgs(firstPendingState.lastVerifiedBatch, newStateRoot, ++currentPendingConsolidated);
verifyTimestamp = (await ethers.provider.getBlock()).timestamp;
currentPendingState++;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(currentPendingConsolidated).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
currentPendingStateData = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
expect(verifyTimestamp).to.be.equal(currentPendingStateData.timestamp);
expect(newBatch).to.be.equal(currentPendingStateData.lastVerifiedBatch);
expect(newLocalExitRoot).to.be.equal(currentPendingStateData.exitRoot);
expect(newStateRoot).to.be.equal(currentPendingStateData.stateRoot);
// Check state consolidated
currentVerifiedBatch += batchesForSequence;
expect(currentVerifiedBatch).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentVerifiedBatch));
// Consolidate using sendBatches
const secondPendingState = await cdkValidiumContract.pendingStateTransitions(2);
await ethers.provider.send('evm_setNextBlockTimestamp', [secondPendingState.timestamp.toNumber() + pendingStateTimeoutDefault]);
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches(sequencesArray, trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.to.emit(cdkValidiumContract, 'ConsolidatePendingState')
.withArgs(secondPendingState.lastVerifiedBatch, newStateRoot, ++currentPendingConsolidated);
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(currentPendingConsolidated).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
// Check state consolidated
currentVerifiedBatch += batchesForSequence;
expect(currentVerifiedBatch).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentVerifiedBatch));
// Put a lot of pending states and check that half of them are consoldiated
for (let i = 0; i < 8; i++) {
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
currentPendingState++;
}
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
currentPendingConsolidated = await cdkValidiumContract.lastPendingStateConsolidated();
const lastPendingState = await cdkValidiumContract.pendingStateTransitions(currentPendingState);
await ethers.provider.send('evm_setNextBlockTimestamp', [lastPendingState.timestamp.toNumber() + pendingStateTimeoutDefault]);
// call verify batches and check that half of them are consolidated
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(currentPendingConsolidated).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
const nextPendingConsolidated = Number(currentPendingConsolidated) + 1;
const nextConsolidatedStateNum = nextPendingConsolidated + Number(Math.floor((currentPendingState - nextPendingConsolidated) / 2));
const nextConsolidatedState = await cdkValidiumContract.pendingStateTransitions(nextConsolidatedStateNum);
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches(sequencesArray, trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.to.emit(cdkValidiumContract, 'ConsolidatePendingState')
.withArgs(nextConsolidatedState.lastVerifiedBatch, newStateRoot, nextConsolidatedStateNum);
// Put pendingState to 0 and check that the pending state is clear after verifyBatches
await expect(
cdkValidiumContract.connect(admin).setPendingStateTimeout(0),
).to.emit(cdkValidiumContract, 'SetPendingStateTimeout').withArgs(0);
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
currentPendingState = 0;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(0).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
// Check consolidated state
currentVerifiedBatch = newBatch;
expect(currentVerifiedBatch).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentVerifiedBatch));
});
it('Activate emergency state due halt timeout', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const maticAmount = await cdkValidiumContract.batchFee();
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: ethers.BigNumber.from(currentTimestamp),
minForcedTimestamp: 0,
};
// Approve tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticAmount),
).to.emit(maticTokenContract, 'Approval');
// Sequence batch
const lastBatchSequenced = 1;
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches([sequence], trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches')
.withArgs(lastBatchSequenced);
const sequencedTimestmap = Number((await cdkValidiumContract.sequencedBatches(1)).sequencedTimestamp);
const haltTimeout = HALT_AGGREGATION_TIMEOUT;
// Try to activate the emergency state
// Check batch is not sequenced
await expect(cdkValidiumContract.connect(aggregator1).activateEmergencyState(2))
.to.be.revertedWith('BatchNotSequencedOrNotSequenceEnd');
// Check batch is already verified
await cdkValidiumContract.setVerifiedBatch(1);
await expect(cdkValidiumContract.connect(aggregator1).activateEmergencyState(1))
.to.be.revertedWith('BatchAlreadyVerified');
await cdkValidiumContract.setVerifiedBatch(0);
// check timeout is not expired
await expect(cdkValidiumContract.connect(aggregator1).activateEmergencyState(1))
.to.be.revertedWith('HaltTimeoutNotExpired');
await ethers.provider.send('evm_setNextBlockTimestamp', [sequencedTimestmap + haltTimeout]);
// Succesfully acitvate emergency state
await expect(cdkValidiumContract.connect(aggregator1).activateEmergencyState(1))
.to.emit(cdkValidiumContract, 'EmergencyStateActivated');
});
it('Test overridePendingState properly', async () => {
const l2txData = '0x123456';
const transactionsHash = calculateBatchHashData(l2txData);
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const batchesForSequence = 5;
const sequence = {
transactionsHash,
globalExitRoot: ethers.constants.HashZero,
timestamp: currentTimestamp,
minForcedTimestamp: 0,
};
const sequencesArray = Array(batchesForSequence).fill(sequence);
// Array(5).fill("girl", 0);
// Approve lots of tokens
await expect(
maticTokenContract.connect(trustedSequencer).approve(cdkValidiumContract.address, maticTokenInitialBalance),
).to.emit(maticTokenContract, 'Approval');
// Make 20 sequences of 5 batches, with 1 minut timestamp difference
for (let i = 0; i < 20; i++) {
await expect(cdkValidiumContract.connect(trustedSequencer).sequenceBatches(sequencesArray, trustedSequencer.address, []))
.to.emit(cdkValidiumContract, 'SequenceBatches');
}
await ethers.provider.send('evm_increaseTime', [60]);
// Forge first sequence with verifyBAtches
const newLocalExitRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const newStateRoot = '0x0000000000000000000000000000000000000000000000000000000000000002';
const zkProofFFlonk = new Array(24).fill(ethers.constants.HashZero);
let currentPendingState = 0;
let currentNumBatch = 0;
let newBatch = currentNumBatch + batchesForSequence;
// Verify batch 2 batches
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
// verify second sequence
currentPendingState++;
currentNumBatch = newBatch;
newBatch += batchesForSequence;
await expect(
cdkValidiumContract.connect(aggregator1).verifyBatches(
currentPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'VerifyBatches')
.withArgs(newBatch, newStateRoot, aggregator1.address);
const finalPendingState = 2;
await expect(
cdkValidiumContract.connect(aggregator1).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OnlyTrustedAggregator');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
finalPendingState + 1,
finalPendingState + 2,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('PendingStateDoesNotExist');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch + 1,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('InitNumBatchDoesNotMatchPendingState');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch,
newBatch + 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchDoesNotMatchPendingState');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
0,
finalPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('OldStateRootDoesNotExist');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
finalPendingState,
finalPendingState,
currentNumBatch + 5,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalPendingStateNumInvalid');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
finalPendingState,
finalPendingState + 2,
currentNumBatch + 5,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalPendingStateNumInvalid');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch,
newBatch + 1,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('FinalNumBatchDoesNotMatchPendingState');
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot,
zkProofFFlonk,
),
).to.be.revertedWith('StoredRootMustBeDifferentThanNewRoot');
const newStateRoot2 = '0x0000000000000000000000000000000000000000000000000000000000000003';
await expect(
cdkValidiumContract.connect(trustedAggregator).overridePendingState(
currentPendingState,
finalPendingState,
currentNumBatch,
newBatch,
newLocalExitRoot,
newStateRoot2,
zkProofFFlonk,
),
).to.emit(cdkValidiumContract, 'OverridePendingState').withArgs(newBatch, newStateRoot2, trustedAggregator.address);
// check pending state is clear
currentPendingState = 0;
expect(currentPendingState).to.be.equal(await cdkValidiumContract.lastPendingState());
expect(0).to.be.equal(await cdkValidiumContract.lastPendingStateConsolidated());
// check consolidated state
const currentVerifiedBatch = newBatch;
expect(currentVerifiedBatch).to.be.equal(await cdkValidiumContract.lastVerifiedBatch());
expect(newStateRoot2).to.be.equal(await cdkValidiumContract.batchNumToStateRoot(currentVerifiedBatch));
});
it('Test batch fees properly', async () => {
const accInputData = ethers.constants.HashZero;
const verifyBatchTimeTarget = Number(await cdkValidiumContract.verifyBatchTimeTarget());
const currentTimestamp = (await ethers.provider.getBlock()).timestamp;
const multiplierFee = ethers.BigNumber.from(await cdkValidiumContract.multiplierBatchFee()); // 1002
const bingNumber1000 = ethers.BigNumber.from(1000);
// Create sequenced to update the fee
await cdkValidiumContract.setSequencedBatches(
50,
accInputData,
currentTimestamp + verifyBatchTimeTarget,
0,
); // Edge case, will be below
await cdkValidiumContract.setSequencedBatches(
100,
accInputData,
currentTimestamp + verifyBatchTimeTarget + 1,
50,
); // Edge case, will be above
// Assert currentFee
let currentBatchFee = await cdkValidiumContract.batchFee();
expect(currentBatchFee).to.be.equal(ethers.utils.parseEther('0.1'));
await ethers.provider.send('evm_setNextBlockTimestamp', [currentTimestamp + verifyBatchTimeTarget * 2]);
await cdkValidiumContract.updateBatchFee(100);
// Fee does not change since there are the same batches above than below
expect(await cdkValidiumContract.batchFee()).to.be.equal(currentBatchFee);
/*
* Now all the batches will be above
* since the MAX_BATCH_MULTIPLIER is 12 this will be the pow
*/
await cdkValidiumContract.updateBatchFee(100);
currentBatchFee = currentBatchFee.mul(multiplierFee.pow(MAX_BATCH_MULTIPLIER)).div(bingNumber1000.pow(MAX_BATCH_MULTIPLIER));
expect(currentBatchFee).to.be.equal(await cdkValidiumContract.batchFee());
// Check the fee is now below
await cdkValidiumContract.setSequencedBatches(50, accInputData, currentTimestamp + verifyBatchTimeTarget * 2, 0); // Below
currentBatchFee = currentBatchFee.mul(bingNumber1000.pow(MAX_BATCH_MULTIPLIER)).div(multiplierFee.pow(MAX_BATCH_MULTIPLIER));
});
});
/* eslint-disable no-plusplus, no-await-in-loop */
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
describe('CDKValidium Deployer', () => {
let deployer; let
owner;
let cdkValidiumDeployerContract;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, owner] = await ethers.getSigners();
// deploy mock verifier
const CDKValidiumDeployerFactory = await ethers.getContractFactory(
'CDKValidiumDeployer',
);
cdkValidiumDeployerContract = await CDKValidiumDeployerFactory.deploy(owner.address);
await cdkValidiumDeployerContract.deployed();
});
it('should check the owner', async () => {
expect(await cdkValidiumDeployerContract.owner()).to.be.equal(owner.address);
});
it('should check to deploy a simple contract and call it', async () => {
const OZERC20PresetFactory = await ethers.getContractFactory(
'ERC20PresetFixedSupply',
);
const salt = ethers.constants.HashZero;
// Encode deploy transaction
const deployTransactionERC20 = (OZERC20PresetFactory.getDeployTransaction(
maticTokenName,
maticTokenSymbol,
maticTokenInitialBalance,
owner.address,
)).data;
const hashInitCode = ethers.utils.solidityKeccak256(['bytes'], [deployTransactionERC20]);
// Precalculate create2 address
const precalculateTokenDeployed = await ethers.utils.getCreate2Address(cdkValidiumDeployerContract.address, salt, hashInitCode);
expect(await cdkValidiumDeployerContract.predictDeterministicAddress(
salt,
hashInitCode,
)).to.be.equal(precalculateTokenDeployed);
const amount = 0;
await expect(cdkValidiumDeployerContract.connect(deployer).deployDeterministic(
amount,
salt,
deployTransactionERC20,
)).to.be.revertedWith('Ownable');
// Deploy using create2
await expect(cdkValidiumDeployerContract.connect(owner).deployDeterministic(
amount,
salt,
deployTransactionERC20,
)).to.emit(cdkValidiumDeployerContract, 'NewDeterministicDeployment').withArgs(precalculateTokenDeployed);
const dataCall = OZERC20PresetFactory.interface.encodeFunctionData('transfer', [owner.address, ethers.utils.parseEther('1')]);
// Check deployed contract
const instanceToken = OZERC20PresetFactory.attach(precalculateTokenDeployed);
expect(await instanceToken.balanceOf(owner.address)).to.be.equal(maticTokenInitialBalance);
await expect(cdkValidiumDeployerContract.functionCall(
precalculateTokenDeployed,
dataCall,
1, // amount
)).to.be.revertedWith('Ownable');
await expect(cdkValidiumDeployerContract.connect(owner).functionCall(
precalculateTokenDeployed,
dataCall,
1, // amount
)).to.be.revertedWith('Address: insufficient balance for call');
await expect(cdkValidiumDeployerContract.connect(owner).functionCall(
precalculateTokenDeployed,
dataCall,
1, // amount
{ value: 1 },
)).to.be.revertedWith('Address: low-level call with value failed');
await expect(cdkValidiumDeployerContract.connect(owner).functionCall(
precalculateTokenDeployed,
dataCall,
0,
)).to.be.revertedWith('ERC20: transfer amount exceeds balance');
// Transfer tokens first
await instanceToken.connect(owner).transfer(cdkValidiumDeployerContract.address, ethers.utils.parseEther('1'));
await expect(cdkValidiumDeployerContract.connect(owner).functionCall(
precalculateTokenDeployed,
dataCall,
0, // amount
)).to.emit(cdkValidiumDeployerContract, 'FunctionCall');
});
it('should check to deploy a simple contract and call it', async () => {
const OZERC20PresetFactory = await ethers.getContractFactory(
'ERC20PresetFixedSupply',
);
const salt = ethers.constants.HashZero;
// Encode deploy transaction
const deployTransactionERC20 = (OZERC20PresetFactory.getDeployTransaction(
maticTokenName,
maticTokenSymbol,
maticTokenInitialBalance,
cdkValidiumDeployerContract.address,
)).data;
const hashInitCode = ethers.utils.solidityKeccak256(['bytes'], [deployTransactionERC20]);
// Precalculate create2 address
const precalculateTokenDeployed = await ethers.utils.getCreate2Address(cdkValidiumDeployerContract.address, salt, hashInitCode);
const dataCall = OZERC20PresetFactory.interface.encodeFunctionData('transfer', [owner.address, ethers.utils.parseEther('1')]);
const amount = 0;
const dataCallFail = OZERC20PresetFactory.interface.encodeFunctionData('transfer', [owner.address, ethers.utils.parseEther('20000001')]);
// Cannot fails internal call, contract not deployed
await expect(cdkValidiumDeployerContract.connect(owner).deployDeterministicAndCall(
amount,
salt,
deployTransactionERC20,
dataCallFail,
)).to.be.revertedWith('ERC20: transfer amount exceeds balance');
// Deploy using create2
await expect(cdkValidiumDeployerContract.connect(owner).deployDeterministicAndCall(
amount,
salt,
deployTransactionERC20,
dataCall,
)).to.emit(cdkValidiumDeployerContract, 'NewDeterministicDeployment').withArgs(precalculateTokenDeployed);
const instanceToken = OZERC20PresetFactory.attach(precalculateTokenDeployed);
expect(await instanceToken.balanceOf(owner.address)).to.be.equal(ethers.utils.parseEther('1'));
// Cannot create 2 times the same contract
await expect(cdkValidiumDeployerContract.connect(owner).deployDeterministicAndCall(
amount,
salt,
deployTransactionERC20,
dataCall,
)).to.be.revertedWith('Create2: Failed on deploy');
});
it('Test keyless deployment', async () => {
const CDKValidiumDeployerFactory = await ethers.getContractFactory(
'CDKValidiumDeployer',
);
const deployTxCDKValidiumDeployer = (CDKValidiumDeployerFactory.getDeployTransaction(
owner.address,
)).data;
const gasLimit = ethers.BigNumber.from(1000000); // Put 1 Million, aprox 650k are necessary
const gasPrice = ethers.BigNumber.from(ethers.utils.parseUnits('100', 'gwei')); // just in case to be able to use always the transaction
const to = '0x'; // deployment transaction, to is 0
const tx = {
to,
nonce: 0,
value: 0,
gasLimit: gasLimit.toHexString(),
gasPrice: gasPrice.toHexString(),
data: deployTxCDKValidiumDeployer,
};
const signature = {
v: 27,
r: '0x247000', // Equals 0x0000000000000000000000000000000000000000000000000000000000247000
s: '0x2470', // Equals 0x0000000000000000000000000000000000000000000000000000000000002470
};
const serializedTransaction = ethers.utils.serializeTransaction(tx, signature);
const resultTransaction = ethers.utils.parseTransaction(serializedTransaction);
const totalEther = gasLimit.mul(gasPrice); // 0.1 ether
// Fund keyless deployment
const params = {
to: resultTransaction.from,
value: totalEther.toHexString(),
};
const cdkValidiumDeployerAddress = ethers.utils.getContractAddress(resultTransaction);
await deployer.sendTransaction(params);
await ethers.provider.sendTransaction(serializedTransaction);
const _cdkValidiumDeployerContract = CDKValidiumDeployerFactory.attach(cdkValidiumDeployerAddress);
expect(await _cdkValidiumDeployerContract.owner()).to.be.equal(owner.address);
});
it('Test Bridge deployment', async () => {
const bridgeFactory = await ethers.getContractFactory(
'PolygonZkEVMBridge',
);
const salt = ethers.constants.HashZero;
// Encode deploy transaction
const deployTransactionBridge = (bridgeFactory.getDeployTransaction()).data;
const hashInitCode = ethers.utils.solidityKeccak256(['bytes'], [deployTransactionBridge]);
// Precalculate create2 address
const precalculateTokenDeployed = await ethers.utils.getCreate2Address(cdkValidiumDeployerContract.address, salt, hashInitCode);
expect(await cdkValidiumDeployerContract.predictDeterministicAddress(
salt,
hashInitCode,
)).to.be.equal(precalculateTokenDeployed);
const amount = 0;
// Deploy using create2
const populatedTransaction = await cdkValidiumDeployerContract.connect(owner).populateTransaction.deployDeterministic(
amount,
salt,
deployTransactionBridge,
);
populatedTransaction.gasLimit = ethers.BigNumber.from(6000000); // Should be more than enough with 5M
await expect(owner.sendTransaction(populatedTransaction))
.to.emit(cdkValidiumDeployerContract, 'NewDeterministicDeployment').withArgs(precalculateTokenDeployed);
});
});
const { expect } = require('chai');
const { ethers, upgrades } = require('hardhat');
// OZ test functions
function genOperation(target, value, data, predecessor, salt) {
const id = ethers.utils.solidityKeccak256([
'address',
'uint256',
'bytes',
'uint256',
'bytes32',
], [
target,
value,
data,
predecessor,
salt,
]);
return {
id, target, value, data, predecessor, salt,
};
}
describe('CDKValidium', () => {
let deployer;
let trustedAggregator;
let trustedSequencer;
let admin;
let timelockContract;
let verifierContract;
let PolygonZkEVMBridgeContract;
let cdkValidiumContract;
let cdkDataCommitteeContract;
let maticTokenContract;
let PolygonZkEVMGlobalExitRoot;
const maticTokenName = 'Matic Token';
const maticTokenSymbol = 'MATIC';
const maticTokenInitialBalance = ethers.utils.parseEther('20000000');
const genesisRoot = '0x0000000000000000000000000000000000000000000000000000000000000001';
const networkIDMainnet = 0;
const urlSequencer = 'http://cdk-validium-json-rpc:8123';
const chainID = 1000;
const networkName = 'cdk-validium';
const version = '0.0.1';
const pendingStateTimeoutDefault = 10;
const trustedAggregatorTimeoutDefault = 10;
let firstDeployment = true;
const minDelay = 60 * 60; // 1 hout
beforeEach('Deploy contract', async () => {
upgrades.silenceWarnings();
// load signers
[deployer, trustedAggregator, trustedSequencer, admin] = await ethers.getSigners();
// deploy mock verifier
const VerifierRollupHelperFactory = await ethers.getContractFactory(
'VerifierRollupHelperMock',
);
verifierContract = await VerifierRollupHelperFactory.deploy();
// deploy MATIC
const maticTokenFactory = await ethers.getContractFactory('ERC20PermitMock');
maticTokenContract = await maticTokenFactory.deploy(
maticTokenName,
maticTokenSymbol,
deployer.address,
maticTokenInitialBalance,
);
await maticTokenContract.deployed();
/*
* deploy global exit root manager
* In order to not have trouble with nonce deploy first proxy admin
*/
await upgrades.deployProxyAdmin();
if ((await upgrades.admin.getInstance()).address !== '0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0') {
firstDeployment = false;
}
const nonceProxyBridge = Number((await ethers.provider.getTransactionCount(deployer.address))) + (firstDeployment ? 3 : 2);
const nonceProxyCommittee = nonceProxyBridge + (firstDeployment ? 2 : 1);
// Always have to redeploy impl since the PolygonZkEVMGlobalExitRoot address changes
const nonceProxyCDKValidium = nonceProxyCommittee + 2;
const precalculateBridgeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyBridge });
const precalculateCommitteeAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCommittee });
const precalculateCDKValidiumAddress = ethers.utils.getContractAddress({ from: deployer.address, nonce: nonceProxyCDKValidium });
firstDeployment = false;
const PolygonZkEVMGlobalExitRootFactory = await ethers.getContractFactory('PolygonZkEVMGlobalExitRoot');
PolygonZkEVMGlobalExitRoot = await upgrades.deployProxy(PolygonZkEVMGlobalExitRootFactory, [], {
initializer: false,
constructorArgs: [precalculateCDKValidiumAddress, precalculateBridgeAddress],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
// deploy PolygonZkEVMBridge
const PolygonZkEVMBridgeFactory = await ethers.getContractFactory('PolygonZkEVMBridge');
PolygonZkEVMBridgeContract = await upgrades.deployProxy(PolygonZkEVMBridgeFactory, [], { initializer: false });
// deploy CDKDataCommittee
const cdkDataCommitteeFactory = await ethers.getContractFactory('CDKDataCommittee');
cdkDataCommitteeContract = await upgrades.deployProxy(
cdkDataCommitteeFactory,
[],
{ initializer: false },
);
// deploy CDKValidiumMock
const CDKValidiumFactory = await ethers.getContractFactory('CDKValidiumMock');
cdkValidiumContract = await upgrades.deployProxy(CDKValidiumFactory, [], {
initializer: false,
constructorArgs: [
PolygonZkEVMGlobalExitRoot.address,
maticTokenContract.address,
verifierContract.address,
PolygonZkEVMBridgeContract.address,
cdkDataCommitteeContract.address,
chainID,
0,
],
unsafeAllow: ['constructor', 'state-variable-immutable'],
});
expect(precalculateBridgeAddress).to.be.equal(PolygonZkEVMBridgeContract.address);
expect(precalculateCommitteeAddress).to.be.equal(cdkDataCommitteeContract.address);
expect(precalculateCDKValidiumAddress).to.be.equal(cdkValidiumContract.address);
await PolygonZkEVMBridgeContract.initialize(networkIDMainnet, PolygonZkEVMGlobalExitRoot.address, cdkValidiumContract.address);
await cdkValidiumContract.initialize(
{
admin: admin.address,
trustedSequencer: trustedSequencer.address,
pendingStateTimeout: pendingStateTimeoutDefault,
trustedAggregator: trustedAggregator.address,
trustedAggregatorTimeout: trustedAggregatorTimeoutDefault,
},
genesisRoot,
urlSequencer,
networkName,
version,
);
// fund sequencer address with Matic tokens
await maticTokenContract.transfer(trustedSequencer.address, ethers.utils.parseEther('1000'));
const proposers = [deployer.address];
const executors = [deployer.address];
const adminAddress = deployer.address;
const timelockContractFactory = await ethers.getContractFactory('CDKValidiumTimelock');
timelockContract = await timelockContractFactory.deploy(minDelay, proposers, executors, adminAddress, cdkValidiumContract.address);
await timelockContract.deployed();
});
it('Should upgrade brdige correctly', async () => {
// Upgrade the contract
const PolygonZkEVMBridgeFactoryV2 = await ethers.getContractFactory('PolygonZkEVMBridgeMock');
const PolygonZkEVMBridgeContractV2 = PolygonZkEVMBridgeFactoryV2.attach(PolygonZkEVMBridgeContract.address);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Upgrade the contract
await upgrades.upgradeProxy(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2);
await expect(await PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.equal(0);
});
it('Should transferOwnership of the proxyAdmin to the timelock', async () => {
// Upgrade the contract
const PolygonZkEVMBridgeFactoryV2 = await ethers.getContractFactory('PolygonZkEVMBridgeMock');
const PolygonZkEVMBridgeContractV2 = PolygonZkEVMBridgeFactoryV2.attach(PolygonZkEVMBridgeContract.address);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Transfer ownership to timelock
await upgrades.admin.transferProxyAdminOwnership(timelockContract.address);
// Can't upgrade the contract since it does not have the ownership
await expect(upgrades.upgradeProxy(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2))
.to.be.reverted;
const implBridgeV2Address = await upgrades.prepareUpgrade(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2);
const proxyAdmin = await upgrades.admin.getInstance();
// Use timelock
const operation = genOperation(
proxyAdmin.address,
0,
proxyAdmin.interface.encodeFunctionData(
'upgrade',
[PolygonZkEVMBridgeContract.address,
implBridgeV2Address],
),
ethers.constants.HashZero,
ethers.constants.HashZero,
);
// Schedule operation
await timelockContract.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
minDelay,
);
// Can't upgrade because the timeout didint expire yet
await expect(timelockContract.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
)).to.be.revertedWith('TimelockController: operation is not ready');
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
await ethers.provider.send('evm_increaseTime', [minDelay]);
await timelockContract.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
);
await expect(await PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.equal(0);
});
it('Should check thet in emergency state the minDelay is 0', async () => {
// Upgrade the contract
const PolygonZkEVMBridgeFactoryV2 = await ethers.getContractFactory('PolygonZkEVMBridgeMock');
const PolygonZkEVMBridgeContractV2 = PolygonZkEVMBridgeFactoryV2.attach(PolygonZkEVMBridgeContract.address);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Transfer ownership to timelock
// Can't upgrade the contract since it does not have the ownership
await expect(upgrades.upgradeProxy(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2))
.to.be.reverted;
const implBridgeV2Address = await upgrades.prepareUpgrade(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2);
const proxyAdmin = await upgrades.admin.getInstance();
// Use timelock
const operation = genOperation(
proxyAdmin.address,
0,
proxyAdmin.interface.encodeFunctionData(
'upgrade',
[PolygonZkEVMBridgeContract.address,
implBridgeV2Address],
),
ethers.constants.HashZero,
ethers.constants.HashZero,
);
// Check current delay
expect(await timelockContract.getMinDelay()).to.be.equal(minDelay);
// Put CDKValidium contract on emergency mode
await cdkValidiumContract.activateEmergencyState(0);
// Check delay is 0
expect(await timelockContract.getMinDelay()).to.be.equal(0);
// Schedule operation
await timelockContract.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
0,
);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Transaction cna be executed, delay is reduced to 0, but fails bc this timelock is not owner
await expect(timelockContract.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
)).to.be.revertedWith('TimelockController: underlying transaction reverted');
});
it('Should reprocude L2 enviromanet and check upgradability', async () => {
const timelockContractFactory = await ethers.getContractFactory('CDKValidiumTimelock');
const proposers = [deployer.address];
const executors = [deployer.address];
const adminAddress = deployer.address;
const timelockContractL2 = await timelockContractFactory.deploy(
minDelay,
proposers,
executors,
adminAddress,
ethers.constants.AddressZero,
);
await timelockContractL2.deployed();
// Check deploy parameters
expect(await timelockContractL2.getMinDelay()).to.be.equal(minDelay);
expect(await timelockContractL2.cdkValidium()).to.be.equal(ethers.constants.AddressZero);
// Upgrade the contract
const PolygonZkEVMBridgeFactoryV2 = await ethers.getContractFactory('PolygonZkEVMBridgeMock');
const PolygonZkEVMBridgeContractV2 = PolygonZkEVMBridgeFactoryV2.attach(PolygonZkEVMBridgeContract.address);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Transfer ownership to timelock
// Can't upgrade the contract since it does not have the ownership
await expect(upgrades.upgradeProxy(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2))
.to.be.reverted;
const implBridgeV2Address = await upgrades.prepareUpgrade(PolygonZkEVMBridgeContract.address, PolygonZkEVMBridgeFactoryV2);
const proxyAdmin = await upgrades.admin.getInstance();
// Use timelock
const operation = genOperation(
proxyAdmin.address,
0,
proxyAdmin.interface.encodeFunctionData(
'upgrade',
[PolygonZkEVMBridgeContract.address,
implBridgeV2Address],
),
ethers.constants.HashZero,
ethers.constants.HashZero,
);
// Check current delay
expect(await timelockContractL2.getMinDelay()).to.be.equal(minDelay);
/*
* Put CDKValidium contract on emergency mode
* Does not affect thsi deployment
*/
await cdkValidiumContract.activateEmergencyState(0);
// Check delay is 0
expect(await timelockContractL2.getMinDelay()).to.be.equal(minDelay);
// Schedule operation
await expect(timelockContractL2.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
0,
)).to.be.revertedWith('TimelockController: insufficient delay');
await timelockContractL2.schedule(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
minDelay,
);
// Check that is the v0 contract
await expect(PolygonZkEVMBridgeContractV2.maxEtherBridge()).to.be.reverted;
// Transaction cna be executed, delay is reduced to 0, but fails bc this timelock is not owner
await expect(timelockContractL2.execute(
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
)).to.be.revertedWith('TimelockController: operation is not ready');
});
});
## Requirements
- node version: 14.x
- npm version: 7.x
## Upgrade
```
npm i
cp .env.example .env
```
Fill `.env` with your `MNEMONIC` and `INFURA_PROJECT_ID`
In order to upgrade the contracts we will need the information on `deployments/${network}_$(date +%s)`
In project root, copy the `${network}.json` of the deployment that you want to upgrade and copy it on the `./.openzeppelin`
e.g. `cp deployments/${network}_$(date +%s)/${network}.json ./.openzeppelin`
Then fill the upgrade parameters:
```
cd deployment
cp upgrade_parameters.json.example upgrade_parameters.json
```
Fill created `upgrade_parameters.json` with appropiate parameters.
You should fullfill the upgrades array, with all the updates that you intend to do ( more information in `upgrade-parameters.json` section)
if the deployment was deployed without a timelock you can use the `simpleUpgradeScript.js`:
- Run the script
Otherwise, in case of timelock use `timeLockUpgrade.js`
- Run the script
- Now the necessary transactions to interact with the timelock are printed in the screen `schedule` and `execute`, also will be saved in
`./upgrade_output_${new Date().getTime() / 1000}.json` file
- With the owner of the timelock (multisig or account), send the data printed by `schedule` to the `Timelock` contract.
- Once the necessary `timelockMinDelay` has expired, with the same account you can now send the data printed by `execute` to the `Timelock` contract and the contracts will be upgraded.
## upgrade-parameters.json
- `timelockMinDelay`: number, timelock delay between the schedule and execution, must be bigger than current min delay
- `upgrades`: Object, Indicates which address and to which implementation must upgrade
- address: address of the current proxy
- contractName: string, contract name that the proxy will be updated to
- constructorArgs: Array, optional, constructor arguments of the new implementation deployed
### Optional Parameters
- `multiplierGas`: number, Gas multiplier. If maxFeePerGas and maxPriorityFeePerGas are set, will not take effect
- `deployerPvtKey`: string, deployerPvtKey of the deployer
- `timelockSalt`: string, Optional salt for the timelock
module.exports = [
'0x6407cf296a27B38fd29c401518504D388F1DFB3d',
'0xF1b13757bcF3EF902a7847f409A6068BA43a89D4',
'0x4ceB990D2E2ee6d0e163fa80d12bac72C0F28D52',
'0xcFA773Cc48FBde3CA4D24eeCb19D224d697026b2',
1440,
2
]
\ No newline at end of file
/* eslint-disable no-await-in-loop */
/* eslint-disable no-console, no-inner-declarations, no-undef, import/no-unresolved */
const { ethers } = require('hardhat');
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
async function main() {
let currentProvider = ethers.provider;
let deployer;
if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
console.log("using mnemonic", deployer.address)
} else {
[deployer] = (await ethers.getSigners());
}
/*
* Deploy verifier
*/
const VerifierRollup = await ethers.getContractFactory('FflonkVerifier', deployer);
const verifierContract = await VerifierRollup.deploy();
await verifierContract.deployed();
console.log('#######################\n');
console.log('Verifier deployed to:', verifierContract.address);
console.log("you can verify the new verifier address with:")
console.log(`npx hardhat verify ${verifierContract.address} --network ${process.env.HARDHAT_NETWORK}`);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
/* eslint-disable no-console, no-unused-vars */
const hre = require('hardhat');
const { ethers, upgrades } = require('hardhat');
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const upgradeParameters = require('./upgrade_parameters.json');
async function main() {
// Set multiplier Gas
let currentProvider = ethers.provider;
if (upgradeParameters.multiplierGas) {
if (process.env.HARDHAT_NETWORK !== 'hardhat') {
const multiplierGas = upgradeParameters.multiplierGas;
currentProvider = new ethers.providers.JsonRpcProvider(`https://${process.env.HARDHAT_NETWORK}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
async function overrideFeeData() {
const feedata = await ethers.provider.getFeeData();
return {
maxFeePerGas: feedata.maxFeePerGas.mul(multiplierGas),
maxPriorityFeePerGas: feedata.maxPriorityFeePerGas.mul(multiplierGas),
};
}
currentProvider.getFeeData = overrideFeeData;
}
}
let deployer;
if (upgradeParameters.deployerPvtKey) {
deployer = new ethers.Wallet(upgradeParameters.deployerPvtKey, currentProvider);
} else if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
} else {
[deployer] = (await ethers.getSigners());
}
// compìle contracts
await hre.run('compile');
for (const upgrade of upgradeParameters.upgrades) {
const proxyPolygonAddress = upgrade.address;
const cdkValidiumFactory = await ethers.getContractFactory(upgrade.contractName, deployer);
if (upgrade.constructorArgs) {
const txZKEVM = await upgrades.upgradeProxy(proxyPolygonAddress, cdkValidiumFactory,
{
constructorArgs: upgrade.constructorArgs,
unsafeAllow: ['constructor', 'state-variable-immutable'],
call: {fn: upgrade.callAfterUpgrade.functionName, args: upgrade.callAfterUpgrade.arguments}
});
console.log(txZKEVM.deployTransaction);
console.log(await txZKEVM.deployTransaction.wait());
console.log('upgrade succesfull', upgrade.contractName);
console.log(txZKEVM.address);
console.log("you can verify the new impl address with:")
console.log(`npx hardhat verify --constructor-args upgrade/arguments.js ${txZKEVM.address} --network ${process.env.HARDHAT_NETWORK}\n`);
console.log("Copy the following constructor arguments on: upgrade/arguments.js \n", upgrade.constructorArgs)
} else {
const txZKEVM = await upgrades.upgradeProxy(proxyPolygonAddress, cdkValidiumFactory)
console.log(txZKEVM.address);
console.log("you can verify the new impl address with:")
console.log(`npx hardhat verify ${txZKEVM.address} --network ${process.env.HARDHAT_NETWORK}`);
}
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
/* eslint-disable no-console, no-unused-vars, no-use-before-define */
const hre = require('hardhat');
const { ethers, upgrades } = require('hardhat');
const path = require('path');
require('dotenv').config({ path: path.resolve(__dirname, '../.env') });
const fs = require('fs');
const upgradeParameters = require('./upgrade_parameters.json');
const pathOutputJson = path.join(__dirname, `./upgrade_output_${new Date().getTime() / 1000}.json`);
async function main() {
// Set multiplier Gas
let currentProvider = ethers.provider;
if (upgradeParameters.multiplierGas) {
if (process.env.HARDHAT_NETWORK !== 'hardhat') {
const multiplierGas = upgradeParameters.multiplierGas;
currentProvider = new ethers.providers.JsonRpcProvider(`https://${process.env.HARDHAT_NETWORK}.infura.io/v3/${process.env.INFURA_PROJECT_ID}`);
async function overrideFeeData() {
const feedata = await ethers.provider.getFeeData();
return {
maxFeePerGas: feedata.maxFeePerGas.mul(multiplierGas),
maxPriorityFeePerGas: feedata.maxPriorityFeePerGas.mul(multiplierGas),
};
}
currentProvider.getFeeData = overrideFeeData;
}
}
// Check contract name existence
for (const upgrade of upgradeParameters.upgrades) {
await ethers.getContractFactory(upgrade.contractName);
}
let deployer;
if (upgradeParameters.deployerPvtKey) {
deployer = new ethers.Wallet(upgradeParameters.deployerPvtKey, currentProvider);
} else if (process.env.MNEMONIC) {
deployer = ethers.Wallet.fromMnemonic(process.env.MNEMONIC, 'm/44\'/60\'/0\'/0/0').connect(currentProvider);
console.log("using mnemonic", deployer.address)
} else {
[deployer] = (await ethers.getSigners());
}
// compìle contracts
await hre.run('compile');
const proxyAdmin = await upgrades.admin.getInstance();
const output = [];
// Upgrade cdkValidium
for (const upgrade of upgradeParameters.upgrades) {
const proxyPolygonAddress = upgrade.address;
const cdkValidiumFactory = await ethers.getContractFactory(upgrade.contractName, deployer);
let newImplPolygonAddress;
if (upgrade.constructorArgs) {
newImplPolygonAddress = await upgrades.prepareUpgrade(proxyPolygonAddress, cdkValidiumFactory,
{
constructorArgs: upgrade.constructorArgs,
unsafeAllow: ['constructor', 'state-variable-immutable'],
},
);
console.log({ newImplPolygonAddress });
console.log("you can verify the new impl address with:")
console.log(`npx hardhat verify --constructor-args upgrade/arguments.js ${newImplPolygonAddress} --network ${process.env.HARDHAT_NETWORK}\n`);
console.log("Copy the following constructor arguments on: upgrade/arguments.js \n", upgrade.constructorArgs)
} else {
newImplPolygonAddress = await upgrades.prepareUpgrade(proxyPolygonAddress, cdkValidiumFactory);
console.log({ newImplPolygonAddress });
console.log("you can verify the new impl address with:")
console.log(`npx hardhat verify ${newImplPolygonAddress} --network ${process.env.HARDHAT_NETWORK}`);
}
// Use timelock
const salt = upgradeParameters.timelockSalt || ethers.constants.HashZero;
let operation;
if (upgrade.callAfterUpgrade) {
operation = genOperation(
proxyAdmin.address,
0, // value
proxyAdmin.interface.encodeFunctionData(
'upgradeAndCall',
[
proxyPolygonAddress,
newImplPolygonAddress,
cdkValidiumFactory.interface.encodeFunctionData(
upgrade.callAfterUpgrade.functionName,
upgrade.callAfterUpgrade.arguments,
)
],
),
ethers.constants.HashZero, // predecesoor
salt, // salt
);
} else {
operation = genOperation(
proxyAdmin.address,
0, // value
proxyAdmin.interface.encodeFunctionData(
'upgrade',
[proxyPolygonAddress,
newImplPolygonAddress],
),
ethers.constants.HashZero, // predecesoor
salt, // salt
);
}
// Timelock operations
const TimelockFactory = await ethers.getContractFactory('CDKValidiumTimelock', deployer);
const minDelay = upgradeParameters.timelockMinDelay || 0;
// Schedule operation
const scheduleData = TimelockFactory.interface.encodeFunctionData(
'schedule',
[
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
minDelay,
],
);
// Execute operation
const executeData = TimelockFactory.interface.encodeFunctionData(
'execute',
[
operation.target,
operation.value,
operation.data,
operation.predecessor,
operation.salt,
],
);
console.log({ scheduleData });
console.log({ executeData });
output.push({
contractName: upgrade.contractName,
scheduleData,
executeData
})
}
fs.writeFileSync(pathOutputJson, JSON.stringify(output, null, 1));
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
// OZ test functions
function genOperation(target, value, data, predecessor, salt) {
const id = ethers.utils.solidityKeccak256([
'address',
'uint256',
'bytes',
'uint256',
'bytes32',
], [
target,
value,
data,
predecessor,
salt,
]);
return {
id, target, value, data, predecessor, salt,
};
}
{
"timelockMinDelay":10,
"upgrades": [
{
"address": "0xEfF10DB3c6445FB06411c6fc74fDCC8D1019aC7d",
"contractName": "CDKValidiumTestnetV2",
"constructorArgs": [
"0x6407cf296a27B38fd29c401518504D388F1DFB3d",
"0xF1b13757bcF3EF902a7847f409A6068BA43a89D4",
"0x4ceB990D2E2ee6d0e163fa80d12bac72C0F28D52",
"0xcFA773Cc48FBde3CA4D24eeCb19D224d697026b2",
1440,
2
],
"callAfterUpgrade":
{
"functionName": "updateVersion",
"arguments": [
"v0.8.0.0-rc.2-forkid.2"
]
}
}
],
"multiplierGas": 0,
"deployerPvtKey": "",
"timelockSalt": ""
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment