diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..2fb19e9
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,35 @@
+name: Mirroring
+
+on:
+ push:
+ branches:
+ - main
+
+jobs:
+ to_bitbucket:
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps: # <-- must use actions/checkout before mirroring!
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: pixta-dev/repository-mirroring-action@v1
+ with:
+ target_repo_url:
+ git@bitbucket.org:punk-domains/modechat-contracts.git
+ ssh_private_key: # <-- use 'secrets' to pass credential information.
+ ${{ secrets.BITBUCKET_SSH_PRIVATE_KEY }}
+
+ to_gitlab:
+ runs-on: ubuntu-latest
+ timeout-minutes: 20
+ steps: # <-- must use actions/checkout before mirroring!
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - uses: pixta-dev/repository-mirroring-action@v1
+ with:
+ target_repo_url:
+ git@gitlab.com:iggy-social/modechat-contracts.git
+ ssh_private_key: # <-- use 'secrets' to pass credential information.
+ ${{ secrets.BITBUCKET_SSH_PRIVATE_KEY }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..06d37d4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,114 @@
+.idea
+flattened
+venv
+.openzeppelin
+*.svg
+
+#Hardhat files
+cache
+artifacts
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# Next.js build output
+.next
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and *not* Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is 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. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ 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.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ 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 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. Use with the GNU Affero General Public License.
+
+ 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 Affero 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 special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 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 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 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
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/contracts/access/OwnableWithManagers.sol b/contracts/access/OwnableWithManagers.sol
new file mode 100644
index 0000000..5252fbe
--- /dev/null
+++ b/contracts/access/OwnableWithManagers.sol
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity 0.8.17;
+
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+/**
+@title Extended Ownable contract with managers functionality
+@author Tempe Techie
+*/
+abstract contract OwnableWithManagers is Ownable {
+ address[] public managers; // array of managers
+ mapping (address => bool) public isManager; // mapping of managers
+
+ // MODIFIERS
+ modifier onlyManagerOrOwner() {
+ require(isManager[msg.sender] || msg.sender == owner(), "OwnableWithManagers: caller is not a manager or owner");
+ _;
+ }
+
+ // EVENTS
+ event ManagerAdd(address indexed owner_, address indexed manager_);
+ event ManagerRemove(address indexed owner_, address indexed manager_);
+
+ // READ
+ function getManagers() external view returns (address[] memory) {
+ return managers;
+ }
+
+ function getManagersLength() external view returns (uint256) {
+ return managers.length;
+ }
+
+ // MANAGER
+
+ function removeYourselfAsManager() external onlyManagerOrOwner {
+ address manager_ = msg.sender;
+
+ isManager[manager_] = false;
+ uint256 length = managers.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (managers[i] == manager_) {
+ managers[i] = managers[length - 1];
+ managers.pop();
+ emit ManagerRemove(msg.sender, manager_);
+ return;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+ }
+
+ // OWNER
+
+ function addManager(address manager_) external onlyOwner {
+ require(!isManager[manager_], "OwnableWithManagers: manager already added");
+ isManager[manager_] = true;
+ managers.push(manager_);
+ emit ManagerAdd(msg.sender, manager_);
+ }
+
+ function removeManagerByAddress(address manager_) external onlyOwner {
+ isManager[manager_] = false;
+ uint256 length = managers.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (managers[i] == manager_) {
+ managers[i] = managers[length - 1];
+ managers.pop();
+ emit ManagerRemove(msg.sender, manager_);
+ return;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+ }
+
+ function removeManagerByIndex(uint256 index_) external onlyOwner {
+ emit ManagerRemove(msg.sender, managers[index_]);
+ isManager[managers[index_]] = false;
+ managers[index_] = managers[managers.length - 1];
+ managers.pop();
+ }
+}
\ No newline at end of file
diff --git a/contracts/activity-points/ActivityPoints.sol b/contracts/activity-points/ActivityPoints.sol
new file mode 100644
index 0000000..b996a2d
--- /dev/null
+++ b/contracts/activity-points/ActivityPoints.sol
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity 0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+interface IStats {
+ function getWeiSpent(address user_) external view returns (uint256);
+ function weiSpentTotal() external view returns (uint256);
+}
+
+/**
+@title Collect all wei spending stats from different contracts and return them as one
+@author Tempe Techie
+*/
+contract ActivityPoints is OwnableWithManagers {
+ address public statsAddress; // stats for NFT launchpad, Friend Keys, Swap etc.
+ address public mintedPostsStatsAddress;
+ address public tldStatsAddress;
+
+ uint256 public bonusWeiTotal; // total bonus wei (without multiplier)
+
+ uint256 public multiplier; // multiplier for points (e.g. 1 means 1 wei spent = 1 point)
+
+ mapping (address => uint256) public bonusWei; // bonus wei (without multiplier)
+
+ // EVENTS
+ event BonusPointsAdded(address indexed manager_, address indexed user_, uint256 bp_);
+ event BonusPointsRemoved(address indexed manager_, address indexed user_, uint256 bp_);
+
+ constructor(
+ address _statsAddress,
+ address _mintedPostsStatsAddress,
+ address _tldStatsAddress,
+ uint256 _multiplier,
+ address _sfsAddress,
+ uint256 _tokenId
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+ statsAddress = _statsAddress;
+ mintedPostsStatsAddress = _mintedPostsStatsAddress;
+ tldStatsAddress = _tldStatsAddress;
+ multiplier = _multiplier;
+ }
+
+ // READ
+
+ function getPoints(address user_) external view returns (uint256) {
+ return (bonusWei[user_] + getTotalWeiSpent(user_)) * multiplier;
+ }
+
+ function getTotalPointsAllUsers() external view returns (uint256) {
+ return (bonusWeiTotal + getTotalWeiSpentAllUsers()) * multiplier;
+ }
+
+ function getTotalWeiSpent(address _user) public view returns (uint256) {
+ uint256 totalWeiSpent;
+
+ if (statsAddress != address(0)) {
+ totalWeiSpent += IStats(statsAddress).getWeiSpent(_user);
+ }
+
+ if (mintedPostsStatsAddress != address(0)) {
+ totalWeiSpent += IStats(mintedPostsStatsAddress).getWeiSpent(_user);
+ }
+
+ if (tldStatsAddress != address(0)) {
+ totalWeiSpent += IStats(tldStatsAddress).getWeiSpent(_user);
+ }
+
+ return totalWeiSpent;
+ }
+
+ function getTotalWeiSpentAllUsers() public view returns (uint256) {
+ uint256 totalWeiSpent;
+
+ if (statsAddress != address(0)) {
+ totalWeiSpent += IStats(statsAddress).weiSpentTotal();
+ }
+
+ if (mintedPostsStatsAddress != address(0)) {
+ totalWeiSpent += IStats(mintedPostsStatsAddress).weiSpentTotal();
+ }
+
+ if (tldStatsAddress != address(0)) {
+ totalWeiSpent += IStats(tldStatsAddress).weiSpentTotal();
+ }
+
+ return totalWeiSpent;
+ }
+
+ // OWNER
+
+ /// @notice These points already include the multiplier
+ function addBonusPoints(address _user, uint256 _bp) external onlyManagerOrOwner {
+ bonusWei[_user] += _bp / multiplier;
+ bonusWeiTotal += _bp / multiplier;
+ emit BonusPointsAdded(msg.sender, _user, _bp);
+ }
+
+ /// @notice These points already include the multiplier
+ function removeBonusPoints(address _user, uint256 _bp) external onlyManagerOrOwner {
+ require(bonusWei[_user] >= _bp / multiplier, "ActivityPoints: not enough bonus points");
+ bonusWei[_user] -= _bp / multiplier;
+ bonusWeiTotal -= _bp / multiplier;
+ emit BonusPointsRemoved(msg.sender, _user, _bp);
+ }
+
+ /// @notice Bonus wei does not include the multiplier
+ function addBonusWei(address _user, uint256 _wei) external onlyManagerOrOwner {
+ bonusWei[_user] += _wei;
+ bonusWeiTotal += _wei;
+ }
+
+ /// @notice Bonus wei does not include the multiplier
+ function removeBonusWei(address _user, uint256 _wei) external onlyManagerOrOwner {
+ require(bonusWei[_user] >= _wei, "ActivityPoints: not enough bonus wei");
+ bonusWei[_user] -= _wei;
+ bonusWeiTotal -= _wei;
+ }
+
+ function setMintedPostsStatsAddress(address _mintedPostsStatsAddress) external onlyManagerOrOwner {
+ mintedPostsStatsAddress = _mintedPostsStatsAddress;
+ }
+
+ function setMultiplier(uint256 _multiplier) external onlyManagerOrOwner {
+ multiplier = _multiplier;
+ }
+
+ function setStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ function setTldStatsAddress(address _tldStatsAddress) external onlyManagerOrOwner {
+ tldStatsAddress = _tldStatsAddress;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/activity-points/ActivityPointsAlt.sol b/contracts/activity-points/ActivityPointsAlt.sol
new file mode 100644
index 0000000..703b51e
--- /dev/null
+++ b/contracts/activity-points/ActivityPointsAlt.sol
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity 0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+
+interface IPunkTLD {
+ function balanceOf(address) external view returns (uint256);
+ function idCounter() external view returns (uint256);
+}
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+interface IStats {
+ function getWeiSpent(address user_) external view returns (uint256);
+ function weiSpentTotal() external view returns (uint256);
+}
+
+/**
+@title Collect all wei spending stats from different contracts and return them as one
+@author Tempe Techie
+*/
+contract ActivityPointsAlt is OwnableWithManagers {
+ address public statsAddress; // stats for NFT launchpad, Friend Keys, Swap etc.
+ address public mintedPostsStatsAddress;
+ address public immutable tldAddress;
+
+ uint256 public bonusWeiTotal; // total bonus wei (without multiplier)
+ uint256 public multiplier; // multiplier for points (e.g. 1 means 1 wei spent = 1 point)
+ uint256 public weiPerDomain = 1690000000000000000;
+
+ mapping (address => uint256) public bonusWei; // bonus wei (without multiplier)
+
+ // EVENTS
+ event BonusPointsAdded(address indexed manager_, address indexed user_, uint256 bp_);
+ event BonusPointsRemoved(address indexed manager_, address indexed user_, uint256 bp_);
+
+ constructor(
+ address _statsAddress,
+ address _mintedPostsStatsAddress,
+ address _tldAddress,
+ uint256 _multiplier,
+ address _sfsAddress,
+ uint256 _tokenId
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+ statsAddress = _statsAddress;
+ mintedPostsStatsAddress = _mintedPostsStatsAddress;
+ tldAddress = _tldAddress;
+ multiplier = _multiplier;
+ }
+
+ // READ
+
+ function getPoints(address user_) external view returns (uint256) {
+ return (bonusWei[user_] + getTotalWeiSpent(user_)) * multiplier;
+ }
+
+ function getTotalPointsAllUsers() external view returns (uint256) {
+ return (bonusWeiTotal + getTotalWeiSpentAllUsers()) * multiplier;
+ }
+
+ function getTotalWeiSpent(address _user) public view returns (uint256) {
+ uint256 totalWeiSpent;
+
+ if (statsAddress != address(0)) {
+ totalWeiSpent += IStats(statsAddress).getWeiSpent(_user);
+ }
+
+ if (mintedPostsStatsAddress != address(0)) {
+ totalWeiSpent += IStats(mintedPostsStatsAddress).getWeiSpent(_user);
+ }
+
+ // check how many domains the user owns
+ uint256 domainsOwned = IPunkTLD(tldAddress).balanceOf(_user);
+ totalWeiSpent += domainsOwned * weiPerDomain;
+
+ return totalWeiSpent;
+ }
+
+ function getTotalWeiSpentAllUsers() public view returns (uint256) {
+ uint256 totalWeiSpent;
+
+ if (statsAddress != address(0)) {
+ totalWeiSpent += IStats(statsAddress).weiSpentTotal();
+ }
+
+ if (mintedPostsStatsAddress != address(0)) {
+ totalWeiSpent += IStats(mintedPostsStatsAddress).weiSpentTotal();
+ }
+
+ // check how many domains exist (idCounter-1)
+ uint256 domainsSupply = IPunkTLD(tldAddress).idCounter() - 1;
+ totalWeiSpent += domainsSupply * weiPerDomain;
+
+ return totalWeiSpent;
+ }
+
+ // OWNER
+
+ /// @notice These points already include the multiplier
+ function addBonusPoints(address _user, uint256 _bp) external onlyManagerOrOwner {
+ bonusWei[_user] += _bp / multiplier;
+ bonusWeiTotal += _bp / multiplier;
+ emit BonusPointsAdded(msg.sender, _user, _bp);
+ }
+
+ /// @notice These points already include the multiplier
+ function removeBonusPoints(address _user, uint256 _bp) external onlyManagerOrOwner {
+ require(bonusWei[_user] >= _bp / multiplier, "ActivityPoints: not enough bonus points");
+ bonusWei[_user] -= _bp / multiplier;
+ bonusWeiTotal -= _bp / multiplier;
+ emit BonusPointsRemoved(msg.sender, _user, _bp);
+ }
+
+ /// @notice Bonus wei does not include the multiplier
+ function addBonusWei(address _user, uint256 _wei) external onlyManagerOrOwner {
+ bonusWei[_user] += _wei;
+ bonusWeiTotal += _wei;
+ }
+
+ /// @notice Bonus wei does not include the multiplier
+ function removeBonusWei(address _user, uint256 _wei) external onlyManagerOrOwner {
+ require(bonusWei[_user] >= _wei, "ActivityPoints: not enough bonus wei");
+ bonusWei[_user] -= _wei;
+ bonusWeiTotal -= _wei;
+ }
+
+ function setMintedPostsStatsAddress(address _mintedPostsStatsAddress) external onlyManagerOrOwner {
+ mintedPostsStatsAddress = _mintedPostsStatsAddress;
+ }
+
+ function setMultiplier(uint256 _multiplier) external onlyManagerOrOwner {
+ multiplier = _multiplier;
+ }
+
+ function setStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ function setWeiPerDomain(uint256 _weiPerDomain) external onlyManagerOrOwner {
+ weiPerDomain = _weiPerDomain;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/custom/IggyPostMinterV2Ambassadors.sol b/contracts/custom/IggyPostMinterV2Ambassadors.sol
new file mode 100644
index 0000000..7ca4f96
--- /dev/null
+++ b/contracts/custom/IggyPostMinterV2Ambassadors.sol
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+interface IChatTokenMinter {
+ function mint(address to, uint256 amount) external;
+}
+
+interface IIggyPostNft {
+
+ function getPostPrice (string memory _postId, address _author) external view returns (uint256);
+
+ function owner() external view returns(address);
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external returns(uint256);
+
+}
+
+interface IIggyPostStats {
+ function addMintedPostId(address _user, uint256 _postId) external;
+}
+
+/**
+@title IggyPostMinterV2Ambassadors
+@notice This contract allows users to mint IggyPost NFTs and paying with ETH.
+@dev Use this contract when CHAT token is deployed.
+*/
+contract IggyPostMinterV2Ambassadors is OwnableWithManagers, ReentrancyGuard {
+ address public immutable chatTokenMinterAddress;
+ address public daoAddress;
+ address public devAddress;
+ address public devFeeUpdaterAddress;
+ address public statsAddress;
+ address public immutable postAddress;
+ address public stakingAddress; // address of the staking contract
+
+ address public ambassador1;
+ address public ambassador2;
+
+ bool public statsEnabled = true; // so that users see minted posts in their profile
+ bool public paused = false;
+
+ uint256 public chatEthRatio = 5; // e.g. 1_000, which means 1 ETH (or payment token) = 1,000 CHAT
+ uint256 public chatRewardsDuration = block.timestamp + (60 * 60 * 24 * 30 * 9); // CHAT rewards duration in seconds
+ uint256 public chatRewardsEnd; // timestamp when CHAT rewards end
+
+ uint256 public constant MAX_BPS = 10_000;
+ uint256 public daoFee = 0; // share of each domain purchase (in bips) that goes to the DAO/community that owns the frontend
+ uint256 public devFee = 200; // share of each domain purchase (in bips) that goes to the developer (Iggy team)
+ uint256 public ambassadorFee = 100; // fee for one ambassador (in bips)
+ uint256 public referrerFee = 200; // share of each domain purchase (in bips) that goes to the referrer
+ uint256 public stakingFee = 1600; // share of each domain purchase (in bips) that goes to the staking contract
+
+ // CONSTRUCTOR
+ constructor(
+ address _chatTokenMinterAddress,
+ address _daoAddress,
+ address _devAddress,
+ address _devFeeUpdaterAddress,
+ address _postAddress,
+ address _stakingAddress,
+ address _ambassador1,
+ address _ambassador2
+ ) {
+ require(_chatTokenMinterAddress != address(0), "IggyPostMinterV2: CHAT token cannot be zero address");
+ require(_postAddress != address(0), "IggyPostMinterV2: Post address cannot be zero address");
+
+ chatTokenMinterAddress = _chatTokenMinterAddress;
+ daoAddress = _daoAddress;
+ devAddress = _devAddress;
+ devFeeUpdaterAddress = _devFeeUpdaterAddress;
+ postAddress = _postAddress;
+ stakingAddress = _stakingAddress;
+
+ ambassador1 = _ambassador1;
+ ambassador2 = _ambassador2;
+ }
+
+ // READ
+
+ function getCurrentChatEthRatio() public view returns(uint256) {
+ // if chat rewards period ended, return 0
+ if (block.timestamp > chatRewardsEnd) {
+ return 0;
+ }
+
+ uint256 diff = chatRewardsEnd - block.timestamp;
+ uint256 diffRatio = (diff * MAX_BPS) / chatRewardsDuration;
+
+ return (chatEthRatio * diffRatio) / MAX_BPS;
+ }
+
+ // WRITE
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ address _referrer,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external nonReentrant payable returns(uint256 tokenId) {
+ require(!paused, "Minting paused");
+
+ // find price
+ uint256 price = IIggyPostNft(postAddress).getPostPrice(_postId, _author) * _quantity;
+
+ require(msg.value >= price, "Value below price");
+
+ // send a referrer fee
+ if (referrerFee > 0 && _referrer != address(0)) {
+ uint256 referrerPayment = (price * referrerFee) / MAX_BPS;
+ (bool sentReferrerFee, ) = _referrer.call{value: referrerPayment}("");
+ require(sentReferrerFee, "Failed to send referrer fee");
+ }
+
+ // send an ambassador1 fee
+ if (ambassadorFee > 0 && ambassador1 != address(0)) {
+ uint256 ambassador1Payment = (price * ambassadorFee) / MAX_BPS;
+ (bool sentAmbassador1Fee, ) = ambassador1.call{value: ambassador1Payment}("");
+ require(sentAmbassador1Fee, "Failed to send ambassador1 fee");
+ }
+
+ // send an ambassador2 fee
+ if (ambassadorFee > 0 && ambassador2 != address(0)) {
+ uint256 ambassador2Payment = (price * ambassadorFee) / MAX_BPS;
+ (bool sentAmbassador2Fee, ) = ambassador2.call{value: ambassador2Payment}("");
+ require(sentAmbassador2Fee, "Failed to send ambassador2 fee");
+ }
+
+ // send a dev fee
+ if (devFee > 0 && devAddress != address(0)) {
+ uint256 devPayment = (price * devFee) / MAX_BPS;
+ (bool sentDevFee, ) = devAddress.call{value: devPayment}("");
+ require(sentDevFee, "Failed to send dev fee");
+ }
+
+ // send a dao fee
+ if (daoFee > 0 && daoAddress != address(0)) {
+ uint256 daoFeePayment = (price * daoFee) / MAX_BPS;
+ (bool sentDaoFee, ) = daoAddress.call{value: daoFeePayment}("");
+ require(sentDaoFee, "Failed to send dao fee");
+ }
+
+ // send a staking fee
+ if (stakingFee > 0 && stakingAddress != address(0)) {
+ uint256 stakingFeePayment = (price * stakingFee) / MAX_BPS;
+ (bool sentStakingFee, ) = stakingAddress.call{value: stakingFeePayment}("");
+ require(sentStakingFee, "Failed to send staking fee");
+ }
+
+ // send the rest to post author
+ (bool sent, ) = _author.call{value: address(this).balance}("");
+ require(sent, "Failed to send payment to the post author");
+
+ // mint the post as NFT
+ tokenId = IIggyPostNft(postAddress).mint(_postId, _author, _nftReceiver, _textPreview, _image, _quantity);
+
+ // store some stats in the stats contract
+ if (statsEnabled && statsAddress != address(0)) {
+ // feel free to comment out the stats that you don't need to track
+ IIggyPostStats(statsAddress).addMintedPostId(_nftReceiver, tokenId);
+ }
+
+ // mint chat tokens for the NFT receiver (use only the fees to calculate the share of chat tokens, not the whole price)
+ if (chatTokenMinterAddress != address(0) && block.timestamp <= chatRewardsEnd) {
+ uint256 fees = (price * (devFee + daoFee + stakingFee + referrerFee)) / MAX_BPS;
+ IChatTokenMinter(chatTokenMinterAddress).mint(_nftReceiver, fees*getCurrentChatEthRatio());
+ }
+ }
+
+ // OWNER
+
+ // change ambassador1 address
+ function changeAmbassador1(address _ambassador1) external onlyManagerOrOwner {
+ ambassador1 = _ambassador1;
+ }
+
+ // change ambassador2 address
+ function changeAmbassador2(address _ambassador2) external onlyManagerOrOwner {
+ ambassador2 = _ambassador2;
+ }
+
+ // change chat token ratio
+ function changeChatEthRatio(uint256 _chatEthRatio) external onlyOwner {
+ chatEthRatio = _chatEthRatio;
+ }
+
+ /// @notice This changes the DAO address in the minter contract
+ function changeDaoAddress(address _daoAddress) external onlyManagerOrOwner {
+ daoAddress = _daoAddress;
+ }
+
+ // change dao fee
+ function changeDaoFee(uint256 _daoFee) external onlyManagerOrOwner {
+ require(_daoFee + devFee + referrerFee + stakingFee <= MAX_BPS, "Fees cannot be more than 100%");
+ daoFee = _daoFee;
+ }
+
+ // change the stats address
+ function changeStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ // change referrer fee
+ function changeReferrerFee(uint256 _referrerFee) external onlyManagerOrOwner {
+ require(daoFee + devFee + _referrerFee + stakingFee <= MAX_BPS, "Fees cannot be more than 100%");
+ referrerFee = _referrerFee;
+ }
+
+ function changeStakingAddress(address _stakingAddress) external onlyManagerOrOwner {
+ stakingAddress = _stakingAddress;
+ }
+
+ // change staking fee
+ function changeStakingFee(uint256 _stakingFee) external onlyManagerOrOwner {
+ require(daoFee + devFee + referrerFee + _stakingFee <= MAX_BPS, "Fees cannot be more than 100%");
+ stakingFee = _stakingFee;
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).transfer(recipient_, tokenAmount_);
+ }
+
+ function toggleStatsEnabled() external onlyManagerOrOwner {
+ statsEnabled = !statsEnabled;
+ }
+
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+
+ /// @notice Withdraw native coins from contract
+ function withdraw() external onlyManagerOrOwner {
+ (bool success, ) = owner().call{value: address(this).balance}("");
+ require(success, "Failed to withdraw native coins from contract");
+ }
+
+ // OTHER WRITE METHODS
+
+ /// @notice This changes the developer's address in the minter contract
+ function changeDevAddress(address _devAddress) external {
+ require(msg.sender == devAddress, "Sender is not the developer");
+ devAddress = _devAddress;
+ }
+
+ /// @notice This changes the dev fee updater's address in the minter contract
+ function changeDevFeeUpdaterAddress(address _devFeeUpdaterAddress) external {
+ require(msg.sender == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ devFeeUpdaterAddress = _devFeeUpdaterAddress;
+ }
+
+ // change dev fee (only dev fee updater can change it)
+ function changeDevFee(uint256 _devFee) external {
+ require(msg.sender == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ require(daoFee + _devFee + referrerFee + stakingFee <= MAX_BPS, "Fees cannot be more than 100%");
+ devFee = _devFee;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/distributor/RevenueDistributor.sol b/contracts/distributor/RevenueDistributor.sol
new file mode 100644
index 0000000..b89bbab
--- /dev/null
+++ b/contracts/distributor/RevenueDistributor.sol
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
+
+interface IERC20 {
+ function transfer(address to, uint256 amount) external returns (bool);
+}
+
+/// @title RevenueDistributor
+/// @author Tempe Techie
+/// @notice Automatically distribute revenue to multiple recipients
+contract RevenueDistributor is OwnableWithManagers, ReentrancyGuard {
+ string public constant NAME = "RevenueDistributor";
+ uint256 private constant LABEL_MAX_LENGTH = 30;
+
+ struct Recipient {
+ address addr;
+ string label;
+ uint256 percentage; // 100% = 1 ether
+ }
+
+ Recipient[] public recipients; // array of Recipient structs
+
+ // EVENTS
+ event RecipientAdd(address indexed adder_, address indexed recipient_, string label_, uint256 percentage_);
+ event RecipientRemove(address indexed remover_, address indexed recipient_);
+ event RecipientRemoveAll(address indexed remover_);
+ event RecipientUpdate(address indexed updater_, address indexed recipient_, address newAddr_, string label_, uint256 percentage_);
+ event WithdrawEth(address indexed owner_, uint256 amount_);
+
+ // READ
+
+ function getRecipient(address recipient_) external view returns (Recipient memory) {
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (recipients[i].addr == recipient_) {
+ return recipients[i];
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+
+ revert("RevenueDistributor: recipient not found");
+ }
+
+ function getRecipients() external view returns (Recipient[] memory) {
+ return recipients;
+ }
+
+ function getRecipientsLength() external view returns (uint256) {
+ return recipients.length;
+ }
+
+ function isRecipient(address addr_) external view returns (bool) {
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (recipients[i].addr == addr_) {
+ return true;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+
+ return false;
+ }
+
+ // MANAGER
+
+ function addRecipient(address addr_, string calldata label_, uint256 percentage_) external onlyManagerOrOwner {
+ require(bytes(label_).length < LABEL_MAX_LENGTH, "RevenueDistributor: label too long");
+
+ uint256 percentageTotal;
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ require(recipients[i].addr != addr_, "RevenueDistributor: recipient already in the list");
+
+ percentageTotal += recipients[i].percentage;
+
+ unchecked {
+ i++;
+ }
+ }
+
+ require(percentageTotal + percentage_ <= 1 ether, "RevenueDistributor: percentage total must be less than or equal to 100%");
+
+ recipients.push(Recipient(addr_, label_, percentage_));
+ emit RecipientAdd(msg.sender, addr_, label_, percentage_);
+ }
+
+ function removeAllRecipients() external onlyManagerOrOwner {
+ delete recipients;
+ emit RecipientRemoveAll(msg.sender);
+ }
+
+ function removeLastRecipient() external onlyManagerOrOwner {
+ emit RecipientRemove(msg.sender, recipients[recipients.length - 1].addr);
+ recipients.pop();
+ }
+
+ function removeRecipientByAddress(address recipient_) external onlyManagerOrOwner {
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (recipients[i].addr == recipient_) {
+ recipients[i] = recipients[length - 1];
+ recipients.pop();
+ emit RecipientRemove(msg.sender, recipient_);
+ return;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+ }
+
+ function removeRecipientByIndex(uint256 index_) external onlyManagerOrOwner {
+ emit RecipientRemove(msg.sender, recipients[index_].addr);
+ recipients[index_] = recipients[recipients.length - 1];
+ recipients.pop();
+ }
+
+ function updateRecipientByAddress(
+ address recipient_,
+ address newAddr_,
+ string calldata label_,
+ uint256 newPercentage_
+ ) external onlyManagerOrOwner {
+ require(bytes(label_).length < LABEL_MAX_LENGTH, "RevenueDistributor: label too long");
+
+ uint256 percentageTotal;
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (recipients[i].addr == recipient_) {
+ recipients[i].addr = newAddr_;
+ recipients[i].label = label_;
+ recipients[i].percentage = newPercentage_;
+ percentageTotal += newPercentage_;
+ emit RecipientUpdate(msg.sender, recipient_, newAddr_, label_, newPercentage_);
+ } else {
+ percentageTotal += recipients[i].percentage;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+
+ require(percentageTotal <= 1 ether, "RevenueDistributor: percentage total must be less than or equal to 100%");
+ }
+
+ function updateRecipientByIndex(
+ uint256 index_,
+ address newAddr_,
+ string calldata label_,
+ uint256 newPercentage_
+ ) external onlyManagerOrOwner {
+ require(bytes(label_).length < LABEL_MAX_LENGTH, "RevenueDistributor: label too long");
+
+ uint256 percentageTotal;
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ if (i == index_) {
+ emit RecipientUpdate(msg.sender, recipients[i].addr, newAddr_, label_, newPercentage_);
+ recipients[i].addr = newAddr_;
+ recipients[i].label = label_;
+ recipients[i].percentage = newPercentage_;
+ percentageTotal += newPercentage_;
+
+ } else {
+ percentageTotal += recipients[i].percentage;
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+
+ require(percentageTotal <= 1 ether, "RevenueDistributor: percentage total must be less than or equal to 100%");
+ }
+
+ // OWNER
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyOwner {
+ IERC20(tokenAddress_).transfer(recipient_, tokenAmount_);
+ }
+
+ /// @notice Recover any ERC-721 token mistakenly sent to this contract address
+ function recoverERC721(address tokenAddress_, uint256 tokenId_, address recipient_) external onlyOwner {
+ IERC721(tokenAddress_).transferFrom(address(this), recipient_, tokenId_);
+ }
+
+ /// @dev Manual withdrawal in case there's an excess of ETH in the contract
+ function withdrawEth() external onlyOwner {
+ emit WithdrawEth(msg.sender, address(this).balance);
+ (bool success, ) = msg.sender.call{ value: address(this).balance }("");
+ require(success, "RevenueDistributor: transfer failed");
+ }
+
+ // INTERNAL
+
+ function _distribute(uint256 value_) internal nonReentrant {
+ uint256 length = recipients.length;
+
+ for (uint256 i = 0; i < length;) {
+ address recipient = recipients[i].addr;
+
+ if (recipient != address(0)) {
+ uint256 percentage = recipients[i].percentage;
+ uint256 amount = (value_ * percentage) / 1 ether;
+
+ (bool success, ) = recipient.call{ value: amount }("");
+ require(success, "RevenueDistributor: transfer failed");
+ }
+
+ unchecked {
+ i++;
+ }
+ }
+ }
+
+ // RECEIVE
+ receive() external payable {
+ _distribute(msg.value);
+ }
+}
\ No newline at end of file
diff --git a/contracts/distributor/RevenueDistributorFactory.sol b/contracts/distributor/RevenueDistributorFactory.sol
new file mode 100644
index 0000000..84cb684
--- /dev/null
+++ b/contracts/distributor/RevenueDistributorFactory.sol
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import "./RevenueDistributor.sol";
+
+/// @title RevenueDistributor factory
+/// @author Tempe Techie
+/// @notice Factory that creates new RevenueDistributor contracts
+contract RevenueDistributorFactory {
+ uint256 private constant ID_MAX_LENGTH = 30;
+
+ // mapping(uniqueID => RevenueDistributor contract address) to easily find a RevenueDistributor contract address
+ mapping (string => address) private distributorAddressById;
+
+ // EVENTS
+ event RevenueDistributorLaunch(address indexed contractOwner_, string uniqueId_, address indexed contractAddress_);
+
+ // READ
+
+ function getDistributorAddressById(string memory uniqueId_) external view returns(address) {
+ return distributorAddressById[uniqueId_];
+ }
+
+ function isUniqueIdAvailable(string memory uniqueId_) public view returns(bool) {
+ return distributorAddressById[uniqueId_] == address(0);
+ }
+
+ // WRITE
+
+ function create(string calldata uniqueId_) external returns(address) {
+ require(bytes(uniqueId_).length <= ID_MAX_LENGTH, "Unique ID is too long");
+ require(isUniqueIdAvailable(uniqueId_), "Unique ID is not available");
+
+ bytes32 saltedHash = keccak256(abi.encodePacked(msg.sender, block.timestamp, uniqueId_));
+ RevenueDistributor distributor = new RevenueDistributor{salt: saltedHash}();
+
+ distributorAddressById[uniqueId_] = address(distributor);
+
+ distributor.transferOwnership(msg.sender);
+
+ emit RevenueDistributorLaunch(msg.sender, uniqueId_, address(distributor));
+
+ return address(distributor);
+ }
+}
\ No newline at end of file
diff --git a/contracts/interfaces/IKeyStats.sol b/contracts/interfaces/IKeyStats.sol
new file mode 100644
index 0000000..3a45100
--- /dev/null
+++ b/contracts/interfaces/IKeyStats.sol
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+interface IKeyStats {
+ function getFee(address _address) external view returns (uint256);
+ function addFee(address _address, uint256 _amount) external;
+}
\ No newline at end of file
diff --git a/contracts/interfaces/IPunkTLD.sol b/contracts/interfaces/IPunkTLD.sol
new file mode 100644
index 0000000..0c26ad8
--- /dev/null
+++ b/contracts/interfaces/IPunkTLD.sol
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+interface IPunkTLD {
+ function getDomainHolder(string calldata _domainName) external view returns(address);
+ function name() external view returns(string memory);
+}
\ No newline at end of file
diff --git a/contracts/keys/FriendKeys.sol b/contracts/keys/FriendKeys.sol
new file mode 100644
index 0000000..c5fa7f9
--- /dev/null
+++ b/contracts/keys/FriendKeys.sol
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity 0.8.17;
+
+import { IPunkTLD } from "../interfaces/IPunkTLD.sol";
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+}
+
+/**
+@title Friend Keys contract for buying and selling keys of punk domains
+@author Tempe Techie
+*/
+contract FriendKeys is OwnableWithManagers, ReentrancyGuard {
+ address public feeReceiver; // protocol fee receiver
+ address public statsAddress; // stats middleware contract
+ address public immutable tldAddress;
+
+ string public tldName;
+
+ uint256 public domainHolderFeePercent;
+ uint256 public protocolFeePercent;
+ uint256 public referrerFeeShare = 100000000000000000; // referrer's share of the protocol and domain holder fee in wei (default: 100000000000000000 wei = 10%)
+ uint256 public immutable ratio;
+ uint256 public totalVolumeWei;
+
+ // Domain => (Holder => Balance)
+ mapping(string => mapping(address => uint256)) public keysBalance;
+
+ // Domain => Supply
+ mapping(string => uint256) public keysSupply;
+
+ // EVENTS
+ event Trade(address trader, string domain, bool isBuy, uint256 keyAmount, uint256 ethAmount, uint256 protocolEthAmount, uint256 subjectEthAmount, uint256 supply);
+
+ // CONSTRUCTOR
+ constructor(
+ address _tldAddress,
+ address _feeReceiver,
+ address _statsAddress,
+ uint256 _protocolFeePercent,
+ uint256 _domainHolderFeePercent,
+ uint256 _ratio
+ ) {
+ tldAddress = _tldAddress;
+ tldName = IPunkTLD(_tldAddress).name();
+
+ feeReceiver = _feeReceiver;
+ statsAddress = _statsAddress;
+
+ protocolFeePercent = _protocolFeePercent;
+ domainHolderFeePercent = _domainHolderFeePercent;
+ ratio = _ratio; // default: set to 1 ether (for 16000 keys)
+ }
+
+ // READ
+
+ function getPrice(uint256 supply, uint256 amount) public view returns (uint256) {
+ uint256 sum1 = supply == 0 ? 0 : (supply - 1 )* (supply) * (2 * (supply - 1) + 1) / 6;
+ uint256 sum2 = supply == 0 && amount == 1 ? 0 : (supply - 1 + amount) * (supply + amount) * (2 * (supply - 1 + amount) + 1) / 6;
+ uint256 summation = sum2 - sum1;
+ return summation * ratio / 16000;
+ }
+
+ function getBuyPrice(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 supply = keysSupply[domainName];
+
+ if (supply == 0) {
+ return ratio / 16000;
+ }
+
+ return getPrice(supply, amount);
+ }
+
+ function getBuyPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 price = getBuyPrice(domainName, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+ return price + protocolFee + subjectFee;
+ }
+
+ function getDomainHolder(string memory domainName) external view returns (address) {
+ return IPunkTLD(tldAddress).getDomainHolder(domainName);
+ }
+
+ function getKeyBalance(string memory domainName, address user) external view returns (uint256) {
+ return keysBalance[domainName][user];
+ }
+
+ function getSellPrice(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 supply = keysSupply[domainName];
+
+ if (supply == 0) {
+ return 0;
+ }
+
+ return getPrice(supply - amount, amount);
+ }
+
+ function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256) {
+ if (keysSupply[domainName] == 0) {
+ return 0;
+ }
+
+ uint256 price = getSellPrice(domainName, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+ return price - protocolFee - subjectFee;
+ }
+
+ function isKeyHolder(string memory domainName, address user) external view returns (bool) {
+ return keysBalance[domainName][user] > 0;
+ }
+
+ // WRITE
+
+ function buyKeys(string memory domainName, uint256 amount, address referrer) external payable nonReentrant {
+ address domainOwner = IPunkTLD(tldAddress).getDomainHolder(domainName);
+ require(domainOwner != address(0), "Domain does not exist");
+
+ uint256 supply = keysSupply[domainName];
+
+ // if supply is zero, send the first key to domainOwner for free
+ if (supply == 0) {
+ keysBalance[domainName][domainOwner] = 1;
+ keysSupply[domainName] = 1;
+ supply = 1;
+ }
+
+ uint256 price = getPrice(supply, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+
+ require(msg.value == price + protocolFee + subjectFee, "Insufficient payment");
+
+ keysBalance[domainName][msg.sender] += amount;
+ keysSupply[domainName] += amount;
+
+ // increase total volume stats
+ totalVolumeWei += (price + protocolFee + subjectFee);
+
+ // if there is a referrer, send them their share of the protocol and domain holder fee
+ if (referrer != address(0)) {
+ uint256 referrerFeeProtocol = protocolFee * referrerFeeShare / 1 ether;
+ uint256 referrerFeeSubject = subjectFee * referrerFeeShare / 1 ether;
+
+ (bool successRef, ) = referrer.call{value: referrerFeeProtocol + referrerFeeSubject}("");
+ require(successRef, "Unable to send funds");
+
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerFeeProtocol + referrerFeeSubject);
+ }
+
+ protocolFee = protocolFee - referrerFeeProtocol;
+ subjectFee = subjectFee - referrerFeeSubject;
+ }
+
+ emit Trade(msg.sender, domainName, true, amount, price, protocolFee, subjectFee, supply + amount);
+
+ (bool success1, ) = feeReceiver.call{value: protocolFee}("");
+ (bool success2, ) = domainOwner.call{value: subjectFee}("");
+
+ // add protocol fees to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(msg.sender, protocolFee);
+ }
+
+ require(success1 && success2, "Unable to send funds");
+ }
+
+ function sellKeys(string memory domainName, uint256 amount, address referrer) external payable nonReentrant {
+ uint256 supply = keysSupply[domainName];
+ require(supply > amount, "Cannot sell the last key");
+
+ uint256 price = getPrice(supply - amount, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+
+ require(keysBalance[domainName][msg.sender] >= amount, "Insufficient keys");
+
+ keysBalance[domainName][msg.sender] -= amount;
+ keysSupply[domainName] = supply - amount;
+
+ // send the key seller their share
+ (bool success1, ) = msg.sender.call{value: price - protocolFee - subjectFee}("");
+
+ // increase total volume stats
+ totalVolumeWei += (price + protocolFee + subjectFee);
+
+ // if there is a referrer, send them their share of the protocol and domain holder fee
+ if (referrer != address(0)) {
+ uint256 referrerFeeProtocol = protocolFee * referrerFeeShare / 1 ether;
+ uint256 referrerFeeSubject = subjectFee * referrerFeeShare / 1 ether;
+
+ (bool successRef, ) = referrer.call{value: referrerFeeProtocol + referrerFeeSubject}("");
+ require(successRef, "Unable to send funds");
+
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerFeeProtocol + referrerFeeSubject);
+ }
+
+ protocolFee = protocolFee - referrerFeeProtocol;
+ subjectFee = subjectFee - referrerFeeSubject;
+ }
+
+ emit Trade(msg.sender, domainName, false, amount, price, protocolFee, subjectFee, supply - amount);
+
+ // send fee to the project
+ (bool success2, ) = feeReceiver.call{value: protocolFee}("");
+
+ // send fee to the domain owner
+ address domainOwner = IPunkTLD(tldAddress).getDomainHolder(domainName);
+ (bool success3, ) = domainOwner.call{value: subjectFee}("");
+
+ // add protocol fees to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(msg.sender, protocolFee);
+ }
+
+ require(success1 && success2 && success3, "Unable to send funds");
+ }
+
+ // OWNER OR MANAGER
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeDomainHolderFeePercent(uint256 _feePercent) public onlyManagerOrOwner {
+ domainHolderFeePercent = _feePercent;
+ }
+
+ function changeFeeReceiver(address _feeReceiver) public onlyManagerOrOwner {
+ feeReceiver = _feeReceiver;
+ }
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeProtocolFeePercent(uint256 _feePercent) public onlyManagerOrOwner {
+ protocolFeePercent = _feePercent;
+ }
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeReferrerFeeShare(uint256 _referrerFeeShare) public onlyManagerOrOwner {
+ referrerFeeShare = _referrerFeeShare;
+ }
+
+ function changeStatsAddress(address _statsAddress) public onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/keys/FriendKeysErc20.sol b/contracts/keys/FriendKeysErc20.sol
new file mode 100644
index 0000000..738fa6b
--- /dev/null
+++ b/contracts/keys/FriendKeysErc20.sol
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity 0.8.17;
+
+import { IPunkTLD } from "../interfaces/IPunkTLD.sol";
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+}
+
+/**
+@title Friend Keys contract for buying and selling keys of punk domains using an ERC-20 token as payment method
+@author Tempe Techie
+@notice IMPORTANT: Only tested with ERC-20 tokens with 18 decimals
+*/
+contract FriendKeysErc20 is OwnableWithManagers, ReentrancyGuard {
+ address public feeReceiver; // protocol fee receiver
+ address public statsAddress; // stats contract
+ address public immutable tldAddress; // Punk Domains TLD contract address
+ address public immutable tokenAddress; // ERC-20 token address for key payments (only tested for tokens with 18 decimals)
+
+ string public tldName;
+
+ uint256 public domainHolderFeePercent;
+ uint256 public protocolFeePercent;
+ uint256 public referrerFeeShare = 100000000000000000; // referrer's share of the protocol and domain holder fee in wei (default: 100000000000000000 wei = 10%)
+ uint256 public immutable ratio;
+ uint256 public totalVolumeWei;
+
+ // Domain => (Holder => Balance)
+ mapping(string => mapping(address => uint256)) public keysBalance;
+
+ // Domain => Supply
+ mapping(string => uint256) public keysSupply;
+
+ // EVENTS
+ event Trade(address trader, string domain, bool isBuy, uint256 keyAmount, uint256 ethAmount, uint256 protocolEthAmount, uint256 subjectEthAmount, uint256 supply);
+
+ // CONSTRUCTOR
+ constructor(
+ address _tldAddress,
+ address _tokenAddress,
+ address _feeReceiver,
+ address _statsAddress,
+ uint256 _protocolFeePercent,
+ uint256 _domainHolderFeePercent,
+ uint256 _ratio
+ ) {
+ tldAddress = _tldAddress;
+ tldName = IPunkTLD(_tldAddress).name();
+
+ tokenAddress = _tokenAddress; // ERC-20 token address
+
+ feeReceiver = _feeReceiver;
+ statsAddress = _statsAddress;
+
+ protocolFeePercent = _protocolFeePercent;
+ domainHolderFeePercent = _domainHolderFeePercent;
+ ratio = _ratio; // default: set to 1 ether (for 16000 keys)
+ }
+
+ // READ
+
+ function getPrice(uint256 supply, uint256 amount) public view returns (uint256) {
+ uint256 sum1 = supply == 0 ? 0 : (supply - 1 )* (supply) * (2 * (supply - 1) + 1) / 6;
+ uint256 sum2 = supply == 0 && amount == 1 ? 0 : (supply - 1 + amount) * (supply + amount) * (2 * (supply - 1 + amount) + 1) / 6;
+ uint256 summation = sum2 - sum1;
+ return summation * ratio / 16000;
+ }
+
+ function getBuyPrice(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 supply = keysSupply[domainName];
+
+ if (supply == 0) {
+ return ratio / 16000;
+ }
+
+ return getPrice(supply, amount);
+ }
+
+ function getBuyPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 price = getBuyPrice(domainName, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+ return price + protocolFee + subjectFee;
+ }
+
+ function getDomainHolder(string memory domainName) external view returns (address) {
+ return IPunkTLD(tldAddress).getDomainHolder(domainName);
+ }
+
+ function getKeyBalance(string memory domainName, address user) external view returns (uint256) {
+ return keysBalance[domainName][user];
+ }
+
+ function getSellPrice(string memory domainName, uint256 amount) public view returns (uint256) {
+ uint256 supply = keysSupply[domainName];
+
+ if (supply == 0) {
+ return 0;
+ }
+
+ return getPrice(supply - amount, amount);
+ }
+
+ function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256) {
+ if (keysSupply[domainName] == 0) {
+ return 0;
+ }
+
+ uint256 price = getSellPrice(domainName, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+ return price - protocolFee - subjectFee;
+ }
+
+ function isKeyHolder(string memory domainName, address user) external view returns (bool) {
+ return keysBalance[domainName][user] > 0;
+ }
+
+ // WRITE
+
+ function buyKeys(string memory domainName, uint256 amount, address referrer) external nonReentrant {
+ address domainOwner = IPunkTLD(tldAddress).getDomainHolder(domainName);
+ require(domainOwner != address(0), "Domain does not exist");
+
+ uint256 supply = keysSupply[domainName];
+
+ // if supply is zero, send the first key to domainOwner for free
+ if (supply == 0) {
+ keysBalance[domainName][domainOwner] = 1;
+ keysSupply[domainName] = 1;
+ supply = 1;
+ }
+
+ uint256 price = getPrice(supply, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+
+ // transfer tokens from key buyer to this contract
+ IERC20(tokenAddress).transferFrom(msg.sender, address(this), price + protocolFee + subjectFee);
+
+ keysBalance[domainName][msg.sender] += amount;
+ keysSupply[domainName] += amount;
+
+ emit Trade(msg.sender, domainName, true, amount, price, protocolFee, subjectFee, supply + amount);
+
+ // increase total volume stats
+ totalVolumeWei += (price + protocolFee + subjectFee);
+
+ // if there is a referrer, send them their share of the protocol and domain holder fee
+ if (referrer != address(0)) {
+ uint256 referrerFeeProtocol = protocolFee * referrerFeeShare / 1 ether;
+ uint256 referrerFeeSubject = subjectFee * referrerFeeShare / 1 ether;
+
+ // send referrer their share in tokens
+ IERC20(tokenAddress).transfer(referrer, referrerFeeProtocol + referrerFeeSubject);
+
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerFeeProtocol + referrerFeeSubject);
+ }
+
+ protocolFee = protocolFee - referrerFeeProtocol;
+ subjectFee = subjectFee - referrerFeeSubject;
+ }
+
+ // send protocol fee to the project, and domain holder fee to the domain owner
+ IERC20(tokenAddress).transfer(feeReceiver, protocolFee);
+ IERC20(tokenAddress).transfer(domainOwner, subjectFee);
+
+ // add protocol fees to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(msg.sender, protocolFee);
+ }
+ }
+
+ function sellKeys(string memory domainName, uint256 amount, address referrer) external nonReentrant {
+ uint256 supply = keysSupply[domainName];
+ require(supply > amount, "Cannot sell the last key");
+
+ uint256 price = getPrice(supply - amount, amount);
+ uint256 protocolFee = price * protocolFeePercent / 1 ether;
+ uint256 subjectFee = price * domainHolderFeePercent / 1 ether;
+
+ require(keysBalance[domainName][msg.sender] >= amount, "Insufficient keys");
+
+ keysBalance[domainName][msg.sender] -= amount;
+ keysSupply[domainName] = supply - amount;
+
+ emit Trade(msg.sender, domainName, false, amount, price, protocolFee, subjectFee, supply - amount);
+
+ // send the key seller their share
+ IERC20(tokenAddress).transfer(msg.sender, price - protocolFee - subjectFee);
+
+ // increase total volume stats
+ totalVolumeWei += (price + protocolFee + subjectFee);
+
+ // if there is a referrer, send them their share of the protocol and domain holder fee
+ if (referrer != address(0)) {
+ uint256 referrerFeeProtocol = protocolFee * referrerFeeShare / 1 ether;
+ uint256 referrerFeeSubject = subjectFee * referrerFeeShare / 1 ether;
+
+ // send referrer their share in tokens
+ IERC20(tokenAddress).transfer(referrer, referrerFeeProtocol + referrerFeeSubject);
+
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerFeeProtocol + referrerFeeSubject);
+ }
+
+ protocolFee = protocolFee - referrerFeeProtocol;
+ subjectFee = subjectFee - referrerFeeSubject;
+ }
+
+ // send protocol fee to the project, and domain holder fee to the domain owner
+ IERC20(tokenAddress).transfer(feeReceiver, protocolFee);
+ address domainOwner = IPunkTLD(tldAddress).getDomainHolder(domainName);
+ IERC20(tokenAddress).transfer(domainOwner, subjectFee);
+
+ // add protocol fees to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(msg.sender, protocolFee);
+ }
+ }
+
+ // OWNER OR MANAGER
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeDomainHolderFeePercent(uint256 _feePercent) public onlyManagerOrOwner {
+ domainHolderFeePercent = _feePercent;
+ }
+
+ function changeFeeReceiver(address _feeReceiver) public onlyManagerOrOwner {
+ feeReceiver = _feeReceiver;
+ }
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeProtocolFeePercent(uint256 _feePercent) public onlyManagerOrOwner {
+ protocolFeePercent = _feePercent;
+ }
+
+ /// @notice Fee in wei, 1 ether = 100%
+ function changeReferrerFeeShare(uint256 _referrerFeeShare) public onlyManagerOrOwner {
+ referrerFeeShare = _referrerFeeShare;
+ }
+
+ function changeStatsAddress(address _statsAddress) public onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/launchpad/erc721/IggyLaunchpad721Bonding.sol b/contracts/launchpad/erc721/IggyLaunchpad721Bonding.sol
new file mode 100644
index 0000000..945ad75
--- /dev/null
+++ b/contracts/launchpad/erc721/IggyLaunchpad721Bonding.sol
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import "./Nft721Bonding.sol";
+
+interface INftDirectory {
+ function addNftAddressToAllWithUniqueId(address _nftAddress, string calldata uniqueId_) external;
+ function getAllFeaturedNftContracts() external view returns(address[] memory);
+ function getFeaturedNftContracts(uint256 amount) external view returns(address[] memory);
+ function getFeaturedNftContractsArrayLength() external view returns(uint256);
+ function getLastNftContracts(uint256 amount) external view returns(address[] memory);
+ function getNftContractAddress(string calldata _uniqueId) external view returns(address);
+ function getNftContracts(uint256 fromIndex, uint256 toIndex) external view returns(address[] memory);
+ function getNftContractsArrayLength() external view returns(uint256);
+ function isUniqueIdAvailable(string calldata _uniqueId) external view returns(bool);
+}
+
+interface INftMetadata {
+ function addImageToCollection(address nftAddress_, string memory imageUrl_) external;
+ function setCollectionPreview(address nftAddress_, string memory collectionPreview_) external;
+ function setDescription(address nftAddress_, string memory description_) external;
+ function setName(address nftAddress_, string memory name_) external;
+}
+
+interface IStatsContract {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+ function addWriterByWriter(address writer_) external;
+}
+
+/**
+@title Factory contract for launching new ERC721 collections with bonding curve pricing
+@author Tempe Techie
+*/
+contract IggyLaunchpad721Bonding is OwnableWithManagers, ReentrancyGuard {
+ address public metadataAddress;
+ address public mintingFeeReceiver; // the address that receives the ETH paid for launching a new NFT contract & minting fees from NFT contracts
+ address public nftDirectoryAddress;
+ address public statsAddress; // usually the stats middleware address
+
+ bool public paused = false; // pause launching collections through the factory contract
+
+ uint256 public maxNftNameLength = 32;
+ uint256 public mintingFeePercentage; // in wei
+ uint256 public price; // price for creating new NFT contract
+ uint256 public referralFeePercentage = 10**17; // in wei (1 ether is 100%, default is 10**17 = 10%)
+
+ // EVENTS
+ event CollectionLaunch(address indexed contractOwner_, address indexed msgSender_, string name_, string uniqueId_, address indexed nftContract_);
+
+ // CONSTRUCTOR
+ constructor(
+ address _metadataAddress,
+ address _mintingFeeReceiver,
+ address _nftDirectoryAddress,
+ address _statsAddress,
+ uint256 _mintingFeePercentage,
+ uint256 _price
+ ) {
+ metadataAddress = _metadataAddress;
+ mintingFeeReceiver = _mintingFeeReceiver;
+ nftDirectoryAddress = _nftDirectoryAddress;
+ statsAddress = _statsAddress;
+ mintingFeePercentage = _mintingFeePercentage;
+ price = _price;
+ }
+
+ // READ
+
+ function getAllFeaturedNftContracts() external view returns(address[] memory) {
+ return INftDirectory(nftDirectoryAddress).getAllFeaturedNftContracts();
+ }
+
+ /// @notice Get last X amount of featured NFT contract addresses
+ function getFeaturedNftContracts(uint256 amount) external view returns(address[] memory) {
+ return INftDirectory(nftDirectoryAddress).getFeaturedNftContracts(amount);
+ }
+
+ /// @notice Get array length of featured NFT contracts
+ function getFeaturedNftContractsArrayLength() external view returns(uint256) {
+ return INftDirectory(nftDirectoryAddress).getFeaturedNftContractsArrayLength();
+ }
+
+ /// @notice Get last X amount of NFT contract addresses
+ function getLastNftContracts(uint256 amount) external view returns(address[] memory) {
+ return INftDirectory(nftDirectoryAddress).getLastNftContracts(amount);
+ }
+
+ /// @notice Get NFT contract address by unique ID
+ function getNftContractAddress(string calldata _uniqueId) external view returns(address) {
+ return INftDirectory(nftDirectoryAddress).getNftContractAddress(_uniqueId);
+ }
+
+ /// @notice Get NFT contract addresses between two indexes
+ function getNftContracts(uint256 fromIndex, uint256 toIndex) external view returns(address[] memory) {
+ return INftDirectory(nftDirectoryAddress).getNftContracts(fromIndex, toIndex);
+ }
+
+ /// @notice Get NFT the length of the allNftContracts array
+ function getNftContractsArrayLength() external view returns(uint256) {
+ return INftDirectory(nftDirectoryAddress).getNftContractsArrayLength();
+ }
+
+ /// @notice Check if unique ID is available
+ function isUniqueIdAvailable(string calldata _uniqueId) public view returns(bool) {
+ return INftDirectory(nftDirectoryAddress).isUniqueIdAvailable(_uniqueId);
+ }
+
+ // WRITE
+
+ /// @notice Launch new ERC721 collection with bonding curve pricing
+ function launch(
+ address contractOwner_,
+ address referrer_,
+ string memory mdDescription_,
+ string memory mdImage_,
+ string memory mdName_,
+ string memory name_,
+ string memory symbol_,
+ string calldata uniqueId_, // to easily find the NFT contract address
+ uint256 ratio // ratio of price increase per token minted for bonding curve (in wei)
+ ) external payable nonReentrant {
+ require(!paused, "Launching new collections is paused");
+ require(msg.value >= price, "Not enough ETH sent to cover price");
+
+ require(ratio >= 1e16 && ratio <= 9_000_000 ether, "Ratio out of bounds");
+
+ require(isUniqueIdAvailable(uniqueId_), "Unique ID is not available");
+ require(bytes(name_).length <= maxNftNameLength, "Name must be 32 characters or less");
+
+ uint256 paid = msg.value;
+
+ // send referral fee
+ if (referrer_ != address(0) && referralFeePercentage > 0) {
+ uint256 referralFee = msg.value * referralFeePercentage / 1 ether;
+ (bool sentRef, ) = referrer_.call{value: referralFee}("");
+ IStatsContract(statsAddress).addWeiSpent(referrer_, referralFee);
+ paid -= referralFee;
+ }
+
+ (bool sent, ) = mintingFeeReceiver.call{value: address(this).balance}("");
+ require(sent, "Failed to send launch payment to the payment receiver");
+
+ // create new NFT contract
+ bytes32 saltedHash = keccak256(abi.encodePacked(msg.sender, block.timestamp, uniqueId_));
+ Nft721Bonding nftContract = new Nft721Bonding{salt: saltedHash}(
+ address(this), metadataAddress, mintingFeeReceiver, name_, symbol_, mintingFeePercentage, ratio
+ );
+
+ // update nftAddressById mapping and allNftContracts array
+ INftDirectory(nftDirectoryAddress).addNftAddressToAllWithUniqueId(address(nftContract), uniqueId_);
+
+ // update metadata contract
+ INftMetadata(metadataAddress).addImageToCollection(address(nftContract), mdImage_);
+ INftMetadata(metadataAddress).setCollectionPreview(address(nftContract), mdImage_);
+ INftMetadata(metadataAddress).setDescription(address(nftContract), mdDescription_);
+ INftMetadata(metadataAddress).setName(address(nftContract), mdName_);
+
+ nftContract.transferOwnership(contractOwner_);
+
+ // update stats
+ if (statsAddress != address(0)) {
+ IStatsContract(statsAddress).addWeiSpent(msg.sender, paid);
+ IStatsContract(statsAddress).addWriterByWriter(address(nftContract));
+ }
+
+ emit CollectionLaunch(contractOwner_, msg.sender, name_, uniqueId_, address(nftContract));
+ }
+
+ // OWNER
+
+ /// @notice Recover ETH sent to this contract
+ function recoverEth() external onlyManagerOrOwner {
+ (bool sent, ) = owner().call{value: address(this).balance}("");
+ require(sent, "Failed to send ETH to TLD owner");
+ }
+
+ /// @notice Set max NFT name length
+ function setMaxNftNameLength(uint256 _maxNftNameLength) external onlyManagerOrOwner {
+ maxNftNameLength = _maxNftNameLength;
+ }
+
+ /// @notice Set metadata contract address
+ function setMetadataAddress(address _metadataAddress) external onlyManagerOrOwner {
+ metadataAddress = _metadataAddress;
+ }
+
+ /// @notice Set royalty fee receiver
+ function setMintingFeeReceiver(address _mintingFeeReceiver) external onlyManagerOrOwner {
+ mintingFeeReceiver = _mintingFeeReceiver;
+ }
+
+ /// @notice Set royalty fee percentage in wei
+ function setMintingFeePercentage(uint256 _mintingFeePercentage) external onlyManagerOrOwner {
+ mintingFeePercentage = _mintingFeePercentage;
+ }
+
+ /// @notice Set NFT directory contract address
+ function setNftDirectoryAddress(address _nftDirectoryAddress) external onlyManagerOrOwner {
+ nftDirectoryAddress = _nftDirectoryAddress;
+ }
+
+ /// @notice Set price for creating new NFT contract
+ function setPrice(uint256 _price) external onlyManagerOrOwner {
+ price = _price;
+ }
+
+ /// @notice Set referral fee percentage in wei (1 ether is 100%)
+ function setReferralFeePercentage(uint256 _referralFeePercentage) external onlyManagerOrOwner {
+ referralFeePercentage = _referralFeePercentage;
+ }
+
+ /// @notice Set stats contract address
+ function setStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ /// @notice Toggle pausing launching new collections
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/Nft721Bonding.sol b/contracts/launchpad/erc721/Nft721Bonding.sol
new file mode 100644
index 0000000..5be3a21
--- /dev/null
+++ b/contracts/launchpad/erc721/Nft721Bonding.sol
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC721, ERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
+import { OwnableWithManagers } from "../../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+
+interface IFactory {
+ function owner() external view returns (address);
+ function statsAddress() external view returns (address);
+}
+
+interface INftMd {
+ function getCollectionPreviewImage(address nftAddress_) external view returns (string memory);
+ function getMetadata(address nftAddress_, uint256 tokenId_) external view returns (string memory);
+}
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+}
+
+/**
+@title ERC-721 contract with bonding curve pricing
+@author Tempe Techie
+*/
+contract Nft721Bonding is ERC721, ERC721Enumerable, OwnableWithManagers, ReentrancyGuard {
+ address public factoryAddress;
+ address public metadataAddress;
+ address public mintingFeeReceiver;
+
+ string public constant pricingType = "bonding";
+
+ uint256 public counter = 1; // counter for the tokenId
+ uint256 public immutable createdAt; // NFT creation time
+ uint256 public mintingFeePercentage; // in wei
+ uint256 public immutable ratio; // ratio for the bonding curve
+
+ // CONSTRUCTOR
+ constructor(
+ address factoryAddress_,
+ address metadataAddress_,
+ address mintingFeeReceiver_,
+ string memory name_,
+ string memory symbol_,
+ uint256 mintingFeePercentage_,
+ uint256 ratio_
+ ) ERC721(name_, symbol_) {
+ factoryAddress = factoryAddress_;
+ metadataAddress = metadataAddress_;
+ mintingFeeReceiver = mintingFeeReceiver_;
+
+ mintingFeePercentage = mintingFeePercentage_;
+ ratio = ratio_;
+ createdAt = block.timestamp;
+ }
+
+ // READ PUBLIC
+
+ /// @notice Get collection preview image
+ function collectionPreview() public view returns (string memory) {
+ return INftMd(metadataAddress).getCollectionPreviewImage(address(this));
+ }
+
+ function getBurnPrice() public view returns (uint256) {
+ uint256 tSupply = totalSupply();
+
+ if (tSupply < 2) {
+ return 0;
+ }
+
+ uint256 price = _getBurnPriceBeforeFees(tSupply);
+
+ uint256 protocolFee = price * mintingFeePercentage / 1 ether;
+ uint256 ownerFee = price * mintingFeePercentage / 1 ether;
+
+ return price - protocolFee - ownerFee;
+ }
+
+ function getMintPrice() public view returns (uint256) {
+ uint256 price = _getMintPriceBeforeFees(totalSupply());
+
+ uint256 protocolFee = price * mintingFeePercentage / 1 ether;
+ uint256 ownerFee = price * mintingFeePercentage / 1 ether;
+
+ return price + protocolFee + ownerFee;
+ }
+
+ function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) {
+ return super.supportsInterface(interfaceId);
+ }
+
+ function tokenURI(uint256 tokenId) public view override returns (string memory) {
+ _requireMinted(tokenId);
+
+ return INftMd(metadataAddress).getMetadata(address(this), tokenId);
+ }
+
+ // READ PRIVATE & INTERNAL
+
+ function _beforeTokenTransfer(address from, address to, uint256 tokenId, uint256 batchSize) internal override(ERC721, ERC721Enumerable) {
+ super._beforeTokenTransfer(from, to, tokenId, batchSize);
+ }
+
+ function _getBurnPriceBeforeFees(uint256 totalSupply_) private view returns (uint256) {
+ if (totalSupply_ < 2) {
+ return 0;
+ }
+
+ return _getPrice(totalSupply_ - 1);
+ }
+
+ function _getMintPriceBeforeFees(uint256 totalSupply_) private view returns (uint256) {
+ if (totalSupply_ == 0) {
+ return _getPrice(1);
+ }
+
+ return _getPrice(totalSupply_);
+ }
+
+ // get price for 1 NFT via bonding curve (supply is always > 1)
+
+ // quadratic bonding curve
+ function _getPrice(uint256 supply) private view returns (uint256) {
+ return (((supply * (supply + 1) * (2 * supply + 1)) - ((supply - 1) * supply * (2 * (supply - 1) + 1))) * 10000 / 42069) * ratio / 31337;
+ }
+
+ /*
+ // alternative: negative exponential bonding curve
+ function _getPrice(uint256 supply) internal view returns (uint256) {
+ uint256 shape = 3;
+ uint256 numerator = supply * ratio / 10000;
+ uint256 denominator = supply + shape;
+ return numerator / denominator;
+ }
+ */
+
+ // WRITE
+
+ function burn(uint256 tokenId) external nonReentrant returns (uint256) {
+ uint256 tSupply = totalSupply();
+
+ require(tSupply > 1, "Cannot sell the last NFT");
+ require(_isApprovedOrOwner(msg.sender, tokenId), "Nft721Bonding: caller is not owner nor approved");
+
+ uint256 price = _getBurnPriceBeforeFees(tSupply);
+
+ uint256 protocolFee = price * mintingFeePercentage / 1 ether;
+ uint256 ownerFee = price * mintingFeePercentage / 1 ether;
+
+ _burn(tokenId);
+
+ // add addWeiSpent call to the stats contract
+ address statsAddress = IFactory(factoryAddress).statsAddress();
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(_ownerOf(tokenId), protocolFee);
+ }
+
+ // send fees
+ (bool successOwner, ) = owner().call{value: ownerFee}("");
+
+ if (!successOwner) {
+ protocolFee += ownerFee;
+ }
+
+ (bool successMfReceiver, ) = mintingFeeReceiver.call{value: protocolFee}("");
+ require(successMfReceiver, "Nft721Bonding: Failed to send protocol fee");
+
+ // send payment to the burn caller
+ (bool successMsgSender, ) = msg.sender.call{value: price - ownerFee - protocolFee}("");
+ require(successMsgSender, "Nft721Bonding: Failed to send payment");
+
+ return price - ownerFee - protocolFee; // return the amount of ETH sent to the caller
+ }
+
+ function mint(address to) external payable nonReentrant returns (uint256) {
+ uint256 tSupply = totalSupply();
+
+ if (tSupply == 0) {
+ _mint(owner(), counter); // mint the first NFT to the owner
+ ++counter;
+ ++tSupply;
+ }
+
+ uint256 price = _getMintPriceBeforeFees(tSupply);
+ uint256 protocolFee = price * mintingFeePercentage / 1 ether;
+ uint256 ownerFee = price * mintingFeePercentage / 1 ether;
+
+ require(msg.value == price + protocolFee + ownerFee, "Insufficient payment");
+
+ // add addWeiSpent call to the stats contract
+ address statsAddress = IFactory(factoryAddress).statsAddress();
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(msg.sender, protocolFee);
+ }
+
+ // send fees
+ (bool successOwner, ) = owner().call{value: ownerFee}("");
+
+ if (!successOwner) {
+ protocolFee += ownerFee;
+ }
+
+ (bool successMfReceiver, ) = mintingFeeReceiver.call{value: protocolFee}("");
+ require(successMfReceiver, "Nft721Bonding: Failed to send protocol fee");
+
+ _mint(to, counter);
+ ++counter;
+
+ return counter - 1; // return the tokenId of the minted NFT for the to address
+ }
+
+ // OWNER
+
+ // set metadata address
+ function setMetadataAddress(address metadataAddress_) external onlyManagerOrOwner {
+ metadataAddress = metadataAddress_;
+ }
+
+ // set minting fee percentage
+ function setMintingFeePercentage(uint256 mintingFeePercentage_) external onlyManagerOrOwner {
+ require(mintingFeePercentage_ < (5 * 1e16), "Nft721Bonding: fee must be lower than 5%");
+ mintingFeePercentage = mintingFeePercentage_;
+ }
+
+ // MINT FEE RECEIVER
+
+ function setMintingFeeReceiver(address mintingFeeReceiver_) external {
+ require(msg.sender == IFactory(factoryAddress).owner(), "Nft721Bonding: Only factory owner can set a new mintingFeeReceiver");
+ mintingFeeReceiver = mintingFeeReceiver_;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/NftDirectory.sol b/contracts/launchpad/erc721/NftDirectory.sol
new file mode 100644
index 0000000..fd59a9c
--- /dev/null
+++ b/contracts/launchpad/erc721/NftDirectory.sol
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../access/OwnableWithManagers.sol";
+
+/**
+@title A directory of ERC-721 NFTs made with a Iggy Launchpad
+@author Tempe Techie
+*/
+contract NftDirectory is OwnableWithManagers {
+ address[] public allNftContracts; // array of all NFT contracts launched by this contract
+ address[] public featuredNftContracts; // array of NFT contracts that are featured by the contract owner
+
+ mapping (address => bool) public writers; // writer contracts that can send stats to this contract
+ mapping (string => address) public nftAddressById; // mapping(uniqueID => NFT contract address) to easily find the NFT contract address
+
+ // READ
+
+ function getAllFeaturedNftContracts() external view returns(address[] memory) {
+ return featuredNftContracts;
+ }
+
+ /// @notice Get last X amount of featured NFT contract addresses
+ function getFeaturedNftContracts(uint256 amount) external view returns(address[] memory) {
+ uint256 length = featuredNftContracts.length;
+
+ if (length <= amount) {
+ return featuredNftContracts; // Return the whole array if it has the same or fewer items than the amount requested
+ }
+
+ address[] memory nftContracts_ = new address[](amount);
+
+ for (uint256 i = 0; i < amount; i++) {
+ nftContracts_[i] = featuredNftContracts[length - amount + i];
+ }
+
+ return nftContracts_;
+ }
+
+ /// @notice Get array length of featured NFT contracts
+ function getFeaturedNftContractsArrayLength() external view returns(uint256) {
+ return featuredNftContracts.length;
+ }
+
+ /// @notice Get last X amount of NFT contract addresses
+ function getLastNftContracts(uint256 amount) external view returns(address[] memory) {
+ uint256 length = allNftContracts.length;
+
+ if (length <= amount) {
+ return allNftContracts; // Return the whole array if it has the same or fewer items than the amount requested
+ }
+
+ address[] memory nftContracts_ = new address[](amount);
+
+ for (uint256 i = 0; i < amount; i++) {
+ nftContracts_[i] = allNftContracts[length - amount + i];
+ }
+
+ return nftContracts_;
+ }
+
+ /// @notice Get NFT contract address by unique ID
+ function getNftContractAddress(string calldata _uniqueId) external view returns(address) {
+ return nftAddressById[_uniqueId];
+ }
+
+ /// @notice Get NFT contract addresses between two indexes
+ function getNftContracts(uint256 fromIndex, uint256 toIndex) external view returns(address[] memory) {
+ require(fromIndex < toIndex, "fromIndex must be less than toIndex");
+ require(toIndex < allNftContracts.length, "toIndex out of bounds");
+
+ address[] memory nftContracts_ = new address[](toIndex - fromIndex + 1);
+
+ for (uint256 i = fromIndex; i <= toIndex; i++) {
+ nftContracts_[i - fromIndex] = allNftContracts[i];
+ }
+
+ return nftContracts_;
+ }
+
+ /// @notice Get NFT the length of the allNftContracts array
+ function getNftContractsArrayLength() external view returns(uint256) {
+ return allNftContracts.length;
+ }
+
+ /// @notice Check if unique ID is available
+ function isUniqueIdAvailable(string calldata _uniqueId) public view returns(bool) {
+ return nftAddressById[_uniqueId] == address(0);
+ }
+
+ // LAUNCHPADS / WRITERS
+ function addNftAddressToAllWithUniqueId(address _nftAddress, string calldata uniqueId_) external {
+ require(writers[msg.sender], "Not a writer contract");
+
+ nftAddressById[uniqueId_] = _nftAddress;
+ allNftContracts.push(_nftAddress);
+ }
+
+ // OWNER
+
+ function addNftAddressToAll(address _nftAddress) external onlyManagerOrOwner {
+ allNftContracts.push(_nftAddress);
+ }
+
+ function addNftAddressToFeatured(address _nftAddress) external onlyManagerOrOwner {
+ featuredNftContracts.push(_nftAddress);
+ }
+
+ function addWriter(address writer_) external onlyManagerOrOwner {
+ writers[writer_] = true;
+ }
+
+ function removeNftAddressFromAllByIndex(uint256 _index) external onlyManagerOrOwner {
+ allNftContracts[_index] = allNftContracts[allNftContracts.length - 1];
+ allNftContracts.pop();
+ }
+
+ function removeNftAddressFromFeatured(address _nftAddress) external onlyManagerOrOwner {
+ for (uint256 i = 0; i < featuredNftContracts.length; i++) {
+ if (featuredNftContracts[i] == _nftAddress) {
+ featuredNftContracts[i] = featuredNftContracts[featuredNftContracts.length - 1];
+ featuredNftContracts.pop();
+ break;
+ }
+ }
+ }
+
+ function removeNftAddressFromFeaturedByIndex(uint256 _index) external onlyManagerOrOwner {
+ featuredNftContracts[_index] = featuredNftContracts[featuredNftContracts.length - 1];
+ featuredNftContracts.pop();
+ }
+}
\ No newline at end of file
diff --git a/contracts/launchpad/erc721/NftMetadata.sol b/contracts/launchpad/erc721/NftMetadata.sol
new file mode 100644
index 0000000..917043c
--- /dev/null
+++ b/contracts/launchpad/erc721/NftMetadata.sol
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+interface INFT {
+ function owner() external view returns(address);
+}
+
+/**
+@title Default metadata contract for ERC-721 NFTs made with Iggy Launchpad
+@author Tempe Techie
+*/
+contract NftMetadata {
+ using Strings for uint256;
+
+ struct Metadata {
+ string name;
+ string description;
+ string collectionPreview;
+ string[] images;
+ string externalUrl;
+ string mdUrl;
+ uint256 mdType;
+ }
+
+ mapping (address => Metadata) public mds;
+
+ // mdType options:
+ // - 0 (default): encoded metadata, no need for metadata from a 3rd party source such as IPFS or a custom API.
+ // - 1: url to metadata IPFS or custom API. One metadata URL for all NFTs (meaning all NFTs have the same metadata and image).
+ // - 2: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (ends with .json extension).
+ // - 3: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (does NOT end with .json extension).
+
+ // INTERNAL
+
+ // get image from the array of image URLs based on the tokenId (use modulo if tokenId is larger than array length)
+ function _getImage(address nftAddress_, uint256 _tokenId) internal view returns(string memory) {
+ if (_tokenId > 0) {
+ uint256 imgLength = mds[nftAddress_].images.length;
+ return mds[nftAddress_].images[(_tokenId-1) % imgLength];
+ } else {
+ return mds[nftAddress_].images[0];
+ }
+ }
+
+ // READ
+
+ function getCollectionDescription(address nftAddress_) external view returns (string memory) {
+ return mds[nftAddress_].description;
+ }
+
+ function getCollectionExternalUrl(address nftAddress_) external view returns (string memory) {
+ return mds[nftAddress_].externalUrl;
+ }
+
+ function getCollectionImages(address nftAddress_) external view returns (string[] memory) {
+ return mds[nftAddress_].images;
+ }
+
+ function getCollectionMetadataType(address nftAddress_) external view returns (uint256) {
+ return mds[nftAddress_].mdType;
+ }
+
+ function getCollectionMetadataUrl(address nftAddress_) external view returns (string memory) {
+ return mds[nftAddress_].mdUrl;
+ }
+
+ function getCollectionName(address nftAddress_) external view returns (string memory) {
+ return mds[nftAddress_].name;
+ }
+
+ function getCollectionPreviewImage(address nftAddress_) external view returns (string memory) {
+ return mds[nftAddress_].collectionPreview;
+ }
+
+ function getMetadata(address nftAddress_, uint256 tokenId_) external view returns (string memory) {
+ if (mds[nftAddress_].mdType == 1) {
+ return mds[nftAddress_].mdUrl;
+ } else if (mds[nftAddress_].mdType == 2) {
+ return string(abi.encodePacked(mds[nftAddress_].mdUrl, tokenId_.toString(), ".json"));
+ } else if (mds[nftAddress_].mdType == 3) {
+ return string(abi.encodePacked(mds[nftAddress_].mdUrl, tokenId_.toString()));
+ }
+
+ // default mdType: 0
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ '{"name": "', mds[nftAddress_].name,' #', tokenId_.toString(),'", ',
+ '"image": "', _getImage(nftAddress_, tokenId_), '", ',
+ '"external_url": "', mds[nftAddress_].externalUrl, '", ',
+ '"description": "', mds[nftAddress_].description, '"',
+ '}'))))
+ );
+ }
+
+ // WRITE
+
+ function addImageToCollection(address nftAddress_, string memory imageUrl_) public {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].images.push(imageUrl_);
+ }
+
+ function changeImage(address nftAddress_, uint256 index_, string memory imageUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].images[index_] = imageUrl_;
+ }
+
+ /// @notice While this removes an image from the array, it also puts the last image in the array into the place of this removed image
+ function removeImageFromCollection(address nftAddress_, string memory imageUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ uint256 imgLength = mds[nftAddress_].images.length;
+
+ for (uint256 i = 0; i < imgLength; i++) {
+ if (keccak256(bytes(mds[nftAddress_].images[i])) == keccak256(bytes(imageUrl_))) {
+ mds[nftAddress_].images[i] = mds[nftAddress_].images[imgLength - 1];
+ mds[nftAddress_].images.pop();
+ break;
+ }
+ }
+ }
+
+ /// @notice While this removes an image from the array, it also puts the last image in the array into the place of this removed image
+ function removeImageFromCollectionByIndex(address nftAddress_, uint256 index_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ uint256 imgLength = mds[nftAddress_].images.length;
+
+ require(index_ < imgLength, "Index out of bounds");
+
+ mds[nftAddress_].images[index_] = mds[nftAddress_].images[imgLength - 1];
+ mds[nftAddress_].images.pop();
+ }
+
+ function setCollectionPreview(address nftAddress_, string memory collectionPreview_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].collectionPreview = collectionPreview_;
+ }
+
+ function setDescription(address nftAddress_, string memory description_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].description = description_;
+ }
+
+ function setExternalUrl(address nftAddress_, string memory externalUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].externalUrl = externalUrl_;
+ }
+
+ function setMdType(address nftAddress_, uint256 mdType_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].mdType = mdType_;
+ }
+
+ function setMdTypeAndUrlOrImage(
+ address nftAddress_,
+ uint256 mdType_,
+ string memory mdUrlOrImage_,
+ string memory collectionImage_
+ ) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+
+ mds[nftAddress_].mdType = mdType_;
+
+ if (mdType_ == 0) {
+ mds[nftAddress_].images[0] = mdUrlOrImage_;
+ } else {
+ mds[nftAddress_].mdUrl = mdUrlOrImage_;
+ }
+
+ if (bytes(collectionImage_).length > 0) {
+ mds[nftAddress_].collectionPreview = collectionImage_;
+ }
+ }
+
+ function setMdUrl(address nftAddress_, string memory mdUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].mdUrl = mdUrl_;
+ }
+
+ function setName(address nftAddress_, string memory name_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mds[nftAddress_].name = name_;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/metadata/NftMetadata2.sol b/contracts/launchpad/erc721/metadata/NftMetadata2.sol
new file mode 100644
index 0000000..9760aae
--- /dev/null
+++ b/contracts/launchpad/erc721/metadata/NftMetadata2.sol
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+interface INFT {
+ function owner() external view returns(address);
+}
+
+/**
+ * DEPRECATED
+@title Default metadata contract for ERC-721 NFTs made with Iggy Launchpad
+@author Tempe Techie
+*/
+contract NftMetadata2 {
+ using Strings for uint256;
+
+ mapping (address => string) public collectionPreviews;
+ mapping (address => string) public descriptions;
+ mapping (address => string) public externalUrls;
+ mapping (address => string) public images;
+ mapping (address => string) public mdUrls; // optional, if mdType is not 0
+ mapping (address => string) public names;
+
+ mapping (address => uint256) public mdTypes;
+ // mdType options:
+ // - 0 (default): encoded metadata, no need for metadata from a 3rd party source such as IPFS or a custom API.
+ // - 1: url to metadata IPFS or custom API. One metadata URL for all NFTs (meaning all NFTs have the same metadata and image).
+ // - 2: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (ends with .json extension).
+ // - 3: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (does NOT end with .json extension).
+
+ // READ
+
+ function getMetadata(address nftAddress_, uint256 tokenId_) external view returns (string memory) {
+ if (mdTypes[nftAddress_] == 1) {
+ return mdUrls[nftAddress_];
+ } else if (mdTypes[nftAddress_] == 2) {
+ return string(abi.encodePacked(mdUrls[nftAddress_], tokenId_.toString(), ".json"));
+ } else if (mdTypes[nftAddress_] == 3) {
+ return string(abi.encodePacked(mdUrls[nftAddress_], tokenId_.toString()));
+ }
+
+ // default mdType: 0
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ '{"name": "', names[nftAddress_],' #', tokenId_.toString(),'", ',
+ '"image": "', images[nftAddress_], '", ',
+ '"external_url": "', externalUrls[nftAddress_], '", ',
+ '"description": "', descriptions[nftAddress_], '"',
+ '}'))))
+ );
+ }
+
+ // WRITE
+
+ function setCollectionPreview(address nftAddress_, string memory collectionPreview_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ collectionPreviews[nftAddress_] = collectionPreview_;
+ }
+
+ function setDescription(address nftAddress_, string memory description_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ descriptions[nftAddress_] = description_;
+ }
+
+ function setExternalUrl(address nftAddress_, string memory externalUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ externalUrls[nftAddress_] = externalUrl_;
+ }
+
+ function setImage(address nftAddress_, string memory image_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ images[nftAddress_] = image_;
+ }
+
+ function setMdType(address nftAddress_, uint256 mdType_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mdTypes[nftAddress_] = mdType_;
+ }
+
+ function setMdTypeAndUrlOrImage(
+ address nftAddress_,
+ uint256 mdType_,
+ string memory mdUrlOrImage_,
+ string memory collectionImage_
+ ) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+
+ mdTypes[nftAddress_] = mdType_;
+
+ if (mdType_ == 0) {
+ images[nftAddress_] = mdUrlOrImage_;
+ } else {
+ mdUrls[nftAddress_] = mdUrlOrImage_;
+ }
+
+ if (bytes(collectionImage_).length > 0) {
+ collectionPreviews[nftAddress_] = collectionImage_;
+ }
+ }
+
+ function setMdUrl(address nftAddress_, string memory mdUrl_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ mdUrls[nftAddress_] = mdUrl_;
+ }
+
+ function setName(address nftAddress_, string memory name_) external {
+ require(msg.sender == INFT(nftAddress_).owner(), "Not owner of NFT smart contract");
+ names[nftAddress_] = name_;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/metadata/NftMetadataOnchainMultipleImages.sol b/contracts/launchpad/erc721/metadata/NftMetadataOnchainMultipleImages.sol
new file mode 100644
index 0000000..dd435e4
--- /dev/null
+++ b/contracts/launchpad/erc721/metadata/NftMetadataOnchainMultipleImages.sol
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../../access/OwnableWithManagers.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+/**
+@title Metadata contract for a single ERC-721 NFT made with Iggy Launchpad. This contract allows for multiple onchain image URLs.
+@author Tempe Techie
+*/
+contract NftMetadataOnchainMultipleImages is OwnableWithManagers {
+ using Strings for uint256;
+
+ string public collectionPreview; // collection preview image URL
+ string public description;
+ string public externalUrl;
+ string[] public imageUrls; // a string of image URLs
+ string public mdUrl; // optional, if mdType is not 0
+
+ string public name;
+
+ uint256 mdType = 0;
+ // mdType options:
+ // - 0: encoded metadata, no need for metadata from a 3rd party source such as IPFS or a custom API.
+ // - 1: url to metadata IPFS or custom API. One metadata URL for all NFTs (meaning all NFTs have the same metadata and image).
+ // - 2: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (ends with .json extension).
+ // - 3: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (does NOT end with .json extension).
+
+ // CONSTRUCTOR
+ constructor (
+ string memory collectionPreview_,
+ string memory description_,
+ string memory externalUrl_,
+ string memory image_,
+ string memory name_
+ ) {
+ collectionPreview = collectionPreview_;
+ description = description_;
+ externalUrl = externalUrl_;
+ imageUrls.push(image_); // set the first image URL in the array
+ name = name_;
+ }
+
+ // INTERNAL
+
+ // get image from the array of image URLs based on the tokenId (use modulo if tokenId is larger than array length)
+ function _getImage(uint256 _tokenId) internal view returns(string memory) {
+ if (_tokenId > 0) {
+ return imageUrls[(_tokenId-1) % imageUrls.length];
+ } else {
+ return imageUrls[0];
+ }
+ }
+
+ // READ
+
+ /// @dev for compatibility with NftMetadata.sol
+ function collectionPreviews(address nftAddress_) external view returns (string memory) {
+ return collectionPreview;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function descriptions(address nftAddress_) external view returns (string memory) {
+ return description;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function externalUrls(address nftAddress_) external view returns (string memory) {
+ return externalUrl;
+ }
+
+ function getImages() external view returns (string[] memory) {
+ return imageUrls;
+ }
+
+ /// @dev nftAddress_ needed for compatibility with NftMetadata.sol
+ function getMetadata(address nftAddress_, uint256 tokenId_) public view returns (string memory) {
+ if (mdType == 1) {
+ return mdUrl;
+ } else if (mdType == 2) {
+ return string(abi.encodePacked(mdUrl, tokenId_.toString(), ".json"));
+ } else if (mdType == 3) {
+ return string(abi.encodePacked(mdUrl, tokenId_.toString()));
+ }
+
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ '{"name": "', name,' #', tokenId_.toString(),'", ',
+ '"image": "', _getImage(tokenId_), '", ',
+ '"external_url": "', externalUrl, '", ',
+ '"description": "', description, '"',
+ '}'))))
+ );
+ }
+
+ /// @dev for compatibility with NftMetadata.sol, returns only the first image URL
+ function images(address nftAddress_) external view returns (string memory) {
+ return imageUrls[0];
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function mdTypes(address nftAddress_) external view returns (uint256) {
+ return mdType;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function mdUrls(address nftAddress_) external view returns (string memory) {
+ return mdUrl;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function names(address nftAddress_) external view returns (string memory) {
+ return name;
+ }
+
+ // OWNER
+
+ function addImageToCollection(string memory imageUrl_) external onlyManagerOrOwner {
+ imageUrls.push(imageUrl_);
+ }
+
+ /// @notice While this removes an image from the array, it also puts the last image in the array into the place of this removed image
+ function removeImageFromCollection(string memory imageUrl_) external onlyManagerOrOwner {
+ for (uint256 i = 0; i < imageUrls.length; i++) {
+ if (keccak256(bytes(imageUrls[i])) == keccak256(bytes(imageUrl_))) {
+ imageUrls[i] = imageUrls[imageUrls.length - 1];
+ imageUrls.pop();
+ break;
+ }
+ }
+ }
+
+ /// @notice While this removes an image from the array, it also puts the last image in the array into the place of this removed image
+ function removeImageFromCollectionByIndex(uint256 index_) external onlyManagerOrOwner {
+ require(index_ < imageUrls.length, "Index out of bounds");
+ imageUrls[index_] = imageUrls[imageUrls.length - 1];
+ imageUrls.pop();
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setCollectionPreview(address nftAddress_, string memory collectionPreview_) external onlyManagerOrOwner {
+ collectionPreview = collectionPreview_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setDescription(address nftAddress_, string memory description_) external onlyManagerOrOwner {
+ description = description_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setExternalUrl(address nftAddress_, string memory externalUrl_) external onlyManagerOrOwner {
+ externalUrl = externalUrl_;
+ }
+
+ /// @dev here for compatibility with NftMetadata.sol
+ function setImage(address nftAddress_, string memory image_) external onlyManagerOrOwner {
+ imageUrls[0] = image_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setMdType(address nftAddress_, uint256 mdType_) external onlyManagerOrOwner {
+ mdType = mdType_;
+ }
+
+ /// @dev This function only changes the first image URL (if mdType is 0) in the array (here for compatibility with NftMetadata.sol)
+ function setMdTypeAndUrlOrImage(
+ address nftAddress_,
+ uint256 mdType_,
+ string memory mdUrlOrImage_,
+ string memory collectionImage_
+ ) external onlyManagerOrOwner {
+ mdType = mdType_;
+
+ if (mdType_ == 0) {
+ imageUrls[0] = mdUrlOrImage_;
+ } else {
+ mdUrl = mdUrlOrImage_;
+ }
+
+ if (bytes(collectionImage_).length > 0) {
+ collectionPreview = collectionImage_;
+ }
+ }
+
+ /// @notice URL to metadata on IPFS or from a custom API (needed for mdType 1 and 2). Needs to end with slash /
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setMdUrl(address nftAddress_, string memory mdUrl_) external onlyManagerOrOwner {
+ mdUrl = mdUrl_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setName(address nftAddress_, string memory name_) external onlyManagerOrOwner {
+ name = name_;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/metadata/NftMetadataSingleCollection.sol b/contracts/launchpad/erc721/metadata/NftMetadataSingleCollection.sol
new file mode 100644
index 0000000..45100b7
--- /dev/null
+++ b/contracts/launchpad/erc721/metadata/NftMetadataSingleCollection.sol
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../../access/OwnableWithManagers.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+/**
+@title Metadata contract for a single ERC-721 NFT made with Iggy Launchpad
+@author Tempe Techie
+*/
+contract NftMetadataSingleCollection is OwnableWithManagers {
+ using Strings for uint256;
+
+ string public collectionPreview;
+ string public description;
+ string public externalUrl;
+ string public image;
+ string public mdUrl; // optional, if mdType is not 0
+
+ string public name;
+
+ uint256 mdType = 0;
+ // mdType options:
+ // - 0: encoded metadata, no need for metadata from a 3rd party source such as IPFS or a custom API.
+ // - 1: url to metadata IPFS or custom API. One metadata URL for all NFTs (meaning all NFTs have the same metadata and image).
+ // - 2: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (ends with .json extension).
+ // - 3: base url to metadata IPFS or custom API. Token ID of each NFT is appended to the base url to get the metadata and image (does NOT end with .json extension).
+
+ // CONSTRUCTOR
+ constructor (
+ string memory collectionPreview_,
+ string memory description_,
+ string memory externalUrl_,
+ string memory image_,
+ string memory name_
+ ) {
+ collectionPreview = collectionPreview_;
+ description = description_;
+ externalUrl = externalUrl_;
+ image = image_;
+ name = name_;
+ }
+
+ // READ
+
+ /// @dev for compatibility with NftMetadata.sol
+ function collectionPreviews(address nftAddress_) external view returns (string memory) {
+ return collectionPreview;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function descriptions(address nftAddress_) external view returns (string memory) {
+ return description;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function externalUrls(address nftAddress_) external view returns (string memory) {
+ return externalUrl;
+ }
+
+ function getMetadata(uint256 tokenId_) public view returns (string memory) {
+ if (mdType == 1) {
+ return mdUrl;
+ } else if (mdType == 2) {
+ return string(abi.encodePacked(mdUrl, tokenId_.toString(), ".json"));
+ } else if (mdType == 3) {
+ return string(abi.encodePacked(mdUrl, tokenId_.toString()));
+ }
+
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ '{"name": "', name,' #', tokenId_.toString(),'", ',
+ '"image": "', image, '", ',
+ '"external_url": "', externalUrl, '", ',
+ '"description": "', description, '"',
+ '}'))))
+ );
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function images(address nftAddress_) external view returns (string memory) {
+ return image;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function mdTypes(address nftAddress_) external view returns (uint256) {
+ return mdType;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function mdUrls(address nftAddress_) external view returns (string memory) {
+ return mdUrl;
+ }
+
+ /// @dev for compatibility with NftMetadata.sol
+ function names(address nftAddress_) external view returns (string memory) {
+ return name;
+ }
+
+ // OWNER
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setCollectionPreview(address nftAddress_, string memory collectionPreview_) external onlyManagerOrOwner {
+ collectionPreview = collectionPreview_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setDescription(address nftAddress_, string memory description_) external onlyManagerOrOwner {
+ description = description_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setExternalUrl(address nftAddress_, string memory externalUrl_) external onlyManagerOrOwner {
+ externalUrl = externalUrl_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setImage(address nftAddress_, string memory image_) external onlyManagerOrOwner {
+ image = image_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setMdType(address nftAddress_, uint256 mdType_) external onlyManagerOrOwner {
+ mdType = mdType_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setMdTypeAndUrlOrImage(
+ address nftAddress_,
+ uint256 mdType_,
+ string memory mdUrlOrImage_,
+ string memory collectionImage_
+ ) external onlyManagerOrOwner {
+ mdType = mdType_;
+
+ if (mdType_ == 0) {
+ image = mdUrlOrImage_;
+ } else {
+ mdUrl = mdUrlOrImage_;
+ }
+
+ if (bytes(collectionImage_).length > 0) {
+ collectionPreview = collectionImage_;
+ }
+ }
+
+ /// @notice URL to metadata on IPFS or from a custom API (needed for mdType 1 and 2). Needs to end with slash /
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setMdUrl(address nftAddress_, string memory mdUrl_) external onlyManagerOrOwner {
+ mdUrl = mdUrl_;
+ }
+
+ /// @dev nftAddress_ as param for compatibility with NftMetadata.sol
+ function setName(address nftAddress_, string memory name_) external onlyManagerOrOwner {
+ name = name_;
+ }
+
+}
diff --git a/contracts/launchpad/erc721/readme.md b/contracts/launchpad/erc721/readme.md
new file mode 100644
index 0000000..b8fd53f
--- /dev/null
+++ b/contracts/launchpad/erc721/readme.md
@@ -0,0 +1,10 @@
+# NFT launchpad smart contracts
+
+Deployment order:
+
+1. Deploy LaunchpadStats contract.
+2. Deploy StatsMiddleware contract.
+3. Enter address of each deployed contracts into another.
+4. Deploy NftMetadata.sol contract.
+5. Deploy IggyLaunchpad contract (enter the stats middleware address and the metadata address in the constructor).
+6. Enter Launchpad address as a writer into the stats middleware contract.
\ No newline at end of file
diff --git a/contracts/lib/ERC5192.sol b/contracts/lib/ERC5192.sol
new file mode 100644
index 0000000..4d90687
--- /dev/null
+++ b/contracts/lib/ERC5192.sol
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-3.0-only
+pragma solidity ^0.8.17;
+
+import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import { IERC5192 } from "./IERC5192.sol";
+
+abstract contract ERC5192 is ERC721, IERC5192 {
+ bool private isLocked;
+
+ error ErrLocked();
+ error ErrNotFound();
+
+ constructor(string memory _name, string memory _symbol, bool _isLocked)
+ ERC721(_name, _symbol)
+ {
+ isLocked = _isLocked;
+ }
+
+ modifier checkLock() {
+ if (isLocked) revert ErrLocked();
+ _;
+ }
+
+ function locked(uint256 tokenId) external view returns (bool) {
+ if (!_exists(tokenId)) revert ErrNotFound();
+ return isLocked;
+ }
+
+ function safeTransferFrom(
+ address from,
+ address to,
+ uint256 tokenId,
+ bytes memory data
+ ) public override checkLock {
+ super.safeTransferFrom(from, to, tokenId, data);
+ }
+
+ function safeTransferFrom(address from, address to, uint256 tokenId)
+ public
+ override
+ checkLock
+ {
+ super.safeTransferFrom(from, to, tokenId);
+ }
+
+ function transferFrom(address from, address to, uint256 tokenId)
+ public
+ override
+ checkLock
+ {
+ super.transferFrom(from, to, tokenId);
+ }
+
+ function approve(address approved, uint256 tokenId) public override checkLock {
+ super.approve(approved, tokenId);
+ }
+
+ function setApprovalForAll(address operator, bool approved)
+ public
+ override
+ checkLock
+ {
+ super.setApprovalForAll(operator, approved);
+ }
+
+ function supportsInterface(bytes4 interfaceId)
+ public
+ view
+ virtual
+ override
+ returns (bool)
+ {
+ return interfaceId == type(IERC5192).interfaceId
+ || super.supportsInterface(interfaceId);
+ }
+}
\ No newline at end of file
diff --git a/contracts/lib/IERC5192.sol b/contracts/lib/IERC5192.sol
new file mode 100644
index 0000000..44aee58
--- /dev/null
+++ b/contracts/lib/IERC5192.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: CC0-1.0
+pragma solidity ^0.8.17;
+
+interface IERC5192 {
+ /// @notice Emitted when the locking status is changed to locked.
+ /// @dev If a token is minted and the status is locked, this event should be emitted.
+ /// @param tokenId The identifier for a token.
+ event Locked(uint256 tokenId);
+
+ /// @notice Emitted when the locking status is changed to unlocked.
+ /// @dev If a token is minted and the status is unlocked, this event should be emitted.
+ /// @param tokenId The identifier for a token.
+ event Unlocked(uint256 tokenId);
+
+ /// @notice Returns the locking status of an Soulbound Token
+ /// @dev SBTs assigned to zero address are considered invalid, and queries
+ /// about them do throw.
+ /// @param tokenId The identifier for an SBT.
+ function locked(uint256 tokenId) external view returns (bool);
+}
\ No newline at end of file
diff --git a/contracts/merkle/MerkleClaimerERC721.sol b/contracts/merkle/MerkleClaimerERC721.sol
new file mode 100644
index 0000000..0553783
--- /dev/null
+++ b/contracts/merkle/MerkleClaimerERC721.sol
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
+import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
+
+interface INFT is IERC721 {
+ function balanceOf(address owner) external view returns (uint256 balance);
+ function mint(address to) external;
+}
+
+contract MerkleClaimerERC721 is OwnableWithManagers {
+ address public immutable nftAddress;
+ bool public paused = false;
+ bytes32 public immutable root; // merkle root
+
+ // CONSTRUCTOR
+ constructor(address nftAddress_, bytes32 root_) {
+ nftAddress = nftAddress_;
+ root = root_;
+ }
+
+ // WRITE
+ function claim(address to_, bytes32[] memory proof_) external {
+ require(!paused, "MerkleClaimerERC721: minting is paused");
+ require(INFT(nftAddress).balanceOf(to_) == 0, "MerkleClaimerERC721: user has already minted the NFT");
+
+ bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(to_, 1)))); // amount is 1 NFT
+ require(MerkleProof.verify(proof_, root, leaf), "MerkleClaimerERC721: Invalid proof");
+
+ INFT(nftAddress).mint(to_);
+ }
+
+ // OWNER
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+}
\ No newline at end of file
diff --git a/contracts/mock/MockErc20TokenDecimals.sol b/contracts/mock/MockErc20TokenDecimals.sol
new file mode 100644
index 0000000..f83a1c7
--- /dev/null
+++ b/contracts/mock/MockErc20TokenDecimals.sol
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.4;
+
+import "@rari-capital/solmate/src/tokens/ERC20.sol";
+
+// Mock token contract for testing purposes only
+contract MockErc20TokenDecimals is ERC20 {
+
+ constructor(
+ string memory _name,
+ string memory _symbol,
+ uint8 _decimals
+ ) ERC20(_name, _symbol, _decimals) {
+ mint(msg.sender, 1000*(10**_decimals)); // 1000 mock tokens to deployer
+ }
+
+ function mint(address receiver, uint amount) public {
+ _mint(receiver, amount);
+ }
+}
diff --git a/contracts/mock/MockErc20TokenVotingBlock.sol b/contracts/mock/MockErc20TokenVotingBlock.sol
new file mode 100644
index 0000000..e210233
--- /dev/null
+++ b/contracts/mock/MockErc20TokenVotingBlock.sol
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC20, ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import { ERC20Votes, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MockErc20TokenVotingBlock is ERC20Burnable, Ownable, ERC20Votes {
+
+ // CONSTRUCTOR
+ constructor (
+ string memory _name,
+ string memory _symbol
+ ) ERC20(_name, _symbol) ERC20Permit(_name) {}
+
+ // MINTER
+
+ function mint(address _to, uint256 _amount) external {
+ _mint(_to, _amount);
+ }
+
+ function _afterTokenTransfer(
+ address from,
+ address to,
+ uint256 amount
+ ) internal override(ERC20, ERC20Votes) {
+ super._afterTokenTransfer(from, to, amount);
+ }
+
+ function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._mint(to, amount);
+ }
+
+ function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._burn(account, amount);
+ }
+
+}
diff --git a/contracts/mock/MockErc20TokenVotingTimestamp.sol b/contracts/mock/MockErc20TokenVotingTimestamp.sol
new file mode 100644
index 0000000..0f7ecea
--- /dev/null
+++ b/contracts/mock/MockErc20TokenVotingTimestamp.sol
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC20, ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import { ERC20Votes, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MockErc20TokenVotingTimestamp is ERC20Burnable, Ownable, ERC20Votes {
+
+ // CONSTRUCTOR
+ constructor (
+ string memory _name,
+ string memory _symbol
+ ) ERC20(_name, _symbol) ERC20Permit(_name) {}
+
+ // MINTER
+
+ function mint(address _to, uint256 _amount) external {
+ _mint(_to, _amount);
+ }
+
+ // Overrides IERC6372 functions to make the token & governor timestamp-based
+ function clock() public view override returns (uint48) {
+ return uint48(block.timestamp);
+ }
+
+ // Overrides IERC6372 functions to make the token & governor timestamp-based
+ function CLOCK_MODE() public pure override returns (string memory) { // solhint-disable-line func-name-mixedcase
+ return "mode=timestamp";
+ }
+
+ function _afterTokenTransfer(
+ address from,
+ address to,
+ uint256 amount
+ ) internal override(ERC20, ERC20Votes) {
+ super._afterTokenTransfer(from, to, amount);
+ }
+
+ function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._mint(to, amount);
+ }
+
+ function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._burn(account, amount);
+ }
+
+}
diff --git a/contracts/mock/MockErc721WithMinter.sol b/contracts/mock/MockErc721WithMinter.sol
new file mode 100644
index 0000000..bd58947
--- /dev/null
+++ b/contracts/mock/MockErc721WithMinter.sol
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+contract MockErc721WithMinter is ERC721, Ownable {
+ address public minterAddress;
+
+ uint256 public counter = 1;
+
+ // CONSTRUCTOR
+ constructor (
+ string memory _name,
+ string memory _symbol
+ ) ERC721(_name, _symbol) {}
+
+ // MINTER
+
+ function mint(address _to) external {
+ require(msg.sender == minterAddress, "MockErc721WithMinter: only minter can mint");
+
+ _mint(_to, counter);
+ counter++;
+ }
+
+ // OWNER
+
+ function setMinterAddress(address _minterAddress) external onlyOwner {
+ minterAddress = _minterAddress;
+ }
+
+}
diff --git a/contracts/mock/MockGovernorBlock.sol b/contracts/mock/MockGovernorBlock.sol
new file mode 100644
index 0000000..d1b05da
--- /dev/null
+++ b/contracts/mock/MockGovernorBlock.sol
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import "@openzeppelin/contracts/governance/Governor.sol";
+import "@openzeppelin/contracts/governance/extensions/GovernorSettings.sol";
+import "@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol";
+import "@openzeppelin/contracts/governance/extensions/GovernorVotes.sol";
+
+contract MockGovernorBlock is Governor, GovernorSettings, GovernorCountingSimple, GovernorVotes {
+
+ constructor(IVotes _token)
+ Governor("MockGovernorBlock")
+ GovernorSettings(50 /* 10 minute */, 300 /* 1 hour */, 10e18)
+ GovernorVotes(_token)
+ {}
+
+ function quorum(uint256 blockNumber) public pure override returns (uint256) {
+ return 50e18;
+ }
+
+ // The following functions are overrides required by Solidity.
+
+ function votingDelay()
+ public
+ view
+ override(IGovernor, GovernorSettings)
+ returns (uint256)
+ {
+ return super.votingDelay();
+ }
+
+ function votingPeriod()
+ public
+ view
+ override(IGovernor, GovernorSettings)
+ returns (uint256)
+ {
+ return super.votingPeriod();
+ }
+
+ function proposalThreshold()
+ public
+ view
+ override(Governor, GovernorSettings)
+ returns (uint256)
+ {
+ return super.proposalThreshold();
+ }
+}
\ No newline at end of file
diff --git a/contracts/mock/MockPunkTld.sol b/contracts/mock/MockPunkTld.sol
new file mode 100644
index 0000000..c904b64
--- /dev/null
+++ b/contracts/mock/MockPunkTld.sol
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+contract MockPunkTld {
+ mapping(string => address) public domainHolders;
+
+ constructor(address domainHolder_, string memory domainName_) {
+ domainHolders[domainName_] = domainHolder_;
+ }
+
+ function defaultNames(address) external pure returns(string memory) {
+ return "mockname";
+ }
+
+ function domains(string calldata _domainName) external view returns(string memory, uint256, address, string memory) {
+ return (
+ _domainName, // domain name
+ 1, // domain NFT ID
+ domainHolders[_domainName], // domain holder
+ "" // domain data
+ );
+ }
+
+ function getDomainHolder(string calldata _domainName) external view returns(address) {
+ return domainHolders[_domainName];
+ }
+
+ function name() external pure returns(string memory) {
+ return ".tld";
+ }
+
+ // WRITE
+
+ // this is mock function for testing purposes only (it does not exist in the real Punk Domains contract)
+ function register(string calldata _domainName, address _domainHolder) external {
+ require(_domainHolder != address(0), "MockPunkTld: domain holder cannot be zero address");
+ require(keccak256(abi.encodePacked(_domainName)) != keccak256(abi.encodePacked("existing1")), "MockPunkTld: domain name already exists");
+
+ domainHolders[_domainName] = _domainHolder;
+ }
+}
\ No newline at end of file
diff --git a/contracts/mock/MockSFS.sol b/contracts/mock/MockSFS.sol
new file mode 100644
index 0000000..44a712c
--- /dev/null
+++ b/contracts/mock/MockSFS.sol
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.4;
+
+/// @title Mock SFS contract
+/// @author Tempe Techie
+/// @notice Mock SFS contract for testing purposes
+contract MockSFS {
+ function register(address _recipient) external returns (uint256 tokenId) {
+ return 123;
+ }
+
+ function assign(uint256 _tokenId) external returns (uint256) {
+ return _tokenId;
+ }
+}
\ No newline at end of file
diff --git a/contracts/mode/ISFS.sol b/contracts/mode/ISFS.sol
new file mode 100644
index 0000000..5ba3b70
--- /dev/null
+++ b/contracts/mode/ISFS.sol
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+interface ISFS {
+ function register(address _recipient) external returns (uint256 tokenId);
+ function assign(uint256 _tokenId) external returns (uint256);
+}
diff --git a/contracts/mode/SfsNftInitialize.sol b/contracts/mode/SfsNftInitialize.sol
new file mode 100644
index 0000000..be0f82c
--- /dev/null
+++ b/contracts/mode/SfsNftInitialize.sol
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ISFS } from "./ISFS.sol";
+
+/// @title SFS NFT generator contract
+/// @author Tempe Techie
+/// @notice Contract that generates the Mode SFS NFT
+contract SfsNftInitialize is OwnableWithManagers {
+ uint256 public immutable sfsNftTokenId;
+
+ // CONSTRUCTOR
+ constructor(address sfsAddress_, address feeReceiver_) {
+ // Testnet SFS addr: 0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6, mainnet: 0x8680CEaBcb9b56913c519c069Add6Bc3494B7020
+ sfsNftTokenId = ISFS(sfsAddress_).register(feeReceiver_);
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/nft/early-stakers/EarlyStakerMetadata.sol b/contracts/nft/early-stakers/EarlyStakerMetadata.sol
new file mode 100644
index 0000000..b045a94
--- /dev/null
+++ b/contracts/nft/early-stakers/EarlyStakerMetadata.sol
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../access/OwnableWithManagers.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+
+contract EarlyStakerMetadata is OwnableWithManagers {
+ string public description;
+ string public externalUrl;
+ string public image;
+ string public name;
+ string public video; // animation_url
+
+ // CONSTRUCTOR
+ constructor (
+ string memory description_,
+ string memory externalUrl_,
+ string memory image_,
+ string memory name_,
+ string memory video_
+ ) {
+ description = description_;
+ externalUrl = externalUrl_;
+ image = image_;
+ name = name_;
+ video = video_;
+ }
+
+ // READ
+
+ function getMetadata(uint256 tokenId_) public view returns (string memory) {
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ '{"name": "', name,'", ',
+ '"image": "', image, '", ',
+ '"external_url": "', externalUrl, '", ',
+ '"description": "', description, '", ',
+ '"animation_url": "', video, '"',
+ '}'))))
+ );
+ }
+
+ // OWNER
+
+ function changeDescription(string memory description_) external onlyManagerOrOwner {
+ description = description_;
+ }
+
+ function changeExternalUrl(string memory externalUrl_) external onlyManagerOrOwner {
+ externalUrl = externalUrl_;
+ }
+
+ function changeImage(string memory image_) external onlyManagerOrOwner {
+ image = image_;
+ }
+
+ function changeName(string memory name_) external onlyManagerOrOwner {
+ name = name_;
+ }
+
+ function changeVideo(string memory video_) external onlyManagerOrOwner {
+ video = video_;
+ }
+
+}
diff --git a/contracts/nft/early-stakers/EarlyStakerNft.sol b/contracts/nft/early-stakers/EarlyStakerNft.sol
new file mode 100644
index 0000000..2af607b
--- /dev/null
+++ b/contracts/nft/early-stakers/EarlyStakerNft.sol
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../../access/OwnableWithManagers.sol";
+import { ERC5192 } from "../../lib/ERC5192.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+
+interface IEarlyStakerMetadata {
+ function getMetadata(uint256 _tokenId) external view returns (string memory);
+}
+
+/**
+ * @title EarlyStakerNft
+ * @author Tempe Techie
+ * @notice ERC-5192 is an extension of ERC-721 that makes the NFT soulbound (e.g. non-transferable)
+ */
+contract EarlyStakerNft is ERC5192, OwnableWithManagers {
+ address public metadataAddress;
+ address public minterAddress;
+
+ bool public paused = false;
+ uint256 public counter = 1;
+
+ // CONSTRUCTOR
+ constructor (
+ address _metadataAddress,
+ string memory _name,
+ string memory _symbol
+ ) ERC5192(_name, _symbol, true) {
+ metadataAddress = _metadataAddress;
+ }
+
+ // READ
+
+ function tokenURI(uint256 _tokenId) public view override returns (string memory) {
+ return IEarlyStakerMetadata(metadataAddress).getMetadata(_tokenId);
+ }
+
+ // WRITE
+
+ function mint(address _to) external {
+ require(!paused, "EarlyStakerNft: minting is paused");
+ require(msg.sender == minterAddress, "EarlyStakerNft: Only minter can mint");
+
+ _mint(_to, counter);
+ counter++;
+ }
+
+ // OWNER
+
+ function changeMetadataAddress(address _metadataAddress) external onlyManagerOrOwner {
+ metadataAddress = _metadataAddress;
+ }
+
+ function changeMinterAddress(address _minterAddress) external onlyManagerOrOwner {
+ minterAddress = _minterAddress;
+ }
+
+ function ownerMintNft(address _to) external onlyManagerOrOwner {
+ _mint(_to, counter);
+ counter++;
+ }
+
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+
+}
diff --git a/contracts/post/IggyPostMetadata.sol b/contracts/post/IggyPostMetadata.sol
new file mode 100644
index 0000000..a170309
--- /dev/null
+++ b/contracts/post/IggyPostMetadata.sol
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+interface IBasePunkTLD {
+ function name() external view returns(string memory);
+ function defaultNames(address) external view returns(string memory);
+}
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+/// @title Metadata contract
+/// @author Tempe Techie
+/// @notice Contract that stores metadata for an Iggy Post NFT
+contract IggyPostMetadata is OwnableWithManagers {
+ using Strings for uint256;
+
+ address public immutable tldAddress;
+
+ string public description;
+ string public name;
+ string public url;
+
+ // EVENTS
+ event DescriptionChanged(address indexed user, string description);
+ event NameChanged(address indexed user, string name);
+
+ // constructor
+ constructor(
+ string memory _name,
+ string memory _description,
+ string memory _url,
+ address _tldAddress,
+ address _sfsAddress,
+ uint256 _tokenId
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+
+ name = _name;
+ description = _description;
+ url = _url;
+
+ tldAddress = _tldAddress;
+ }
+
+ // INTERNAL
+ function _randomHueNum(uint256 _tokenId) internal view returns(uint256) {
+ return uint256(keccak256(abi.encodePacked(_tokenId, tldAddress, name))) % 361;
+ }
+
+ // READ
+ function getMetadata(
+ uint256 _tokenId,
+ string calldata _postId,
+ address _author,
+ string calldata _textPreview,
+ string memory _image,
+ uint256 _timestamp
+ ) public view returns(string memory) {
+ if (bytes(_textPreview).length > 0) {
+ // if there's text preview, always generate a new SVG image
+ // if there's no text preview, use the provided _image
+ _image = _getImage(_tokenId, _textPreview, _author);
+ }
+
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ _getInitialData(_tokenId, _postId),
+ _getAttributes(_postId, _author, _timestamp),
+ '"image": "', _image, '"}'))))
+ );
+ }
+
+ function _getImage(uint256 _tokenId, string memory _textPreview, address _author) internal view returns (string memory) {
+ string memory hue = _randomHueNum(_tokenId).toString();
+ string memory authorName = IBasePunkTLD(tldAddress).defaultNames(_author);
+
+ if (bytes(authorName).length == 0) {
+ authorName = "Anonymous";
+ } else {
+ authorName = string(abi.encodePacked(authorName, IBasePunkTLD(tldAddress).name()));
+ }
+
+ string memory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(
+ ''
+ ))));
+
+ return string(abi.encodePacked("data:image/svg+xml;base64,", svgBase64Encoded));
+ }
+
+ function _getAttributes(string calldata _postId, address _author, uint256 _timestamp) internal pure returns (string memory) {
+ return string(abi.encodePacked(
+ '"attributes": [',
+ '{"trait_type": "post id", "value": "', _postId, '"}, ',
+ '{"trait_type": "author", "value": "', Strings.toHexString(uint160(_author), 20), '"}, ',
+ '{"trait_type": "created", "display_type": "date", "value": "', _timestamp.toString(), '"}'
+ '], '
+ ));
+ }
+
+ function _getInitialData(uint256 _tokenId, string calldata _postId) internal view returns (string memory) {
+ return string(abi.encodePacked(
+ '{"name": "', name, ' #', _tokenId.toString(), '", ',
+ '"description": "', description, '", ',
+ '"external_url": "', url, '?id=', _postId, '", '
+ ));
+ }
+
+ // WRITE (OWNER)
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeName(string calldata _name) external onlyManagerOrOwner {
+ name = _name;
+ emit NameChanged(msg.sender, _name);
+ }
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeDescription(string calldata _description) external onlyManagerOrOwner {
+ description = _description;
+ emit DescriptionChanged(msg.sender, _description);
+ }
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeUrl(string calldata _url) external onlyManagerOrOwner {
+ url = _url;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostMetadataStaticColor.sol b/contracts/post/IggyPostMetadataStaticColor.sol
new file mode 100644
index 0000000..e33a2aa
--- /dev/null
+++ b/contracts/post/IggyPostMetadataStaticColor.sol
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { Base64 } from "@openzeppelin/contracts/utils/Base64.sol";
+import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
+
+interface IBasePunkTLD {
+ function name() external view returns(string memory);
+ function defaultNames(address) external view returns(string memory);
+}
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+/// @title Metadata contract with static color background and text preview
+/// @author Tempe Techie
+/// @notice Contract that stores metadata for an Iggy Post NFT
+contract IggyPostMetadataStaticColor is OwnableWithManagers {
+ using Strings for uint256;
+
+ address public immutable tldAddress;
+
+ string public colorCode;
+ string public description;
+ string public name;
+ string public url;
+
+ // EVENTS
+ event DescriptionChanged(address indexed user, string description);
+ event NameChanged(address indexed user, string name);
+
+ // constructor
+ constructor(
+ string memory _colorCode,
+ string memory _name,
+ string memory _description,
+ string memory _url,
+ address _tldAddress,
+ address _sfsAddress,
+ uint256 _tokenId
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+
+ colorCode = _colorCode;
+ name = _name;
+ description = _description;
+ url = _url;
+
+ tldAddress = _tldAddress;
+ }
+
+ // READ
+ function getMetadata(
+ uint256 _tokenId,
+ string calldata _postId,
+ address _author,
+ string calldata _textPreview,
+ string memory _image,
+ uint256 _timestamp
+ ) public view returns(string memory) {
+ if (bytes(_textPreview).length > 0) {
+ // if there's text preview, always generate a new SVG image
+ // if there's no text preview, use the provided _image
+ _image = _getImage(_textPreview, _author);
+ }
+
+ return string(
+ abi.encodePacked("data:application/json;base64,", Base64.encode(bytes(abi.encodePacked(
+ _getInitialData(_tokenId, _postId),
+ _getAttributes(_postId, _author, _timestamp),
+ '"image": "', _image, '"}'))))
+ );
+ }
+
+ function _getImage(string memory _textPreview, address _author) internal view returns (string memory) {
+ string memory authorName = IBasePunkTLD(tldAddress).defaultNames(_author);
+
+ if (bytes(authorName).length == 0) {
+ authorName = "Anonymous";
+ } else {
+ authorName = string(abi.encodePacked(authorName, IBasePunkTLD(tldAddress).name()));
+ }
+
+ string memory svgBase64Encoded = Base64.encode(bytes(string(abi.encodePacked(
+ ''
+ ))));
+
+ return string(abi.encodePacked("data:image/svg+xml;base64,", svgBase64Encoded));
+ }
+
+ function _getAttributes(string calldata _postId, address _author, uint256 _timestamp) internal pure returns (string memory) {
+ return string(abi.encodePacked(
+ '"attributes": [',
+ '{"trait_type": "post id", "value": "', _postId, '"}, ',
+ '{"trait_type": "author", "value": "', Strings.toHexString(uint160(_author), 20), '"}, ',
+ '{"trait_type": "created", "display_type": "date", "value": "', _timestamp.toString(), '"}'
+ '], '
+ ));
+ }
+
+ function _getInitialData(uint256 _tokenId, string calldata _postId) internal view returns (string memory) {
+ return string(abi.encodePacked(
+ '{"name": "', name, ' #', _tokenId.toString(), '", ',
+ '"description": "', description, '", ',
+ '"external_url": "', url, '?id=', _postId, '", '
+ ));
+ }
+
+ // WRITE (OWNER)
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeColorCode(string calldata _colorCode) external onlyManagerOrOwner {
+ colorCode = _colorCode;
+ }
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeName(string calldata _name) external onlyManagerOrOwner {
+ name = _name;
+ emit NameChanged(msg.sender, _name);
+ }
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeDescription(string calldata _description) external onlyManagerOrOwner {
+ description = _description;
+ emit DescriptionChanged(msg.sender, _description);
+ }
+
+ /// @notice Only metadata contract owner can call this function.
+ function changeUrl(string calldata _url) external onlyManagerOrOwner {
+ url = _url;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostMinter.sol b/contracts/post/IggyPostMinter.sol
new file mode 100644
index 0000000..c00fdc9
--- /dev/null
+++ b/contracts/post/IggyPostMinter.sol
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+interface IIggyPostNft {
+
+ function getPostPrice (string memory _postId, address _author) external view returns (uint256);
+
+ function owner() external view returns(address);
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external returns(uint256);
+
+}
+
+interface IIggyPostStats {
+ function addMintedPostId(address _user, uint256 _postId) external;
+ function addMintedWei(address _user, uint256 _wei) external;
+ function addWeiEarnedByAuthorPerPostId(uint256 _postId, uint256 _wei) external;
+}
+
+/**
+@title IggyPostMinter (V1)
+@notice This contract allows users to mint IggyPost NFTs and paying with ETH.
+@dev Use this contract when CHAT token is NOT deployed yet.
+*/
+contract IggyPostMinter is OwnableWithManagers, ReentrancyGuard {
+ address public daoAddress;
+ address public devAddress;
+ address public devFeeUpdaterAddress;
+ address public immutable postAddress;
+ address public statsAddress;
+
+ bool public statsEnabled = false;
+ bool public paused = false;
+
+ uint256 public constant MAX_BPS = 10_000;
+ uint256 public daoFee; // share of each domain purchase (in bips) that goes to the DAO/community that owns the frontend
+ uint256 public devFee; // share of each domain purchase (in bips) that goes to the developer (Iggy team)
+ uint256 public referrerFee; // share of each domain purchase (in bips) that goes to the referrer
+
+ // CONSTRUCTOR
+ constructor(
+ address _sfsAddress,
+ address _daoAddress,
+ address _devAddress,
+ uint256 _tokenId,
+ address _postAddress,
+ uint256 _daoFee,
+ uint256 _devFee,
+ uint256 _referrerFee
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+
+ daoAddress = _daoAddress;
+ devAddress = _devAddress;
+ devFeeUpdaterAddress = _devAddress;
+ postAddress = _postAddress;
+
+ daoFee = _daoFee;
+ devFee = _devFee;
+ referrerFee = _referrerFee;
+ }
+
+ // WRITE
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ address _referrer,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external nonReentrant payable returns(uint256 tokenId) {
+ require(!paused, "Minting paused");
+
+ // find price
+ uint256 price = IIggyPostNft(postAddress).getPostPrice(_postId, _author) * _quantity;
+
+ require(msg.value >= price, "Value below price");
+
+ // send a referrer fee
+ uint256 referrerPayment;
+ if (referrerFee > 0 && _referrer != address(0)) {
+ referrerPayment = (price * referrerFee) / MAX_BPS;
+ (bool sentReferrerFee, ) = _referrer.call{value: referrerPayment}("");
+ require(sentReferrerFee, "Failed to send referrer fee");
+ }
+
+ // send a dev fee
+ if (devFee > 0 && devAddress != address(0)) {
+ uint256 devPayment = (price * devFee) / MAX_BPS;
+ (bool sentDevFee, ) = devAddress.call{value: devPayment}("");
+ require(sentDevFee, "Failed to send dev fee");
+ }
+
+ // send a dao fee
+ if (daoFee > 0 && daoAddress != address(0)) {
+ uint256 daoFeePayment = (price * daoFee) / MAX_BPS;
+ (bool sentDaoFee, ) = daoAddress.call{value: daoFeePayment}("");
+ require(sentDaoFee, "Failed to send dao fee");
+ }
+
+ // send the rest to post author
+ (bool sent, ) = _author.call{value: address(this).balance}("");
+ require(sent, "Failed to send payment to the post author");
+
+ // mint the post as NFT
+ tokenId = IIggyPostNft(postAddress).mint(_postId, _author, _nftReceiver, _textPreview, _image, _quantity);
+
+ // store some stats in the stats contract
+ if (statsEnabled && statsAddress != address(0)) {
+ uint256 fees;
+
+ if (referrerPayment > 0) {
+ // referral fee was taken and sent to the referrer
+ fees = (price * (devFee + daoFee)) / MAX_BPS;
+
+ // asign wei from referrer fee to referrer in stats
+ IIggyPostStats(statsAddress).addMintedWei(_referrer, referrerPayment);
+ } else {
+ // referral fee was not taken
+ fees = (price * (referrerFee + devFee + daoFee)) / MAX_BPS;
+ }
+
+ // feel free to comment out the stats that you don't need to track
+ IIggyPostStats(statsAddress).addMintedWei(_nftReceiver, fees);
+
+ // exclude fees from the price
+ price -= fees;
+ IIggyPostStats(statsAddress).addWeiEarnedByAuthorPerPostId(tokenId, price);
+
+ IIggyPostStats(statsAddress).addMintedPostId(_nftReceiver, tokenId);
+ }
+ }
+
+ // OWNER
+
+ // change dao fee
+ function changeDaoFee(uint256 _daoFee) external onlyManagerOrOwner {
+ require(_daoFee <= MAX_BPS, "Fee cannot be more than 100%");
+ daoFee = _daoFee;
+ }
+
+ // change stats address
+ function changeStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ // change referrer fee
+ function changeReferrerFee(uint256 _referrerFee) external onlyManagerOrOwner {
+ require(_referrerFee <= 2000, "Fee cannot be more than 20%");
+ referrerFee = _referrerFee;
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).transfer(recipient_, tokenAmount_);
+ }
+
+ function toggleStatsEnabled() external onlyManagerOrOwner {
+ statsEnabled = !statsEnabled;
+ }
+
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+
+ /// @notice Withdraw native coins from contract
+ function withdraw() external onlyManagerOrOwner {
+ (bool success, ) = owner().call{value: address(this).balance}("");
+ require(success, "Failed to withdraw native coins from contract");
+ }
+
+ // OTHER WRITE METHODS
+
+ /// @notice This changes the DAO address in the minter contract
+ function changeDaoAddress(address _daoAddress) external {
+ require(_msgSender() == daoAddress, "Sender is not the DAO");
+ daoAddress = _daoAddress;
+ }
+
+ /// @notice This changes the developer's address in the minter contract
+ function changeDevAddress(address _devAddress) external {
+ require(_msgSender() == devAddress, "Sender is not the developer");
+ devAddress = _devAddress;
+ }
+
+ /// @notice This changes the dev fee updater's address in the minter contract
+ function changeDevFeeUpdaterAddress(address _devFeeUpdaterAddress) external {
+ require(_msgSender() == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ devFeeUpdaterAddress = _devFeeUpdaterAddress;
+ }
+
+ // change dev fee (only dev fee updater can change it)
+ function changeDevFee(uint256 _devFee) external {
+ require(_msgSender() == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ require(_devFee <= 2000, "Fee cannot be more than 20%");
+ devFee = _devFee;
+ }
+
+ // RECEIVE & FALLBACK
+ receive() external payable {}
+ fallback() external payable {}
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostMinterV2.sol b/contracts/post/IggyPostMinterV2.sol
new file mode 100644
index 0000000..4c2fc9f
--- /dev/null
+++ b/contracts/post/IggyPostMinterV2.sol
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+interface IChatTokenMinter {
+ function mint(address to, uint256 amount) external;
+}
+
+interface IIggyPostNft {
+
+ function getPostPrice (string memory _postId, address _author) external view returns (uint256);
+
+ function owner() external view returns(address);
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external returns(uint256);
+
+}
+
+interface IIggyPostStats {
+ function addMintedPostId(address _user, uint256 _postId) external;
+}
+
+/**
+@title IggyPostMinterV2
+@notice This contract allows users to mint IggyPost NFTs and paying with ETH.
+@dev Use this contract when CHAT token is deployed.
+*/
+contract IggyPostMinterV2 is OwnableWithManagers, ReentrancyGuard {
+ address public immutable chatTokenMinterAddress;
+ address public daoAddress; // can be a distributor contract that also sends ETH to staking contract
+ address public devAddress;
+ address public devFeeUpdaterAddress;
+ address public statsAddress;
+ address public immutable postAddress;
+
+ bool public statsEnabled = false;
+ bool public paused = false;
+
+ uint256 public chatEthRatio; // e.g. 1_000, which means 1 ETH (or payment token) = 1,000 CHAT
+ uint256 public immutable chatRewardsDuration; // CHAT rewards duration in seconds
+ uint256 public immutable chatRewardsEnd; // timestamp when CHAT rewards end
+
+ uint256 public constant MAX_BPS = 10_000;
+ uint256 public daoFee = 450; // share of each domain purchase (in bips) that goes to the DAO/community that owns the frontend
+ uint256 public devFee = 900; // share of each domain purchase (in bips) that goes to the developer (Iggy team)
+ uint256 public referrerFee = 200; // share of each domain purchase (in bips) that goes to the referrer
+
+ // CONSTRUCTOR
+ constructor(
+ address _chatTokenMinterAddress,
+ address _daoAddress,
+ address _devAddress,
+ address _devFeeUpdaterAddress,
+ address _postAddress,
+ uint256 _chatEthRatio, // e.g. 1_000, which means 1 ETH (or payment token) = 1,000 CHAT
+ uint256 _chatRewardsDuration // CHAT rewards duration in seconds
+ ) {
+ require(_chatTokenMinterAddress != address(0), "IggyPostMinterV2: CHAT token cannot be zero address");
+ require(_postAddress != address(0), "IggyPostMinterV2: Post address cannot be zero address");
+
+ chatTokenMinterAddress = _chatTokenMinterAddress;
+ daoAddress = _daoAddress;
+ devAddress = _devAddress;
+ devFeeUpdaterAddress = _devFeeUpdaterAddress;
+ postAddress = _postAddress;
+
+ chatEthRatio = _chatEthRatio;
+
+ chatRewardsDuration = _chatRewardsDuration; // e.g. 1 year (31_536_000 seconds)
+ chatRewardsEnd = block.timestamp + _chatRewardsDuration;
+ }
+
+ // READ
+
+ function getCurrentChatEthRatio() public view returns(uint256) {
+ // if chat rewards period ended, return 0
+ if (block.timestamp > chatRewardsEnd) {
+ return 0;
+ }
+
+ uint256 diff = chatRewardsEnd - block.timestamp;
+ uint256 diffRatio = (diff * MAX_BPS) / chatRewardsDuration;
+
+ return (chatEthRatio * diffRatio) / MAX_BPS;
+ }
+
+ // WRITE
+
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ address _referrer,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) external nonReentrant payable returns(uint256 tokenId) {
+ require(!paused, "Minting paused");
+
+ // find price
+ uint256 price = IIggyPostNft(postAddress).getPostPrice(_postId, _author) * _quantity;
+
+ require(msg.value >= price, "Value below price");
+
+ // send a referrer fee
+ if (referrerFee > 0 && _referrer != address(0)) {
+ uint256 referrerPayment = (price * referrerFee) / MAX_BPS;
+ (bool sentReferrerFee, ) = payable(_referrer).call{value: referrerPayment}("");
+ require(sentReferrerFee, "Failed to send referrer fee");
+ }
+
+ // send a dev fee
+ if (devFee > 0 && devAddress != address(0)) {
+ uint256 devPayment = (price * devFee) / MAX_BPS;
+ (bool sentDevFee, ) = payable(devAddress).call{value: devPayment}("");
+ require(sentDevFee, "Failed to send dev fee");
+ }
+
+ // send a dao fee
+ if (daoFee > 0 && daoAddress != address(0)) {
+ uint256 daoFeePayment = (price * daoFee) / MAX_BPS;
+ (bool sentDaoFee, ) = payable(daoAddress).call{value: daoFeePayment}("");
+ require(sentDaoFee, "Failed to send dao fee");
+ }
+
+ // send the rest to post author
+ (bool sent, ) = payable(_author).call{value: address(this).balance}("");
+ require(sent, "Failed to send payment to the post author");
+
+ // mint the post as NFT
+ tokenId = IIggyPostNft(postAddress).mint(_postId, _author, _nftReceiver, _textPreview, _image, _quantity);
+
+ // store some stats in the stats contract
+ if (statsEnabled && statsAddress != address(0)) {
+ // feel free to comment out the stats that you don't need to track
+ IIggyPostStats(statsAddress).addMintedPostId(_nftReceiver, tokenId);
+ }
+
+ // mint chat tokens for the NFT receiver (use only the fees to calculate the share of chat tokens, not the whole price)
+ if (chatTokenMinterAddress != address(0) && block.timestamp <= chatRewardsEnd) {
+ uint256 fees = (price * (devFee + daoFee + referrerFee)) / MAX_BPS;
+ IChatTokenMinter(chatTokenMinterAddress).mint(_nftReceiver, fees*getCurrentChatEthRatio());
+ }
+ }
+
+ // OWNER
+
+ // change chat token ratio
+ function changeChatEthRatio(uint256 _chatEthRatio) external onlyOwner {
+ chatEthRatio = _chatEthRatio;
+ }
+
+ /// @notice This changes the DAO address in the minter contract
+ function changeDaoAddress(address _daoAddress) external onlyManagerOrOwner {
+ daoAddress = _daoAddress;
+ }
+
+ // change dao fee
+ function changeDaoFee(uint256 _daoFee) external onlyManagerOrOwner {
+ require(_daoFee + devFee + referrerFee <= MAX_BPS, "Fees cannot be more than 100%");
+ daoFee = _daoFee;
+ }
+
+ // change stats address
+ function changeStatsAddress(address _statsAddress) external onlyManagerOrOwner {
+ statsAddress = _statsAddress;
+ }
+
+ // change referrer fee
+ function changeReferrerFee(uint256 _referrerFee) external onlyManagerOrOwner {
+ require(daoFee + devFee + _referrerFee <= MAX_BPS, "Fees cannot be more than 100%");
+ referrerFee = _referrerFee;
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).transfer(recipient_, tokenAmount_);
+ }
+
+ function toggleStatsEnabled() external onlyManagerOrOwner {
+ statsEnabled = !statsEnabled;
+ }
+
+ function togglePaused() external onlyManagerOrOwner {
+ paused = !paused;
+ }
+
+ /// @notice Withdraw native coins from contract
+ function withdraw() external onlyManagerOrOwner {
+ (bool success, ) = owner().call{value: address(this).balance}("");
+ require(success, "Failed to withdraw native coins from contract");
+ }
+
+ // OTHER WRITE METHODS
+
+ /// @notice This changes the developer's address in the minter contract
+ function changeDevAddress(address _devAddress) external {
+ require(msg.sender == devAddress, "Sender is not the developer");
+ devAddress = _devAddress;
+ }
+
+ /// @notice This changes the dev fee updater's address in the minter contract
+ function changeDevFeeUpdaterAddress(address _devFeeUpdaterAddress) external {
+ require(msg.sender == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ devFeeUpdaterAddress = _devFeeUpdaterAddress;
+ }
+
+ // change dev fee (only dev fee updater can change it)
+ function changeDevFee(uint256 _devFee) external {
+ require(msg.sender == devFeeUpdaterAddress, "Sender is not the dev fee updater");
+ require(daoFee + _devFee + referrerFee <= MAX_BPS, "Fees cannot be more than 100%");
+ devFee = _devFee;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostNft1155.sol b/contracts/post/IggyPostNft1155.sol
new file mode 100644
index 0000000..9a93565
--- /dev/null
+++ b/contracts/post/IggyPostNft1155.sol
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ERC1155 } from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+interface IIggyPostNftMetadata {
+ function getMetadata(
+ uint256 _tokenId,
+ string memory _postId,
+ address _author,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _timestamp
+ ) external view returns (string memory);
+}
+
+/// @title Iggy Social Post NFT
+contract IggyPostNft1155 is ERC1155, OwnableWithManagers, ReentrancyGuard {
+ address public metadataAddress; // address of the metadata contract
+ address public minterAddress; // address of the minter contract
+
+ bool public postChangingDisabledForever = false; // if true, owner can no longer change text preview or image of a post
+
+ uint256 public counter = 1; // id counter, starts with 1
+ uint256 public defaultPrice; // default price for minting a post
+ uint256 public maxTextPreviewLength = 243; // max length of the text preview
+
+ string public name;
+ string public symbol;
+
+ struct Post {
+ uint256 tokenId;
+ string postId; // post id on Ceramic network
+ address author;
+ string textPreview; // usually a post has text, unless it's just an image
+ string image; // image URL (optional)
+ uint256 timestamp;
+ }
+
+ // post mappings
+ mapping (uint256 => Post) public getPost; // mapping from token id to post
+ mapping (string => mapping (address => uint256)) public getPostTokenId; // mapping (postId => mapping (authorAddress => tokenId))
+
+ // price mappings
+ mapping (address => uint256) public getAuthorsDefaultPrice; // mapping (authorAddress => price), if zero, use default price
+ mapping (string => mapping (address => uint256)) public getPriceForPost; // mapping (postId => mapping (authorAddress => price)), if zero, use author's default price
+
+ // post minting deadline (important: minting time starts when the first NFT of a post is minted)
+ mapping (string => mapping (address => uint256)) public getPostMintingTime; // mapping (postId => mapping (authorAddress => secondsToMint)), if zero, there's no deadline to mint
+
+ // events
+ event MintPost(address nftReceiver, string post, address author, uint256 quantity);
+
+ // constructor
+ constructor(
+ uint256 _defaultPrice,
+ address _sfsAddress,
+ uint256 _tokenId,
+ address _metadataAddress,
+ string memory _name,
+ string memory _symbol
+ ) ERC1155("") {
+ ISFS(_sfsAddress).assign(_tokenId);
+
+ defaultPrice = _defaultPrice;
+ metadataAddress = _metadataAddress;
+ name = _name;
+ symbol = _symbol;
+ }
+
+ // READ
+
+ function doesPostExist(string memory _postId, address _authorId) external view returns (bool) {
+ return getPostTokenId[_postId][_authorId] != 0;
+ }
+
+ function getPostPrice(string memory _postId, address _author) external view returns (uint256) {
+ uint256 price = getPriceForPost[_postId][_author];
+
+ if (price == 0) {
+ price = getAuthorsDefaultPrice[_author];
+
+ if (price == 0) {
+ price = defaultPrice;
+ }
+ }
+
+ return price;
+ }
+
+ function uri(uint256 _tokenId) public view override returns (string memory) {
+ require(_tokenId < counter, "IggyPost: Token id does not exist");
+
+ Post memory post = getPost[_tokenId];
+
+ return IIggyPostNftMetadata(metadataAddress).getMetadata(
+ _tokenId,
+ post.postId,
+ post.author,
+ post.textPreview,
+ post.image,
+ post.timestamp
+ );
+ }
+
+ // WRITE
+
+ function authorSetDefaultPrice(uint256 _price) external {
+ getAuthorsDefaultPrice[_msgSender()] = _price;
+ }
+
+ function authorSetMintTime(string memory _postId, uint256 _secondsToMint) external {
+ getPostMintingTime[_postId][_msgSender()] = _secondsToMint;
+ }
+
+ function authorSetPostPrice(string memory _postId, uint256 _price) external {
+ getPriceForPost[_postId][_msgSender()] = _price;
+ }
+
+ /// @notice Mint a post NFT
+ function mint(
+ string memory _postId,
+ address _author,
+ address _nftReceiver,
+ string memory _textPreview,
+ string memory _image,
+ uint256 _quantity
+ ) nonReentrant external returns(uint256 tokenId) {
+ require(_msgSender() == minterAddress, "IggyPost: Only minter can mint");
+ require(bytes(_textPreview).length <= maxTextPreviewLength, "IggyPost: Text preview is too long");
+
+ tokenId = getPostTokenId[_postId][_author];
+
+ if (tokenId == 0) {
+ tokenId = counter;
+ counter++;
+
+ getPostTokenId[_postId][_author] = tokenId;
+ getPost[tokenId] = Post(tokenId, _postId, _author, _textPreview, _image, block.timestamp);
+ }
+
+ // check if author has set up a mint time
+ uint256 mintTime = getPostMintingTime[_postId][_author];
+
+ if (mintTime != 0) {
+ require(block.timestamp <= (getPost[tokenId].timestamp + mintTime), "IggyPost: Minting deadline has passed");
+ }
+
+ _mint(_nftReceiver, tokenId, _quantity, "");
+
+ emit MintPost(_nftReceiver, _postId, _author, _quantity);
+ }
+
+ // OWNER
+
+ // change default price
+ function ownerChangeDefaultPrice(uint256 _newDefaultPrice) external onlyManagerOrOwner {
+ defaultPrice = _newDefaultPrice;
+ }
+
+ // owner can change image (if inappropiate)
+ function ownerChangeImage(uint256 _tokenId, string memory _newImage) external onlyManagerOrOwner {
+ require(_tokenId < counter, "IggyPost: Token id does not exist");
+ require(!postChangingDisabledForever, "IggyPost: Post changing is disabled forever");
+ getPost[_tokenId].image = _newImage;
+ }
+
+ // change metadata address
+ function ownerChangeMetadataAddress(address _newMetadataAddress) external onlyManagerOrOwner {
+ metadataAddress = _newMetadataAddress;
+ }
+
+ // change minter address
+ function ownerChangeMinterAddress(address _newMinterAddress) external onlyManagerOrOwner {
+ minterAddress = _newMinterAddress;
+ }
+
+ // owner can change text preview of a post
+ function ownerChangeTextPreview(uint256 _tokenId, string memory _newTextPreview) external onlyManagerOrOwner {
+ require(_tokenId < counter, "IggyPost: Token id does not exist");
+ require(!postChangingDisabledForever, "IggyPost: Post changing is disabled forever");
+ require(bytes(_newTextPreview).length <= maxTextPreviewLength, "IggyPost: Text preview is too long");
+ getPost[_tokenId].textPreview = _newTextPreview;
+ }
+
+ // change text preview length
+ function ownerChangeMaxTextPreviewLength(uint256 _newMaxTextPreviewLength) external onlyManagerOrOwner {
+ maxTextPreviewLength = _newMaxTextPreviewLength;
+ }
+
+ // owner disable post changing forever (this action is irreversible!)
+ function ownerDisablePostChangingForever() external onlyOwner {
+ postChangingDisabledForever = true;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostNft721.sol b/contracts/post/IggyPostNft721.sol
new file mode 100644
index 0000000..a646dd2
--- /dev/null
+++ b/contracts/post/IggyPostNft721.sol
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
+
+/// @title Iggy Social Post NFT
+contract IggyPostNft721 is ERC721, OwnableWithManagers {
+ address public metadataAddress; // address of the metadata contract
+ address public minterAddress; // address of the minter contract
+
+ uint256 public counter = 1; // id counter, starts with 1
+ uint256 public defaultPrice; // default price for minting a post
+
+ struct Post {
+ uint256 tokenId;
+ string postId; // post id on Ceramic network
+ address authorId;
+ string textPreview;
+ }
+
+ // constructor
+ constructor(
+ uint256 _defaultPrice,
+ string memory _name,
+ string memory _symbol
+ ) ERC721(_name, _symbol) {
+ defaultPrice = _defaultPrice;
+ }
+
+ function tokenURI(uint256) public view override returns (string memory) {
+ // call metadata contract to get uri
+ return "";
+ }
+
+ // WRITE
+ // WIP - not production ready yet
+
+ // function mint
+
+ // OWNER
+
+ // set default price
+ function ownerChangeDefaultPrice (uint256 _newDefaultPrice) public onlyManagerOrOwner {
+ defaultPrice = _newDefaultPrice;
+ }
+
+ // set metadata address
+ function ownerChangeMetadataAddress (address _newMetadataAddress) public onlyManagerOrOwner {
+ metadataAddress = _newMetadataAddress;
+ }
+
+ // change minter address
+ function ownerChangeMinterAddress (address _newMinterAddress) public onlyManagerOrOwner {
+ minterAddress = _newMinterAddress;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/post/IggyPostStats.sol b/contracts/post/IggyPostStats.sol
new file mode 100644
index 0000000..40c07b5
--- /dev/null
+++ b/contracts/post/IggyPostStats.sol
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+/// @title Iggy post stats contract
+/// @author Tempe Techie
+/// @notice Contract that keeps track of who minted which Iggy Post IDs
+contract IggyPostStats is OwnableWithManagers {
+ address public minterAddress;
+ uint256 public weiSpentTotal; // total wei spent
+
+ mapping (address => uint256[]) public getMintedPostIds; // user => postIds; get a list of post IDs minted by a user
+ mapping (address => uint256) public getMintedWei; // user => wei; get the total amount of wei paid for minting posts by a user
+ mapping (uint256 => uint256) public weiEarnedByAuthorPerPostId; // postId => wei; get the total amount of wei earned by author for a given post ID
+ mapping (uint256 => uint256) public postMintedCounter; // postId => counter; get the number of times a post has been minted
+
+ // EVENTS
+ event MinterAddressChanged(address indexed user, address minterAddress);
+
+ // constructor
+ constructor(
+ address _sfsAddress,
+ uint256 _tokenId,
+ address _minterAddress
+ ) {
+ ISFS(_sfsAddress).assign(_tokenId);
+
+ minterAddress = _minterAddress;
+ }
+
+ // READ
+
+ // get minted post IDs array
+ function getMintedPostIdsArray(address _user) external view returns (uint256[] memory) {
+ return getMintedPostIds[_user];
+ }
+
+ // get minted post IDs length
+ function getMintedPostIdsLength(address _user) external view returns (uint256) {
+ return getMintedPostIds[_user].length;
+ }
+
+ function getWeiEarnedByAuthorPerPostId(uint256 _postId) external view returns (uint256) {
+ return weiEarnedByAuthorPerPostId[_postId];
+ }
+
+ function getWeiSpent(address user_) external view returns (uint256) {
+ return getMintedWei[user_];
+ }
+
+ // WRITE
+
+ function addMintedPostId(address _user, uint256 _postId) external {
+ require(_msgSender() == minterAddress, "IggyPostStats: Only minter can add minted post ID");
+ getMintedPostIds[_user].push(_postId);
+ postMintedCounter[_postId] += 1;
+ }
+
+ function addMintedWei(address _user, uint256 _wei) external {
+ require(_msgSender() == minterAddress, "IggyPostStats: Only minter can add minted wei");
+ getMintedWei[_user] += _wei;
+ weiSpentTotal += _wei;
+ }
+
+ function addWeiEarnedByAuthorPerPostId(uint256 _postId, uint256 _wei) external {
+ require(_msgSender() == minterAddress, "IggyPostStats: Only minter can add wei earned by author per post ID");
+ weiEarnedByAuthorPerPostId[_postId] += _wei;
+ }
+
+ // OWNER
+
+ function setMinterAddress(address _minterAddress) external onlyManagerOrOwner {
+ minterAddress = _minterAddress;
+ emit MinterAddressChanged(_msgSender(), _minterAddress);
+ }
+
+ // RECOVERY
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).transfer(recipient_, tokenAmount_);
+ }
+
+ /// @notice Withdraw native coins from contract
+ function withdraw() external onlyManagerOrOwner {
+ (bool success, ) = owner().call{value: address(this).balance}("");
+ require(success, "Failed to withdraw native coins from contract");
+ }
+}
\ No newline at end of file
diff --git a/contracts/staking/IggyStakingRewards.sol b/contracts/staking/IggyStakingRewards.sol
new file mode 100644
index 0000000..4b8d937
--- /dev/null
+++ b/contracts/staking/IggyStakingRewards.sol
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
+import { ERC20Votes, ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
+
+/**
+@title Staking contract with periodic ETH rewards (with voting and permit)
+@author Tempe Techie
+@notice Adapted from the PeriodicEthRewardsVotes.sol contract. The contract issues a receipt token for any
+staked token in 1:1 ratio. Receipt token holders can claim ETH rewards periodically. Receipt token holders
+can also vote in governance (ERC20Votes) or have tokens transferred using the erc-2612 permit() function (ERC20Permit).
+*/
+contract IggyStakingRewards is ERC20, OwnableWithManagers, ReentrancyGuard, ERC20Votes {
+ using SafeERC20 for IERC20;
+
+ address public immutable asset; // staked token address (rebase tokens and tokens with fee-on-transfer are NOT supported!)
+ address public immutable weth; // WETH address (for calculating APY)
+
+ bool public withdrawalsDisabled = false; // owner/governance can turn this on or off
+ bool public withdrawalsDisabledForever = false; // once withdrawals are disabled forever, they can't be re-enabled
+
+ uint256 public claimRewardsTotal; // total ETH rewards that can be claimed for the previous period
+ uint256 public claimRewardsMinimum; // if minimum not reached, no one can claim (all ETH rewards go to next period)
+
+ uint256 public futureRewards; // ETH rewards that have not been claimed yet
+ uint256 public lastClaimPeriod; // timestamp of the last claim period
+
+ uint256 public maxDeposit = type(uint256).max; // maximum amount of tokens that can be deposited by a user (in wei)
+ uint256 public minDeposit; // minimum amount of tokens that can be deposited by a user (in wei)
+
+ uint256 public immutable periodLength; // length of the claim period (in seconds), the most common is 1 week (604800s)
+
+ // analytics
+ uint256 immutable stakingStartTimestamp; // timestamp of the start of staking
+ uint256 totalEthReceived; // total ETH received by the contract (usable for analytics)
+
+ mapping (address => uint256) public lastClaimed; // timestamp of the last claim for each user
+ mapping (address => uint256) public lastDeposit; // timestamp of the last deposit for each user
+
+ // EVENTS
+ event Claim(address indexed user, address indexed owner, uint256 rewards);
+ event Deposit(address indexed user, uint256 assets);
+ event LastClaimPeriodUpdate(address indexed user, uint256 timestamp, uint256 claimRewardsTotal_, uint256 futureRewards_);
+ event OwnerClaimRewardsMinimumSet(address indexed owner, uint256 claimRewardsMinimum_);
+ event OwnerMaxDepositSet(address indexed owner, uint256 maxDeposit_);
+ event OwnerMinDepositSet(address indexed owner, uint256 minDeposit_);
+ event OwnerRecoverErc20(address indexed owner, address indexed token, uint256 amount);
+ event OwnerWithdrawalsDisabled(address indexed owner);
+ event OwnerWithdrawalsEnabled(address indexed owner);
+ event OwnerWithdrawalsDisabledForever(address indexed owner);
+ event Withdraw(address indexed user, uint256 assets);
+
+ // CONSTRUCTOR
+ constructor(
+ address _asset,
+ address _weth,
+ string memory _receiptTokenName,
+ string memory _receiptTokenSymbol,
+ uint256 _claimRewardsMinimum,
+ uint256 _minDeposit,
+ uint256 _periodLength
+ ) ERC20(_receiptTokenName, _receiptTokenSymbol) ERC20Permit(_receiptTokenName) {
+ require(_asset != address(0), "PeriodicEthRewards: asset is the zero address");
+ require(_weth != address(0), "PeriodicEthRewards: weth is the zero address");
+ require(_periodLength > 0, "PeriodicEthRewards: period length is zero");
+ require(bytes(_receiptTokenName).length > 0, "PeriodicEthRewards: receipt token name is empty");
+ require(bytes(_receiptTokenSymbol).length > 0, "PeriodicEthRewards: receipt token symbol is empty");
+
+ asset = _asset;
+ weth = _weth;
+
+ claimRewardsMinimum = _claimRewardsMinimum;
+ emit OwnerClaimRewardsMinimumSet(msg.sender, _claimRewardsMinimum);
+
+ minDeposit = _minDeposit;
+ emit OwnerMinDepositSet(msg.sender, _minDeposit);
+
+ periodLength = _periodLength;
+
+ lastClaimPeriod = block.timestamp;
+ stakingStartTimestamp = block.timestamp;
+ }
+
+ // RECEIVE (receive ETH)
+ receive() external payable {
+ // futureRewards update must happen before _updateLastClaimPeriod()
+ // because claimRewardsTotal is then set to current balance
+ futureRewards += msg.value;
+ totalEthReceived += msg.value;
+
+ _updateLastClaimPeriod();
+ }
+
+ // READ
+
+ // Overrides IERC6372 functions to make the token & governor timestamp-based
+ function clock() public view override returns (uint48) {
+ return uint48(block.timestamp);
+ }
+
+ // Overrides IERC6372 functions to make the token & governor timestamp-based
+ function CLOCK_MODE() public pure override returns (string memory) { // solhint-disable-line func-name-mixedcase
+ return "mode=timestamp";
+ }
+
+ /// @notice Returns the APY based on previous ETH rewards and current liquidity.
+ function getApy() external view returns (uint256) {
+ if (totalEthReceived == 0) {
+ return 0;
+ }
+
+ // get asset contract's balance of WETH and multiply it by 2 (asset is LP pool, so 2x means the whole liquidity)
+ uint256 lpLiquidityEth = IERC20(weth).balanceOf(asset) * 2;
+
+ // get asset total supply from the asset contract
+ uint256 lpTotalSupply = IERC20(asset).totalSupply();
+
+ // calculate the amount of liquidity that is staked (in WETH)
+ uint256 stakedLiqudityEth = totalSupply() * lpLiquidityEth / lpTotalSupply;
+
+ // get total amount ETH received per second and extrapolate it to 1 year (31536000 seconds)
+ uint256 ethReceivedPerYear = totalEthReceived * 31_536_000 / (block.timestamp - stakingStartTimestamp);
+
+ // calculate APY by dividing the amount of ETH received per year by the amount of liquidity that is staked (in WETH) and multiplying by 100
+ return ethReceivedPerYear * 100 / stakedLiqudityEth;
+ }
+
+ /// @notice Returns the amount of time left (in seconds) until the user can withdraw their assets.
+ function getLockedTimeLeft(address _user) external view returns (uint256) {
+ uint256 _lastDeposit = lastDeposit[_user];
+
+ if (_lastDeposit == 0) {
+ // in case periodLength is bigger than block.timestamp
+ return 0;
+ }
+
+ if ((_lastDeposit + periodLength) > block.timestamp) {
+ return _lastDeposit + periodLength - block.timestamp;
+ }
+
+ return 0;
+ }
+
+ /// @notice Returns the amount of ETH that can be claimed for a given user
+ function previewClaim(address _claimer) public view returns (uint256) {
+ if (lastClaimed[_claimer] < lastClaimPeriod && totalSupply() > 0) {
+ return claimRewardsTotal * balanceOf(_claimer) / totalSupply(); // get current ETH claim for a given user
+ }
+
+ return 0;
+ }
+
+ /** @notice Returns the amount of ETH that may be claimed for a given user in the next claim period. The amount can
+ change up or down until the current period is over. */
+ function previewFutureClaim(address _claimer) external view returns (uint256) {
+ if (totalSupply() > 0) {
+ return futureRewards * balanceOf(_claimer) / totalSupply(); // get future ETH claim for a given user
+ }
+
+ return 0;
+ }
+
+ // WRITE
+
+ /// @notice Claim ETH rewards for yourself.
+ function claimRewards() external nonReentrant returns (uint256) {
+ return _claim(msg.sender); // returns the amount of ETH claimed
+ }
+
+ /// @notice Claim ETH rewards for someone else.
+ function claimRewardsFor(address _claimer) external nonReentrant returns (uint256) {
+ return _claim(_claimer); // returns the amount of ETH claimed
+ }
+
+ /// @notice Deposit assets and mint receipt tokens.
+ function deposit(uint256 _assets) external nonReentrant returns (uint256) {
+ require(_assets <= maxDeposit, "PeriodicEthRewards: deposit is more than max");
+ require(_assets >= minDeposit, "PeriodicEthRewards: deposit is less than min");
+
+ lastDeposit[msg.sender] = block.timestamp; // after deposit withdrawals are disabled for periodLength
+
+ IERC20(asset).safeTransferFrom(msg.sender, address(this), _assets); // transfer staking tokens to this contract
+
+ _mint(msg.sender, _assets); // mint receipt tokens
+
+ emit Deposit(msg.sender, _assets);
+
+ return _assets;
+ }
+
+ /// @notice Manually update the last claim period (if needed). Anyone can call this function.
+ function updateLastClaimPeriod() external nonReentrant {
+ // it's better to call _claim() instead of _updateLastClaimPeriod() in case user has forgotten to claim
+ // _claim() will call _updateLastClaimPeriod() anyway
+ _claim(msg.sender);
+ }
+
+ /// @notice Withdraw assets and burn receipt tokens.
+ function withdraw(uint256 _assets) external nonReentrant returns (uint256) {
+ require(!withdrawalsDisabledForever, "PeriodicEthRewards: withdrawals are disabled forever");
+ require(!withdrawalsDisabled, "PeriodicEthRewards: withdrawals are disabled");
+
+ uint _balance = balanceOf(msg.sender);
+
+ require(_assets > 0, "PeriodicEthRewards: cannot withdraw 0");
+ require(_assets <= _balance, "PeriodicEthRewards: cannot withdraw more than balance");
+ require(block.timestamp > (lastDeposit[msg.sender] + periodLength), "PeriodicEthRewards: assets are still locked");
+
+ // if not full withdraw, require balance to stay at least the min user deposit amount
+ if (_balance > _assets) {
+ require(
+ (_balance - _assets) >= minDeposit,
+ "PeriodicEthRewards: the remaining balance too low"
+ );
+ }
+
+ _burn(msg.sender, _assets); // burn receipt tokens
+
+ IERC20(asset).safeTransfer(msg.sender, _assets); // receive back the asset tokens (staking tokens)
+
+ // note: if user withdraws all staked tokens, they forfeit their claim for the current
+ // staking period (unless they deposit again)
+
+ emit Withdraw(msg.sender, _assets);
+
+ return _assets;
+ }
+
+ // OWNER
+
+ /// @notice Disable withdrawals forever.
+ function disableWithdrawalsForever() external onlyOwner {
+ withdrawalsDisabledForever = true;
+ emit OwnerWithdrawalsDisabledForever(msg.sender);
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address (except the staking and receipt tokens)
+ function recoverERC20(address _tokenAddress, uint256 _tokenAmount, address _recipient) external onlyOwner {
+ require(_tokenAddress != asset, "PeriodicEthRewards: cannot recover staking token");
+ require(_tokenAddress != address(this), "PeriodicEthRewards: cannot recover receipt token");
+
+ IERC20(_tokenAddress).safeTransfer(_recipient, _tokenAmount);
+
+ emit OwnerRecoverErc20(msg.sender, _tokenAddress, _tokenAmount);
+ }
+
+ /**
+ @notice
+ Sets the minimum amount of ETH that must be in the contract for rewards to be distributed.
+ If minimum is not met, rewards roll over into the next period.
+ */
+ function setClaimRewardsMinimum(uint256 _claimRewardsMinimum) external onlyManagerOrOwner {
+ claimRewardsMinimum = _claimRewardsMinimum;
+ emit OwnerClaimRewardsMinimumSet(msg.sender, _claimRewardsMinimum);
+ }
+
+ /// @notice Sets the maximum amount of assets that a user can deposit at once.
+ function setMaxDeposit(uint256 _maxDeposit) external onlyManagerOrOwner {
+ maxDeposit = _maxDeposit;
+ emit OwnerMaxDepositSet(msg.sender, _maxDeposit);
+ }
+
+ /// @notice Sets the minimum amount of assets that a user can deposit.
+ function setMinDeposit(uint256 _minDeposit) external onlyManagerOrOwner {
+ minDeposit = _minDeposit;
+ emit OwnerMinDepositSet(msg.sender, _minDeposit);
+ }
+
+ /// @notice Toggle withdrawals on/off
+ function toggleWithdrawals() external onlyManagerOrOwner {
+ withdrawalsDisabled = !withdrawalsDisabled;
+
+ if (withdrawalsDisabled) {
+ emit OwnerWithdrawalsDisabled(msg.sender);
+ } else {
+ emit OwnerWithdrawalsEnabled(msg.sender);
+ }
+ }
+
+ // INTERNAL
+
+ function _afterTokenTransfer(
+ address from,
+ address to,
+ uint256 amount
+ ) internal override(ERC20, ERC20Votes) {
+ super._afterTokenTransfer(from, to, amount);
+ }
+
+ function _beforeTokenTransfer(
+ address _from,
+ address _to,
+ uint256 _amount
+ ) internal override {
+ super._beforeTokenTransfer(_from, _to, _amount);
+ require(_to != address(this), "PeriodicEthRewards: cannot transfer to token contract");
+
+ // this does not run on mint or burn, only on transfer
+ if (_from != address(0) && _from != address(0)) {
+ // if sender's assets are locked, receipt tokens cannot be transferred
+ require(block.timestamp > (lastDeposit[_from] + periodLength), "PeriodicEthRewards: assets are still locked");
+ }
+
+ // this does not run on mint, but it runs on burn or transfer
+ if (_from != address(0)) {
+ _claim(_from);
+ }
+
+ // this does not run on burn, but it runs on mint or transfer
+ if (_to != address(0)) {
+ _claim(_to);
+ // Set lastClaimed to the current timestamp just in case the receiver had no previous claims.
+ // This prevents double claiming of rewards, because the sender should have gotten all
+ // the rewards from the current claim period.
+ // This also prevents a new depositor from claiming rewards from the previous period right away.
+ // They have to wait until the next period to claim. It prevents gaming the system.
+ lastClaimed[_to] = block.timestamp;
+ }
+ }
+
+ function _claim(address _claimer) internal returns (uint256 _ethToClaim) {
+ // check if claimer has any ETH (left) to claim
+ _ethToClaim = previewClaim(_claimer);
+
+ if (_ethToClaim > 0) {
+ // update lastClaimed
+ lastClaimed[_claimer] = block.timestamp;
+
+ // send ETH to the claimer
+ (bool _success, ) = _claimer.call{value: _ethToClaim}("");
+ require(_success, "ETH transfer failed");
+
+ emit Claim(msg.sender, _claimer, _ethToClaim);
+ }
+
+ _updateLastClaimPeriod();
+ }
+
+ function _mint(address to, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._mint(to, amount);
+ }
+
+ function _burn(address account, uint256 amount) internal override(ERC20, ERC20Votes) {
+ super._burn(account, amount);
+ }
+
+ function _updateLastClaimPeriod() internal {
+ // only run if the current period has ended (start a new period)
+ if (block.timestamp > (lastClaimPeriod + periodLength)) {
+ lastClaimPeriod = block.timestamp;
+
+ // set total rewards to be claimed for the previous period
+ if (address(this).balance >= claimRewardsMinimum) {
+ // if the minimum is reached, claimRewardsTotal is set to the current balance
+ claimRewardsTotal = address(this).balance;
+ futureRewards = 0; // reset future rewards to 0
+ } else {
+ // if minimum not reached, no one can claim. All ETH rewards go into the next period
+ claimRewardsTotal = 0;
+ futureRewards = address(this).balance; // set future rewards to the current balance
+ }
+
+ emit LastClaimPeriodUpdate(msg.sender, block.timestamp, claimRewardsTotal, futureRewards);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/stats/Stats.sol b/contracts/stats/Stats.sol
new file mode 100644
index 0000000..493dce2
--- /dev/null
+++ b/contracts/stats/Stats.sol
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+/**
+@title Iggy Stats
+@author Tempe Techie
+*/
+contract Stats is OwnableWithManagers {
+ address public statsWriterAddress;
+ uint256 public totalVolumeWei;
+ mapping (address => uint256) public weiSpentPerAddress;
+
+ // CONSTRUCTOR
+ constructor(address sfsAddress_, uint256 tokenId_) {
+ ISFS(sfsAddress_).assign(tokenId_);
+ }
+
+ // READ
+
+ function getWeiSpent(address user_) external view returns (uint256) {
+ return weiSpentPerAddress[user_];
+ }
+
+ function weiSpentTotal() external view returns (uint256) {
+ return totalVolumeWei;
+ }
+
+ // WRITE
+
+ function addWeiSpent(address user_, uint256 weiSpent_) external {
+ require(msg.sender == statsWriterAddress, "Not a stats writer");
+
+ weiSpentPerAddress[user_] += weiSpent_;
+ totalVolumeWei += weiSpent_;
+ }
+
+ // OWNER
+
+ function addWriter(address statsWriterAddress_) external onlyManagerOrOwner {
+ // for compatibility with deployment scripts, in case someone wants to use the stats contract directly, without the middleware contract
+ statsWriterAddress = statsWriterAddress_;
+ }
+
+ function setStatsWriterAddress(address statsWriterAddress_) external onlyManagerOrOwner {
+ statsWriterAddress = statsWriterAddress_;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/stats/StatsMiddleware.sol b/contracts/stats/StatsMiddleware.sol
new file mode 100644
index 0000000..e2bc828
--- /dev/null
+++ b/contracts/stats/StatsMiddleware.sol
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+
+interface ISFS {
+ function assign(uint256 _tokenId) external returns (uint256);
+}
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+ function getWeiSpent(address user_) external view returns (uint256);
+ function weiSpentTotal() external view returns (uint256);
+}
+
+/**
+@title Stats Middleware
+@author Tempe Techie
+*/
+contract StatsMiddleware is OwnableWithManagers {
+ address public statsAddress;
+ mapping (address => bool) public writers; // writer contracts that can send stats to this contract
+
+ // CONSTRUCTOR
+ constructor(address sfsAddress_, uint256 tokenId_, address statsAddress_) {
+ ISFS(sfsAddress_).assign(tokenId_);
+ statsAddress = statsAddress_;
+ }
+
+ // READ
+
+ function getWeiSpent(address user_) external view returns (uint256) {
+ return IStats(statsAddress).getWeiSpent(user_);
+ }
+
+ function weiSpentTotal() external view returns (uint256) {
+ return IStats(statsAddress).weiSpentTotal();
+ }
+
+ // WRITER
+
+ function addWeiSpent(address user_, uint256 weiSpent_) external {
+ require(writers[msg.sender], "Not a writer contract");
+
+ IStats(statsAddress).addWeiSpent(user_, weiSpent_);
+ }
+
+ function addWriterByWriter(address writer_) external {
+ require(writers[msg.sender], "Not a writer contract");
+ writers[writer_] = true;
+ }
+
+ // OWNER
+ function addWriter(address writer_) external onlyManagerOrOwner {
+ writers[writer_] = true;
+ }
+
+ function removeWriter(address writer_) external onlyManagerOrOwner {
+ writers[writer_] = false;
+ }
+
+ function setStatsAddress(address statsAddress_) external onlyManagerOrOwner {
+ statsAddress = statsAddress_;
+ }
+
+}
\ No newline at end of file
diff --git a/contracts/swap/IggySwapRouter.sol b/contracts/swap/IggySwapRouter.sol
new file mode 100644
index 0000000..286ef44
--- /dev/null
+++ b/contracts/swap/IggySwapRouter.sol
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+}
+
+interface IUniswapV2Factory {
+ function getPair(address tokenA, address tokenB) external view returns (address pair);
+}
+
+interface IUniswapV2Pair {
+ function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
+}
+
+interface IUniswapV2Router02 {
+ function WETH() external pure returns (address);
+ function factory() external pure returns (address);
+ function getAmountsOut(uint amountIn, address[] memory path) external view returns (uint[] memory amounts);
+
+ function addLiquidity(
+ address tokenA,
+ address tokenB,
+ uint amountADesired,
+ uint amountBDesired,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB, uint liquidity);
+
+ function addLiquidityETH(
+ address token,
+ uint amountTokenDesired,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
+
+ function removeLiquidity(
+ address tokenA,
+ address tokenB,
+ uint liquidity,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB);
+
+ function removeLiquidityETH(
+ address token,
+ uint liquidity,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountToken, uint amountETH);
+
+ function swapExactTokensForTokens(
+ uint amountIn,
+ uint amountOutMin,
+ address[] calldata path,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts);
+}
+
+interface IWETH {
+ function deposit() external payable;
+ function withdraw(uint) external;
+}
+
+/// @title Iggy swap router contract
+/// @author Tempe Techie
+/// @notice Contract that helps an Iggy frontend to swap tokens (custom because it's specific to a particular frontend)
+contract IggySwapRouter is OwnableWithManagers {
+ using SafeERC20 for IERC20;
+
+ address public feeChangerAddress; // a special role that is allowed to change fees and share amounts
+ address public frontendAddress; // address of a DAO/community which runs the frontend
+ address public iggyAddress;
+ address public routerAddress; // DEX router address
+ address public stakingAddress; // staking contract address
+ address public statsAddress; // stats middleware contract address
+ address public immutable wethAddress;
+
+ uint256 public constant MAX_BPS = 10_000;
+ uint256 public swapFee = 80; // 0.8% default fee
+ uint256 public referrerShare = 1000; // 10% share of the swap fee (in bips)
+ uint256 public stakingShare = 8000; // in bips
+
+ // after referrer & staking shares are deducted, the rest goes to the frontend operator and iggy based on the following frontendShare:
+ uint256 public frontendShare = 5000; // 50% share of the swap fee in bips (after referrer & staking shares are deducted)
+
+ // MODIFIERS
+ modifier onlyFeeChanger() {
+ require(msg.sender == feeChangerAddress, "IggySwap: Sender is not the Fee Changer");
+ _;
+ }
+
+ // CONSTRUCTOR
+ constructor(
+ address _frontendAddress,
+ address _iggyAddress,
+ address _routerAddress,
+ address _stakingAddress,
+ address _statsAddress,
+ uint256 _swapFee,
+ uint256 _stakingShare,
+ uint256 _frontendShare
+ ) {
+ frontendAddress = _frontendAddress;
+ iggyAddress = _iggyAddress;
+ routerAddress = _routerAddress;
+ stakingAddress = _stakingAddress;
+ statsAddress = _statsAddress;
+
+ swapFee = _swapFee;
+ stakingShare = _stakingShare;
+ frontendShare = _frontendShare;
+
+ feeChangerAddress = msg.sender;
+ wethAddress = IUniswapV2Router02(_routerAddress).WETH();
+ }
+
+ // RECEIVE
+ receive() external payable {}
+
+ // READ PUBLIC/EXTERNAL
+
+ /// @notice Get the amount of tokens and ETH to receive when removing liquidity
+ /// @dev This is useful to calculate min amount of tokens and ETH, but consider reducing it by slippage on your frontend
+ function calculateETHAndTokensToReceive(
+ address _token, // token to receive after removing liquidity
+ uint256 _liquidity
+ ) external view returns (uint256 amountToken, uint256 amountETH) {
+ address factoryAddress = IUniswapV2Router02(routerAddress).factory();
+
+ address pair = IUniswapV2Factory(factoryAddress).getPair(_token, wethAddress);
+
+ (address token0,) = _sortTokens(_token, wethAddress);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (uint256 reserveToken, uint256 reserveETH) = _token == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+ uint256 lpTotalSupply = IERC20(pair).totalSupply();
+
+ amountToken = (reserveToken * _liquidity) / lpTotalSupply;
+ amountETH = (reserveETH * _liquidity) / lpTotalSupply;
+ }
+
+ /// @notice Calculate the amount of ETH needed to add liquidity
+ /// @dev This is useful to calculate min amount of ETH, but consider reducing it by slippage on your frontend
+ function calculateETHForLiquidity(address addressToken, uint256 amountToken) external view returns (uint256) {
+ address factoryAddress = IUniswapV2Router02(routerAddress).factory();
+
+ address pair = IUniswapV2Factory(factoryAddress).getPair(addressToken, wethAddress);
+
+ if (pair == address(0)) {
+ return 0;
+ }
+
+ (address token0,) = _sortTokens(addressToken, wethAddress);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (uint256 reserveToken, uint256 reserveETH) = addressToken == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+
+ return (amountToken * reserveETH) / reserveToken; // return amount of ETH needed to add/remove liquidity
+ }
+
+ /// @notice Preview the amount of tokens that would be received for a given swap
+ function getAmountsOut(
+ uint amountIn,
+ address[] memory path
+ ) public view returns (uint[] memory amounts) {
+ amounts = _getTokensAmountOut(amountIn, path);
+ amounts[amounts.length - 1] = amounts[amounts.length - 1] - _getFeeAmount(amounts[amounts.length - 1]); // deduce swap fee from amount out
+ }
+
+ /// @notice Get LP (pair) token address for a given pair of tokens
+ function getLpTokenAddress(address tokenA, address tokenB) external view returns (address) {
+ if (tokenA == address(0)) {
+ tokenA = wethAddress;
+ }
+
+ if (tokenB == address(0)) {
+ tokenB = wethAddress;
+ }
+
+ return IUniswapV2Factory(IUniswapV2Router02(routerAddress).factory()).getPair(tokenA, tokenB);
+ }
+
+ /// @notice Calculates the price impact of a swap (in bips)
+ function getPriceImpact(
+ address tokenIn,
+ address tokenOut,
+ uint amountIn
+ ) external view returns (uint) {
+ if (tokenIn == address(0)) {
+ tokenIn = wethAddress;
+ }
+
+ if (tokenOut == address(0)) {
+ tokenOut = wethAddress;
+ }
+
+ if (tokenIn == tokenOut) {
+ return 0;
+ }
+
+ // get factory address from router
+ address factoryAddress = IUniswapV2Router02(routerAddress).factory();
+
+ // get reserves for both tokens (reserve is a token total amount in a pool)
+ (uint reserveIn, uint reserveOut) = _getReserves(factoryAddress, tokenIn, tokenOut);
+
+ uint k = reserveIn * reserveOut; // calculate a constant k (x * y = k, standard Uniswap V2 formula)
+
+ // calculate the amount of tokens user would receive if they swapped
+ uint newReserveOut = k / (reserveIn + amountIn);
+
+ uint amountOut = reserveOut - newReserveOut;
+
+ return (amountOut * MAX_BPS) / newReserveOut; // return price impact in bips
+ }
+
+ // WRITE PUBLIC/EXTERNAL
+
+ /// @notice Add liquidity to a pool (both tokens must be ERC-20 tokens)
+ function addLiquidity(
+ address tokenA,
+ address tokenB,
+ uint amountADesired,
+ uint amountBDesired,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB, uint liquidity) {
+ // transfer tokens to this contract
+ IERC20(tokenA).safeTransferFrom(msg.sender, address(this), amountADesired);
+ IERC20(tokenB).safeTransferFrom(msg.sender, address(this), amountBDesired);
+
+ // approve tokens to be spent by router
+ IERC20(tokenA).approve(routerAddress, amountADesired);
+ IERC20(tokenB).approve(routerAddress, amountBDesired);
+
+ // add liquidity
+ (amountA, amountB, liquidity) = IUniswapV2Router02(routerAddress).addLiquidity(
+ tokenA,
+ tokenB,
+ amountADesired,
+ amountBDesired,
+ amountAMin,
+ amountBMin,
+ to,
+ deadline
+ );
+ }
+
+ function addLiquidityETH(
+ address token,
+ uint amountTokenDesired,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external payable returns (uint amountToken, uint amountETH, uint liquidity) {
+ // transfer tokens to this contract
+ IERC20(token).safeTransferFrom(msg.sender, address(this), amountTokenDesired);
+
+ // approve tokens to be spent by router
+ IERC20(token).approve(routerAddress, amountTokenDesired);
+
+ // add liquidity
+ (amountToken, amountETH, liquidity) = IUniswapV2Router02(routerAddress).addLiquidityETH{value: msg.value}(
+ token,
+ amountTokenDesired,
+ amountTokenMin,
+ amountETHMin,
+ to,
+ deadline
+ );
+ }
+
+ function removeLiquidity(
+ address tokenA,
+ address tokenB,
+ uint liquidity,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB) {
+ // get factory address from router
+ address factoryAddress = IUniswapV2Router02(routerAddress).factory();
+
+ // get LP token address
+ address pair = IUniswapV2Factory(factoryAddress).getPair(tokenA, tokenB);
+
+ // transfer liquidity tokens to this contract
+ IERC20(pair).safeTransferFrom(msg.sender, address(this), liquidity);
+
+ // approve tokens to be spent by router
+ IERC20(pair).approve(routerAddress, liquidity);
+
+ // remove liquidity
+ (amountA, amountB) = IUniswapV2Router02(routerAddress).removeLiquidity(
+ tokenA,
+ tokenB,
+ liquidity,
+ amountAMin,
+ amountBMin,
+ to,
+ deadline
+ );
+ }
+
+ function removeLiquidityETH(
+ address token,
+ uint liquidity,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountToken, uint amountETH) {
+ // get factory address from router
+ address factoryAddress = IUniswapV2Router02(routerAddress).factory();
+
+ // get LP token address
+ address pair = IUniswapV2Factory(factoryAddress).getPair(token, wethAddress);
+
+ // transfer liquidity tokens to this contract
+ IERC20(pair).safeTransferFrom(msg.sender, address(this), liquidity);
+
+ // approve tokens to be spent by router
+ IERC20(pair).approve(routerAddress, liquidity);
+
+ // remove liquidity
+ (amountToken, amountETH) = IUniswapV2Router02(routerAddress).removeLiquidityETH(
+ token,
+ liquidity,
+ amountTokenMin,
+ amountETHMin,
+ to,
+ deadline
+ );
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ERC-20 tokens
+ function swapExactTokensForTokens(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] calldata path,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, address(0), false); // no referrer
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ERC-20 tokens (with referrer)
+ function swapExactTokensForTokensWithReferrer(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] calldata path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, referrer, false);
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ETH
+ function swapExactTokensForETH(
+ uint amountIn,
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, address(0), true); // no referrer
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ETH (with referrer)
+ function swapExactTokensForETHWithReferrer(
+ uint amountIn,
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, referrer, true);
+ }
+
+ /// @notice Swap exact ETH for ERC-20 tokens
+ function swapExactETHForTokens(
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline
+ ) external payable returns (uint[] memory amounts) {
+ require(msg.value > 0, "IggySwap: Native coin amount is zero");
+
+ IWETH(wethAddress).deposit{value: msg.value}(); // convert ETH to WETH
+
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ amounts = _swap(msg.value, amountOutMin, path, to, deadline, address(0), false); // no referrer
+ }
+
+ /// @notice Swap exact ETH for ERC-20 tokens (with referrer)
+ function swapExactETHForTokensWithReferrer(
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external payable returns (uint[] memory amounts) {
+ require(msg.value > 0, "IggySwap: Native coin amount is zero");
+
+ IWETH(wethAddress).deposit{value: msg.value}(); // convert ETH to WETH
+
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ amounts = _swap(msg.value, amountOutMin, path, to, deadline, referrer, false);
+ }
+
+ // FEE CHANGER
+ function changeFeeChangerAddress(address _newFeeChangerAddress) external onlyFeeChanger {
+ feeChangerAddress = _newFeeChangerAddress;
+ }
+
+ function changeFrontendShare(uint256 _newFrontendShare) external onlyFeeChanger {
+ require(_newFrontendShare <= MAX_BPS, "IggySwap: Frontend share is greater than MAX_BPS");
+ frontendShare = _newFrontendShare;
+ }
+
+ function changeReferrerShare(uint256 _newReferrerShare) external onlyFeeChanger {
+ require(_newReferrerShare <= MAX_BPS, "IggySwap: Referrer share is greater than MAX_BPS");
+ referrerShare = _newReferrerShare;
+ }
+
+ function changeStakingShare(uint256 _newStakingShare) external onlyFeeChanger {
+ require(_newStakingShare <= MAX_BPS, "IggySwap: Staking share is greater than MAX_BPS");
+ stakingShare = _newStakingShare;
+ }
+
+ function changeSwapFee(uint256 _newSwapFee) external onlyFeeChanger {
+ require(_newSwapFee <= MAX_BPS, "IggySwap: Swap fee is greater than MAX_BPS");
+ swapFee = _newSwapFee;
+ }
+
+ // FRONTEND OWNER
+
+ /// @notice Change frontend address
+ function changeFrontendAddress(address _newFrontendAddress) external {
+ require(msg.sender == frontendAddress, "IggySwap: Sender is not the frontend owner");
+ frontendAddress = _newFrontendAddress;
+ }
+
+ // IGGY
+
+ /// @notice Change Iggy address
+ function changeIggyAddress(address _newIggyAddress) external {
+ require(msg.sender == iggyAddress, "IggySwap: Sender is not Iggy");
+ iggyAddress = _newIggyAddress;
+ }
+
+ // OWNER
+
+ /// @notice Change router address
+ function changeRouterAddress(address _newRouterAddress) external onlyManagerOrOwner {
+ routerAddress = _newRouterAddress;
+ }
+
+ /// @notice Change staking address
+ function changeStakingAddress(address _newStakingAddress) external onlyManagerOrOwner {
+ stakingAddress = _newStakingAddress;
+ }
+
+ /// @notice Change stats address
+ function changeStatsAddress(address _newStatsAddress) external onlyManagerOrOwner {
+ statsAddress = _newStatsAddress;
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).safeTransfer(recipient_, tokenAmount_);
+ }
+
+ /// @notice Recover native coins from contract
+ function recoverETH() external onlyManagerOrOwner {
+ (bool success, ) = owner().call{value: address(this).balance}("");
+ require(success, "Failed to recover native coins from contract");
+ }
+
+ // INTERNAL - READ
+ function _getFeeAmount(uint _amount) internal view returns (uint) {
+ return (_amount * swapFee) / MAX_BPS;
+ }
+
+ // fetches and sorts the reserves for a pair
+ function _getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
+ (address token0,) = _sortTokens(tokenA, tokenB);
+ address pair = IUniswapV2Factory(factory).getPair(tokenA, tokenB);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+ }
+
+ function _getTokensAmountOut(
+ uint amountIn,
+ address[] memory path
+ ) internal view returns(uint[] memory amounts) {
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ return IUniswapV2Router02(routerAddress).getAmountsOut(amountIn, path);
+ }
+
+ function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
+ require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
+ (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
+ require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
+ }
+
+ // INTERNAL - WRITE
+ function _swap(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer,
+ bool convertToNative
+ ) internal returns (uint[] memory amounts) {
+ IERC20(path[0]).approve(routerAddress, amountIn); // approve router to spend tokens
+
+ // make the swap via router
+ amounts = IUniswapV2Router02(routerAddress).swapExactTokensForTokens(
+ amountIn,
+ amountOutMin,
+ path,
+ address(this), // initially the receiver is this contract (tokens will be later transferred to the recipient and to fee receivers)
+ deadline
+ );
+
+ uint256 _amountOut = amounts[amounts.length - 1]; // total amount out (including fee)
+ uint256 _feeAmount = _getFeeAmount(_amountOut); // swap fee amount
+
+ require((_amountOut - _feeAmount) >= amountOutMin, "IggySwap: Amount out is less than the minimum amount out");
+
+ address tokenOut = path[path.length - 1]; // receiving token address
+
+ // transfer tokens to the recipient (deduct the fee)
+ if (convertToNative && tokenOut == wethAddress) {
+ // if: tokenOut is the native coin (ETH)
+
+ IWETH(tokenOut).withdraw(_amountOut); // convert WETH to ETH
+
+ (bool sentWeth, ) = to.call{value: (_amountOut - _feeAmount)}("");
+ require(sentWeth, "Failed to send native coins to the recipient");
+
+ // if there's a referrer, send them a share of the fee
+ uint256 referrerShareAmountNative;
+ if (referrer != address(0) && referrerShare > 0) {
+ referrerShareAmountNative = (_feeAmount * referrerShare) / MAX_BPS;
+
+ (bool sentWethReferrer, ) = referrer.call{value: referrerShareAmountNative}("");
+ require(sentWethReferrer, "Failed to send native coins to the referrer");
+
+ _feeAmount -= referrerShareAmountNative; // deduct referrer's share from the fee
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerShareAmountNative); // add referrer's share to stats
+ }
+ }
+
+ // if there's a staking contract, send them a share of the fee
+ if (stakingAddress != address(0) && stakingShare > 0) {
+ uint256 stakingShareAmountNative = (_feeAmount * stakingShare) / MAX_BPS;
+
+ (bool sentWethStaking, ) = stakingAddress.call{value: stakingShareAmountNative}("");
+ require(sentWethStaking, "Failed to send native coins to the staking contract");
+
+ _feeAmount -= stakingShareAmountNative; // deduct staking contract's share from the fee
+ }
+
+ // send a share of the fee to the frontend operator
+ if (frontendAddress != address(0) && frontendShare > 0) {
+ uint256 frontendShareAmountNative = (_feeAmount * frontendShare) / MAX_BPS;
+ (bool sentWethFrontend, ) = frontendAddress.call{value: frontendShareAmountNative}("");
+ require(sentWethFrontend, "Failed to send native coins to the frontend operator");
+ }
+
+ // send the rest to Iggy
+ (bool sentWethIggy, ) = iggyAddress.call{value: address(this).balance}("");
+ require(sentWethIggy, "Failed to send native coins to Iggy");
+
+ // add user's fee (without referral's share) to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(to, _feeAmount-referrerShareAmountNative);
+ }
+ } else {
+ // else: tokenOut is NOT the native coin
+
+ IERC20(tokenOut).safeTransfer(to, (_amountOut - _feeAmount));
+
+ // if there's a referrer, send them a share of the fee
+ if (referrer != address(0) && referrerShare > 0) {
+ uint256 referrerShareAmount = (_feeAmount * referrerShare) / MAX_BPS;
+ IERC20(tokenOut).safeTransfer(referrer, referrerShareAmount);
+ _feeAmount -= referrerShareAmount; // deduct referrer's share from the fee
+ }
+
+ // note that staking share is not taken here, because staking contract only accepts native coins (ETH)
+
+ if (frontendAddress != address(0) && frontendShare > 0) {
+ // calculate frontend and iggy fee share amounts
+ uint256 frontendShareAmount = (_feeAmount * frontendShare) / MAX_BPS;
+
+ // transfer tokens to fee receivers
+ IERC20(tokenOut).safeTransfer(frontendAddress, frontendShareAmount); // send part of the fee to the frontend operator
+ }
+
+ // find the remaining balance of tokenOut (to avoid leaving dust in the contract)
+ uint256 tokenOutRemainingBalance = IERC20(tokenOut).balanceOf(address(this));
+
+ // send the rest of the fee to iggy
+ IERC20(tokenOut).safeTransfer(iggyAddress, tokenOutRemainingBalance);
+ }
+
+ }
+}
diff --git a/contracts/swap/IggySwapRouterSolidly.sol b/contracts/swap/IggySwapRouterSolidly.sol
new file mode 100644
index 0000000..84a1cb0
--- /dev/null
+++ b/contracts/swap/IggySwapRouterSolidly.sol
@@ -0,0 +1,674 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { OwnableWithManagers } from "../access/OwnableWithManagers.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
+
+interface IStats {
+ function addWeiSpent(address user_, uint256 weiSpent_) external;
+}
+
+interface IUniswapV2Pair {
+ function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
+}
+
+interface IUniswapV2Router02 {
+
+ struct Route {
+ address from;
+ address to;
+ bool stable;
+ }
+
+ function getAmountsOut(uint amountIn, Route[] memory routes) external view returns (uint[] memory amounts);
+ function pairFor(address tokenA, address tokenB, bool stable) external view returns (address pair);
+
+ function addLiquidity(
+ address tokenA,
+ address tokenB,
+ bool stable,
+ uint amountADesired,
+ uint amountBDesired,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB, uint liquidity);
+
+ function addLiquidityETH(
+ address token,
+ bool stable,
+ uint amountTokenDesired,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
+
+ function removeLiquidity(
+ address tokenA,
+ address tokenB,
+ bool stable,
+ uint liquidity,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB);
+
+ function removeLiquidityETH(
+ address token,
+ bool stable,
+ uint liquidity,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountToken, uint amountETH);
+
+ function swapExactTokensForTokens(
+ uint amountIn,
+ uint amountOutMin,
+ Route[] calldata routes,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts);
+}
+
+interface IWETH {
+ function deposit() external payable;
+ function withdraw(uint) external;
+}
+
+/// @title Iggy swap router contract
+/// @author Tempe Techie
+/// @notice Contract that helps an Iggy frontend to swap tokens (custom because it's specific to a particular frontend)
+contract IggySwapRouterSolidly is OwnableWithManagers {
+ using SafeERC20 for IERC20;
+
+ address public feeChangerAddress; // a special role that is allowed to change fees and share amounts
+ address public frontendAddress; // address of a DAO/community which runs the frontend
+ address public iggyAddress;
+ address public routerAddress; // DEX router address
+ address public stakingAddress; // staking contract address
+ address public statsAddress; // stats contract address
+ address public immutable wethAddress;
+
+ uint256 public constant MAX_BPS = 10_000;
+ uint256 public swapFee; // 0.8% default fee
+ uint256 public referrerShare = 1000; // 10% share of the swap fee (in bips)
+ uint256 public stakingShare; // in bips
+
+ // after referrer & staking shares are deducted, the rest goes to the frontend operator and iggy based on the following frontendShare:
+ uint256 public frontendShare; // 50% share of the swap fee in bips (after referrer & staking shares are deducted)
+
+ // MODIFIERS
+ modifier onlyFeeChanger() {
+ require(msg.sender == feeChangerAddress, "IggySwap: Sender is not the Fee Changer");
+ _;
+ }
+
+ // CONSTRUCTOR
+ constructor(
+ address _frontendAddress,
+ address _iggyAddress,
+ address _routerAddress,
+ address _stakingAddress,
+ address _statsAddress,
+ address _wethAddress,
+ uint256 _swapFee,
+ uint256 _stakingShare,
+ uint256 _frontendShare
+ ) {
+ frontendAddress = _frontendAddress;
+ iggyAddress = _iggyAddress;
+ routerAddress = _routerAddress;
+ stakingAddress = _stakingAddress;
+ statsAddress = _statsAddress;
+ wethAddress = _wethAddress;
+
+ swapFee = _swapFee;
+ stakingShare = _stakingShare;
+ frontendShare = _frontendShare;
+
+ feeChangerAddress = msg.sender;
+ }
+
+ // RECEIVE
+ receive() external payable {}
+
+ // READ PUBLIC/EXTERNAL
+
+ /// @notice Get the amount of tokens and ETH to receive when removing liquidity
+ /// @dev This is useful to calculate min amount of tokens and ETH, but consider reducing it by slippage on your frontend
+ function calculateETHAndTokensToReceive(
+ address _token, // token to receive after removing liquidity
+ uint256 _liquidity
+ ) external view returns (uint256 amountToken, uint256 amountETH) {
+ address pair = IUniswapV2Router02(routerAddress).pairFor(_token, wethAddress, false);
+
+ (address token0,) = _sortTokens(_token, wethAddress);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (uint256 reserveToken, uint256 reserveETH) = _token == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+ uint256 lpTotalSupply = IERC20(pair).totalSupply();
+
+ amountToken = (reserveToken * _liquidity) / lpTotalSupply;
+ amountETH = (reserveETH * _liquidity) / lpTotalSupply;
+ }
+
+ /// @notice Calculate the amount of ETH needed to add liquidity
+ /// @dev This is useful to calculate min amount of ETH, but consider reducing it by slippage on your frontend
+ function calculateETHForLiquidity(address addressToken, uint256 amountToken) external view returns (uint256) {
+ address pair = IUniswapV2Router02(routerAddress).pairFor(addressToken, wethAddress, false);
+
+ if (pair == address(0)) {
+ return 0;
+ }
+
+ (address token0,) = _sortTokens(addressToken, wethAddress);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (uint256 reserveToken, uint256 reserveETH) = addressToken == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+
+ return (amountToken * reserveETH) / reserveToken; // return amount of ETH needed to add/remove liquidity
+ }
+
+ /// @notice Preview the amount of tokens that would be received for a given swap
+ function getAmountsOut(
+ uint amountIn,
+ address[] memory path
+ ) public view returns (uint[] memory amounts) {
+ amounts = _getTokensAmountOut(amountIn, path);
+ amounts[amounts.length - 1] = amounts[amounts.length - 1] - _getFeeAmount(amounts[amounts.length - 1]); // deduce swap fee from amount out
+ }
+
+ /// @notice Get LP (pair) token address for a given pair of tokens
+ function getLpTokenAddress(address tokenA, address tokenB) external view returns (address) {
+ if (tokenA == address(0)) {
+ tokenA = wethAddress;
+ }
+
+ if (tokenB == address(0)) {
+ tokenB = wethAddress;
+ }
+
+ return IUniswapV2Router02(routerAddress).pairFor(tokenA, tokenB, false);
+ }
+
+ /// @notice Calculates the price impact of a swap (in bips)
+ function getPriceImpact(
+ address tokenIn,
+ address tokenOut,
+ uint amountIn
+ ) external view returns (uint) {
+ if (tokenIn == address(0)) {
+ tokenIn = wethAddress;
+ }
+
+ if (tokenOut == address(0)) {
+ tokenOut = wethAddress;
+ }
+
+ if (tokenIn == tokenOut) {
+ return 0;
+ }
+
+ // get reserves for both tokens (reserve is a token total amount in a pool)
+ (uint reserveIn, uint reserveOut) = _getReserves(tokenIn, tokenOut);
+
+ uint k = reserveIn * reserveOut; // calculate a constant k (x * y = k, standard Uniswap V2 formula)
+
+ // calculate the amount of tokens user would receive if they swapped
+ uint newReserveOut = k / (reserveIn + amountIn);
+
+ uint amountOut = reserveOut - newReserveOut;
+
+ return (amountOut * MAX_BPS) / newReserveOut; // return price impact in bips
+ }
+
+ // WRITE PUBLIC/EXTERNAL
+
+ /// @notice Add liquidity to a pool (both tokens must be ERC-20 tokens)
+ function addLiquidity(
+ address tokenA,
+ address tokenB,
+ uint amountADesired,
+ uint amountBDesired,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB, uint liquidity) {
+ // transfer tokens to this contract
+ IERC20(tokenA).safeTransferFrom(msg.sender, address(this), amountADesired);
+ IERC20(tokenB).safeTransferFrom(msg.sender, address(this), amountBDesired);
+
+ // approve tokens to be spent by router
+ IERC20(tokenA).approve(routerAddress, amountADesired);
+ IERC20(tokenB).approve(routerAddress, amountBDesired);
+
+ // add liquidity
+ (amountA, amountB, liquidity) = IUniswapV2Router02(routerAddress).addLiquidity(
+ tokenA,
+ tokenB,
+ false, // not stable
+ amountADesired,
+ amountBDesired,
+ amountAMin,
+ amountBMin,
+ to,
+ deadline
+ );
+ }
+
+ function addLiquidityETH(
+ address token,
+ uint amountTokenDesired,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external payable returns (uint amountToken, uint amountETH, uint liquidity) {
+ // transfer tokens to this contract
+ IERC20(token).safeTransferFrom(msg.sender, address(this), amountTokenDesired);
+
+ // approve tokens to be spent by router
+ IERC20(token).approve(routerAddress, amountTokenDesired);
+
+ // add liquidity
+ (amountToken, amountETH, liquidity) = IUniswapV2Router02(routerAddress).addLiquidityETH{value: msg.value}(
+ token,
+ false, // not stable
+ amountTokenDesired,
+ amountTokenMin,
+ amountETHMin,
+ to,
+ deadline
+ );
+ }
+
+ function removeLiquidity(
+ address tokenA,
+ address tokenB,
+ uint liquidity,
+ uint amountAMin,
+ uint amountBMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountA, uint amountB) {
+ address pair = IUniswapV2Router02(routerAddress).pairFor(tokenA, tokenB, false);
+
+ // transfer liquidity tokens to this contract
+ IERC20(pair).safeTransferFrom(msg.sender, address(this), liquidity);
+
+ // approve tokens to be spent by router
+ IERC20(pair).approve(routerAddress, liquidity);
+
+ // remove liquidity
+ (amountA, amountB) = IUniswapV2Router02(routerAddress).removeLiquidity(
+ tokenA,
+ tokenB,
+ false, // not stable
+ liquidity,
+ amountAMin,
+ amountBMin,
+ to,
+ deadline
+ );
+ }
+
+ function removeLiquidityETH(
+ address token,
+ uint liquidity,
+ uint amountTokenMin,
+ uint amountETHMin,
+ address to,
+ uint deadline
+ ) external returns (uint amountToken, uint amountETH) {
+ // get LP token address
+ address pair = IUniswapV2Router02(routerAddress).pairFor(token, wethAddress, false);
+
+ // transfer liquidity tokens to this contract
+ IERC20(pair).safeTransferFrom(msg.sender, address(this), liquidity);
+
+ // approve tokens to be spent by router
+ IERC20(pair).approve(routerAddress, liquidity);
+
+ // remove liquidity
+ (amountToken, amountETH) = IUniswapV2Router02(routerAddress).removeLiquidityETH(
+ token,
+ false, // not stable
+ liquidity,
+ amountTokenMin,
+ amountETHMin,
+ to,
+ deadline
+ );
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ERC-20 tokens
+ function swapExactTokensForTokens(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] calldata path,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, address(0), false); // no referrer
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ERC-20 tokens (with referrer)
+ function swapExactTokensForTokensWithReferrer(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] calldata path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, referrer, false);
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ETH
+ function swapExactTokensForETH(
+ uint amountIn,
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, address(0), true); // no referrer
+ }
+
+ /// @notice Swap exact ERC-20 tokens for ETH (with referrer)
+ function swapExactTokensForETHWithReferrer(
+ uint amountIn,
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external returns (uint[] memory amounts) {
+ IERC20(path[0]).safeTransferFrom(msg.sender, address(this), amountIn); // send user's tokens to this contract
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ amounts = _swap(amountIn, amountOutMin, path, to, deadline, referrer, true);
+ }
+
+ /// @notice Swap exact ETH for ERC-20 tokens
+ function swapExactETHForTokens(
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline
+ ) external payable returns (uint[] memory amounts) {
+ require(msg.value > 0, "IggySwap: Native coin amount is zero");
+
+ IWETH(wethAddress).deposit{value: msg.value}(); // convert ETH to WETH
+
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ amounts = _swap(msg.value, amountOutMin, path, to, deadline, address(0), false); // no referrer
+ }
+
+ /// @notice Swap exact ETH for ERC-20 tokens (with referrer)
+ function swapExactETHForTokensWithReferrer(
+ uint amountOutMin,
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer
+ ) external payable returns (uint[] memory amounts) {
+ require(msg.value > 0, "IggySwap: Native coin amount is zero");
+
+ IWETH(wethAddress).deposit{value: msg.value}(); // convert ETH to WETH
+
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ amounts = _swap(msg.value, amountOutMin, path, to, deadline, referrer, false);
+ }
+
+ // FEE CHANGER
+ function changeFeeChangerAddress(address _newFeeChangerAddress) external onlyFeeChanger {
+ feeChangerAddress = _newFeeChangerAddress;
+ }
+
+ function changeFrontendShare(uint256 _newFrontendShare) external onlyFeeChanger {
+ require(_newFrontendShare <= MAX_BPS, "IggySwap: Frontend share is greater than MAX_BPS");
+ frontendShare = _newFrontendShare;
+ }
+
+ function changeReferrerShare(uint256 _newReferrerShare) external onlyFeeChanger {
+ require(_newReferrerShare <= MAX_BPS, "IggySwap: Referrer share is greater than MAX_BPS");
+ referrerShare = _newReferrerShare;
+ }
+
+ function changeStakingShare(uint256 _newStakingShare) external onlyFeeChanger {
+ require(_newStakingShare <= MAX_BPS, "IggySwap: Staking share is greater than MAX_BPS");
+ stakingShare = _newStakingShare;
+ }
+
+ function changeSwapFee(uint256 _newSwapFee) external onlyFeeChanger {
+ require(_newSwapFee <= MAX_BPS, "IggySwap: Swap fee is greater than MAX_BPS");
+ swapFee = _newSwapFee;
+ }
+
+ // FRONTEND OWNER
+
+ /// @notice Change frontend address
+ function changeFrontendAddress(address _newFrontendAddress) external {
+ require(msg.sender == frontendAddress, "IggySwap: Sender is not the frontend owner");
+ frontendAddress = _newFrontendAddress;
+ }
+
+ // IGGY
+
+ /// @notice Change Iggy address
+ function changeIggyAddress(address _newIggyAddress) external {
+ require(msg.sender == iggyAddress, "IggySwap: Sender is not Iggy");
+ iggyAddress = _newIggyAddress;
+ }
+
+ // OWNER
+
+ /// @notice Change router address
+ function changeRouterAddress(address _newRouterAddress) external onlyManagerOrOwner {
+ routerAddress = _newRouterAddress;
+ }
+
+ /// @notice Change staking address
+ function changeStakingAddress(address _newStakingAddress) external onlyManagerOrOwner {
+ stakingAddress = _newStakingAddress;
+ }
+
+ /// @notice Change stats address
+ function changeStatsAddress(address _newStatsAddress) external onlyManagerOrOwner {
+ statsAddress = _newStatsAddress;
+ }
+
+ /// @notice Recover any ERC-20 token mistakenly sent to this contract address
+ function recoverERC20(address tokenAddress_, uint256 tokenAmount_, address recipient_) external onlyManagerOrOwner {
+ IERC20(tokenAddress_).safeTransfer(recipient_, tokenAmount_);
+ }
+
+ /// @notice Recover native coins from contract
+ function recoverETH() external onlyManagerOrOwner {
+ (bool success, ) = payable(owner()).call{value: address(this).balance}("");
+ require(success, "Failed to recover native coins from contract");
+ }
+
+ // INTERNAL - READ
+ function _getFeeAmount(uint _amount) internal view returns (uint) {
+ return (_amount * swapFee) / MAX_BPS;
+ }
+
+ // fetches and sorts the reserves for a pair
+ function _getReserves(address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
+ (address token0,) = _sortTokens(tokenA, tokenB);
+ address pair = IUniswapV2Router02(routerAddress).pairFor(tokenA, tokenB, false);
+ (uint reserve0, uint reserve1,) = IUniswapV2Pair(pair).getReserves();
+ (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
+ }
+
+ function _getTokensAmountOut(
+ uint amountIn,
+ address[] memory path
+ ) internal view returns(uint[] memory amounts) {
+ if (path[0] == address(0)) {
+ path[0] = wethAddress;
+ }
+
+ if (path[path.length - 1] == address(0)) {
+ path[path.length - 1] = wethAddress;
+ }
+
+ IUniswapV2Router02.Route[] memory routes = new IUniswapV2Router02.Route[](path.length-1);
+
+ for (uint256 i = 0; i < path.length-1; i++) {
+ routes[i] = IUniswapV2Router02.Route({
+ from: path[i],
+ to: path[i + 1],
+ stable: false // Set stable to false for each Route
+ });
+ }
+
+ return IUniswapV2Router02(routerAddress).getAmountsOut(amountIn, routes);
+ }
+
+ function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
+ require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
+ (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
+ require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
+ }
+
+ // INTERNAL - WRITE
+ function _swap(
+ uint amountIn,
+ uint amountOutMin, // amount out deducted by slippage
+ address[] memory path,
+ address to,
+ uint deadline,
+ address referrer,
+ bool convertToNative
+ ) internal returns (uint[] memory amounts) {
+ IERC20(path[0]).approve(routerAddress, amountIn); // approve router to spend tokens
+
+ // create routes out of path
+ IUniswapV2Router02.Route[] memory routes = new IUniswapV2Router02.Route[](path.length-1);
+
+ for (uint256 i = 0; i < path.length-1; i++) {
+ routes[i] = IUniswapV2Router02.Route({
+ from: path[i],
+ to: path[i + 1],
+ stable: false // Set stable to false for each Route
+ });
+ }
+
+ // make the swap via router
+ amounts = IUniswapV2Router02(routerAddress).swapExactTokensForTokens(
+ amountIn,
+ amountOutMin,
+ routes,
+ address(this), // initially the receiver is this contract (tokens will be later transferred to the recipient and to fee receivers)
+ deadline
+ );
+
+ uint256 _amountOut = amounts[amounts.length - 1]; // total amount out (including fee)
+ uint256 _feeAmount = _getFeeAmount(_amountOut); // swap fee amount
+
+ require((_amountOut - _feeAmount) >= amountOutMin, "IggySwap: Amount out is less than the minimum amount out");
+
+ address tokenOut = path[path.length - 1]; // receiving token address
+
+ // transfer tokens to the recipient (deduct the fee)
+ if (convertToNative && tokenOut == wethAddress) {
+ // if: tokenOut is the native coin (ETH)
+
+ IWETH(tokenOut).withdraw(_amountOut); // convert WETH to ETH
+
+ (bool sentWeth, ) = payable(to).call{value: (_amountOut - _feeAmount)}("");
+ require(sentWeth, "Failed to send native coins to the recipient");
+
+ // if there's a referrer, send them a share of the fee
+ uint256 referrerShareAmountNative;
+ if (referrer != address(0) && referrerShare > 0) {
+ referrerShareAmountNative = (_feeAmount * referrerShare) / MAX_BPS;
+
+ (bool sentWethReferrer, ) = payable(referrer).call{value: referrerShareAmountNative}("");
+ require(sentWethReferrer, "Failed to send native coins to the referrer");
+
+ _feeAmount -= referrerShareAmountNative; // deduct referrer's share from the fee
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(referrer, referrerShareAmountNative); // add referrer's share to stats
+ }
+ }
+
+ // if there's a staking contract, send them a share of the fee
+ if (stakingAddress != address(0) && stakingShare > 0) {
+ uint256 stakingShareAmountNative = (_feeAmount * stakingShare) / MAX_BPS;
+
+ (bool sentWethStaking, ) = payable(stakingAddress).call{value: stakingShareAmountNative}("");
+ require(sentWethStaking, "Failed to send native coins to the staking contract");
+
+ _feeAmount -= stakingShareAmountNative; // deduct staking contract's share from the fee
+ }
+
+ // send a share of the fee to the frontend operator
+ uint256 frontendShareAmountNative = (_feeAmount * frontendShare) / MAX_BPS;
+ (bool sentWethFrontend, ) = payable(frontendAddress).call{value: frontendShareAmountNative}("");
+ require(sentWethFrontend, "Failed to send native coins to the frontend operator");
+
+ // send the rest to Iggy
+ (bool sentWethIggy, ) = payable(iggyAddress).call{value: address(this).balance}("");
+ require(sentWethIggy, "Failed to send native coins to Iggy");
+
+ // add user's fee (without referral's share) to stats
+ if (statsAddress != address(0)) {
+ IStats(statsAddress).addWeiSpent(to, _feeAmount-referrerShareAmountNative);
+ }
+ } else {
+ // else: tokenOut is NOT the native coin
+
+ IERC20(tokenOut).safeTransfer(to, (_amountOut - _feeAmount));
+
+ // if there's a referrer, send them a share of the fee
+ if (referrer != address(0) && referrerShare > 0) {
+ uint256 referrerShareAmount = (_feeAmount * referrerShare) / MAX_BPS;
+ IERC20(tokenOut).safeTransfer(referrer, referrerShareAmount);
+ _feeAmount -= referrerShareAmount; // deduct referrer's share from the fee
+ }
+
+ // note that staking share is not taken here, because staking contract only accepts native coins (ETH)
+
+ // calculate frontend and iggy fee share amounts
+ uint256 frontendShareAmount = (_feeAmount * frontendShare) / MAX_BPS;
+
+ // transfer tokens to fee receivers
+ IERC20(tokenOut).safeTransfer(frontendAddress, frontendShareAmount); // send part of the fee to the frontend operator
+
+ // find the remaining balance of tokenOut (to avoid leaving dust in the contract)
+ uint256 tokenOutRemainingBalance = IERC20(tokenOut).balanceOf(address(this));
+
+ // send the rest of the fee to iggy
+ IERC20(tokenOut).safeTransfer(iggyAddress, tokenOutRemainingBalance);
+ }
+
+ }
+}
diff --git a/contracts/token/ChatToken.sol b/contracts/token/ChatToken.sol
new file mode 100644
index 0000000..ca34408
--- /dev/null
+++ b/contracts/token/ChatToken.sol
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { ERC20, ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
+import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+contract ChatToken is ERC20Burnable, Ownable, ERC20Permit {
+ address public minter;
+
+ // CONSTRUCTOR
+ constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) ERC20Permit(_name) {}
+
+ // EVENTS
+ event MinterAddressChanged(address indexed _owner, address indexed _minter);
+
+ // MINTER
+
+ function mint(address _to, uint256 _amount) external {
+ require(msg.sender == minter, "ChatToken: only minter can mint");
+ _mint(_to, _amount);
+ }
+
+ // OWNER
+
+ function setMinter(address _minter) external onlyOwner {
+ minter = _minter;
+ emit MinterAddressChanged(msg.sender, _minter);
+ }
+
+}
diff --git a/contracts/token/ChatTokenClaimActivityPoints.sol b/contracts/token/ChatTokenClaimActivityPoints.sol
new file mode 100644
index 0000000..f84225d
--- /dev/null
+++ b/contracts/token/ChatTokenClaimActivityPoints.sol
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+interface IChatTokenMinter is IERC20 {
+ function mint(address to, uint256 amount) external;
+}
+
+interface IActivityPoints {
+ function getTotalWeiSpent(address _address) external view returns (uint256);
+}
+
+/**
+@title Chat Token Claim For Activity Points
+@notice Smart contract for claiming chat tokens earned via activity points.
+*/
+contract ChatTokenClaimActivityPoints is Ownable {
+ address public immutable chatTokenMinter;
+ address public immutable apAddress; // activity points smart contract address
+
+ bool public paused = false;
+
+ uint256 public immutable chatEthRatio; // for example, 1000: 1 ETH = 1,000 CHAT
+
+ mapping(address => bool) public hasClaimed; // addresses that have already claimed
+
+ // CONSTRUCTOR
+ constructor(
+ address _chatTokenMinter,
+ address _apAddress,
+ uint256 _chatEthRatio
+ ) {
+ require(_chatEthRatio > 0, "ChatTokenClaimActivityPoints: chatEthRatio must be greater than 0");
+ require(_chatTokenMinter != address(0), "ChatTokenClaimActivityPoints: chatTokenMinter cannot be zero address");
+ require(_apAddress != address(0), "ChatTokenClaimActivityPoints: apAddress cannot be zero address");
+
+ chatTokenMinter = _chatTokenMinter;
+ apAddress = _apAddress;
+ chatEthRatio = _chatEthRatio;
+ }
+
+ // READ
+
+ function claimPreview(address _address) public view returns (uint256) {
+ if (hasClaimed[_address]) return 0; // already claimed
+
+ uint256 _mintedWei = IActivityPoints(apAddress).getTotalWeiSpent(_address);
+ return _mintedWei * chatEthRatio;
+ }
+
+ // WRITE
+
+ function claim() external {
+ require(!paused, "ChatTokenClaimActivityPoints: claiming is paused");
+ require(!hasClaimed[msg.sender], "ChatTokenClaimActivityPoints: user already claimed");
+
+ uint256 _claimAmount = claimPreview(msg.sender);
+ require(_claimAmount > 0, "ChatTokenClaimActivityPoints: no tokens to claim");
+
+ hasClaimed[msg.sender] = true; // mark as claimed
+ IChatTokenMinter(chatTokenMinter).mint(msg.sender, _claimAmount);
+ }
+
+ // OWNER
+
+ function togglePaused() external onlyOwner {
+ paused = !paused;
+ }
+}
\ No newline at end of file
diff --git a/contracts/token/ChatTokenClaimDomains.sol b/contracts/token/ChatTokenClaimDomains.sol
new file mode 100644
index 0000000..f8ca8a9
--- /dev/null
+++ b/contracts/token/ChatTokenClaimDomains.sol
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+interface IChatTokenMinter is IERC20 {
+ function mint(address to, uint256 amount) external;
+}
+
+interface IBasePunkTLD {
+ function domains(string calldata _domainName) external view returns(string memory, uint256, address, string memory);
+ function getDomainHolder(string calldata _domainName) external view returns(address);
+}
+
+/**
+@title Chat Token Claim For Domain Holders
+@notice Airdrop for domain holders
+*/
+contract ChatTokenClaimDomains is Ownable {
+ address public immutable chatTokenMinter;
+ address public immutable domainAddress; // address of the punk domain smart contract
+
+ bool public paused = false;
+
+ uint256 public chatReward; // how many tokens a domain gets (in wei)
+ uint256 public maxIdEligible; // max domain ID eligible for claiming (aka snapshot)
+
+ mapping(string => bool) public hasClaimed; // domain names that have already claimed
+
+ // CONSTRUCTOR
+ constructor(
+ address _chatTokenMinter,
+ address _domainAddress,
+ uint256 _chatReward,
+ uint256 _maxIdEligible
+ ) {
+ require(_chatReward > 0, "ChatTokenClaimDomains: chatReward must be greater than 0");
+ require(_chatTokenMinter != address(0), "ChatTokenClaimDomains: chatTokenMinter cannot be zero address");
+ require(_domainAddress != address(0), "ChatTokenClaimDomains: domain contract cannot be zero address");
+
+ chatTokenMinter = _chatTokenMinter;
+ domainAddress = _domainAddress;
+ chatReward = _chatReward;
+ maxIdEligible = _maxIdEligible;
+ }
+
+ // WRITE
+
+ /**
+ @notice Claim chat tokens for a domain name. Anyone can claim for any domain name, but only holder gets the tokens.
+ */
+ function claim(string calldata _domainName) external {
+ require(!paused, "ChatTokenClaimDomains: claiming is paused");
+ require(!hasClaimed[_domainName], "ChatTokenClaimDomains: domain already claimed");
+
+ (, uint256 _domainId, , ) = IBasePunkTLD(domainAddress).domains(_domainName); // check if domain exists
+ require(_domainId <= maxIdEligible, "ChatTokenClaimDomains: domain ID not eligible for claiming");
+
+ address _domainHolder = IBasePunkTLD(domainAddress).getDomainHolder(_domainName);
+ require(_domainHolder != address(0), "ChatTokenClaimDomains: domain not registered");
+
+ hasClaimed[_domainName] = true; // mark as claimed
+ IChatTokenMinter(chatTokenMinter).mint(_domainHolder, chatReward);
+ }
+
+ // OWNER
+
+ function changeChatReward(uint256 _chatReward) external onlyOwner {
+ require(_chatReward > 0, "ChatTokenClaimDomains: chatReward must be greater than 0");
+ chatReward = _chatReward;
+ }
+
+ function changeMaxIdEligible(uint256 _maxIdEligible) external onlyOwner {
+ maxIdEligible = _maxIdEligible;
+ }
+
+ function togglePaused() external onlyOwner {
+ paused = !paused;
+ }
+}
\ No newline at end of file
diff --git a/contracts/token/ChatTokenMinter.sol b/contracts/token/ChatTokenMinter.sol
new file mode 100644
index 0000000..eb1b772
--- /dev/null
+++ b/contracts/token/ChatTokenMinter.sol
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity ^0.8.17;
+
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
+
+interface IChatToken is IERC20 {
+ function mint(address to, uint256 amount) external;
+}
+
+contract ChatTokenMinter is Ownable {
+ address public immutable chatToken;
+ bool public paused = false;
+
+ mapping(address => bool) public isMinter; // addresses that have minting privileges
+
+ // CONSTRUCTOR
+ constructor(address _chatToken) {
+ chatToken = _chatToken;
+ }
+
+ // MINTER
+
+ function mint(address _to, uint256 _amount) external {
+ require(!paused, "ChatTokenMinter: minting is paused");
+ require(isMinter[msg.sender], "ChatTokenMinter: only minters can mint");
+
+ IChatToken(chatToken).mint(_to, _amount);
+ }
+
+ // OWNER
+
+ function addMinter(address _minter) external onlyOwner {
+ isMinter[_minter] = true;
+ }
+
+ function removeMinter(address _minter) external onlyOwner {
+ isMinter[_minter] = false;
+ }
+
+ function togglePaused() external onlyOwner {
+ paused = !paused;
+ }
+}
\ No newline at end of file
diff --git a/hardhat.config.js b/hardhat.config.js
new file mode 100644
index 0000000..a9aded1
--- /dev/null
+++ b/hardhat.config.js
@@ -0,0 +1,459 @@
+require("@nomicfoundation/hardhat-toolbox");
+require("@nomiclabs/hardhat-etherscan");
+require('dotenv').config();
+
+/**
+ * @type import('hardhat/config').HardhatUserConfig
+ */
+module.exports = {
+ defaultNetwork: 'hardhat',
+
+ networks: {
+ hardhat: {
+ gas: "auto", // gas limit
+ },
+ localhost: {
+ gas: "auto", // gas limit
+ },
+ arbitrumOne: {
+ //url: 'https://arb-mainnet.g.alchemy.com/v2/' + process.env.ALCHEMY_API_KEY_ARBITRUM,
+ url: "https://arb1.arbitrum.io/rpc",
+ chainId: 42161,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 100000000, // 1 gwei
+ },
+ arbitrumGoerli: {
+ url: 'https://goerli-rollup.arbitrum.io/rpc',
+ //url: 'https://endpoints.omniatech.io/v1/arbitrum/goerli/public',
+ chainId: 421613,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ allowUnlimitedContractSize: true
+ },
+ arbitrumNova: {
+ //url: "https://arbitrum-nova.public.blastapi.io",
+ url: "https://nova.arbitrum.io/rpc",
+ chainId: 42170,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 10000000, // 0.01 gwei
+ },
+ aurora: {
+ url: 'https://mainnet.aurora.dev',
+ chainId: 1313161554,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ auroraTestnet: {
+ url: 'https://testnet.aurora.dev',
+ chainId: 1313161555,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ base: {
+ url: 'https://mainnet.base.org',
+ //url: 'https://rpc.notadegen.com/base',
+ //url: 'https://base-mainnet.public.blastapi.io',
+ chainId: 8453,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 12000000, // 0.012 gwei
+ },
+ baseTestnet: {
+ url: 'https://base-goerli.public.blastapi.io',
+ chainId: 84531,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ blastSepolia: { // Blast testnet
+ url: 'https://sepolia.blast.io/',
+ chainId: 168587773,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ bsc: { // BNB Smart Chain mainnet
+ url: 'https://bscrpc.com',
+ chainId: 56,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 5000000000, // 5 gwei
+ },
+ flare: { // Flare mainnet
+ url: 'https://flare-api.flare.network/ext/C/rpc',
+ chainId: 14,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 125000000000, // 125 gwei
+ },
+ flareCoston: { // Flare Coston Testnet
+ url: 'https://coston-api.flare.network/ext/bc/C/rpc',
+ chainId: 16,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 25000000000, // 25 gwei
+ },
+ ftmTestnet: { // Fantom testnet
+ url: "https://rpc.ankr.com/fantom_testnet", //'https://rpc.testnet.fantom.network',
+ chainId: 4002,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 2000000000, // 1 gwei
+ },
+ gnosis: { // Gnosis Chain mainnet (xdai)
+ url: 'https://gnosischain-rpc.gateway.pokt.network',
+ chainId: 100,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 20000000000, // 20 gwei
+ },
+ linea: { // Linea mainnet
+ url: "https://rpc.linea.build/",
+ chainId: 59144,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 2000000000, // 2 gwei
+ },
+ mainnet: { // Ethereum
+ url: 'https://eth-mainnet.g.alchemy.com/v2/' + process.env.ALCHEMY_API_KEY_ETHEREUM,
+ chainId: 1,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 35000000000, // 30 gwei
+ },
+ mantleTestnet: { // Mantle testnet
+ url: 'https://rpc.testnet.mantle.xyz',
+ chainId: 5001,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1, // 1 wei
+ },
+ modeMainnet: { // Mode mainnet
+ url: 'https://mainnet.mode.network/',
+ chainId: 34443,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ modeTestnet: { // Mode testnet
+ url: 'https://sepolia.mode.network/',
+ chainId: 919,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ opera: { // Fantom mainnet
+ url: "https://fantom-mainnet.public.blastapi.io", // 'https://rpc.fantom.network', // "https://rpc.ankr.com/fantom", //
+ chainId: 250,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 600000000000, // 600 gwei
+ },
+ optimisticEthereum: {
+ url: 'https://rpc.ankr.com/optimism',
+ chainId: 10,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 20000000, // 0.02 gwei
+ },
+ optimisticGoerli: {
+ url: 'https://goerli.optimism.io',
+ chainId: 420,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ polygon: {
+ //url: 'https://polygon-mainnet.g.alchemy.com/v2/' + process.env.ALCHEMY_API_KEY_POLYGON,
+ url: "https://rpc.ankr.com/polygon",
+ chainId: 137,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 200000000000, // 200 gwei
+ },
+ polygonMumbai: {
+ //url: 'https://polygon-mumbai.g.alchemy.com/v2/' + process.env.ALCHEMY_API_KEY_MUMBAI,
+ //url: 'https://rpc.ankr.com/polygon_mumbai',
+ url: 'https://rpc-mumbai.maticvigil.com',
+ chainId: 80001,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 2000000000, // 2 gwei
+ },
+ polygonZkEvm: {
+ url: 'https://zkevm-rpc.com',
+ chainId: 1101,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 20000000000, // 20 gwei
+ },
+ polygonZkEvmTestnet: {
+ url: 'https://rpc.public.zkevm-test.net',
+ chainId: 1442,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 20000000000, // 20 gwei
+ },
+ scroll: { // Scroll Mainnet
+ url: 'https://rpc.scroll.io',
+ chainId: 534352,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ sokol: { // Gnosis Chain testnet
+ url: 'https://sokol.poa.network',
+ chainId: 77,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 40000000000, // 20 gwei
+ },
+ songbird: { // Songbird Mainnet
+ url: 'https://songbird-api.flare.network/ext/C/rpc',
+ chainId: 19,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 125000000000, // 125 gwei
+ },
+ taikoJolnir: { // Taiko testnet (L2)
+ url: 'https://rpc.jolnir.taiko.xyz',
+ chainId: 167007,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 1000000000, // 1 gwei
+ },
+ zkfair: { // zkFair mainnet
+ url: 'https://rpc.zkfair.io',
+ chainId: 42766,
+ accounts: [process.env.DEPLOYER_PRIVATE_KEY],
+ gas: "auto", // gas limit
+ gasPrice: 5000000000000, // 5000 gwei
+ }
+ },
+
+ etherscan: {
+ apiKey: { // all possible key names here: https://gist.github.com/tempe-techie/95a3ad4e81b46c895928a0524fc2b7ac
+ arbitrumOne: process.env.ARBISCAN_API_KEY,
+ arbitrumGoerli: process.env.ARBISCAN_API_KEY,
+ arbitrumNova: process.env.NOVAARBISCAN_API_KEY,
+ aurora: process.env.AURORASCAN_API_KEY,
+ auroraTestnet: process.env.AURORASCAN_API_KEY,
+ blastSepolia: "randomstring",
+ base: process.env.BASESCAN_API_KEY,
+ baseTestnet: process.env.BASESCAN_API_KEY,
+ bsc: process.env.BSC_API_KEY,
+ flare: "randomstring",
+ flareCoston: "randomstring",
+ ftmTestnet: process.env.FTMSCAN_API_KEY,
+ gnosis: process.env.GNOSISSCAN_API_KEY, // xdai
+ linea: process.env.LINEASCAN_API_KEY,
+ mainnet: process.env.ETHERSCAN_API_KEY,
+ mantleTestnet: "randomstring",
+ modeMainnet: "randomstring",
+ modeTestnet: "randomstring",
+ opera: process.env.FTMSCAN_API_KEY, // fantom
+ optimisticEthereum: process.env.OPTIMISTIC_ETHERSCAN_API_KEY,
+ optimisticGoerli: process.env.OPTIMISTIC_ETHERSCAN_API_KEY,
+ polygon: process.env.POLYGONSCAN_API_KEY,
+ polygonMumbai: process.env.POLYGONSCAN_API_KEY,
+ polygonZkEvm: process.env.POLYGONSCAN_ZKEVM_API_KEY,
+ polygonZkEvmTestnet: process.env.POLYGONSCAN_ZKEVM_API_KEY,
+ scroll: process.env.SCROLLSCAN_API_KEY,
+ sokol: "randomstring",
+ songbird: "randomstring",
+ taikoJolnir: "42069",
+ zkfair: "randomstring"
+ },
+ customChains: [
+ {
+ network: "arbitrumGoerli",
+ chainId: 421613,
+ urls: {
+ apiURL: "https://api-goerli.arbiscan.io/api",
+ browserURL: "https://goerli.arbiscan.io"
+ }
+ },
+ {
+ network: "arbitrumNova",
+ chainId: 42170,
+ urls: {
+ apiURL: "https://api-nova.arbiscan.io/api",
+ browserURL: "https://nova.arbiscan.io"
+ }
+ },
+ {
+ network: "blastSepolia",
+ chainId: 168587773,
+ urls: {
+ apiURL: "https://api.routescan.io/v2/network/testnet/evm/168587773/etherscan",
+ browserURL: "https://testnet.blastscan.io"
+ }
+ },
+ /* */
+ {
+ network: "base", // BaseScan (Etherscan)
+ chainId: 8453,
+ urls: {
+ apiURL: "https://api.basescan.org/api",
+ browserURL: "https://basescan.org"
+ }
+ },
+
+
+ /*
+ {
+ network: "base", // Blockscout
+ chainId: 8453,
+ urls: {
+ apiURL: "https://base.blockscout.com/api",
+ browserURL: "https://base.blockscout.com"
+ }
+ },
+ */
+ {
+ network: "baseTestnet",
+ chainId: 84531,
+ urls: {
+ apiURL: "https://base-goerli.blockscout.com/api", // "https://api-goerli.basescan.org/api",
+ browserURL: "https://base-goerli.blockscout.com" // "https://goerli.basescan.org"
+ }
+ },
+ {
+ network: "flare",
+ chainId: 14,
+ urls: {
+ apiURL: "https://flare-explorer.flare.network/api",
+ browserURL: "https://flare-explorer.flare.network"
+ }
+ },
+ {
+ network: "flareCoston",
+ chainId: 16,
+ urls: {
+ apiURL: "https://coston-explorer.flare.network/api",
+ browserURL: "https://coston-explorer.flare.network"
+ }
+ },
+ {
+ network: "linea",
+ chainId: 59144,
+ urls: {
+ apiURL: "https://api.lineascan.build/api",
+ browserURL: "https://lineascan.build"
+ }
+ },
+ {
+ network: "mantleTestnet",
+ chainId: 5001,
+ urls: {
+ apiURL: "https://explorer.testnet.mantle.xyz/api",
+ browserURL: "https://explorer.testnet.mantle.xyz"
+ }
+ },
+ {
+ network: "modeMainnet",
+ chainId: 34443,
+ urls: {
+ apiURL: "https://explorer.mode.network/api",
+ browserURL: "https://explorer.mode.network/"
+ }
+ },
+ {
+ network: "modeTestnet",
+ chainId: 919,
+ urls: {
+ apiURL: "https://sepolia.explorer.mode.network/api",
+ browserURL: "https://sepolia.explorer.mode.network/"
+ }
+ },
+ {
+ network: "optimisticGoerli",
+ chainId: 420,
+ urls: {
+ apiURL: "https://api-goerli-optimism.etherscan.io/api",
+ browserURL: "https://goerli-optimism.etherscan.io/"
+ }
+ },
+ {
+ network: "polygonZkEvm",
+ chainId: 1101,
+ urls: {
+ apiURL: "https://api-zkevm.polygonscan.com",
+ browserURL: "https://zkevm.polygonscan.com"
+ }
+ },
+ {
+ network: "polygonZkEvmTestnet",
+ chainId: 1442,
+ urls: {
+ apiURL: "https://api-testnet-zkevm.polygonscan.com/api",
+ browserURL: "https://testnet-zkevm.polygonscan.com"
+ }
+ },
+ {
+ network: "scroll",
+ chainId: 534352,
+ urls: {
+ apiURL: "https://api.scrollscan.com/api",
+ browserURL: "https://scrollscan.com/"
+ }
+ },
+ {
+ network: "songbird",
+ chainId: 19,
+ urls: {
+ apiURL: "https://songbird-explorer.flare.network/api",
+ browserURL: "https://songbird-explorer.flare.network/"
+ }
+ },
+ {
+ network: "taikoJolnir",
+ chainId: 167007,
+ urls: {
+ //apiURL: "https://api.routescan.io/v2/network/testnet/evm/167007/etherscan",
+ apiURL: "https://explorer.jolnir.taiko.xyz/api",
+ browserURL: "https://explorer.jolnir.taiko.xyz/"
+ }
+ },
+ {
+ network: "zkfair",
+ chainId: 42766,
+ urls: {
+ apiURL: "https://scan.zkfair.io/api",
+ browserURL: "https://scan.zkfair.io/"
+ }
+ },
+
+ ]
+ },
+
+ solidity: {
+ compilers: [
+ {
+ version: "0.5.0",
+ },
+ {
+ version: "0.5.5",
+ },
+ {
+ version: "0.6.6",
+ },
+ {
+ version: "0.8.17",
+ settings: {
+ optimizer: {
+ enabled: true,
+ runs: 200
+ }
+ }
+ },
+ ],
+
+ }
+
+};
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..9702c2a
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,16053 @@
+{
+ "name": "modechat-contracts",
+ "version": "1.0.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "modechat-contracts",
+ "version": "1.0.0",
+ "license": "ISC",
+ "dependencies": {
+ "@openzeppelin/merkle-tree": "^1.0.5",
+ "@rari-capital/solmate": "^6.4.0",
+ "fs": "^0.0.1-security"
+ },
+ "devDependencies": {
+ "@nomicfoundation/hardhat-toolbox": "^2.0.0",
+ "@nomiclabs/hardhat-etherscan": "^3.1.1",
+ "@openzeppelin/contracts": "^v4.9.0-rc.0",
+ "dotenv": "^16.0.3",
+ "hardhat": "^2.12.0"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@ethersproject/abi": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz",
+ "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/abstract-provider": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz",
+ "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/networks": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/web": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/abstract-signer": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz",
+ "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/address": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz",
+ "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/base64": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz",
+ "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/basex": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz",
+ "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/bignumber": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz",
+ "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "bn.js": "^5.2.1"
+ }
+ },
+ "node_modules/@ethersproject/bytes": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz",
+ "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/constants": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz",
+ "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bignumber": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/contracts": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz",
+ "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.7.0",
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/hash": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz",
+ "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/hdnode": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz",
+ "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/basex": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/pbkdf2": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/wordlists": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/json-wallets": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz",
+ "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hdnode": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/pbkdf2": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "aes-js": "3.0.0",
+ "scrypt-js": "3.0.1"
+ }
+ },
+ "node_modules/@ethersproject/keccak256": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz",
+ "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "js-sha3": "0.8.0"
+ }
+ },
+ "node_modules/@ethersproject/logger": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz",
+ "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ]
+ },
+ "node_modules/@ethersproject/networks": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz",
+ "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/pbkdf2": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz",
+ "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/properties": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz",
+ "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/providers": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz",
+ "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/basex": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/networks": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/web": "^5.7.0",
+ "bech32": "1.1.4",
+ "ws": "7.4.6"
+ }
+ },
+ "node_modules/@ethersproject/providers/node_modules/ws": {
+ "version": "7.4.6",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@ethersproject/random": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz",
+ "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/rlp": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz",
+ "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/sha2": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz",
+ "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "hash.js": "1.1.7"
+ }
+ },
+ "node_modules/@ethersproject/signing-key": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz",
+ "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "bn.js": "^5.2.1",
+ "elliptic": "6.5.4",
+ "hash.js": "1.1.7"
+ }
+ },
+ "node_modules/@ethersproject/solidity": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz",
+ "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/strings": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz",
+ "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/transactions": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz",
+ "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/units": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz",
+ "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/wallet": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz",
+ "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/hdnode": "^5.7.0",
+ "@ethersproject/json-wallets": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/wordlists": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/web": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz",
+ "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "dependencies": {
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "node_modules/@ethersproject/wordlists": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz",
+ "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@metamask/eth-sig-util": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz",
+ "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==",
+ "dev": true,
+ "dependencies": {
+ "ethereumjs-abi": "^0.6.8",
+ "ethereumjs-util": "^6.2.1",
+ "ethjs-util": "^0.1.6",
+ "tweetnacl": "^1.0.3",
+ "tweetnacl-util": "^0.15.1"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
+ "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ]
+ },
+ "node_modules/@noble/secp256k1": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz",
+ "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ]
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-block": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz",
+ "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-block/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-blockchain": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz",
+ "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-ethash": "^2.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "abstract-level": "^1.0.3",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "level": "^8.0.0",
+ "lru-cache": "^5.1.1",
+ "memory-level": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-blockchain/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-common": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz",
+ "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "crc-32": "^1.2.0"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-ethash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz",
+ "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "abstract-level": "^1.0.3",
+ "bigint-crypto-utils": "^3.0.23",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-ethash/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-evm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz",
+ "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@types/async-eventemitter": "^0.2.1",
+ "async-eventemitter": "^0.2.4",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "mcl-wasm": "^0.7.1",
+ "rustbn.js": "~0.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-evm/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-rlp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz",
+ "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==",
+ "dev": true,
+ "bin": {
+ "rlp": "bin/rlp"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-statemanager": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz",
+ "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "functional-red-black-tree": "^1.0.1"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-statemanager/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-trie": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz",
+ "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-trie/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-tx": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz",
+ "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-tx/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-util": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz",
+ "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-util/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-vm": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz",
+ "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==",
+ "dev": true,
+ "dependencies": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-blockchain": "^6.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-evm": "^1.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-statemanager": "^1.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@types/async-eventemitter": "^0.2.1",
+ "async-eventemitter": "^0.2.4",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "functional-red-black-tree": "^1.0.1",
+ "mcl-wasm": "^0.7.1",
+ "rustbn.js": "~0.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@nomicfoundation/ethereumjs-vm/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/hardhat-chai-matchers": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.4.tgz",
+ "integrity": "sha512-n/5UMwGaUK2zM8ALuMChVwB1lEPeDTb5oBjQ1g7hVsUdS8x+XG9JIEp4Ze6Bwy98tghA7Y1+PCH4SNE2P3UQ2g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.1.2",
+ "@types/chai-as-promised": "^7.1.3",
+ "chai-as-promised": "^7.1.1",
+ "chalk": "^2.4.2",
+ "deep-eql": "^4.0.1",
+ "ordinal": "^1.0.3"
+ },
+ "peerDependencies": {
+ "@nomiclabs/hardhat-ethers": "^2.0.0",
+ "chai": "^4.2.0",
+ "ethers": "^5.0.0",
+ "hardhat": "^2.9.4"
+ }
+ },
+ "node_modules/@nomicfoundation/hardhat-network-helpers": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.6.tgz",
+ "integrity": "sha512-a35iVD4ycF6AoTfllAnKm96IPIzzHpgKX/ep4oKc2bsUKFfMlacWdyntgC/7d5blyCTXfFssgNAvXDZfzNWVGQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ethereumjs-util": "^7.1.4"
+ },
+ "peerDependencies": {
+ "hardhat": "^2.9.5"
+ }
+ },
+ "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/@nomicfoundation/hardhat-network-helpers/node_modules/ethereumjs-util": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+ "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/bn.js": "^5.1.0",
+ "bn.js": "^5.1.2",
+ "create-hash": "^1.1.2",
+ "ethereum-cryptography": "^0.1.3",
+ "rlp": "^2.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@nomicfoundation/hardhat-toolbox": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.0.tgz",
+ "integrity": "sha512-BoOPbzLQ1GArnBZd4Jz4IU8FY3RY4nUwpXlfymXwxlXNimngkPRJj7ivVNurD7igohEjf90v/Axn2M5WwAdCJQ==",
+ "dev": true,
+ "peerDependencies": {
+ "@ethersproject/abi": "^5.4.7",
+ "@ethersproject/providers": "^5.4.7",
+ "@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
+ "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
+ "@nomiclabs/hardhat-ethers": "^2.0.0",
+ "@nomiclabs/hardhat-etherscan": "^3.0.0",
+ "@typechain/ethers-v5": "^10.1.0",
+ "@typechain/hardhat": "^6.1.2",
+ "@types/chai": "^4.2.0",
+ "@types/mocha": "^9.1.0",
+ "@types/node": ">=12.0.0",
+ "chai": "^4.2.0",
+ "ethers": "^5.4.7",
+ "hardhat": "^2.11.0",
+ "hardhat-gas-reporter": "^1.0.8",
+ "solidity-coverage": "^0.8.1",
+ "ts-node": ">=8.0.0",
+ "typechain": "^8.1.0",
+ "typescript": ">=4.5.0"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz",
+ "integrity": "sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12"
+ },
+ "optionalDependencies": {
+ "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.0"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz",
+ "integrity": "sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz",
+ "integrity": "sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz",
+ "integrity": "sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz",
+ "integrity": "sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz",
+ "integrity": "sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz",
+ "integrity": "sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz",
+ "integrity": "sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz",
+ "integrity": "sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz",
+ "integrity": "sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz",
+ "integrity": "sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nomiclabs/hardhat-ethers": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.0.tgz",
+ "integrity": "sha512-kKCW7xawuD/lw69Yr1yqUUrF0IKmnLNGf+pTVbJ/ctHaRcPrwKI0EPkO1RNXBHlOOZkv6v4DK2PPvq0lL2ykig==",
+ "dev": true,
+ "peer": true,
+ "peerDependencies": {
+ "ethers": "^5.0.0",
+ "hardhat": "^2.0.0"
+ }
+ },
+ "node_modules/@nomiclabs/hardhat-etherscan": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.1.tgz",
+ "integrity": "sha512-a6+fJlHTiPjzUYnvwkcduJN0rAKWagQsQNoHJP/9mJ1CZjIkGysGtvVAjNpnrYWocj/Hbi36XmZ0H2aIKlol7A==",
+ "dev": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.1.2",
+ "@ethersproject/address": "^5.0.2",
+ "cbor": "^5.0.2",
+ "chalk": "^2.4.2",
+ "debug": "^4.1.1",
+ "fs-extra": "^7.0.1",
+ "lodash": "^4.17.11",
+ "semver": "^6.3.0",
+ "table": "^6.8.0",
+ "undici": "^5.4.0"
+ },
+ "peerDependencies": {
+ "hardhat": "^2.0.4"
+ }
+ },
+ "node_modules/@openzeppelin/contracts": {
+ "version": "4.9.0-rc.0",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.0-rc.0.tgz",
+ "integrity": "sha512-gD1V+kF1v8rPZ1CREstOTqKDeIsvvhlN4W+P0GKMHaM68hkmkumPjR1I35kg5pGhIBPockH51/tnOerklH+mGQ==",
+ "dev": true
+ },
+ "node_modules/@openzeppelin/merkle-tree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/merkle-tree/-/merkle-tree-1.0.5.tgz",
+ "integrity": "sha512-JkwG2ysdHeIphrScNxYagPy6jZeNONgDRyqU6lbFgE8HKCZFSkcP8r6AjZs+3HZk4uRNV0kNBBzuWhKQ3YV7Kw==",
+ "dependencies": {
+ "@ethersproject/abi": "^5.7.0",
+ "ethereum-cryptography": "^1.1.2"
+ }
+ },
+ "node_modules/@rari-capital/solmate": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/@rari-capital/solmate/-/solmate-6.4.0.tgz",
+ "integrity": "sha512-BXWIHHbG5Zbgrxi0qVYe0Zs+bfx+XgOciVUACjuIApV0KzC0kY8XdO1higusIei/ZKCC+GUKdcdQZflxYPUTKQ==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
+ },
+ "node_modules/@scure/base": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
+ "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ]
+ },
+ "node_modules/@scure/bip32": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz",
+ "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "@noble/hashes": "~1.1.1",
+ "@noble/secp256k1": "~1.6.0",
+ "@scure/base": "~1.1.0"
+ }
+ },
+ "node_modules/@scure/bip39": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
+ "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "@noble/hashes": "~1.1.1",
+ "@scure/base": "~1.1.0"
+ }
+ },
+ "node_modules/@sentry/core": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz",
+ "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/minimal": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/hub": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz",
+ "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/minimal": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz",
+ "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/node": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz",
+ "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/core": "5.30.0",
+ "@sentry/hub": "5.30.0",
+ "@sentry/tracing": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "cookie": "^0.4.1",
+ "https-proxy-agent": "^5.0.0",
+ "lru_map": "^0.3.3",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/tracing": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz",
+ "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/minimal": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/types": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
+ "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@sentry/utils": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
+ "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
+ "dev": true,
+ "dependencies": {
+ "@sentry/types": "5.30.0",
+ "tslib": "^1.9.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@solidity-parser/parser": {
+ "version": "0.14.3",
+ "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.3.tgz",
+ "integrity": "sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "antlr4ts": "^0.5.0-alpha.4"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@typechain/ethers-v5": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.1.0.tgz",
+ "integrity": "sha512-3LIb+eUpV3mNCrjUKT5oqp8PBsZYSnVrkfk6pY/ZM0boRs2mKxjFZ7bktx42vfDye8PPz3NxtW4DL5NsNsFqlg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "lodash": "^4.17.15",
+ "ts-essentials": "^7.0.1"
+ },
+ "peerDependencies": {
+ "@ethersproject/abi": "^5.0.0",
+ "@ethersproject/bytes": "^5.0.0",
+ "@ethersproject/providers": "^5.0.0",
+ "ethers": "^5.1.3",
+ "typechain": "^8.1.0",
+ "typescript": ">=4.3.0"
+ }
+ },
+ "node_modules/@typechain/hardhat": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.3.tgz",
+ "integrity": "sha512-e1H9MVl286ma0HuD9CBL248+pbdA7lWF6+I7FYwzykIrjilKhvLUv0Q7LtcyZztzgbP2g4Tyg1UPE+xy+qR7cA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fs-extra": "^9.1.0"
+ },
+ "peerDependencies": {
+ "@ethersproject/abi": "^5.4.7",
+ "@ethersproject/providers": "^5.4.7",
+ "@typechain/ethers-v5": "^10.1.0",
+ "ethers": "^5.4.7",
+ "hardhat": "^2.9.9",
+ "typechain": "^8.1.0"
+ }
+ },
+ "node_modules/@typechain/hardhat/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typechain/hardhat/node_modules/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@typechain/hardhat/node_modules/universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@types/async-eventemitter": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz",
+ "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==",
+ "dev": true
+ },
+ "node_modules/@types/bn.js": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz",
+ "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/chai": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
+ "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/chai-as-promised": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz",
+ "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/chai": "*"
+ }
+ },
+ "node_modules/@types/concat-stream": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
+ "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/form-data": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
+ "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==",
+ "dev": true
+ },
+ "node_modules/@types/minimatch": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/mocha": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
+ "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/node": {
+ "version": "18.11.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
+ "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
+ "dev": true
+ },
+ "node_modules/@types/pbkdf2": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
+ "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/@types/secp256k1": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz",
+ "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/abbrev": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
+ "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/abstract-level": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz",
+ "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^6.0.3",
+ "catering": "^2.1.0",
+ "is-buffer": "^2.0.5",
+ "level-supports": "^4.0.0",
+ "level-transcoder": "^1.0.1",
+ "module-error": "^1.0.1",
+ "queue-microtask": "^1.2.3"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+ "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/address": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz",
+ "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.0"
+ }
+ },
+ "node_modules/aes-js": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
+ "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.2"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/antlr4ts": {
+ "version": "0.5.0-alpha.4",
+ "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
+ "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/array-back": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+ "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array.prototype.reduce": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
+ "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.2",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/async-eventemitter": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz",
+ "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==",
+ "dev": true,
+ "dependencies": {
+ "async": "^2.4.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base-x": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+ "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/bcrypt-pbkdf/node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/bech32": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+ "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/bigint-crypto-utils": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.7.tgz",
+ "integrity": "sha512-zpCQpIE2Oy5WIQpjC9iYZf8Uh9QqoS51ZCooAcNvzv1AQ3VWdT52D0ksr1+/faeK8HVIej1bxXcP75YcqH3KPA==",
+ "dev": true,
+ "dependencies": {
+ "bigint-mod-arith": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10.4.0"
+ }
+ },
+ "node_modules/bigint-mod-arith": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz",
+ "integrity": "sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.4.0"
+ }
+ },
+ "node_modules/bignumber.js": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz",
+ "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/blakejs": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
+ "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==",
+ "dev": true
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
+ },
+ "node_modules/browser-level": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz",
+ "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==",
+ "dev": true,
+ "dependencies": {
+ "abstract-level": "^1.0.2",
+ "catering": "^2.1.1",
+ "module-error": "^1.0.2",
+ "run-parallel-limit": "^1.1.0"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dev": true,
+ "dependencies": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "node_modules/bs58check": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
+ "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
+ "dev": true,
+ "dependencies": {
+ "bs58": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+ "dev": true
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dev": true,
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/catering": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz",
+ "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cbor": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz",
+ "integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==",
+ "dev": true,
+ "dependencies": {
+ "bignumber.js": "^9.0.1",
+ "nofilter": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/chai": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+ "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chai-as-promised": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
+ "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "check-error": "^1.0.2"
+ },
+ "peerDependencies": {
+ "chai": ">= 2.1.2 < 5"
+ }
+ },
+ "node_modules/chai/node_modules/deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/classic-level": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz",
+ "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "abstract-level": "^1.0.2",
+ "catering": "^2.1.0",
+ "module-error": "^1.0.1",
+ "napi-macros": "~2.0.0",
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-table3": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
+ "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "object-assign": "^4.1.0",
+ "string-width": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "optionalDependencies": {
+ "colors": "^1.1.2"
+ }
+ },
+ "node_modules/cli-table3/node_modules/ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cli-table3/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cli-table3/node_modules/string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cli-table3/node_modules/strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "node_modules/colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/command-exists": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
+ "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
+ "dev": true
+ },
+ "node_modules/command-line-args": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz",
+ "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-back": "^3.1.0",
+ "find-replace": "^3.0.0",
+ "lodash.camelcase": "^4.3.0",
+ "typical": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/command-line-usage": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz",
+ "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-back": "^4.0.2",
+ "chalk": "^2.4.2",
+ "table-layout": "^1.0.2",
+ "typical": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/command-line-usage/node_modules/array-back": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+ "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/command-line-usage/node_modules/typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
+ "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "peer": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/concat-stream/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/concat-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/concat-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/death": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz",
+ "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.1.tgz",
+ "integrity": "sha512-rc6HkZswtl+KMi/IODZ8k7C/P37clC2Rf1HYI11GqdbgvggIyHjsU5MdjlTlaP6eu24c0sR3mcW2SqsVZ1sXUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/detect-port": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz",
+ "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "address": "^1.0.1",
+ "debug": "4"
+ },
+ "bin": {
+ "detect": "bin/detect-port.js",
+ "detect-port": "bin/detect-port.js"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/difflib": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz",
+ "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "heap": ">= 0.2.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-abstract/node_modules/object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
+ "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "esprima": "^2.7.1",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.2.0"
+ }
+ },
+ "node_modules/escodegen/node_modules/source-map": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+ "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "dependencies": {
+ "amdefine": ">=0.0.4"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eth-gas-reporter": {
+ "version": "0.2.25",
+ "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz",
+ "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.0.0-beta.146",
+ "@solidity-parser/parser": "^0.14.0",
+ "cli-table3": "^0.5.0",
+ "colors": "1.4.0",
+ "ethereum-cryptography": "^1.0.3",
+ "ethers": "^4.0.40",
+ "fs-readdir-recursive": "^1.1.0",
+ "lodash": "^4.17.14",
+ "markdown-table": "^1.1.3",
+ "mocha": "^7.1.1",
+ "req-cwd": "^2.0.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.5",
+ "sha1": "^1.1.1",
+ "sync-request": "^6.0.0"
+ },
+ "peerDependencies": {
+ "@codechecks/client": "^0.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@codechecks/client": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/chokidar": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.2.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.1.1"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/ethers": {
+ "version": "4.0.49",
+ "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz",
+ "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "aes-js": "3.0.0",
+ "bn.js": "^4.11.9",
+ "elliptic": "6.5.4",
+ "hash.js": "1.1.3",
+ "js-sha3": "0.5.7",
+ "scrypt-js": "2.0.4",
+ "setimmediate": "1.0.4",
+ "uuid": "2.0.1",
+ "xmlhttprequest": "1.8.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/flat": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
+ "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-buffer": "~2.0.3"
+ },
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "deprecated": "\"Please update to latest v2.3 or v2.2\"",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/hash.js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+ "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/js-sha3": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
+ "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "chalk": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/mocha": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
+ "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.0",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "3.0.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.5",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.6",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/readdirp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/scrypt-js": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz",
+ "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/setimmediate": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz",
+ "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/uuid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz",
+ "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/eth-gas-reporter/node_modules/yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "node_modules/eth-gas-reporter/node_modules/yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ethereum-bloom-filters": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz",
+ "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "js-sha3": "^0.8.0"
+ }
+ },
+ "node_modules/ethereum-cryptography": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz",
+ "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==",
+ "dependencies": {
+ "@noble/hashes": "1.1.2",
+ "@noble/secp256k1": "1.6.3",
+ "@scure/bip32": "1.1.0",
+ "@scure/bip39": "1.1.0"
+ }
+ },
+ "node_modules/ethereumjs-abi": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz",
+ "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
+ }
+ },
+ "node_modules/ethereumjs-abi/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/ethereumjs-util": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz",
+ "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==",
+ "dev": true,
+ "dependencies": {
+ "@types/bn.js": "^4.11.3",
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "elliptic": "^6.5.2",
+ "ethereum-cryptography": "^0.1.3",
+ "ethjs-util": "0.1.6",
+ "rlp": "^2.2.3"
+ }
+ },
+ "node_modules/ethereumjs-util/node_modules/@types/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/ethereumjs-util/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/ethers": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz",
+ "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+ },
+ {
+ "type": "individual",
+ "url": "https://www.buymeacoffee.com/ricmoo"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abi": "5.7.0",
+ "@ethersproject/abstract-provider": "5.7.0",
+ "@ethersproject/abstract-signer": "5.7.0",
+ "@ethersproject/address": "5.7.0",
+ "@ethersproject/base64": "5.7.0",
+ "@ethersproject/basex": "5.7.0",
+ "@ethersproject/bignumber": "5.7.0",
+ "@ethersproject/bytes": "5.7.0",
+ "@ethersproject/constants": "5.7.0",
+ "@ethersproject/contracts": "5.7.0",
+ "@ethersproject/hash": "5.7.0",
+ "@ethersproject/hdnode": "5.7.0",
+ "@ethersproject/json-wallets": "5.7.0",
+ "@ethersproject/keccak256": "5.7.0",
+ "@ethersproject/logger": "5.7.0",
+ "@ethersproject/networks": "5.7.1",
+ "@ethersproject/pbkdf2": "5.7.0",
+ "@ethersproject/properties": "5.7.0",
+ "@ethersproject/providers": "5.7.2",
+ "@ethersproject/random": "5.7.0",
+ "@ethersproject/rlp": "5.7.0",
+ "@ethersproject/sha2": "5.7.0",
+ "@ethersproject/signing-key": "5.7.0",
+ "@ethersproject/solidity": "5.7.0",
+ "@ethersproject/strings": "5.7.0",
+ "@ethersproject/transactions": "5.7.0",
+ "@ethersproject/units": "5.7.0",
+ "@ethersproject/wallet": "5.7.0",
+ "@ethersproject/web": "5.7.1",
+ "@ethersproject/wordlists": "5.7.0"
+ }
+ },
+ "node_modules/ethjs-unit": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz",
+ "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "bn.js": "4.11.6",
+ "number-to-bn": "1.7.0"
+ },
+ "engines": {
+ "node": ">=6.5.0",
+ "npm": ">=3"
+ }
+ },
+ "node_modules/ethjs-unit/node_modules/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/ethjs-util": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
+ "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
+ "dev": true,
+ "dependencies": {
+ "is-hex-prefixed": "1.0.0",
+ "strip-hex-prefix": "1.0.0"
+ },
+ "engines": {
+ "node": ">=6.5.0",
+ "npm": ">=3"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "peer": true
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-replace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
+ "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-back": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/fp-ts": {
+ "version": "1.19.3",
+ "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz",
+ "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==",
+ "dev": true
+ },
+ "node_modules/fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+ },
+ "node_modules/fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+ "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+ "dev": true
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-port": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
+ "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+ "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/ghost-testrpc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz",
+ "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "chalk": "^2.4.2",
+ "node-emoji": "^1.10.0"
+ },
+ "bin": {
+ "testrpc-sc": "index.js"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/global-modules": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "global-prefix": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/globby": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
+ "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/glob": "^7.1.1",
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.0.3",
+ "glob": "^7.1.3",
+ "ignore": "^5.1.1",
+ "merge2": "^1.2.3",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "node_modules/growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4.x"
+ }
+ },
+ "node_modules/handlebars": {
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "handlebars": "bin/handlebars"
+ },
+ "engines": {
+ "node": ">=0.4.7"
+ },
+ "optionalDependencies": {
+ "uglify-js": "^3.1.4"
+ }
+ },
+ "node_modules/har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "deprecated": "this library is no longer supported",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/hardhat": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.12.0.tgz",
+ "integrity": "sha512-mNJFbVG479HwOzxiaLxobyvED2M1aEAuPPYhEo1+88yicMDSTrU2JIS7vV+V0GSNQKaDoiHCmV6bcKjiljT/dQ==",
+ "dev": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.1.2",
+ "@metamask/eth-sig-util": "^4.0.0",
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-blockchain": "^6.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-evm": "^1.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-statemanager": "^1.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@nomicfoundation/ethereumjs-vm": "^6.0.0",
+ "@nomicfoundation/solidity-analyzer": "^0.1.0",
+ "@sentry/node": "^5.18.1",
+ "@types/bn.js": "^5.1.0",
+ "@types/lru-cache": "^5.1.0",
+ "abort-controller": "^3.0.0",
+ "adm-zip": "^0.4.16",
+ "aggregate-error": "^3.0.0",
+ "ansi-escapes": "^4.3.0",
+ "chalk": "^2.4.2",
+ "chokidar": "^3.4.0",
+ "ci-info": "^2.0.0",
+ "debug": "^4.1.1",
+ "enquirer": "^2.3.0",
+ "env-paths": "^2.2.0",
+ "ethereum-cryptography": "^1.0.3",
+ "ethereumjs-abi": "^0.6.8",
+ "find-up": "^2.1.0",
+ "fp-ts": "1.19.3",
+ "fs-extra": "^7.0.1",
+ "glob": "7.2.0",
+ "immutable": "^4.0.0-rc.12",
+ "io-ts": "1.10.4",
+ "keccak": "^3.0.2",
+ "lodash": "^4.17.11",
+ "mnemonist": "^0.38.0",
+ "mocha": "^10.0.0",
+ "p-map": "^4.0.0",
+ "qs": "^6.7.0",
+ "raw-body": "^2.4.1",
+ "resolve": "1.17.0",
+ "semver": "^6.3.0",
+ "solc": "0.7.3",
+ "source-map-support": "^0.5.13",
+ "stacktrace-parser": "^0.1.10",
+ "tsort": "0.0.1",
+ "undici": "^5.4.0",
+ "uuid": "^8.3.2",
+ "ws": "^7.4.6"
+ },
+ "bin": {
+ "hardhat": "internal/cli/cli.js"
+ },
+ "engines": {
+ "node": "^14.0.0 || ^16.0.0 || ^18.0.0"
+ },
+ "peerDependencies": {
+ "ts-node": "*",
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "ts-node": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/hardhat-gas-reporter": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz",
+ "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-uniq": "1.0.3",
+ "eth-gas-reporter": "^0.2.25",
+ "sha1": "^1.1.1"
+ },
+ "peerDependencies": {
+ "hardhat": "^2.0.2"
+ }
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/heap": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
+ "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/http-basic": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
+ "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "caseless": "^0.12.0",
+ "concat-stream": "^1.6.2",
+ "http-response-object": "^3.0.1",
+ "parse-cache-control": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-response-object": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
+ "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/node": "^10.0.3"
+ }
+ },
+ "node_modules/http-response-object/node_modules/@types/node": {
+ "version": "10.17.60",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
+ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.3.7"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/ignore": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+ "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+ "dev": true
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+ "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/io-ts": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz",
+ "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==",
+ "dev": true,
+ "dependencies": {
+ "fp-ts": "^1.0.0"
+ }
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-hex-prefixed": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
+ "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.5.0",
+ "npm": ">=3"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/js-sha3": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+ "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonschema": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
+ "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/keccak": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz",
+ "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
+ "dev": true,
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "node_modules/level": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz",
+ "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==",
+ "dev": true,
+ "dependencies": {
+ "browser-level": "^1.0.1",
+ "classic-level": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/level"
+ }
+ },
+ "node_modules/level-supports": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz",
+ "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/level-transcoder": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz",
+ "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==",
+ "dev": true,
+ "dependencies": {
+ "buffer": "^6.0.3",
+ "module-error": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
+ "dev": true
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/loupe": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+ "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-func-name": "^2.0.0"
+ }
+ },
+ "node_modules/lru_map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==",
+ "dev": true
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/markdown-table": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz",
+ "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/mcl-wasm": {
+ "version": "0.7.9",
+ "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz",
+ "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/memory-level": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz",
+ "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==",
+ "dev": true,
+ "dependencies": {
+ "abstract-level": "^1.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "module-error": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "peer": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mnemonist": {
+ "version": "0.38.5",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz",
+ "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==",
+ "dev": true,
+ "dependencies": {
+ "obliterator": "^2.0.0"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz",
+ "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.3",
+ "debug": "4.3.4",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.2.0",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "5.0.1",
+ "ms": "2.1.3",
+ "nanoid": "3.3.3",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "workerpool": "6.2.1",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha.js"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/mocha/node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mocha/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mocha/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mocha/node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/mocha/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/mocha/node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mocha/node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mocha/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/module-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz",
+ "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
+ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/napi-macros": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
+ "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==",
+ "dev": true
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/node-addon-api": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
+ "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==",
+ "dev": true
+ },
+ "node_modules/node-emoji": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
+ "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "lodash": "^4.17.21"
+ }
+ },
+ "node_modules/node-environment-flags": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
+ "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ }
+ },
+ "node_modules/node-environment-flags/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
+ "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
+ "dev": true,
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/nofilter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz",
+ "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/number-to-bn": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz",
+ "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "bn.js": "4.11.6",
+ "strip-hex-prefix": "1.0.0"
+ },
+ "engines": {
+ "node": ">=6.5.0",
+ "npm": ">=3"
+ }
+ },
+ "node_modules/number-to-bn/node_modules/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.getownpropertydescriptors": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
+ "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array.prototype.reduce": "^1.0.4",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obliterator": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
+ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==",
+ "dev": true
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ordinal": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz",
+ "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/parse-cache-control": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
+ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "dev": true,
+ "dependencies": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/promise": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz",
+ "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "asap": "~2.0.6"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dev": true,
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "resolve": "^1.1.6"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/recursive-readdir": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
+ "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimatch": "3.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/recursive-readdir/node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/reduce-flatten": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
+ "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+ "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/req-cwd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz",
+ "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "req-from": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/req-from": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz",
+ "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "resolve-from": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/request-promise-core": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "lodash": "^4.17.19"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "peerDependencies": {
+ "request": "^2.34"
+ }
+ },
+ "node_modules/request-promise-native": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+ "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "request-promise-core": "1.1.4",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=0.12.0"
+ },
+ "peerDependencies": {
+ "request": "^2.34"
+ }
+ },
+ "node_modules/request/node_modules/qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/request/node_modules/uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "uuid": "bin/uuid"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "dependencies": {
+ "path-parse": "^1.0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/rlp": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz",
+ "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^5.2.0"
+ },
+ "bin": {
+ "rlp": "bin/rlp"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "peer": true,
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/run-parallel-limit": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz",
+ "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rustbn.js": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz",
+ "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==",
+ "dev": true
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/sc-istanbul": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz",
+ "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.8.x",
+ "esprima": "2.7.x",
+ "glob": "^5.0.15",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "bin": {
+ "istanbul": "lib/cli.js"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/sc-istanbul/node_modules/glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/sc-istanbul/node_modules/resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/sc-istanbul/node_modules/supports-color": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+ "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/scrypt-js": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",
+ "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==",
+ "dev": true
+ },
+ "node_modules/secp256k1": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz",
+ "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "dependencies": {
+ "elliptic": "^6.5.4",
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
+ "node_modules/sha1": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
+ "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "charenc": ">= 0.0.1",
+ "crypt": ">= 0.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/shelljs": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+ "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "glob": "^7.0.0",
+ "interpret": "^1.0.0",
+ "rechoir": "^0.6.2"
+ },
+ "bin": {
+ "shjs": "bin/shjs"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/solc": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz",
+ "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==",
+ "dev": true,
+ "dependencies": {
+ "command-exists": "^1.2.8",
+ "commander": "3.0.2",
+ "follow-redirects": "^1.12.1",
+ "fs-extra": "^0.30.0",
+ "js-sha3": "0.8.0",
+ "memorystream": "^0.3.1",
+ "require-from-string": "^2.0.0",
+ "semver": "^5.5.0",
+ "tmp": "0.0.33"
+ },
+ "bin": {
+ "solcjs": "solcjs"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/solc/node_modules/fs-extra": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
+ "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0",
+ "path-is-absolute": "^1.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "node_modules/solc/node_modules/jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+ "dev": true,
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/solc/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/solidity-coverage": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz",
+ "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@ethersproject/abi": "^5.0.9",
+ "@solidity-parser/parser": "^0.14.1",
+ "chalk": "^2.4.2",
+ "death": "^1.1.0",
+ "detect-port": "^1.3.0",
+ "difflib": "^0.2.4",
+ "fs-extra": "^8.1.0",
+ "ghost-testrpc": "^0.0.2",
+ "global-modules": "^2.0.0",
+ "globby": "^10.0.1",
+ "jsonschema": "^1.2.4",
+ "lodash": "^4.17.15",
+ "mocha": "7.1.2",
+ "node-emoji": "^1.10.0",
+ "pify": "^4.0.1",
+ "recursive-readdir": "^2.2.2",
+ "sc-istanbul": "^0.4.5",
+ "semver": "^7.3.4",
+ "shelljs": "^0.8.3",
+ "web3-utils": "^1.3.6"
+ },
+ "bin": {
+ "solidity-coverage": "plugins/bin.js"
+ },
+ "peerDependencies": {
+ "hardhat": "^2.11.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/chokidar": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.2.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.1.1"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/solidity-coverage/node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/flat": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
+ "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-buffer": "~2.0.3"
+ },
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "deprecated": "\"Please update to latest v2.3 or v2.2\"",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "peer": true,
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "chalk": "^2.4.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "minimist": "^1.2.5"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/mocha": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz",
+ "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.0",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "3.0.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.5",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.6",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/solidity-coverage/node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/readdirp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/solidity-coverage/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/solidity-coverage/node_modules/yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "node_modules/solidity-coverage/node_modules/yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sshpk/node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/stacktrace-parser": {
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz",
+ "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.7.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/stacktrace-parser/node_modules/type-fest": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
+ "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-format": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
+ "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
+ "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
+ "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-hex-prefix": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
+ "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==",
+ "dev": true,
+ "dependencies": {
+ "is-hex-prefixed": "1.0.0"
+ },
+ "engines": {
+ "node": ">=6.5.0",
+ "npm": ">=3"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/sync-request": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
+ "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "http-response-object": "^3.0.1",
+ "sync-rpc": "^1.2.1",
+ "then-request": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/sync-rpc": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
+ "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "get-port": "^3.1.0"
+ }
+ },
+ "node_modules/table": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
+ "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^8.0.1",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/table-layout": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz",
+ "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "array-back": "^4.0.1",
+ "deep-extend": "~0.6.0",
+ "typical": "^5.2.0",
+ "wordwrapjs": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/table-layout/node_modules/array-back": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+ "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/table-layout/node_modules/typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/table/node_modules/ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/table/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/then-request": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
+ "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/concat-stream": "^1.6.0",
+ "@types/form-data": "0.0.33",
+ "@types/node": "^8.0.0",
+ "@types/qs": "^6.2.31",
+ "caseless": "~0.12.0",
+ "concat-stream": "^1.6.0",
+ "form-data": "^2.2.0",
+ "http-basic": "^8.1.1",
+ "http-response-object": "^3.0.1",
+ "promise": "^8.0.0",
+ "qs": "^6.4.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/then-request/node_modules/@types/node": {
+ "version": "8.10.66",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
+ "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/ts-command-line-args": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz",
+ "integrity": "sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "command-line-args": "^5.1.1",
+ "command-line-usage": "^6.1.0",
+ "string-format": "^2.0.0"
+ },
+ "bin": {
+ "write-markdown": "dist/write-markdown.js"
+ }
+ },
+ "node_modules/ts-command-line-args/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ts-command-line-args/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ts-command-line-args/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ts-command-line-args/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/ts-command-line-args/node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ts-command-line-args/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ts-essentials": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz",
+ "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==",
+ "dev": true,
+ "peer": true,
+ "peerDependencies": {
+ "typescript": ">=3.7.0"
+ }
+ },
+ "node_modules/ts-node": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ts-node/node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "node_modules/tsort": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz",
+ "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==",
+ "dev": true
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
+ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
+ "dev": true
+ },
+ "node_modules/tweetnacl-util": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
+ "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==",
+ "dev": true
+ },
+ "node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typechain": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.1.0.tgz",
+ "integrity": "sha512-5jToLgKTjHdI1VKqs/K8BLYy42Sr3o8bV5ojh4MnR9ExHO83cyyUdw+7+vMJCpKXUiVUvARM4qmHTFuyaCMAZQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/prettier": "^2.1.1",
+ "debug": "^4.3.1",
+ "fs-extra": "^7.0.0",
+ "glob": "7.1.7",
+ "js-sha3": "^0.8.0",
+ "lodash": "^4.17.15",
+ "mkdirp": "^1.0.4",
+ "prettier": "^2.3.1",
+ "ts-command-line-args": "^2.2.0",
+ "ts-essentials": "^7.0.1"
+ },
+ "bin": {
+ "typechain": "dist/cli/cli.js"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.3.0"
+ }
+ },
+ "node_modules/typechain/node_modules/glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/typechain/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "dev": true,
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/typical": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
+ "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/uglify-js": {
+ "version": "3.17.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz",
+ "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "uglifyjs": "bin/uglifyjs"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/undici": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.11.0.tgz",
+ "integrity": "sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==",
+ "dev": true,
+ "dependencies": {
+ "busboy": "^1.6.0"
+ },
+ "engines": {
+ "node": ">=12.18"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/utf8": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
+ "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "peer": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/web3-utils": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.0.tgz",
+ "integrity": "sha512-7nUIl7UWpLVka2f09CMbKOSEvorvHnaugIabU4mj7zfMvm0tSByLcEu3eyV9qgS11qxxLuOkzBIwCstTflhmpQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "bn.js": "^5.2.1",
+ "ethereum-bloom-filters": "^1.0.6",
+ "ethereumjs-util": "^7.1.0",
+ "ethjs-unit": "0.1.6",
+ "number-to-bn": "1.7.0",
+ "randombytes": "^2.1.0",
+ "utf8": "3.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/web3-utils/node_modules/ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "node_modules/web3-utils/node_modules/ethereumjs-util": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+ "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "@types/bn.js": "^5.1.0",
+ "bn.js": "^5.1.2",
+ "create-hash": "^1.1.2",
+ "ethereum-cryptography": "^0.1.3",
+ "rlp": "^2.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "node_modules/wide-align/node_modules/ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/wide-align/node_modules/strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "ansi-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true,
+ "peer": true
+ },
+ "node_modules/wordwrapjs": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz",
+ "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==",
+ "dev": true,
+ "peer": true,
+ "dependencies": {
+ "reduce-flatten": "^2.0.0",
+ "typical": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/wordwrapjs/node_modules/typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/workerpool": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xmlhttprequest": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+ "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ },
+ "dependencies": {
+ "@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ }
+ },
+ "@ethersproject/abi": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz",
+ "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==",
+ "requires": {
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "@ethersproject/abstract-provider": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz",
+ "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==",
+ "requires": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/networks": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/web": "^5.7.0"
+ }
+ },
+ "@ethersproject/abstract-signer": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz",
+ "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==",
+ "requires": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0"
+ }
+ },
+ "@ethersproject/address": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz",
+ "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==",
+ "requires": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0"
+ }
+ },
+ "@ethersproject/base64": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz",
+ "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0"
+ }
+ },
+ "@ethersproject/basex": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz",
+ "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0"
+ }
+ },
+ "@ethersproject/bignumber": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz",
+ "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "bn.js": "^5.2.1"
+ }
+ },
+ "@ethersproject/bytes": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz",
+ "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==",
+ "requires": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/constants": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz",
+ "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==",
+ "requires": {
+ "@ethersproject/bignumber": "^5.7.0"
+ }
+ },
+ "@ethersproject/contracts": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz",
+ "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abi": "^5.7.0",
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0"
+ }
+ },
+ "@ethersproject/hash": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz",
+ "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==",
+ "requires": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "@ethersproject/hdnode": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz",
+ "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/basex": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/pbkdf2": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/wordlists": "^5.7.0"
+ }
+ },
+ "@ethersproject/json-wallets": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz",
+ "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hdnode": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/pbkdf2": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "aes-js": "3.0.0",
+ "scrypt-js": "3.0.1"
+ }
+ },
+ "@ethersproject/keccak256": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz",
+ "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "js-sha3": "0.8.0"
+ }
+ },
+ "@ethersproject/logger": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz",
+ "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig=="
+ },
+ "@ethersproject/networks": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz",
+ "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==",
+ "requires": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/pbkdf2": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz",
+ "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0"
+ }
+ },
+ "@ethersproject/properties": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz",
+ "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==",
+ "requires": {
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/providers": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz",
+ "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/basex": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/networks": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/web": "^5.7.0",
+ "bech32": "1.1.4",
+ "ws": "7.4.6"
+ },
+ "dependencies": {
+ "ws": {
+ "version": "7.4.6",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+ "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+ "dev": true,
+ "peer": true,
+ "requires": {}
+ }
+ }
+ },
+ "@ethersproject/random": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz",
+ "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/rlp": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz",
+ "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/sha2": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz",
+ "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "hash.js": "1.1.7"
+ }
+ },
+ "@ethersproject/signing-key": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz",
+ "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "bn.js": "^5.2.1",
+ "elliptic": "6.5.4",
+ "hash.js": "1.1.7"
+ }
+ },
+ "@ethersproject/solidity": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz",
+ "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/sha2": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "@ethersproject/strings": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz",
+ "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==",
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/transactions": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz",
+ "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==",
+ "requires": {
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/rlp": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0"
+ }
+ },
+ "@ethersproject/units": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz",
+ "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/constants": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0"
+ }
+ },
+ "@ethersproject/wallet": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz",
+ "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abstract-provider": "^5.7.0",
+ "@ethersproject/abstract-signer": "^5.7.0",
+ "@ethersproject/address": "^5.7.0",
+ "@ethersproject/bignumber": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/hdnode": "^5.7.0",
+ "@ethersproject/json-wallets": "^5.7.0",
+ "@ethersproject/keccak256": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/random": "^5.7.0",
+ "@ethersproject/signing-key": "^5.7.0",
+ "@ethersproject/transactions": "^5.7.0",
+ "@ethersproject/wordlists": "^5.7.0"
+ }
+ },
+ "@ethersproject/web": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz",
+ "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==",
+ "requires": {
+ "@ethersproject/base64": "^5.7.0",
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "@ethersproject/wordlists": {
+ "version": "5.7.0",
+ "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz",
+ "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/bytes": "^5.7.0",
+ "@ethersproject/hash": "^5.7.0",
+ "@ethersproject/logger": "^5.7.0",
+ "@ethersproject/properties": "^5.7.0",
+ "@ethersproject/strings": "^5.7.0"
+ }
+ },
+ "@jridgewell/resolve-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+ "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+ "dev": true,
+ "peer": true
+ },
+ "@jridgewell/sourcemap-codec": {
+ "version": "1.4.14",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+ "dev": true,
+ "peer": true
+ },
+ "@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "@metamask/eth-sig-util": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz",
+ "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==",
+ "dev": true,
+ "requires": {
+ "ethereumjs-abi": "^0.6.8",
+ "ethereumjs-util": "^6.2.1",
+ "ethjs-util": "^0.1.6",
+ "tweetnacl": "^1.0.3",
+ "tweetnacl-util": "^0.15.1"
+ }
+ },
+ "@noble/hashes": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz",
+ "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA=="
+ },
+ "@noble/secp256k1": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz",
+ "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ=="
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "peer": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@nomicfoundation/ethereumjs-block": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-4.0.0.tgz",
+ "integrity": "sha512-bk8uP8VuexLgyIZAHExH1QEovqx0Lzhc9Ntm63nCRKLHXIZkobaFaeCVwTESV7YkPKUk7NiK11s8ryed4CS9yA==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-blockchain": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-6.0.0.tgz",
+ "integrity": "sha512-pLFEoea6MWd81QQYSReLlLfH7N9v7lH66JC/NMPN848ySPPQA5renWnE7wPByfQFzNrPBuDDRFFULMDmj1C0xw==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-ethash": "^2.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "abstract-level": "^1.0.3",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "level": "^8.0.0",
+ "lru-cache": "^5.1.1",
+ "memory-level": "^1.0.0"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-common": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-3.0.0.tgz",
+ "integrity": "sha512-WS7qSshQfxoZOpHG/XqlHEGRG1zmyjYrvmATvc4c62+gZXgre1ymYP8ZNgx/3FyZY0TWe9OjFlKOfLqmgOeYwA==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "crc-32": "^1.2.0"
+ }
+ },
+ "@nomicfoundation/ethereumjs-ethash": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-2.0.0.tgz",
+ "integrity": "sha512-WpDvnRncfDUuXdsAXlI4lXbqUDOA+adYRQaEezIkxqDkc+LDyYDbd/xairmY98GnQzo1zIqsIL6GB5MoMSJDew==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "abstract-level": "^1.0.3",
+ "bigint-crypto-utils": "^3.0.23",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-evm": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-1.0.0.tgz",
+ "integrity": "sha512-hVS6qRo3V1PLKCO210UfcEQHvlG7GqR8iFzp0yyjTg2TmJQizcChKgWo8KFsdMw6AyoLgLhHGHw4HdlP8a4i+Q==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@types/async-eventemitter": "^0.2.1",
+ "async-eventemitter": "^0.2.4",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "mcl-wasm": "^0.7.1",
+ "rustbn.js": "~0.2.0"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-rlp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-4.0.0.tgz",
+ "integrity": "sha512-GaSOGk5QbUk4eBP5qFbpXoZoZUj/NrW7MRa0tKY4Ew4c2HAS0GXArEMAamtFrkazp0BO4K5p2ZCG3b2FmbShmw==",
+ "dev": true
+ },
+ "@nomicfoundation/ethereumjs-statemanager": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-1.0.0.tgz",
+ "integrity": "sha512-jCtqFjcd2QejtuAMjQzbil/4NHf5aAWxUc+CvS0JclQpl+7M0bxMofR2AJdtz+P3u0ke2euhYREDiE7iSO31vQ==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "functional-red-black-tree": "^1.0.1"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-trie": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-5.0.0.tgz",
+ "integrity": "sha512-LIj5XdE+s+t6WSuq/ttegJzZ1vliwg6wlb+Y9f4RlBpuK35B9K02bO7xU+E6Rgg9RGptkWd6TVLdedTI4eNc2A==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3",
+ "readable-stream": "^3.6.0"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-tx": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-4.0.0.tgz",
+ "integrity": "sha512-Gg3Lir2lNUck43Kp/3x6TfBNwcWC9Z1wYue9Nz3v4xjdcv6oDW9QSMJxqsKw9QEGoBBZ+gqwpW7+F05/rs/g1w==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-util": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-8.0.0.tgz",
+ "integrity": "sha512-2emi0NJ/HmTG+CGY58fa+DQuAoroFeSH9gKu9O6JnwTtlzJtgfTixuoOqLEgyyzZVvwfIpRueuePb8TonL1y+A==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0-beta.2",
+ "ethereum-cryptography": "0.1.3"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/ethereumjs-vm": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-6.0.0.tgz",
+ "integrity": "sha512-JMPxvPQ3fzD063Sg3Tp+UdwUkVxMoo1uML6KSzFhMH3hoQi/LMuXBoEHAoW83/vyNS9BxEe6jm6LmT5xdeEJ6w==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-blockchain": "^6.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-evm": "^1.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-statemanager": "^1.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@types/async-eventemitter": "^0.2.1",
+ "async-eventemitter": "^0.2.4",
+ "debug": "^4.3.3",
+ "ethereum-cryptography": "0.1.3",
+ "functional-red-black-tree": "^1.0.1",
+ "mcl-wasm": "^0.7.1",
+ "rustbn.js": "~0.2.0"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/hardhat-chai-matchers": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-1.0.4.tgz",
+ "integrity": "sha512-n/5UMwGaUK2zM8ALuMChVwB1lEPeDTb5oBjQ1g7hVsUdS8x+XG9JIEp4Ze6Bwy98tghA7Y1+PCH4SNE2P3UQ2g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abi": "^5.1.2",
+ "@types/chai-as-promised": "^7.1.3",
+ "chai-as-promised": "^7.1.1",
+ "chalk": "^2.4.2",
+ "deep-eql": "^4.0.1",
+ "ordinal": "^1.0.3"
+ }
+ },
+ "@nomicfoundation/hardhat-network-helpers": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.6.tgz",
+ "integrity": "sha512-a35iVD4ycF6AoTfllAnKm96IPIzzHpgKX/ep4oKc2bsUKFfMlacWdyntgC/7d5blyCTXfFssgNAvXDZfzNWVGQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ethereumjs-util": "^7.1.4"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "ethereumjs-util": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+ "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/bn.js": "^5.1.0",
+ "bn.js": "^5.1.2",
+ "create-hash": "^1.1.2",
+ "ethereum-cryptography": "^0.1.3",
+ "rlp": "^2.2.4"
+ }
+ }
+ }
+ },
+ "@nomicfoundation/hardhat-toolbox": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-2.0.0.tgz",
+ "integrity": "sha512-BoOPbzLQ1GArnBZd4Jz4IU8FY3RY4nUwpXlfymXwxlXNimngkPRJj7ivVNurD7igohEjf90v/Axn2M5WwAdCJQ==",
+ "dev": true,
+ "requires": {}
+ },
+ "@nomicfoundation/solidity-analyzer": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.0.tgz",
+ "integrity": "sha512-xGWAiVCGOycvGiP/qrlf9f9eOn7fpNbyJygcB0P21a1MDuVPlKt0Srp7rvtBEutYQ48ouYnRXm33zlRnlTOPHg==",
+ "dev": true,
+ "requires": {
+ "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-freebsd-x64": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": "0.1.0",
+ "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.0"
+ }
+ },
+ "@nomicfoundation/solidity-analyzer-darwin-arm64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.0.tgz",
+ "integrity": "sha512-vEF3yKuuzfMHsZecHQcnkUrqm8mnTWfJeEVFHpg+cO+le96xQA4lAJYdUan8pXZohQxv1fSReQsn4QGNuBNuCw==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-darwin-x64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.0.tgz",
+ "integrity": "sha512-dlHeIg0pTL4dB1l9JDwbi/JG6dHQaU1xpDK+ugYO8eJ1kxx9Dh2isEUtA4d02cQAl22cjOHTvifAk96A+ItEHA==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-freebsd-x64": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.0.tgz",
+ "integrity": "sha512-WFCZYMv86WowDA4GiJKnebMQRt3kCcFqHeIomW6NMyqiKqhK1kIZCxSLDYsxqlx396kKLPN1713Q1S8tu68GKg==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.0.tgz",
+ "integrity": "sha512-DTw6MNQWWlCgc71Pq7CEhEqkb7fZnS7oly13pujs4cMH1sR0JzNk90Mp1zpSCsCs4oKan2ClhMlLKtNat/XRKQ==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-linux-arm64-musl": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.0.tgz",
+ "integrity": "sha512-wUpUnR/3GV5Da88MhrxXh/lhb9kxh9V3Jya2NpBEhKDIRCDmtXMSqPMXHZmOR9DfCwCvG6vLFPr/+YrPCnUN0w==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-linux-x64-gnu": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.0.tgz",
+ "integrity": "sha512-lR0AxK1x/MeKQ/3Pt923kPvwigmGX3OxeU5qNtQ9pj9iucgk4PzhbS3ruUeSpYhUxG50jN4RkIGwUMoev5lguw==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-linux-x64-musl": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.0.tgz",
+ "integrity": "sha512-A1he/8gy/JeBD3FKvmI6WUJrGrI5uWJNr5Xb9WdV+DK0F8msuOqpEByLlnTdLkXMwW7nSl3awvLezOs9xBHJEg==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.0.tgz",
+ "integrity": "sha512-7x5SXZ9R9H4SluJZZP8XPN+ju7Mx+XeUMWZw7ZAqkdhP5mK19I4vz3x0zIWygmfE8RT7uQ5xMap0/9NPsO+ykw==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.0.tgz",
+ "integrity": "sha512-m7w3xf+hnE774YRXu+2mGV7RiF3QJtUoiYU61FascCkQhX3QMQavh7saH/vzb2jN5D24nT/jwvaHYX/MAM9zUw==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomicfoundation/solidity-analyzer-win32-x64-msvc": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.0.tgz",
+ "integrity": "sha512-xCuybjY0sLJQnJhupiFAXaek2EqF0AP0eBjgzaalPXSNvCEN6ZYHvUzdA50ENDVeSYFXcUsYf3+FsD3XKaeptA==",
+ "dev": true,
+ "optional": true
+ },
+ "@nomiclabs/hardhat-ethers": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.2.0.tgz",
+ "integrity": "sha512-kKCW7xawuD/lw69Yr1yqUUrF0IKmnLNGf+pTVbJ/ctHaRcPrwKI0EPkO1RNXBHlOOZkv6v4DK2PPvq0lL2ykig==",
+ "dev": true,
+ "peer": true,
+ "requires": {}
+ },
+ "@nomiclabs/hardhat-etherscan": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.1.tgz",
+ "integrity": "sha512-a6+fJlHTiPjzUYnvwkcduJN0rAKWagQsQNoHJP/9mJ1CZjIkGysGtvVAjNpnrYWocj/Hbi36XmZ0H2aIKlol7A==",
+ "dev": true,
+ "requires": {
+ "@ethersproject/abi": "^5.1.2",
+ "@ethersproject/address": "^5.0.2",
+ "cbor": "^5.0.2",
+ "chalk": "^2.4.2",
+ "debug": "^4.1.1",
+ "fs-extra": "^7.0.1",
+ "lodash": "^4.17.11",
+ "semver": "^6.3.0",
+ "table": "^6.8.0",
+ "undici": "^5.4.0"
+ }
+ },
+ "@openzeppelin/contracts": {
+ "version": "4.9.0-rc.0",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.0-rc.0.tgz",
+ "integrity": "sha512-gD1V+kF1v8rPZ1CREstOTqKDeIsvvhlN4W+P0GKMHaM68hkmkumPjR1I35kg5pGhIBPockH51/tnOerklH+mGQ==",
+ "dev": true
+ },
+ "@openzeppelin/merkle-tree": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@openzeppelin/merkle-tree/-/merkle-tree-1.0.5.tgz",
+ "integrity": "sha512-JkwG2ysdHeIphrScNxYagPy6jZeNONgDRyqU6lbFgE8HKCZFSkcP8r6AjZs+3HZk4uRNV0kNBBzuWhKQ3YV7Kw==",
+ "requires": {
+ "@ethersproject/abi": "^5.7.0",
+ "ethereum-cryptography": "^1.1.2"
+ }
+ },
+ "@rari-capital/solmate": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/@rari-capital/solmate/-/solmate-6.4.0.tgz",
+ "integrity": "sha512-BXWIHHbG5Zbgrxi0qVYe0Zs+bfx+XgOciVUACjuIApV0KzC0kY8XdO1higusIei/ZKCC+GUKdcdQZflxYPUTKQ=="
+ },
+ "@scure/base": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
+ "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA=="
+ },
+ "@scure/bip32": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz",
+ "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==",
+ "requires": {
+ "@noble/hashes": "~1.1.1",
+ "@noble/secp256k1": "~1.6.0",
+ "@scure/base": "~1.1.0"
+ }
+ },
+ "@scure/bip39": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
+ "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
+ "requires": {
+ "@noble/hashes": "~1.1.1",
+ "@scure/base": "~1.1.0"
+ }
+ },
+ "@sentry/core": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz",
+ "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==",
+ "dev": true,
+ "requires": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/minimal": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/hub": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz",
+ "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==",
+ "dev": true,
+ "requires": {
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/minimal": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz",
+ "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==",
+ "dev": true,
+ "requires": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/node": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz",
+ "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==",
+ "dev": true,
+ "requires": {
+ "@sentry/core": "5.30.0",
+ "@sentry/hub": "5.30.0",
+ "@sentry/tracing": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "cookie": "^0.4.1",
+ "https-proxy-agent": "^5.0.0",
+ "lru_map": "^0.3.3",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/tracing": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz",
+ "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==",
+ "dev": true,
+ "requires": {
+ "@sentry/hub": "5.30.0",
+ "@sentry/minimal": "5.30.0",
+ "@sentry/types": "5.30.0",
+ "@sentry/utils": "5.30.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@sentry/types": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz",
+ "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==",
+ "dev": true
+ },
+ "@sentry/utils": {
+ "version": "5.30.0",
+ "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz",
+ "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==",
+ "dev": true,
+ "requires": {
+ "@sentry/types": "5.30.0",
+ "tslib": "^1.9.3"
+ }
+ },
+ "@solidity-parser/parser": {
+ "version": "0.14.3",
+ "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.3.tgz",
+ "integrity": "sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "antlr4ts": "^0.5.0-alpha.4"
+ }
+ },
+ "@tsconfig/node10": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
+ "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==",
+ "dev": true,
+ "peer": true
+ },
+ "@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "peer": true
+ },
+ "@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "peer": true
+ },
+ "@tsconfig/node16": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz",
+ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
+ "dev": true,
+ "peer": true
+ },
+ "@typechain/ethers-v5": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.1.0.tgz",
+ "integrity": "sha512-3LIb+eUpV3mNCrjUKT5oqp8PBsZYSnVrkfk6pY/ZM0boRs2mKxjFZ7bktx42vfDye8PPz3NxtW4DL5NsNsFqlg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "lodash": "^4.17.15",
+ "ts-essentials": "^7.0.1"
+ }
+ },
+ "@typechain/hardhat": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.3.tgz",
+ "integrity": "sha512-e1H9MVl286ma0HuD9CBL248+pbdA7lWF6+I7FYwzykIrjilKhvLUv0Q7LtcyZztzgbP2g4Tyg1UPE+xy+qR7cA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fs-extra": "^9.1.0"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ }
+ },
+ "jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "graceful-fs": "^4.1.6",
+ "universalify": "^2.0.0"
+ }
+ },
+ "universalify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
+ "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "@types/async-eventemitter": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@types/async-eventemitter/-/async-eventemitter-0.2.1.tgz",
+ "integrity": "sha512-M2P4Ng26QbAeITiH7w1d7OxtldgfAe0wobpyJzVK/XOb0cUGKU2R4pfAhqcJBXAe2ife5ZOhSv4wk7p+ffURtg==",
+ "dev": true
+ },
+ "@types/bn.js": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz",
+ "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/chai": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
+ "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
+ "dev": true,
+ "peer": true
+ },
+ "@types/chai-as-promised": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.5.tgz",
+ "integrity": "sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/chai": "*"
+ }
+ },
+ "@types/concat-stream": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
+ "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/form-data": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
+ "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==",
+ "dev": true
+ },
+ "@types/minimatch": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
+ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
+ "dev": true,
+ "peer": true
+ },
+ "@types/mocha": {
+ "version": "9.1.1",
+ "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
+ "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==",
+ "dev": true,
+ "peer": true
+ },
+ "@types/node": {
+ "version": "18.11.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.3.tgz",
+ "integrity": "sha512-fNjDQzzOsZeKZu5NATgXUPsaFaTxeRgFXoosrHivTl8RGeV733OLawXsGfEk9a8/tySyZUyiZ6E8LcjPFZ2y1A==",
+ "dev": true
+ },
+ "@types/pbkdf2": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz",
+ "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "@types/prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
+ "dev": true,
+ "peer": true
+ },
+ "@types/qs": {
+ "version": "6.9.7",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
+ "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
+ "dev": true,
+ "peer": true
+ },
+ "@types/secp256k1": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz",
+ "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "abbrev": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz",
+ "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==",
+ "dev": true,
+ "peer": true
+ },
+ "abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "requires": {
+ "event-target-shim": "^5.0.0"
+ }
+ },
+ "abstract-level": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz",
+ "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==",
+ "dev": true,
+ "requires": {
+ "buffer": "^6.0.3",
+ "catering": "^2.1.0",
+ "is-buffer": "^2.0.5",
+ "level-supports": "^4.0.0",
+ "level-transcoder": "^1.0.1",
+ "module-error": "^1.0.1",
+ "queue-microtask": "^1.2.3"
+ }
+ },
+ "acorn": {
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+ "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
+ "dev": true,
+ "peer": true
+ },
+ "acorn-walk": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
+ "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
+ "dev": true,
+ "peer": true
+ },
+ "address": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/address/-/address-1.2.1.tgz",
+ "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==",
+ "dev": true,
+ "peer": true
+ },
+ "adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true
+ },
+ "aes-js": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
+ "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==",
+ "dev": true,
+ "peer": true
+ },
+ "agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "requires": {
+ "debug": "4"
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "amdefine": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "ansi-colors": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.21.3"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "antlr4ts": {
+ "version": "0.5.0-alpha.4",
+ "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz",
+ "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==",
+ "dev": true,
+ "peer": true
+ },
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "peer": true
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "array-back": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
+ "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==",
+ "dev": true,
+ "peer": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true,
+ "peer": true
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==",
+ "dev": true,
+ "peer": true
+ },
+ "array.prototype.reduce": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz",
+ "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.2",
+ "es-array-method-boxes-properly": "^1.0.0",
+ "is-string": "^1.0.7"
+ }
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "dev": true,
+ "peer": true
+ },
+ "asn1": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
+ "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "peer": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "peer": true
+ },
+ "astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.4",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
+ "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "async-eventemitter": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz",
+ "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==",
+ "dev": true,
+ "requires": {
+ "async": "^2.4.0"
+ }
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "peer": true
+ },
+ "at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "peer": true
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
+ "dev": true,
+ "peer": true
+ },
+ "aws4": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+ "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+ "dev": true,
+ "peer": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "base-x": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+ "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ },
+ "dependencies": {
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "bech32": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+ "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+ "dev": true,
+ "peer": true
+ },
+ "bigint-crypto-utils": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.1.7.tgz",
+ "integrity": "sha512-zpCQpIE2Oy5WIQpjC9iYZf8Uh9QqoS51ZCooAcNvzv1AQ3VWdT52D0ksr1+/faeK8HVIej1bxXcP75YcqH3KPA==",
+ "dev": true,
+ "requires": {
+ "bigint-mod-arith": "^3.1.0"
+ }
+ },
+ "bigint-mod-arith": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bigint-mod-arith/-/bigint-mod-arith-3.1.2.tgz",
+ "integrity": "sha512-nx8J8bBeiRR+NlsROFH9jHswW5HO8mgfOSqW0AmjicMMvaONDa8AO+5ViKDUUNytBPWiwfvZP4/Bj4Y3lUfvgQ==",
+ "dev": true
+ },
+ "bignumber.js": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.0.tgz",
+ "integrity": "sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "blakejs": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
+ "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==",
+ "dev": true
+ },
+ "bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w=="
+ },
+ "browser-level": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz",
+ "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==",
+ "dev": true,
+ "requires": {
+ "abstract-level": "^1.0.2",
+ "catering": "^2.1.1",
+ "module-error": "^1.0.2",
+ "run-parallel-limit": "^1.1.0"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "bs58": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+ "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+ "dev": true,
+ "requires": {
+ "base-x": "^3.0.2"
+ }
+ },
+ "bs58check": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
+ "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
+ "dev": true,
+ "requires": {
+ "bs58": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+ "dev": true
+ },
+ "busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dev": true,
+ "requires": {
+ "streamsearch": "^1.1.0"
+ }
+ },
+ "bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true
+ },
+ "call-bind": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+ "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ }
+ },
+ "camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
+ "dev": true,
+ "peer": true
+ },
+ "catering": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz",
+ "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==",
+ "dev": true
+ },
+ "cbor": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/cbor/-/cbor-5.2.0.tgz",
+ "integrity": "sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A==",
+ "dev": true,
+ "requires": {
+ "bignumber.js": "^9.0.1",
+ "nofilter": "^1.0.4"
+ }
+ },
+ "chai": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+ "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ },
+ "dependencies": {
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ }
+ }
+ },
+ "chai-as-promised": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz",
+ "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "check-error": "^1.0.2"
+ }
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "charenc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
+ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+ "dev": true,
+ "peer": true
+ },
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
+ "dev": true,
+ "peer": true
+ },
+ "chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "ci-info": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
+ "dev": true
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "classic-level": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.2.0.tgz",
+ "integrity": "sha512-qw5B31ANxSluWz9xBzklRWTUAJ1SXIdaVKTVS7HcTGKOAmExx65Wo5BUICW+YGORe2FOUaDghoI9ZDxj82QcFg==",
+ "dev": true,
+ "requires": {
+ "abstract-level": "^1.0.2",
+ "catering": "^2.1.0",
+ "module-error": "^1.0.1",
+ "napi-macros": "~2.0.0",
+ "node-gyp-build": "^4.3.0"
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "cli-table3": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
+ "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "colors": "^1.1.2",
+ "object-assign": "^4.1.0",
+ "string-width": "^2.1.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "peer": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true,
+ "peer": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "command-exists": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
+ "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
+ "dev": true
+ },
+ "command-line-args": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz",
+ "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-back": "^3.1.0",
+ "find-replace": "^3.0.0",
+ "lodash.camelcase": "^4.3.0",
+ "typical": "^4.0.0"
+ }
+ },
+ "command-line-usage": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz",
+ "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-back": "^4.0.2",
+ "chalk": "^2.4.2",
+ "table-layout": "^1.0.2",
+ "typical": "^5.2.0"
+ },
+ "dependencies": {
+ "array-back": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+ "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+ "dev": true,
+ "peer": true
+ },
+ "typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "commander": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz",
+ "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "peer": true
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ }
+ }
+ },
+ "cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true,
+ "peer": true
+ },
+ "crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "peer": true
+ },
+ "crypt": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
+ "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+ "dev": true,
+ "peer": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "death": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz",
+ "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==",
+ "dev": true,
+ "peer": true
+ },
+ "debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.1.tgz",
+ "integrity": "sha512-rc6HkZswtl+KMi/IODZ8k7C/P37clC2Rf1HYI11GqdbgvggIyHjsU5MdjlTlaP6eu24c0sR3mcW2SqsVZ1sXUw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "peer": true
+ },
+ "deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "peer": true
+ },
+ "define-properties": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
+ "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "peer": true
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
+ "detect-port": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz",
+ "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "address": "^1.0.1",
+ "debug": "4"
+ }
+ },
+ "diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true
+ },
+ "difflib": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz",
+ "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "heap": ">= 0.2.0"
+ }
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dotenv": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
+ "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
+ "dev": true
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "requires": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ }
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "enquirer": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz",
+ "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^4.1.1"
+ }
+ },
+ "env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true
+ },
+ "es-abstract": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ },
+ "dependencies": {
+ "object.assign": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
+ "object-keys": "^1.1.1"
+ }
+ }
+ }
+ },
+ "es-array-method-boxes-properly": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz",
+ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==",
+ "dev": true,
+ "peer": true
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
+ "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "esprima": "^2.7.1",
+ "estraverse": "^1.9.1",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.2.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+ "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==",
+ "dev": true,
+ "optional": true,
+ "peer": true,
+ "requires": {
+ "amdefine": ">=0.0.4"
+ }
+ }
+ }
+ },
+ "esprima": {
+ "version": "2.7.3",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==",
+ "dev": true,
+ "peer": true
+ },
+ "estraverse": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==",
+ "dev": true,
+ "peer": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "peer": true
+ },
+ "eth-gas-reporter": {
+ "version": "0.2.25",
+ "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.25.tgz",
+ "integrity": "sha512-1fRgyE4xUB8SoqLgN3eDfpDfwEfRxh2Sz1b7wzFbyQA+9TekMmvSjjoRu9SKcSVyK+vLkLIsVbJDsTWjw195OQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abi": "^5.0.0-beta.146",
+ "@solidity-parser/parser": "^0.14.0",
+ "cli-table3": "^0.5.0",
+ "colors": "1.4.0",
+ "ethereum-cryptography": "^1.0.3",
+ "ethers": "^4.0.40",
+ "fs-readdir-recursive": "^1.1.0",
+ "lodash": "^4.17.14",
+ "markdown-table": "^1.1.3",
+ "mocha": "^7.1.1",
+ "req-cwd": "^2.0.0",
+ "request": "^2.88.0",
+ "request-promise-native": "^1.0.5",
+ "sha1": "^1.1.1",
+ "sync-request": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true,
+ "peer": true
+ },
+ "ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "peer": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true,
+ "peer": true
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "peer": true
+ },
+ "chokidar": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.1",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.2.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "peer": true
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true,
+ "peer": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true,
+ "peer": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true
+ },
+ "ethers": {
+ "version": "4.0.49",
+ "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.49.tgz",
+ "integrity": "sha512-kPltTvWiyu+OktYy1IStSO16i2e7cS9D9OxZ81q2UUaiNPVrm/RTcbxamCXF9VUSKzJIdJV68EAIhTEVBalRWg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "aes-js": "3.0.0",
+ "bn.js": "^4.11.9",
+ "elliptic": "6.5.4",
+ "hash.js": "1.1.3",
+ "js-sha3": "0.5.7",
+ "scrypt-js": "2.0.4",
+ "setimmediate": "1.0.4",
+ "uuid": "2.0.1",
+ "xmlhttprequest": "1.8.0"
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
+ "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz",
+ "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true
+ },
+ "js-sha3": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz",
+ "integrity": "sha512-GII20kjaPX0zJ8wzkTbNDYMY7msuZcTWk8S5UOh6806Jq/wz1J8/bnr8uGU0DAUmYDjj2Mr4X1cW8v/GLYnR+g==",
+ "dev": true,
+ "peer": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "chalk": "^2.4.2"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "mocha": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz",
+ "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.0",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "3.0.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.5",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.6",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true,
+ "peer": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "peer": true
+ },
+ "readdirp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "picomatch": "^2.0.4"
+ }
+ },
+ "scrypt-js": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz",
+ "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==",
+ "dev": true,
+ "peer": true
+ },
+ "setimmediate": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz",
+ "integrity": "sha512-/TjEmXQVEzdod/FFskf3o7oOAsGhHf2j1dZqRFbDzq4F3mvvxflIIi4Hd3bLQE9y/CpwqfSQam5JakI/mi3Pog==",
+ "dev": true,
+ "peer": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "peer": true
+ },
+ "supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "uuid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz",
+ "integrity": "sha512-nWg9+Oa3qD2CQzHIP4qKUqwNfzKn8P0LtFhotaCTFchsV7ZfDhAybeip/HZVeMIpZi9JgY1E3nUlwaCmZT1sEg==",
+ "dev": true,
+ "peer": true
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "peer": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ }
+ }
+ }
+ },
+ "ethereum-bloom-filters": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz",
+ "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "js-sha3": "^0.8.0"
+ }
+ },
+ "ethereum-cryptography": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz",
+ "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==",
+ "requires": {
+ "@noble/hashes": "1.1.2",
+ "@noble/secp256k1": "1.6.3",
+ "@scure/bip32": "1.1.0",
+ "@scure/bip39": "1.1.0"
+ }
+ },
+ "ethereumjs-abi": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz",
+ "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.11.8",
+ "ethereumjs-util": "^6.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ }
+ }
+ },
+ "ethereumjs-util": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz",
+ "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==",
+ "dev": true,
+ "requires": {
+ "@types/bn.js": "^4.11.3",
+ "bn.js": "^4.11.0",
+ "create-hash": "^1.1.2",
+ "elliptic": "^6.5.2",
+ "ethereum-cryptography": "^0.1.3",
+ "ethjs-util": "0.1.6",
+ "rlp": "^2.2.3"
+ },
+ "dependencies": {
+ "@types/bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*"
+ }
+ },
+ "bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ }
+ }
+ },
+ "ethers": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz",
+ "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abi": "5.7.0",
+ "@ethersproject/abstract-provider": "5.7.0",
+ "@ethersproject/abstract-signer": "5.7.0",
+ "@ethersproject/address": "5.7.0",
+ "@ethersproject/base64": "5.7.0",
+ "@ethersproject/basex": "5.7.0",
+ "@ethersproject/bignumber": "5.7.0",
+ "@ethersproject/bytes": "5.7.0",
+ "@ethersproject/constants": "5.7.0",
+ "@ethersproject/contracts": "5.7.0",
+ "@ethersproject/hash": "5.7.0",
+ "@ethersproject/hdnode": "5.7.0",
+ "@ethersproject/json-wallets": "5.7.0",
+ "@ethersproject/keccak256": "5.7.0",
+ "@ethersproject/logger": "5.7.0",
+ "@ethersproject/networks": "5.7.1",
+ "@ethersproject/pbkdf2": "5.7.0",
+ "@ethersproject/properties": "5.7.0",
+ "@ethersproject/providers": "5.7.2",
+ "@ethersproject/random": "5.7.0",
+ "@ethersproject/rlp": "5.7.0",
+ "@ethersproject/sha2": "5.7.0",
+ "@ethersproject/signing-key": "5.7.0",
+ "@ethersproject/solidity": "5.7.0",
+ "@ethersproject/strings": "5.7.0",
+ "@ethersproject/transactions": "5.7.0",
+ "@ethersproject/units": "5.7.0",
+ "@ethersproject/wallet": "5.7.0",
+ "@ethersproject/web": "5.7.1",
+ "@ethersproject/wordlists": "5.7.0"
+ }
+ },
+ "ethjs-unit": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz",
+ "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "bn.js": "4.11.6",
+ "number-to-bn": "1.7.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "ethjs-util": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
+ "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
+ "dev": true,
+ "requires": {
+ "is-hex-prefixed": "1.0.0",
+ "strip-hex-prefix": "1.0.0"
+ }
+ },
+ "event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "peer": true
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
+ "dev": true,
+ "peer": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "peer": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "peer": true
+ },
+ "fastq": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz",
+ "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-replace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz",
+ "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-back": "^3.0.1"
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true
+ },
+ "follow-redirects": {
+ "version": "1.15.2",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+ "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
+ "dev": true,
+ "peer": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "fp-ts": {
+ "version": "1.19.3",
+ "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz",
+ "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==",
+ "dev": true
+ },
+ "fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+ },
+ "fs-extra": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
+ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs-readdir-recursive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz",
+ "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==",
+ "dev": true,
+ "peer": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "function.prototype.name": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz",
+ "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ }
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
+ "dev": true
+ },
+ "functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "peer": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true,
+ "peer": true
+ },
+ "get-intrinsic": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.3"
+ }
+ },
+ "get-port": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
+ "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
+ "dev": true,
+ "peer": true
+ },
+ "get-symbol-description": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz",
+ "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ }
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "ghost-testrpc": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz",
+ "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "node-emoji": "^1.10.0"
+ }
+ },
+ "glob": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
+ "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "global-modules": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+ "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "global-prefix": "^3.0.0"
+ }
+ },
+ "global-prefix": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+ "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ini": "^1.3.5",
+ "kind-of": "^6.0.2",
+ "which": "^1.3.1"
+ }
+ },
+ "globby": {
+ "version": "10.0.2",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz",
+ "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/glob": "^7.1.1",
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.0.3",
+ "glob": "^7.1.3",
+ "ignore": "^5.1.1",
+ "merge2": "^1.2.3",
+ "slash": "^3.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true,
+ "peer": true
+ },
+ "handlebars": {
+ "version": "4.7.7",
+ "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
+ "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimist": "^1.2.5",
+ "neo-async": "^2.6.0",
+ "source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
+ "wordwrap": "^1.0.0"
+ }
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
+ "dev": true,
+ "peer": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "hardhat": {
+ "version": "2.12.0",
+ "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.12.0.tgz",
+ "integrity": "sha512-mNJFbVG479HwOzxiaLxobyvED2M1aEAuPPYhEo1+88yicMDSTrU2JIS7vV+V0GSNQKaDoiHCmV6bcKjiljT/dQ==",
+ "dev": true,
+ "requires": {
+ "@ethersproject/abi": "^5.1.2",
+ "@metamask/eth-sig-util": "^4.0.0",
+ "@nomicfoundation/ethereumjs-block": "^4.0.0",
+ "@nomicfoundation/ethereumjs-blockchain": "^6.0.0",
+ "@nomicfoundation/ethereumjs-common": "^3.0.0",
+ "@nomicfoundation/ethereumjs-evm": "^1.0.0",
+ "@nomicfoundation/ethereumjs-rlp": "^4.0.0",
+ "@nomicfoundation/ethereumjs-statemanager": "^1.0.0",
+ "@nomicfoundation/ethereumjs-trie": "^5.0.0",
+ "@nomicfoundation/ethereumjs-tx": "^4.0.0",
+ "@nomicfoundation/ethereumjs-util": "^8.0.0",
+ "@nomicfoundation/ethereumjs-vm": "^6.0.0",
+ "@nomicfoundation/solidity-analyzer": "^0.1.0",
+ "@sentry/node": "^5.18.1",
+ "@types/bn.js": "^5.1.0",
+ "@types/lru-cache": "^5.1.0",
+ "abort-controller": "^3.0.0",
+ "adm-zip": "^0.4.16",
+ "aggregate-error": "^3.0.0",
+ "ansi-escapes": "^4.3.0",
+ "chalk": "^2.4.2",
+ "chokidar": "^3.4.0",
+ "ci-info": "^2.0.0",
+ "debug": "^4.1.1",
+ "enquirer": "^2.3.0",
+ "env-paths": "^2.2.0",
+ "ethereum-cryptography": "^1.0.3",
+ "ethereumjs-abi": "^0.6.8",
+ "find-up": "^2.1.0",
+ "fp-ts": "1.19.3",
+ "fs-extra": "^7.0.1",
+ "glob": "7.2.0",
+ "immutable": "^4.0.0-rc.12",
+ "io-ts": "1.10.4",
+ "keccak": "^3.0.2",
+ "lodash": "^4.17.11",
+ "mnemonist": "^0.38.0",
+ "mocha": "^10.0.0",
+ "p-map": "^4.0.0",
+ "qs": "^6.7.0",
+ "raw-body": "^2.4.1",
+ "resolve": "1.17.0",
+ "semver": "^6.3.0",
+ "solc": "0.7.3",
+ "source-map-support": "^0.5.13",
+ "stacktrace-parser": "^0.1.10",
+ "tsort": "0.0.1",
+ "undici": "^5.4.0",
+ "uuid": "^8.3.2",
+ "ws": "^7.4.6"
+ }
+ },
+ "hardhat-gas-reporter": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz",
+ "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-uniq": "1.0.3",
+ "eth-gas-reporter": "^0.2.25",
+ "sha1": "^1.1.1"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-bigints": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
+ "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==",
+ "dev": true,
+ "peer": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true
+ },
+ "has-property-descriptors": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz",
+ "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "get-intrinsic": "^1.1.1"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
+ "dev": true
+ },
+ "has-tostringtag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz",
+ "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "heap": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz",
+ "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==",
+ "dev": true,
+ "peer": true
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "http-basic": {
+ "version": "8.1.3",
+ "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
+ "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "caseless": "^0.12.0",
+ "concat-stream": "^1.6.2",
+ "http-response-object": "^3.0.1",
+ "parse-cache-control": "^1.0.1"
+ }
+ },
+ "http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "dev": true,
+ "requires": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ }
+ },
+ "http-response-object": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
+ "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/node": "^10.0.3"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "10.17.60",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
+ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "requires": {
+ "agent-base": "6",
+ "debug": "4"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
+ "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
+ "dev": true,
+ "peer": true
+ },
+ "immutable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz",
+ "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true,
+ "peer": true
+ },
+ "internal-slot": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
+ "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ }
+ },
+ "interpret": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+ "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+ "dev": true,
+ "peer": true
+ },
+ "io-ts": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz",
+ "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==",
+ "dev": true,
+ "requires": {
+ "fp-ts": "^1.0.0"
+ }
+ },
+ "is-bigint": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz",
+ "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-bigints": "^1.0.1"
+ }
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-boolean-object": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz",
+ "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz",
+ "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "peer": true
+ },
+ "is-date-object": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz",
+ "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-hex-prefixed": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
+ "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
+ "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==",
+ "dev": true,
+ "peer": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-number-object": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz",
+ "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true
+ },
+ "is-regex": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
+ "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-shared-array-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
+ "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "is-string": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
+ "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-tostringtag": "^1.0.0"
+ }
+ },
+ "is-symbol": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz",
+ "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-symbols": "^1.0.2"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
+ "dev": true,
+ "peer": true
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true
+ },
+ "is-weakref": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
+ "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "peer": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "peer": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
+ "dev": true,
+ "peer": true
+ },
+ "js-sha3": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+ "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q=="
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
+ "dev": true,
+ "peer": true
+ },
+ "json-schema": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
+ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==",
+ "dev": true,
+ "peer": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "peer": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true,
+ "peer": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonschema": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz",
+ "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==",
+ "dev": true,
+ "peer": true
+ },
+ "jsprim": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
+ "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "keccak": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz",
+ "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==",
+ "dev": true,
+ "requires": {
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0",
+ "readable-stream": "^3.6.0"
+ }
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "peer": true
+ },
+ "klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.9"
+ }
+ },
+ "level": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz",
+ "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==",
+ "dev": true,
+ "requires": {
+ "browser-level": "^1.0.1",
+ "classic-level": "^1.2.0"
+ }
+ },
+ "level-supports": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz",
+ "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==",
+ "dev": true
+ },
+ "level-transcoder": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz",
+ "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==",
+ "dev": true,
+ "requires": {
+ "buffer": "^6.0.3",
+ "module-error": "^1.0.1"
+ }
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true
+ },
+ "lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+ "dev": true,
+ "peer": true
+ },
+ "lodash.truncate": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz",
+ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "loupe": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+ "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "get-func-name": "^2.0.0"
+ }
+ },
+ "lru_map": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
+ "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "peer": true
+ },
+ "markdown-table": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz",
+ "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==",
+ "dev": true,
+ "peer": true
+ },
+ "mcl-wasm": {
+ "version": "0.7.9",
+ "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz",
+ "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==",
+ "dev": true
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "memory-level": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz",
+ "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==",
+ "dev": true,
+ "requires": {
+ "abstract-level": "^1.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "module-error": "^1.0.1"
+ }
+ },
+ "memorystream": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+ "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "peer": true
+ },
+ "micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ }
+ },
+ "mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "peer": true
+ },
+ "mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "mime-db": "1.52.0"
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg=="
+ },
+ "minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz",
+ "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==",
+ "dev": true,
+ "peer": true
+ },
+ "mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimist": "^1.2.6"
+ }
+ },
+ "mnemonist": {
+ "version": "0.38.5",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz",
+ "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==",
+ "dev": true,
+ "requires": {
+ "obliterator": "^2.0.0"
+ }
+ },
+ "mocha": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.1.0.tgz",
+ "integrity": "sha512-vUF7IYxEoN7XhQpFLxQAEMtE4W91acW4B6En9l97MwE9stL1A9gusXfoHZCLVHDUJ/7V5+lbCM6yMqzo5vNymg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.3",
+ "debug": "4.3.4",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.2.0",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "5.0.1",
+ "ms": "2.1.3",
+ "nanoid": "3.3.3",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "workerpool": "6.2.1",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+ "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "module-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz",
+ "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
+ "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==",
+ "dev": true
+ },
+ "napi-macros": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz",
+ "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true,
+ "peer": true
+ },
+ "node-addon-api": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
+ "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==",
+ "dev": true
+ },
+ "node-emoji": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz",
+ "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "lodash": "^4.17.21"
+ }
+ },
+ "node-environment-flags": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz",
+ "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "node-gyp-build": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
+ "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
+ "dev": true
+ },
+ "nofilter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-1.0.4.tgz",
+ "integrity": "sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA==",
+ "dev": true
+ },
+ "nopt": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
+ "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "abbrev": "1"
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "number-to-bn": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz",
+ "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "bn.js": "4.11.6",
+ "strip-hex-prefix": "1.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.6",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true,
+ "peer": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "peer": true
+ },
+ "object-inspect": {
+ "version": "1.12.2",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
+ "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
+ "dev": true
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "peer": true
+ },
+ "object.assign": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "define-properties": "^1.1.2",
+ "function-bind": "^1.1.1",
+ "has-symbols": "^1.0.0",
+ "object-keys": "^1.0.11"
+ }
+ },
+ "object.getownpropertydescriptors": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz",
+ "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array.prototype.reduce": "^1.0.4",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.20.1"
+ }
+ },
+ "obliterator": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
+ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "ordinal": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz",
+ "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==",
+ "dev": true,
+ "peer": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
+ "dev": true
+ },
+ "parse-cache-control": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
+ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
+ "dev": true,
+ "peer": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true,
+ "peer": true
+ },
+ "pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "peer": true
+ },
+ "pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
+ "dev": true,
+ "peer": true
+ },
+ "picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "peer": true
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true,
+ "peer": true
+ },
+ "prettier": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
+ "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
+ "dev": true,
+ "peer": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "peer": true
+ },
+ "promise": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz",
+ "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "asap": "~2.0.6"
+ }
+ },
+ "psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true,
+ "peer": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
+ "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "dev": true,
+ "requires": {
+ "side-channel": "^1.0.4"
+ }
+ },
+ "queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "raw-body": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
+ "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ }
+ },
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "rechoir": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "resolve": "^1.1.6"
+ }
+ },
+ "recursive-readdir": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.2.tgz",
+ "integrity": "sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimatch": "3.0.4"
+ },
+ "dependencies": {
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ }
+ }
+ },
+ "reduce-flatten": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz",
+ "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==",
+ "dev": true,
+ "peer": true
+ },
+ "regexp.prototype.flags": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz",
+ "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ }
+ },
+ "req-cwd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz",
+ "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "req-from": "^2.0.0"
+ }
+ },
+ "req-from": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz",
+ "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
+ "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
+ "dev": true,
+ "peer": true
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "request-promise-core": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+ "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "lodash": "^4.17.19"
+ }
+ },
+ "request-promise-native": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+ "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "request-promise-core": "1.1.4",
+ "stealthy-require": "^1.1.1",
+ "tough-cookie": "^2.3.3"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true
+ },
+ "require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true,
+ "peer": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
+ "dev": true,
+ "peer": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "peer": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "rlp": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz",
+ "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^5.2.0"
+ }
+ },
+ "run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "run-parallel-limit": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz",
+ "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==",
+ "dev": true,
+ "requires": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "rustbn.js": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz",
+ "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sc-istanbul": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz",
+ "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "abbrev": "1.0.x",
+ "async": "1.x",
+ "escodegen": "1.8.x",
+ "esprima": "2.7.x",
+ "glob": "^5.0.15",
+ "handlebars": "^4.0.1",
+ "js-yaml": "3.x",
+ "mkdirp": "0.5.x",
+ "nopt": "3.x",
+ "once": "1.x",
+ "resolve": "1.1.x",
+ "supports-color": "^3.1.0",
+ "which": "^1.1.1",
+ "wordwrap": "^1.0.0"
+ },
+ "dependencies": {
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "async": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==",
+ "dev": true,
+ "peer": true
+ },
+ "glob": {
+ "version": "5.0.15",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
+ "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "2 || 3",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
+ "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==",
+ "dev": true,
+ "peer": true
+ },
+ "js-yaml": {
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
+ "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "dependencies": {
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==",
+ "dev": true,
+ "peer": true
+ },
+ "supports-color": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
+ "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-flag": "^1.0.0"
+ }
+ }
+ }
+ },
+ "scrypt-js": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",
+ "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==",
+ "dev": true
+ },
+ "secp256k1": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz",
+ "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==",
+ "dev": true,
+ "requires": {
+ "elliptic": "^6.5.4",
+ "node-addon-api": "^2.0.0",
+ "node-gyp-build": "^4.2.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
+ "dev": true,
+ "peer": true
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "sha1": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz",
+ "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "charenc": ">= 0.0.1",
+ "crypt": ">= 0.0.1"
+ }
+ },
+ "shelljs": {
+ "version": "0.8.5",
+ "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+ "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "glob": "^7.0.0",
+ "interpret": "^1.0.0",
+ "rechoir": "^0.6.2"
+ }
+ },
+ "side-channel": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
+ "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ }
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true,
+ "peer": true
+ },
+ "slice-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+ "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "solc": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz",
+ "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==",
+ "dev": true,
+ "requires": {
+ "command-exists": "^1.2.8",
+ "commander": "3.0.2",
+ "follow-redirects": "^1.12.1",
+ "fs-extra": "^0.30.0",
+ "js-sha3": "0.8.0",
+ "memorystream": "^0.3.1",
+ "require-from-string": "^2.0.0",
+ "semver": "^5.5.0",
+ "tmp": "0.0.33"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
+ "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^2.1.0",
+ "klaw": "^1.0.0",
+ "path-is-absolute": "^1.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "solidity-coverage": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.2.tgz",
+ "integrity": "sha512-cv2bWb7lOXPE9/SSleDO6czkFiMHgP4NXPj+iW9W7iEKLBk7Cj0AGBiNmGX3V1totl9wjPrT0gHmABZKZt65rQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@ethersproject/abi": "^5.0.9",
+ "@solidity-parser/parser": "^0.14.1",
+ "chalk": "^2.4.2",
+ "death": "^1.1.0",
+ "detect-port": "^1.3.0",
+ "difflib": "^0.2.4",
+ "fs-extra": "^8.1.0",
+ "ghost-testrpc": "^0.0.2",
+ "global-modules": "^2.0.0",
+ "globby": "^10.0.1",
+ "jsonschema": "^1.2.4",
+ "lodash": "^4.17.15",
+ "mocha": "7.1.2",
+ "node-emoji": "^1.10.0",
+ "pify": "^4.0.1",
+ "recursive-readdir": "^2.2.2",
+ "sc-istanbul": "^0.4.5",
+ "semver": "^7.3.4",
+ "shelljs": "^0.8.3",
+ "web3-utils": "^1.3.6"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true,
+ "peer": true
+ },
+ "ansi-regex": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
+ "dev": true,
+ "peer": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true,
+ "peer": true
+ },
+ "chokidar": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz",
+ "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.1",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.2.0"
+ }
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "dev": true,
+ "peer": true
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true,
+ "peer": true
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true,
+ "peer": true
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "peer": true
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "flat": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.1.tgz",
+ "integrity": "sha512-FmTtBsHskrU6FJ2VxCnsDb84wu9zhmO3cUX2kGFb5tuwhfXxGciiT0oRY+cck35QmG+NmGh5eLz6lLCpWTqwpA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ }
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true
+ },
+ "js-yaml": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
+ "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "chalk": "^2.4.2"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "mocha": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.1.2.tgz",
+ "integrity": "sha512-o96kdRKMKI3E8U0bjnfqW4QMk12MwZ4mhdBTf+B5a1q9+aq2HRnj+3ZdJu0B/ZhJeK78MgYuv6L8d/rA5AeBJA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.3.0",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "3.0.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.5",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.6",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.2",
+ "yargs-parser": "13.1.2",
+ "yargs-unparser": "1.6.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true,
+ "peer": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "peer": true
+ },
+ "readdirp": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz",
+ "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "picomatch": "^2.0.4"
+ }
+ },
+ "semver": {
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==",
+ "dev": true,
+ "peer": true
+ },
+ "supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true,
+ "peer": true
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "peer": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ },
+ "yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "peer": true
+ },
+ "sshpk": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
+ "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "dependencies": {
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "stacktrace-parser": {
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz",
+ "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.7.1"
+ },
+ "dependencies": {
+ "type-fest": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz",
+ "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==",
+ "dev": true
+ }
+ }
+ },
+ "statuses": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true
+ },
+ "stealthy-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+ "integrity": "sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==",
+ "dev": true,
+ "peer": true
+ },
+ "streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "string-format": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz",
+ "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==",
+ "dev": true,
+ "peer": true
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz",
+ "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz",
+ "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-hex-prefix": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
+ "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==",
+ "dev": true,
+ "requires": {
+ "is-hex-prefixed": "1.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "sync-request": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
+ "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "http-response-object": "^3.0.1",
+ "sync-rpc": "^1.2.1",
+ "then-request": "^6.0.0"
+ }
+ },
+ "sync-rpc": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
+ "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "get-port": "^3.1.0"
+ }
+ },
+ "table": {
+ "version": "6.8.0",
+ "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz",
+ "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==",
+ "dev": true,
+ "requires": {
+ "ajv": "^8.0.1",
+ "lodash.truncate": "^4.4.2",
+ "slice-ansi": "^4.0.0",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1"
+ },
+ "dependencies": {
+ "ajv": {
+ "version": "8.11.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz",
+ "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ }
+ }
+ },
+ "table-layout": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz",
+ "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "array-back": "^4.0.1",
+ "deep-extend": "~0.6.0",
+ "typical": "^5.2.0",
+ "wordwrapjs": "^4.0.0"
+ },
+ "dependencies": {
+ "array-back": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz",
+ "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==",
+ "dev": true,
+ "peer": true
+ },
+ "typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "then-request": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
+ "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/concat-stream": "^1.6.0",
+ "@types/form-data": "0.0.33",
+ "@types/node": "^8.0.0",
+ "@types/qs": "^6.2.31",
+ "caseless": "~0.12.0",
+ "concat-stream": "^1.6.0",
+ "form-data": "^2.2.0",
+ "http-basic": "^8.1.1",
+ "http-response-object": "^3.0.1",
+ "promise": "^8.0.0",
+ "qs": "^6.4.0"
+ },
+ "dependencies": {
+ "@types/node": {
+ "version": "8.10.66",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
+ "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "ts-command-line-args": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.3.1.tgz",
+ "integrity": "sha512-FR3y7pLl/fuUNSmnPhfLArGqRrpojQgIEEOVzYx9DhTmfIN7C9RWSfpkJEF4J+Gk7aVx5pak8I7vWZsaN4N84g==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "command-line-args": "^5.1.1",
+ "command-line-usage": "^6.1.0",
+ "string-format": "^2.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "peer": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "peer": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "ts-essentials": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz",
+ "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {}
+ },
+ "ts-node": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
+ "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "dependencies": {
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ },
+ "tsort": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz",
+ "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
+ "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
+ "dev": true
+ },
+ "tweetnacl-util": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
+ "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true,
+ "peer": true
+ },
+ "type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true
+ },
+ "typechain": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.1.0.tgz",
+ "integrity": "sha512-5jToLgKTjHdI1VKqs/K8BLYy42Sr3o8bV5ojh4MnR9ExHO83cyyUdw+7+vMJCpKXUiVUvARM4qmHTFuyaCMAZQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/prettier": "^2.1.1",
+ "debug": "^4.3.1",
+ "fs-extra": "^7.0.0",
+ "glob": "7.1.7",
+ "js-sha3": "^0.8.0",
+ "lodash": "^4.17.15",
+ "mkdirp": "^1.0.4",
+ "prettier": "^2.3.1",
+ "ts-command-line-args": "^2.2.0",
+ "ts-essentials": "^7.0.1"
+ },
+ "dependencies": {
+ "glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "dev": true,
+ "peer": true
+ },
+ "typescript": {
+ "version": "4.8.4",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz",
+ "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
+ "dev": true,
+ "peer": true
+ },
+ "typical": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
+ "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==",
+ "dev": true,
+ "peer": true
+ },
+ "uglify-js": {
+ "version": "3.17.3",
+ "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.3.tgz",
+ "integrity": "sha512-JmMFDME3iufZnBpyKL+uS78LRiC+mK55zWfM5f/pWBJfpOttXAqYfdDGRukYhJuyRinvPVAtUhvy7rlDybNtFg==",
+ "dev": true,
+ "optional": true,
+ "peer": true
+ },
+ "unbox-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz",
+ "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ }
+ },
+ "undici": {
+ "version": "5.11.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-5.11.0.tgz",
+ "integrity": "sha512-oWjWJHzFet0Ow4YZBkyiJwiK5vWqEYoH7BINzJAJOLedZ++JpAlCbUktW2GQ2DS2FpKmxD/JMtWUUWl1BtghGw==",
+ "dev": true,
+ "requires": {
+ "busboy": "^1.6.0"
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "utf8": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
+ "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==",
+ "dev": true,
+ "peer": true
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true
+ },
+ "v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "peer": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "web3-utils": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.8.0.tgz",
+ "integrity": "sha512-7nUIl7UWpLVka2f09CMbKOSEvorvHnaugIabU4mj7zfMvm0tSByLcEu3eyV9qgS11qxxLuOkzBIwCstTflhmpQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "bn.js": "^5.2.1",
+ "ethereum-bloom-filters": "^1.0.6",
+ "ethereumjs-util": "^7.1.0",
+ "ethjs-unit": "0.1.6",
+ "number-to-bn": "1.7.0",
+ "randombytes": "^2.1.0",
+ "utf8": "3.0.0"
+ },
+ "dependencies": {
+ "ethereum-cryptography": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+ "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/pbkdf2": "^3.0.0",
+ "@types/secp256k1": "^4.0.1",
+ "blakejs": "^1.1.0",
+ "browserify-aes": "^1.2.0",
+ "bs58check": "^2.1.2",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "hash.js": "^1.1.7",
+ "keccak": "^3.0.0",
+ "pbkdf2": "^3.0.17",
+ "randombytes": "^2.1.0",
+ "safe-buffer": "^5.1.2",
+ "scrypt-js": "^3.0.0",
+ "secp256k1": "^4.0.1",
+ "setimmediate": "^1.0.5"
+ }
+ },
+ "ethereumjs-util": {
+ "version": "7.1.5",
+ "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+ "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "@types/bn.js": "^5.1.0",
+ "bn.js": "^5.1.2",
+ "create-hash": "^1.1.2",
+ "ethereum-cryptography": "^0.1.3",
+ "rlp": "^2.2.4"
+ }
+ }
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-boxed-primitive": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz",
+ "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==",
+ "dev": true,
+ "peer": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
+ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz",
+ "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==",
+ "dev": true,
+ "peer": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
+ "dev": true,
+ "peer": true
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ }
+ }
+ },
+ "word-wrap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+ "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+ "dev": true,
+ "peer": true
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==",
+ "dev": true,
+ "peer": true
+ },
+ "wordwrapjs": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz",
+ "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==",
+ "dev": true,
+ "peer": true,
+ "requires": {
+ "reduce-flatten": "^2.0.0",
+ "typical": "^5.2.0"
+ },
+ "dependencies": {
+ "typical": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz",
+ "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==",
+ "dev": true,
+ "peer": true
+ }
+ }
+ },
+ "workerpool": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+ "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "ws": {
+ "version": "7.5.9",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
+ "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
+ "dev": true,
+ "requires": {}
+ },
+ "xmlhttprequest": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+ "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+ "dev": true,
+ "peer": true
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "dev": true
+ },
+ "yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ }
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "peer": true
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a60719d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "modechat-contracts",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@nomicfoundation/hardhat-toolbox": "^2.0.0",
+ "@nomiclabs/hardhat-etherscan": "^3.1.1",
+ "@openzeppelin/contracts": "^v4.9.0-rc.0",
+ "dotenv": "^16.0.3",
+ "hardhat": "^2.12.0"
+ },
+ "dependencies": {
+ "@openzeppelin/merkle-tree": "^1.0.5",
+ "@rari-capital/solmate": "^6.4.0",
+ "fs": "^0.0.1-security"
+ }
+}
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..c596fdb
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,128 @@
+# Iggy Social smart contracts
+
+Smart contracts that enable and enhance the web3 social experience.
+
+See the instructions below to run the code on localhost and for blockchain deployment.
+
+### .env
+
+Create a `.env` file with the following keys:
+
+```bash
+DEPLOYER_PRIVATE_KEY=enter-key-here
+```
+
+### Compile
+
+```bash
+npx hardhat compile
+```
+
+### Test
+
+```bash
+npx hardhat test
+```
+
+Run tests in a specific folder:
+
+```bash
+npx hardhat test test/*test.js
+```
+
+Run a specific test:
+
+```bash
+npx hardhat test test/some.test.js
+```
+
+### Run on localhost
+
+Start a localhost node:
+
+```bash
+npx hardhat node
+```
+
+Make sure to add one of the private keys presented as deployer key in `.env` file.
+
+In a separate terminal tab then run the following command:
+
+```bash
+npx hardhat run scripts/deploy.js --network localhost
+```
+
+### Deployment
+
+```bash
+npx hardhat run scripts/deploy.js --network
+```
+
+> Also make sure you have the `@nomiclabs/hardhat-etherscan` library `3.1.0` or above.
+
+### Verify TLD contracts
+
+Verifying TLD contracts generated through the factory is a bit tricky, but there is a way around the issue. See `scripts/temp/deployTld.js` for instructions.
+
+## Audit tools
+
+### Flatten the contracts
+
+Most audit tools will require you to flatten the contracts. This means that all contracts that are defined under the imports will actually be imported into one .sol file, so all code is in one place.
+
+First create a new folder called flattened:
+
+```bash
+mkdir flattened
+```
+
+To flatten a contract, run this command:
+
+```bash
+npx hardhat flatten >> flattened/.sol
+```
+
+You may also need to give all contracts in the flattened file the same Solidity version. And you may need to delete all SPDX lines except the very first one.
+
+### Mythrill
+
+```bash
+myth -v4 analyze flattened/PunkForbiddenTlds.sol
+```
+
+Flags:
+
+- `v4`: verbose
+- `o`: output
+- `a`: address onchain
+- `l`: automatically retrieve dependencies
+- `max-depth`: maximum recursion depth
+
+Docs: https://mythril-classic.readthedocs.io/en/master/security-analysis.html
+
+### Slither
+
+Install Slither:
+
+```bash
+pip3 install slither-analyzer --user
+```
+
+Run it in the `flattened` folder:
+
+```bash
+slither .
+```
+
+Docs: https://github.com/crytic/slither
+
+## Debugging
+
+### Error: ENOENT: no such file or directory
+
+Run `npx hardhat clean` and then `npx hardhat compile`.
+
+## Frontends
+
+- Development preview: https://iggy-social-frontend.vercel.app/
+- Landing page: https://iggy.social
diff --git a/scripts/activity-points/activityPoints.deploy.js b/scripts/activity-points/activityPoints.deploy.js
new file mode 100644
index 0000000..a77896e
--- /dev/null
+++ b/scripts/activity-points/activityPoints.deploy.js
@@ -0,0 +1,45 @@
+// npx hardhat run scripts/activity-points/activityPoints.deploy.js --network modeTestnet
+
+const contractName = "ActivityPoints";
+
+const statsAddress = ""; // stats contract
+const mintedPostsStatsAddress = "";
+const tldStatsAddress = ethers.constants.AddressZero;
+const multiplier = 100000; // 1 wei = 100000 points
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ statsAddress,
+ mintedPostsStatsAddress,
+ tldStatsAddress,
+ multiplier,
+ sfsAddress,
+ sfsNftTokenId
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + statsAddress + " " + mintedPostsStatsAddress + " " + tldStatsAddress + ' "' + multiplier + '" ' + sfsAddress + ' "' + sfsNftTokenId + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/activity-points/activityPointsAlt.deploy.js b/scripts/activity-points/activityPointsAlt.deploy.js
new file mode 100644
index 0000000..8d3be9b
--- /dev/null
+++ b/scripts/activity-points/activityPointsAlt.deploy.js
@@ -0,0 +1,45 @@
+// npx hardhat run scripts/activity-points/activityPointsAlt.deploy.js --network modeTestnet
+
+const contractName = "ActivityPointsAlt";
+
+const statsAddress = ""; // stats contract
+const mintedPostsStatsAddress = "";
+const tldStatsAddress = ethers.constants.AddressZero;
+const multiplier = 100000; // 1 wei = 100000 points
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ statsAddress,
+ mintedPostsStatsAddress,
+ tldStatsAddress,
+ multiplier,
+ sfsAddress,
+ sfsNftTokenId
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + statsAddress + " " + mintedPostsStatsAddress + " " + tldStatsAddress + ' "' + multiplier + '" ' + sfsAddress + ' "' + sfsNftTokenId + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/activity-points/apLeaderboardFairchat.js b/scripts/activity-points/apLeaderboardFairchat.js
new file mode 100644
index 0000000..cf55161
--- /dev/null
+++ b/scripts/activity-points/apLeaderboardFairchat.js
@@ -0,0 +1,108 @@
+// npx hardhat run scripts/activity-points/apLeaderboardFairchat.js --network zkfair
+
+const contractName = "ActivityPoints";
+
+const apAddress = "0xc486B08Ed47fFe5c1b4b1A2ff5c671EA0083D9bA";
+const resolverAddress = "0xeA2f99fE93E5D07F61334C5Eb9c54c5D5C957a6a";
+const tld = ".fairchat";
+
+const addresses = [
+ "0xe7D358c169789dC794f0bd411aa2897209675D72",
+ "0x330232Ec8bae141CbA1116bD49E6bc6c191520B5",
+ "0xb8234960308eEC310eE05E6A8A3AfA9727D0b47B",
+ "0x462B6A0A8fc932C21A18fCca5Bb17Ab8FeBbD2A9",
+ "0x3A1F7fAA40c93Be8e4A3f76b7e910Dcd8776cf76",
+ "0x2e06842A6Bf721caa2463e3A5Bb56CbaCD255612",
+ "0xb75Da0c0280dD1e01f5a2dd6a8574f34696723bF",
+ "0x8b91FC90B38F0592460d34F5738f8cC3fE7d5708",
+ "0x63feA96fbfa64EA4A1d56072Dbcea70F136aC754",
+ "0x250517C5AbBd54B9D2f88dfBc6DD8f3b55cA1D10",
+ "0xa00431795AcDCbA4BA0bC1DB12fE755963eBdFFa",
+ "0x0D8DEC825e73979Eacf1a713F1985BE7509756Cc",
+ "0xbCfA5A4E62649A040378f7536044C2A1a9EA81E4",
+ "0xFd6aeE13B037fe2644dBFF826e3642338A205907",
+ "0xF17135dDde942EA02BDE97eBE530557049265D40",
+ "0xb29050965A5AC70ab487aa47546cdCBc97dAE45D",
+ "0x5FfD23B1B0350debB17A2cB668929aC5f76d0E18",
+ "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2",
+];
+
+async function main() {
+ const [caller] = await ethers.getSigners();
+
+ console.log("GENERATE AP LEADERBOARD");
+ console.log("Calling functions with the account:", caller.address);
+
+ const apContract = await ethers.getContractFactory(contractName);
+ const apInstance = await apContract.attach(apAddress);
+
+ const resolverInterface = new ethers.utils.Interface([
+ "function getDefaultDomain(address _addr, string calldata _tld) public view returns(string memory)",
+ ]);
+ const resolverInstance = new ethers.Contract(resolverAddress, resolverInterface, caller);
+
+ // get current multiplier
+ const multiplier = await apInstance.multiplier();
+ console.log("AP multiplier:", multiplier.toString());
+
+ await sleep(1000);
+
+ // getTotalWeiSpentAllUsers
+ const totalPointsAllUsers = await apInstance.getTotalPointsAllUsers();
+ console.log("Total points of all users:", ethers.utils.formatEther(totalPointsAllUsers));
+
+ await sleep(1000);
+
+ console.log();
+ console.log("-------");
+ console.log();
+
+ let leaderboard = [];
+
+ for (let i = 0; i < addresses.length; i++) {
+ await sleep(1000);
+ let username = await resolverInstance.getDefaultDomain(addresses[i], tld);
+
+ if (username) {
+ username = username + tld;
+ }
+
+ let points = await apInstance.getPoints(addresses[i]);
+ console.log(username + " (" + addresses[i] + "): " + ethers.utils.formatEther(points));
+
+ leaderboard.push({
+ username: username,
+ address: addresses[i],
+ points: ethers.utils.formatEther(points),
+ });
+
+ }
+
+ // sort leaderboard by points from highest to lowest
+ leaderboard.sort((a, b) => b.points - a.points);
+
+ console.log();
+ console.log("-------");
+ console.log();
+
+ // print as csv
+ console.log("username,address,points");
+ for (let i = 0; i < leaderboard.length; i++) {
+ console.log(leaderboard[i].username + "," + leaderboard[i].address + "," + leaderboard[i].points);
+ }
+
+ console.log();
+ console.log("END");
+
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/activity-points/calls.js b/scripts/activity-points/calls.js
new file mode 100644
index 0000000..48635b7
--- /dev/null
+++ b/scripts/activity-points/calls.js
@@ -0,0 +1,51 @@
+// npx hardhat run scripts/activity-points/calls.js --network zkfair
+
+const contractName = "ActivityPoints";
+
+const apAddress = "0xc486B08Ed47fFe5c1b4b1A2ff5c671EA0083D9bA";
+
+async function main() {
+ const [caller] = await ethers.getSigners();
+
+ console.log("Calling functions with the account:", caller.address);
+ console.log("Account balance:", (await caller.getBalance()).toString());
+
+ const apContract = await ethers.getContractFactory(contractName);
+ const apInstance = await apContract.attach(apAddress);
+
+ // get current multiplier
+ const currentMultiplierBefore = await apInstance.multiplier();
+ console.log("Current multiplier (before):", currentMultiplierBefore.toString());
+
+ // set new multiplier
+ const newMultiplier = 100;
+
+ /*
+ console.log("Setting new multiplier...");
+ const tx1 = await apInstance.setMultiplier(newMultiplier);
+ await tx1.wait();
+
+ // get current multiplier
+ const currentMultiplierAfter = await apInstance.multiplier();
+ console.log("Current multiplier (after):", currentMultiplierAfter.toString());
+ */
+
+ // getPoints for caller
+ const pointsCaller = await apInstance.getPoints("0xb29050965A5AC70ab487aa47546cdCBc97dAE45D");
+ console.log("Points for caller:", ethers.utils.formatEther(pointsCaller));
+
+ // getTotalWeiSpentAllUsers
+ const totalPointsAllUsers = await apInstance.getTotalPointsAllUsers();
+ console.log("Total points of all users:", ethers.utils.formatEther(totalPointsAllUsers));
+
+ // check statsAddress
+ const statsAddress = await apInstance.statsAddress();
+ console.log("Stats address:", statsAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/custom/minterV2ambassadors.deploy.js b/scripts/custom/minterV2ambassadors.deploy.js
new file mode 100644
index 0000000..052de5d
--- /dev/null
+++ b/scripts/custom/minterV2ambassadors.deploy.js
@@ -0,0 +1,108 @@
+// Deploy minter V2 contract
+// npx hardhat run scripts/custom/minterV2ambassadors.deploy.js --network songbird
+// It will automatically set different fees (if needed) and set the staking contract address (if needed).
+// It will also automatically add the minter to the ChatTokenMinter contract and change the minter address in the post contract.
+// If any of these actions fail, you must do them manually.
+
+const contractName = "IggyPostMinterV2Ambassadors";
+
+const chatTokenMinterAddress = "0x31CfDF366dd9753b8443B6fc3c59598415697131";
+const daoAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // DAO or web3 community which owns the frontend
+const devAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // person or entity that is doing the development
+const devFeeUpdaterAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // the address that can change dev fee (can be a multisig)
+const postAddress = "0xE33F27496A9cE75313f6d1FA2BA95657Fc904387"; // post NFT contract
+const stakingContractAddress = "0xCA9749778327CD67700d3a777731a712330beB9A"; // if you don't have it yet, leave it blank (but you'll need to set it later)
+const ambassador1 = "0x17a2063e1f5C6034F4c94cfb0F4970483647a2E5"; // ambassador 1 address
+const ambassador2 = "0x772bA1Faf2a2b49B452A5b287B2165cba89EfAE2"; // ambassador 2 address
+
+// stats contract
+const statsEnabled = true; // have it enabled by default so that users can see minted posts on their profile
+const statsAddress = "";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ chatTokenMinterAddress,
+ daoAddress,
+ devAddress,
+ devFeeUpdaterAddress,
+ postAddress,
+ stakingContractAddress,
+ ambassador1,
+ ambassador2
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ await instance.deployed();
+
+ console.log("Deploy transaction mined!");
+
+ console.log("Add post minter contract address as a minter in the ChatTokenMinter contract");
+
+ // add as minter to ChatTokenMinter contract
+ const chatTokenMinterContract = await ethers.getContractFactory("ChatTokenMinter");
+ const chatTokenMinterInstance = await chatTokenMinterContract.attach(chatTokenMinterAddress);
+ const tx1 = await chatTokenMinterInstance.addMinter(instance.address);
+ await tx1.wait();
+
+ // change minter address in post contract
+ console.log("Change minter address in post contract");
+ console.log("MAKE SURE THE POST CONTRACT IS OWNED BY THIS DEPLOYER! (Otherwise make the transaction manually)");
+
+ const postContract = await ethers.getContractFactory("IggyPostNft1155");
+ const postContractInstance = await postContract.attach(postAddress);
+
+ const postContractOwner = await postContractInstance.owner();
+ console.log("Post contract owner:", postContractOwner);
+
+ if (String(postContractOwner).toLowerCase() == String(deployer.address).toLowerCase()) {
+ const tx2 = await postContractInstance.ownerChangeMinterAddress(instance.address);
+ await tx2.wait();
+ } else {
+ console.log("Post contract is not owned by this deployer. Please change the minter address manually.");
+ }
+
+ // change post minter address in stats contract
+ if (statsEnabled && statsAddress) {
+ console.log("Change post minter address in stats contract");
+
+ const statsContract = await ethers.getContractFactory("IggyPostStats");
+ const statsContractInstance = await statsContract.attach(statsAddress);
+
+ // setMinterAddress
+ const tx2b = await statsContractInstance.setMinterAddress(instance.address);
+ await tx2b.wait();
+
+ // check if statsEnabled in smart contract is true
+ const statsEnabledInContract = await instance.statsEnabled();
+
+ if (!statsEnabledInContract) {
+ console.log("Stats enabled in contract is false. Enabling it now...");
+ const tx2c = await instance.toggleStatsEnabled();
+ await tx2c.wait();
+ }
+
+ // change stats address in post minter contract
+ console.log("Change stats address in post minter contract");
+ const tx2d = await instance.changeStatsAddress(statsAddress);
+ await tx2d.wait();
+ }
+
+ // verify contract
+ console.log("Wait a minute and then run this command to verify contract on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + chatTokenMinterAddress + " " + daoAddress + " " + devAddress + " " + devFeeUpdaterAddress + " " + postAddress + " " + stakingContractAddress + " " + ambassador1 + " " + ambassador2);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/distributor/distributor.deploy.js b/scripts/distributor/distributor.deploy.js
new file mode 100644
index 0000000..a82891c
--- /dev/null
+++ b/scripts/distributor/distributor.deploy.js
@@ -0,0 +1,58 @@
+// npx hardhat run scripts/distributor/distributor.deploy.js --network bsc
+
+const contractName = "RevenueDistributor";
+
+const recipients = [];
+const managers = [];
+
+/*
+// recipients receive a percentage of the revenue that comes to the contract
+const recipients = [
+ { address: "0xCA9749778327CD67700d3a777731a712330beB9A", label: "Staking contract", percentage: ethers.utils.parseEther("0.9") },
+ { address: "0x17a2063e1f5C6034F4c94cfb0F4970483647a2E5", label: "Digital McDuck", percentage: ethers.utils.parseEther("0.05") },
+ { address: "0x772bA1Faf2a2b49B452A5b287B2165cba89EfAE2", label: "MyCryptoPlayground", percentage: ethers.utils.parseEther("0.05") }
+];
+
+// managers are allowed to add/remove recipients
+const managers = ["0xb29050965a5ac70ab487aa47546cdcbc97dae45d", "0x5ffd23b1b0350debb17a2cb668929ac5f76d0e18"];
+*/
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy();
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add recipients
+ for (let i = 0; i < recipients.length; i++) {
+ const recipient = recipients[i];
+ console.log("Adding recipient:", recipient.address, "-", recipient.label);
+ let tx1 = await instance.addRecipient(recipient.address, recipient.label, recipient.percentage);
+ await tx1.wait();
+ }
+
+ // add managers
+ for (let i = 0; i < managers.length; i++) {
+ const manager = managers[i];
+ console.log("Adding manager:", manager);
+ let tx2 = await instance.addManager(manager);
+ await tx2.wait();
+ }
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/distributor/distributorFactory.deploy.js b/scripts/distributor/distributorFactory.deploy.js
new file mode 100644
index 0000000..8b38387
--- /dev/null
+++ b/scripts/distributor/distributorFactory.deploy.js
@@ -0,0 +1,27 @@
+// npx hardhat run scripts/distributor/distributorFactory.deploy.js --network bsc
+
+const contractName = "RevenueDistributorFactory";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy();
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/keys/1_friendKeys.deploy.js b/scripts/keys/1_friendKeys.deploy.js
new file mode 100644
index 0000000..f4f590d
--- /dev/null
+++ b/scripts/keys/1_friendKeys.deploy.js
@@ -0,0 +1,52 @@
+// 1. Deploy FriendKeys contract and automatically add it's address to the Stats middleware contract.
+// npx hardhat run scripts/keys/1_friendKeys.deploy.js --network zkfair
+
+const contractName = "FriendKeys";
+
+const tldAddress = "0x4087fb91A1fBdef05761C02714335D232a2Bf3a1";
+const feeReceiver = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // distributor contract address
+const statsAddress = "0x3Fa0EaC3058828Cc4BA97F51A33597C695bF6F9e"; // stats middleware contract address
+
+const protocolFeePercent = ethers.utils.parseEther("0.05"); // 1 is 100%
+const domainHolderFeePercent = ethers.utils.parseEther("0.05"); // 1 is 100%
+const ratio = ethers.utils.parseEther("69"); // 1 ETH for 16000 keys
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tldAddress,
+ feeReceiver,
+ statsAddress,
+ protocolFeePercent,
+ domainHolderFeePercent,
+ ratio
+ );
+
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add this address to the Stats middleware contract
+ console.log("Adding this address to the stats middleware contract:");
+ const statsContract = await ethers.getContractFactory("StatsMiddleware");
+ const statsInstance = await statsContract.attach(statsAddress);
+ const tx1 = await statsInstance.addWriter(instance.address);
+ await tx1.wait();
+ console.log("Done!");
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + tldAddress + " " + feeReceiver + " " + statsAddress + ' "' + protocolFeePercent + '" "' + domainHolderFeePercent + '" "' + ratio + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/1_nftMetadata.deploy.js b/scripts/launchpad/erc721/1_nftMetadata.deploy.js
new file mode 100644
index 0000000..0611307
--- /dev/null
+++ b/scripts/launchpad/erc721/1_nftMetadata.deploy.js
@@ -0,0 +1,28 @@
+// 1. Deploy NftMetadata contract.
+// npx hardhat run scripts/launchpad/erc721/1_nftMetadata.deploy.js --network modeTestnet
+
+const contractName = "NftMetadata";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy();
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/2_nftDirectory.deploy.js b/scripts/launchpad/erc721/2_nftDirectory.deploy.js
new file mode 100644
index 0000000..49519b6
--- /dev/null
+++ b/scripts/launchpad/erc721/2_nftDirectory.deploy.js
@@ -0,0 +1,28 @@
+// 2. Deploy NftDirectory contract.
+// npx hardhat run scripts/launchpad/erc721/2_nftDirectory.deploy.js --network modeTestnet
+
+const contractName = "NftDirectory";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy();
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/3_launchpadBonding.deploy.js b/scripts/launchpad/erc721/3_launchpadBonding.deploy.js
new file mode 100644
index 0000000..8490e1e
--- /dev/null
+++ b/scripts/launchpad/erc721/3_launchpadBonding.deploy.js
@@ -0,0 +1,60 @@
+// 3. Deploy IggyLaunchpad721Bonding contract.
+// npx hardhat run scripts/launchpad/erc721/3_launchpadBonding.deploy.js --network modeTestnet
+
+const contractName = "IggyLaunchpad721Bonding";
+
+const metadataAddress = "0xc486B08Ed47fFe5c1b4b1A2ff5c671EA0083D9bA";
+const mintingFeeReceiver = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // revenue distributor contract address
+const directoryAddress = "0x6AbDd1Bf5078cC6b0D75caFCdDC69A8339067F50";
+const statsMiddlewareAddress = "0xce314209aB485bE222CE85F653Ac918f54532503";
+const mintingFeePercentage = ethers.utils.parseEther("0.02");
+const price = ethers.utils.parseEther("0.0001"); // price for creating a new NFT collection
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ metadataAddress,
+ mintingFeeReceiver,
+ directoryAddress,
+ statsMiddlewareAddress,
+ mintingFeePercentage,
+ price
+ );
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // create a stats middleware contract instance
+ const statsMiddlewareContract = await ethers.getContractFactory("StatsMiddleware");
+ const statsMiddlewareInstance = await statsMiddlewareContract.attach(statsMiddlewareAddress);
+
+ // set the launchpad contract address as writer in the stats middleware contract (addWriter function)
+ console.log("Adding " + contractName + " contract as writer in the stats middleware contract...");
+ const tx1 = await statsMiddlewareInstance.addWriter(instance.address);
+ await tx1.wait();
+
+ // create a directroy contract instance
+ const directoryContract = await ethers.getContractFactory("NftDirectory");
+ const directoryInstance = await directoryContract.attach(directoryAddress);
+
+ // set the launchpad contract address as writer in the directory contract (addWriter function)
+ console.log("Adding " + contractName + " contract as writer in the directory contract...");
+ const tx2 = await directoryInstance.addWriter(instance.address);
+ await tx2.wait();
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + metadataAddress + " " + mintingFeeReceiver + " " + directoryAddress + " " + statsMiddlewareAddress + ' "' + mintingFeePercentage + '" "' + price + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/4_arguments.js b/scripts/launchpad/erc721/4_arguments.js
new file mode 100644
index 0000000..9f453c0
--- /dev/null
+++ b/scripts/launchpad/erc721/4_arguments.js
@@ -0,0 +1,9 @@
+module.exports = [
+ "0x498e0e6B245898c5E2dD0299d0456a8928F58ECC", // factory address
+ "0x4A82158ff4B0504F3DB4c7555FfB6298452985E2", // metadata address
+ "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2", // minting fee receiver address
+ "Always Liquid on Polygon", // collection name
+ "ALPOLY", // collection symbol
+ "20000000000000000", // minting fee percentage
+ "100000000000000000" // ratio
+];
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/4_verifyNftContract.js b/scripts/launchpad/erc721/4_verifyNftContract.js
new file mode 100644
index 0000000..4318959
--- /dev/null
+++ b/scripts/launchpad/erc721/4_verifyNftContract.js
@@ -0,0 +1,20 @@
+// TODO:
+// 1. Create the first NFT collection through the factory.
+// 2. Verify the contract on block explorer using this script (run the command below).
+// Run: npx hardhat run scripts/launchpad/erc721/4_verifyNftContract.js --network polygon
+
+const networkName = "polygon";
+const contractAddress = "0x73Bf93b294AF8514a7E2dEf4E37877AeaE854a90";
+
+async function main() {
+ console.log("Copy the line below and paste it in your terminal to verify the TLD contract on Etherscan:");
+ console.log("");
+ console.log("npx hardhat verify --network " + networkName + " --constructor-args scripts/launchpad/erc721/4_arguments.js " + contractAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/5_mockNftContract.js b/scripts/launchpad/erc721/5_mockNftContract.js
new file mode 100644
index 0000000..7cb437a
--- /dev/null
+++ b/scripts/launchpad/erc721/5_mockNftContract.js
@@ -0,0 +1,45 @@
+// If verifying contract via step 3 does not work, use this script instead
+// 5. Deploy mock/test Nft721Bonding contract so that others created through factory can get auto-verified.
+// npx hardhat run scripts/launchpad/erc721/5_mockNftContract.js --network modeTestnet
+
+const contractName = "Nft721Bonding";
+
+const factoryAddress = "0x3Fa0EaC3058828Cc4BA97F51A33597C695bF6F9e";
+const metadataAddress = "0xc486B08Ed47fFe5c1b4b1A2ff5c671EA0083D9bA";
+const mintingFeeReceiver = "0xb29050965a5ac70ab487aa47546cdcbc97dae45d";
+const cName = "Test collection";
+const cSymbol = "TEST";
+const mintingFeePercentage = ethers.utils.parseEther("0.02");
+const ratio = ethers.utils.parseEther("4200");
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ factoryAddress,
+ metadataAddress,
+ mintingFeeReceiver,
+ cName,
+ cSymbol,
+ mintingFeePercentage,
+ ratio
+ );
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + factoryAddress + " " + metadataAddress + " " + mintingFeeReceiver + ' "' + cName + '" "' + cSymbol + '" "' + mintingFeePercentage + '" "' + ratio + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/other/NftMetadataOnchainMultipleImages.deploy.js b/scripts/launchpad/erc721/other/NftMetadataOnchainMultipleImages.deploy.js
new file mode 100644
index 0000000..43deefa
--- /dev/null
+++ b/scripts/launchpad/erc721/other/NftMetadataOnchainMultipleImages.deploy.js
@@ -0,0 +1,80 @@
+// Deploy NftMetadata contract for multiple onchain images
+// npx hardhat run scripts/launchpad/erc721/other/NftMetadataOnchainMultipleImages.deploy.js --network flare
+
+const contractName = "NftMetadataOnchainMultipleImages";
+
+const collectionPreviewImage = "https://bafkreihn7v7ugcu4yjnapsha3tij4cq7qatj2wjofpvxlkp6s4sl5nujn4.ipfs.w3s.link/";
+const description = "Viewing Japanese landscapes through a film.";
+const externalUrl = "https://flr.chat/nft/collection?id=0xC4CFd446b1AD6dc958c6C28f137B62CE03989E98";
+const image = "https://bafkreihn7v7ugcu4yjnapsha3tij4cq7qatj2wjofpvxlkp6s4sl5nujn4.ipfs.w3s.link/"; // 1st image
+const nftname = "et_puis BK";
+
+const images = [
+ "https://bafybeidim3mkbj4obd3cbf7fcrolioo2zpeha3xbzg2jvuajrtmvjk5lnq.ipfs.w3s.link/et_puisBK_2.jpg",
+ "https://bafybeicbrgmgugbmk7g2nofbndysbq4pwezm65t6yjlyo7j32q3hgsxtxm.ipfs.w3s.link/et_puisBK_3.jpg",
+ "https://bafybeif2gszl6ts4hmjdpdribflwakqtx5mljdfpqmobizrgjbumaksdwy.ipfs.w3s.link/et_puisBK_4.jpg",
+ "https://bafybeigahpskfomwq3kfbklcdtatz3g5pun5rwatjje3i2rwye6ktanszq.ipfs.w3s.link/et_puisBK_5.jpg",
+ "https://bafybeihgmzfxbtigxzzcvmfpr6atmkgvr54muzxq3ew3minfx6ofugwvhi.ipfs.w3s.link/et_puisBK_6.jpg",
+ "https://bafybeif4rte6ltbcc3gfwvneqwwrxvoif26zjmcuhoyksxdcnpjjjpbsxm.ipfs.w3s.link/et_puisBK_7.jpg",
+ "https://bafybeid5sn5wfmijo4ixlcvzqnnmpjqucqi7pf6mnr5upf7tg3zsiarzhy.ipfs.w3s.link/et_puisBK_8.jpg",
+ "https://bafybeibpgwuchjkm42hwpbrujeglpp77diw4o6f27kjpihbrhfrndxfvzu.ipfs.w3s.link/et_puisBK_9.jpg",
+ "https://bafybeigw6zhwvnmiy6t6dxhtjvfnmy2uv5iib7g465ii2pp7otjmbitmsq.ipfs.w3s.link/et_puisBK_10.jpg",
+ "https://bafybeibjtypyvp5gjvldbbo7z4jnq52qw6if6cqgwwn32r526dygzdoyf4.ipfs.w3s.link/et_puisBK_11.jpg",
+ "https://bafybeicamgpbio5fghlau4t6cajxemttsjmf3mcsueoen2ehybgdemm7jm.ipfs.w3s.link/et_puisBK_12.jpg",
+ "https://bafybeigcgk54wvsrcqwk6rzwqxfri27u4th3rukcuhfhztp2dfncgtexiy.ipfs.w3s.link/et_puisBK_13.jpg",
+ "https://bafybeiewptq3ylx32gckjxaxpg4gioro5hz2r74a6ut54wwkqzeas4dwxa.ipfs.w3s.link/et_puisBK_14.jpg",
+ "https://bafybeibxscafzx2zizv3fcbewk7g4scvbfwd7pwyay2fpcw76ixov2dquy.ipfs.w3s.link/et_puisBK_15.jpg",
+ "https://bafybeiahyzchhowf3chkbs26y3u743kz7eh4idogpbmad66lk7iulsondi.ipfs.w3s.link/et_puisBK_16.jpg",
+ "https://bafybeic5wboyolq736quq4i3dm2qg7rei35ujst6nywwgrlxawrllz3sie.ipfs.dweb.link/et_puisBK_17.jpg",
+ "https://bafybeiawebroia6j7qglx4dhlsev4slqtgu2mqjnepdi6ka6jtjmj762qy.ipfs.w3s.link/et_puisBK_18.jpg",
+ "https://bafybeibscztchiurjtq2tzrjh5jtzpkoygmqsrmrqdat34o3amxsfkkzm4.ipfs.w3s.link/et_puisBK_19.jpg",
+ "https://bafybeicneamma5bz2byzynk5re3rwklgglcmlk2xbqp3xcda3nd4sypmu4.ipfs.w3s.link/et_puisBK_20.jpg",
+ "https://bafybeidznwxrql55ig4rumlv2tovfzcu2iaja66vy6lscfufeyxrbmgax4.ipfs.w3s.link/et_puisBK_21.jpg",
+ "https://bafybeicom6nyaarxnxqk5kz66uxidhd2accwzi6oyxzgfzyrcvnfmrn7lq.ipfs.w3s.link/et_puisBK_22.jpg",
+ "https://bafybeid2yip54zx4opmv2hzing6qxalzsugj5jhxjndktvwzpiaxnywemi.ipfs.w3s.link/et_puisBK_23.jpg",
+ "https://bafybeihrxuqdzcdmxwkeggzm62rbjezgmeyyiya5xorwjmktrxyshw3neq.ipfs.w3s.link/et_puisBK_24.jpg",
+ "https://bafybeifnw66wjbngkbybcqc4bes2f7pujt3r7qe6koqml3cyuo2xaiztoq.ipfs.w3s.link/et_puisBK_25.jpg",
+ "https://bafybeihnlgdwhzityuro4jbcljd2eubqtpqc3o4qkbbolypp2wjhveqc3y.ipfs.w3s.link/et_puisBK_26.jpg"
+];
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ collectionPreviewImage,
+ description,
+ externalUrl,
+ image,
+ nftname
+ );
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add images to contract via addImageToCollection() function
+ for (let i = 0; i < images.length; i++) {
+ console.log("Adding image", images[i]);
+ const tx = await instance.addImageToCollection(images[i]);
+ await tx.wait();
+ sleep(2000);
+ }
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + collectionPreviewImage + '" "' + description + '" "' + externalUrl + '" "' + image + '" "' + nftname + '"');
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/other/addOldNftsToNewLaunchpad.js b/scripts/launchpad/erc721/other/addOldNftsToNewLaunchpad.js
new file mode 100644
index 0000000..00dcff4
--- /dev/null
+++ b/scripts/launchpad/erc721/other/addOldNftsToNewLaunchpad.js
@@ -0,0 +1,39 @@
+// Add NFTs from the old launchpad to the new one
+// npx hardhat run scripts/launchpad/erc721/other/addOldNftsToNewLaunchpad.js --network flare
+
+const launchpadContractName = "IggyLaunchpad721Bonding";
+const newLaunchpadAddress = "";
+
+const nfts = [
+
+];
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Signer account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ const launchpadContract = await ethers.getContractFactory(launchpadContractName);
+ const instance = await launchpadContract.attach(newLaunchpadAddress);
+
+ // add nfts to contract via addImageToCollection() function
+ for (let i = 0; i < nfts.length; i++) {
+ console.log("Adding NFT", nfts[i]);
+ const tx = await instance.addNftAddressToAll(nfts[i]);
+ await tx.wait();
+ sleep(2000);
+ }
+
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/other/calls.js b/scripts/launchpad/erc721/other/calls.js
new file mode 100644
index 0000000..9cbaf7c
--- /dev/null
+++ b/scripts/launchpad/erc721/other/calls.js
@@ -0,0 +1,27 @@
+// npx hardhat run scripts/launchpad/erc721/other/calls.js --network zkfair
+
+const contractName = "IggyLaunchpad721Bonding";
+
+const apAddress = "0x50045895e1983F39FDC149C9a5AC29C39BEA18fe";
+
+async function main() {
+ const [caller] = await ethers.getSigners();
+
+ console.log("Calling functions with the account:", caller.address);
+ console.log("Account balance:", (await caller.getBalance()).toString());
+
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.attach(apAddress);
+
+ // check statsAddress
+ const statsAddress = await instance.statsAddress();
+ console.log("Stats address:", statsAddress);
+
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/launchpad/erc721/other/nftRaffle.js b/scripts/launchpad/erc721/other/nftRaffle.js
new file mode 100644
index 0000000..a090fdd
--- /dev/null
+++ b/scripts/launchpad/erc721/other/nftRaffle.js
@@ -0,0 +1,90 @@
+// script to select 10 random winners from a list of NFT holders and creators
+// npx hardhat run scripts/launchpad/erc721/other/nftRaffle.js --network scroll
+
+const { ethers } = require("hardhat");
+
+const nftContracts = [
+ {"address": "0xF06f2Dd99661b970186d56EA1a0685761C17b8eF", "startId": 1 },
+ {"address": "0x96dBE349c2028C5Fe479CB2f550e71572F1b1c37", "startId": 1 },
+ {"address": "0x1dE2C37C0cf63FeF18D278038D16c2C1D2b4e53F", "startId": 1 },
+ {"address": "0xa9BD3C96B25D9dc4C9D2Dc00f4185608C0f5f8dF", "startId": 1 },
+ {"address": "0xE4e2D31ABB4DdD278fCD50111DefC51bB2305b0E", "startId": 1 },
+ {"address": "0x412E803AE9b993dF7ce1cc0e4518bCa54E4ecb84", "startId": 1 },
+ {"address": "0x090c7b67f741c7e16b974F97650b6B6fdB0fE700", "startId": 1 },
+ {"address": "0x72aEa1226a5CEcf1301913068F3fD4a428F568B7", "startId": 1 },
+ {"address": "0x4011977b0C5F12E0A69D4EDb82b9bA2D9C3dB445", "startId": 1 },
+ {"address": "0x12657039647c9ff3cB867D499AD8599A246e22c9", "startId": 1 },
+ {"address": "0xA8E849F15D6Fb33e2b5DF2569Cfd57E5482E3a38", "startId": 1 },
+];
+
+// holder/creator addresses to exclude from the raffle
+const excludedAddresses = [
+ "0xb29050965A5AC70ab487aa47546cdCBc97dAE45D",
+ "0x5FfD23B1B0350debB17A2cB668929aC5f76d0E18",
+ "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"
+];
+
+async function main() {
+ let eligibleAddresses = [];
+
+ for (let i = 0; i < nftContracts.length; i++) {
+ sleep(1000);
+ console.log("Contract", nftContracts[i].address);
+ const nftContract = await ethers.getContractFactory("Nft721Bonding");
+ const instance = nftContract.attach(nftContracts[i].address);
+
+ // get contract owner
+ const owner = await instance.owner();
+ console.log("owner", owner);
+ if (!excludedAddresses.includes(owner) && owner != ethers.constants.AddressZero) {
+ console.log("adding owner", owner);
+ eligibleAddresses.push(owner);
+ }
+
+ sleep(1000);
+
+ // TODO!!!
+ // if counter variable was not set to public (meaning you cannot call counter() view function), then
+ // set a large enough counter value manually
+ const counter = 100; //await instance.counter();
+
+ for (let j = nftContracts[i].startId; j <= counter; j++) {
+ try {
+ sleep(1000);
+ const holder = await instance.ownerOf(j);
+ console.log("holder", holder);
+ if (excludedAddresses.includes(holder) || holder == ethers.constants.AddressZero) {
+ console.log("skipping", j);
+ continue;
+ }
+
+ eligibleAddresses.push(holder);
+ } catch (e) {
+ console.log("Non-existent token ID:", j);
+ break;
+ }
+ }
+ }
+
+ // remove duplicates
+ eligibleAddresses = [...new Set(eligibleAddresses)];
+
+ // if eligibleAddresses length is larger than 10, select 10 random addresses
+ if (eligibleAddresses.length > 10) {
+ eligibleAddresses = shuffle(eligibleAddresses);
+ eligibleAddresses = eligibleAddresses.slice(0, 10);
+ }
+
+ console.log("eligibleAddresses", eligibleAddresses);
+}
+
+function sleep(ms) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/merkle/claimers.json b/scripts/merkle/claimers.json
new file mode 100644
index 0000000..c272b24
--- /dev/null
+++ b/scripts/merkle/claimers.json
@@ -0,0 +1,7 @@
+{
+ "claimers": [
+ ["0xb29050965a5ac70ab487aa47546cdcbc97dae45d", 1],
+ ["0x5FfD23B1B0350debB17A2cB668929aC5f76d0E18", 1],
+ ["0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2", 1]
+ ]
+}
\ No newline at end of file
diff --git a/scripts/merkle/merkleClaimerERC721.deploy.js b/scripts/merkle/merkleClaimerERC721.deploy.js
new file mode 100644
index 0000000..9f614ab
--- /dev/null
+++ b/scripts/merkle/merkleClaimerERC721.deploy.js
@@ -0,0 +1,47 @@
+// npx hardhat run scripts/merkle/merkleClaimerERC721.deploy.js --network polygonMumbai
+
+const data = require("./claimers.json");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+
+const contractName = "MerkleClaimerERC721";
+const nftContractName = "EarlyStakerNft"; // TODO: Update this contract name
+
+const nftAddress = ""; // TODO: address of deployed nft contract
+
+// create merkle tree
+tree = StandardMerkleTree.of(data.claimers, ["address", "uint256"]); // TODO: Make sure you have the right data in claimers.json
+const merkleRoot = String(tree.root);
+
+console.log("Merkle root: " + merkleRoot);
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contract with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ nftAddress,
+ merkleRoot
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add minter address to nft contract
+ const nftContract = await ethers.getContractFactory(nftContractName);
+ const nftInstance = await nftContract.attach(nftAddress);
+
+ await nftInstance.changeMinterAddress(instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + nftAddress + " " + merkleRoot);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/mock/mockCreateGovernanceProposal.js b/scripts/mock/mockCreateGovernanceProposal.js
new file mode 100644
index 0000000..134a959
--- /dev/null
+++ b/scripts/mock/mockCreateGovernanceProposal.js
@@ -0,0 +1,50 @@
+// npx hardhat run scripts/mock/mockCreateGovernanceProposal.js --network flareCoston
+
+const tokenAddress = "0x633Ae857445cF0cd02B21C6a3033C7CE74fB32c2"; // governance token contract address
+const governorAddress = "0x06A7Ab7Bb68b0ad6eB7688C5781E60BE6AFc658d"; // governor contract address
+
+const tokenReceiverAddress = "0xb29050965A5AC70ab487aa47546cdCBc97dAE45D"; // address to receive tokens through governance proposal
+const grantAmount = ethers.utils.parseEther("69"); // amount of tokens to send through governance proposal
+
+async function main() {
+ // create token instance
+ const tokenContract = await ethers.getContractFactory("MockErc20TokenVotingBlock");
+ const tokenContractInstance = await tokenContract.attach(tokenAddress);
+
+ // create governor instance
+ const governorContract = await ethers.getContractFactory("MockGovernorBlock");
+ const governorContractInstance = await governorContract.attach(governorAddress);
+
+ console.log("Make sure that deployer address has enough tokens delegated to, to send through governance proposal");
+
+ // proposal to send some tokens from governor contract to another address
+ const transferCalldata = tokenContractInstance.interface.encodeFunctionData("transfer", [tokenReceiverAddress, grantAmount]);
+
+ /*
+ const proposalId = await governorContractInstance.propose(
+ [tokenAddress],
+ [0],
+ [transferCalldata],
+ "Proposal #4: Give grant to team",
+ );
+
+ console.log(proposalId);
+ */
+
+ const proposalId2 = await governorContractInstance.hashProposal(
+ [tokenAddress],
+ [0],
+ [transferCalldata],
+ ethers.utils.keccak256(ethers.utils.toUtf8Bytes("Proposal #4: Give grant to team")),
+ );
+
+ console.log(proposalId2);
+ console.log(String(proposalId2));
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/mock/mockERC721.deploy.js b/scripts/mock/mockERC721.deploy.js
new file mode 100644
index 0000000..83e7534
--- /dev/null
+++ b/scripts/mock/mockERC721.deploy.js
@@ -0,0 +1,31 @@
+// npx hardhat run scripts/mock/mockERC721.deploy.js --network polygonMumbai
+
+const contractName = "MockErc721WithMinter";
+
+const tokenName = "Mock NFT";
+const symbol = "MNFT";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tokenName, symbol
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + tokenName + '" "' + symbol + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/mock/mockGovernorBlock.deploy.js b/scripts/mock/mockGovernorBlock.deploy.js
new file mode 100644
index 0000000..e10ab5f
--- /dev/null
+++ b/scripts/mock/mockGovernorBlock.deploy.js
@@ -0,0 +1,30 @@
+// npx hardhat run scripts/mock/mockGovernorBlock.deploy.js --network flareCoston
+
+const contractName = "MockGovernorBlock";
+
+const tokenAddress = "0x633Ae857445cF0cd02B21C6a3033C7CE74fB32c2";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tokenAddress
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' ' + tokenAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/mock/mockToken.deploy.js b/scripts/mock/mockToken.deploy.js
new file mode 100644
index 0000000..c479f0a
--- /dev/null
+++ b/scripts/mock/mockToken.deploy.js
@@ -0,0 +1,32 @@
+// npx hardhat run scripts/mock/mockToken.deploy.js --network polygonMumbai
+
+const contractName = "MockErc20TokenDecimals";
+
+const tokenName = "Mock LP Token";
+const symbol = "MLPT";
+const decimals = 18;
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tokenName, symbol, decimals
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + tokenName + '" "' + symbol + '" "' + decimals + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/mock/mockTokenVotingBlock.deploy.js b/scripts/mock/mockTokenVotingBlock.deploy.js
new file mode 100644
index 0000000..c54e726
--- /dev/null
+++ b/scripts/mock/mockTokenVotingBlock.deploy.js
@@ -0,0 +1,31 @@
+// npx hardhat run scripts/mock/mockTokenVotingBlock.deploy.js --network flareCoston
+
+const contractName = "MockErc20TokenVotingBlock";
+
+const tokenName = "Block Voting Token";
+const symbol = "BVT";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tokenName, symbol
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + tokenName + '" "' + symbol + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/nft/early-stakers/addresses.csv b/scripts/nft/early-stakers/addresses.csv
new file mode 100644
index 0000000..6cb6746
--- /dev/null
+++ b/scripts/nft/early-stakers/addresses.csv
@@ -0,0 +1,39 @@
+0xfbB47737EBa09AE9A1bda64e739E2B73807F2d05
+0x88e19Feaa4F8cc573B06553186E3A2b5bf797490
+0x1f4150C5266e24c61f7109b650FF391ac17EBca3
+0xCE30720EDE61E9541FB9Fe99397e362530D75ccD
+0xb29050965A5AC70ab487aa47546cdCBc97dAE45D
+0x5FfD23B1B0350debB17A2cB668929aC5f76d0E18
+0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2
+0xDac4F20b21bcae1eb74c2D7256e7E945d8146dDB
+0x9AB3868ee9E6A0bA8595D26c30802f7344f80AcB
+0x648537ecE8c8022208212a61Dc41441BD0DeA23F
+0x52dee9C4b0E085e90c5E5eC9c150cE39f3940935
+0x5193877dD31b569Fedf762308aFae70B9091b951
+0x72B871dc4Fbca049a009Ec9c3c3fdBD531BeFb5F
+0x396739Ee44a7cEE1F5bF645A94BF7D489bDc0fca
+0x617258CdbcCEf7058b251a11cC4c8905B2550a7A
+0xDD3cEeae522C8030BA7649Be56B1D9aD58aA70d3
+0x877B37D3E5467B4aAE7687Dd3480A46C8D3e16Be
+0xcA4599ae99Cc0e11ecb2085A9B9458e56a555866
+0x5E8B54705FA6141FED6dd661F6bEBB58F8B1a3c2
+0x335E1cD1b9278C74F2A250F2D80E4E89A225b75E
+0x0968438CE241Eb2631EC8a7fC72DD4040A80C750
+0x5412DFA75F988849ab8dc47Bf5cb2fB1fd556078
+0x075286D1a22B083ebCAF6B7Fb4CF970cFC4A18F0
+0x0D069d8471A2bA6815CeD486bc3DC8285772283c
+0x17a2063e1f5C6034F4c94cfb0F4970483647a2E5
+0x7910B8E68AADECcD2CaCcA31c110Dbf1E37CAd3E
+0xcF5E01d5E5A0f4A4FBC1435E0C079dD4B834093c
+0x896fba9Eb262Dc7c434C6b267FA84D44f16A95f0
+0x81D8929d2821E900033b58B4B4bAce7D7ADcBA5b
+0xB4e4B1ABe50Cc1d2CD776158063a060164598669
+0x5d05626252299C3932F2F95Ab9aFEfAC97bFC656
+0x83593d13fec30806c989410f9325Fd18245b246B
+0x9001c4b60C19A2fF1Fb68b90a039509a709474A0
+0x9C95Be085a01989E5CC3592d77E015ECac44Da34
+0xb46eD5AeB116901c2523D5501152A225c38a4C45
+0xdFf0bE434270bACf2EB1B69A57dA0c0E18119471
+0x1905E8fdc8483B075f41043212Bfc17Cef0F3D27
+0x78B45FF8ffaEA7257996767d40674E717051Ce5E
+0xc16D2e43904d5359F6c2f2E9b5417f09b84702C1
\ No newline at end of file
diff --git a/scripts/nft/early-stakers/claimers.json b/scripts/nft/early-stakers/claimers.json
new file mode 100644
index 0000000..e2444d1
--- /dev/null
+++ b/scripts/nft/early-stakers/claimers.json
@@ -0,0 +1,160 @@
+{
+ "claimers": [
+ [
+ "0xfbB47737EBa09AE9A1bda64e739E2B73807F2d05",
+ 1
+ ],
+ [
+ "0x88e19Feaa4F8cc573B06553186E3A2b5bf797490",
+ 1
+ ],
+ [
+ "0x1f4150C5266e24c61f7109b650FF391ac17EBca3",
+ 1
+ ],
+ [
+ "0xCE30720EDE61E9541FB9Fe99397e362530D75ccD",
+ 1
+ ],
+ [
+ "0xb29050965A5AC70ab487aa47546cdCBc97dAE45D",
+ 1
+ ],
+ [
+ "0x5FfD23B1B0350debB17A2cB668929aC5f76d0E18",
+ 1
+ ],
+ [
+ "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2",
+ 1
+ ],
+ [
+ "0xDac4F20b21bcae1eb74c2D7256e7E945d8146dDB",
+ 1
+ ],
+ [
+ "0x9AB3868ee9E6A0bA8595D26c30802f7344f80AcB",
+ 1
+ ],
+ [
+ "0x648537ecE8c8022208212a61Dc41441BD0DeA23F",
+ 1
+ ],
+ [
+ "0x52dee9C4b0E085e90c5E5eC9c150cE39f3940935",
+ 1
+ ],
+ [
+ "0x5193877dD31b569Fedf762308aFae70B9091b951",
+ 1
+ ],
+ [
+ "0x72B871dc4Fbca049a009Ec9c3c3fdBD531BeFb5F",
+ 1
+ ],
+ [
+ "0x396739Ee44a7cEE1F5bF645A94BF7D489bDc0fca",
+ 1
+ ],
+ [
+ "0x617258CdbcCEf7058b251a11cC4c8905B2550a7A",
+ 1
+ ],
+ [
+ "0xDD3cEeae522C8030BA7649Be56B1D9aD58aA70d3",
+ 1
+ ],
+ [
+ "0x877B37D3E5467B4aAE7687Dd3480A46C8D3e16Be",
+ 1
+ ],
+ [
+ "0xcA4599ae99Cc0e11ecb2085A9B9458e56a555866",
+ 1
+ ],
+ [
+ "0x5E8B54705FA6141FED6dd661F6bEBB58F8B1a3c2",
+ 1
+ ],
+ [
+ "0x335E1cD1b9278C74F2A250F2D80E4E89A225b75E",
+ 1
+ ],
+ [
+ "0x0968438CE241Eb2631EC8a7fC72DD4040A80C750",
+ 1
+ ],
+ [
+ "0x5412DFA75F988849ab8dc47Bf5cb2fB1fd556078",
+ 1
+ ],
+ [
+ "0x075286D1a22B083ebCAF6B7Fb4CF970cFC4A18F0",
+ 1
+ ],
+ [
+ "0x0D069d8471A2bA6815CeD486bc3DC8285772283c",
+ 1
+ ],
+ [
+ "0x17a2063e1f5C6034F4c94cfb0F4970483647a2E5",
+ 1
+ ],
+ [
+ "0x7910B8E68AADECcD2CaCcA31c110Dbf1E37CAd3E",
+ 1
+ ],
+ [
+ "0xcF5E01d5E5A0f4A4FBC1435E0C079dD4B834093c",
+ 1
+ ],
+ [
+ "0x896fba9Eb262Dc7c434C6b267FA84D44f16A95f0",
+ 1
+ ],
+ [
+ "0x81D8929d2821E900033b58B4B4bAce7D7ADcBA5b",
+ 1
+ ],
+ [
+ "0xB4e4B1ABe50Cc1d2CD776158063a060164598669",
+ 1
+ ],
+ [
+ "0x5d05626252299C3932F2F95Ab9aFEfAC97bFC656",
+ 1
+ ],
+ [
+ "0x83593d13fec30806c989410f9325Fd18245b246B",
+ 1
+ ],
+ [
+ "0x9001c4b60C19A2fF1Fb68b90a039509a709474A0",
+ 1
+ ],
+ [
+ "0x9C95Be085a01989E5CC3592d77E015ECac44Da34",
+ 1
+ ],
+ [
+ "0xb46eD5AeB116901c2523D5501152A225c38a4C45",
+ 1
+ ],
+ [
+ "0xdFf0bE434270bACf2EB1B69A57dA0c0E18119471",
+ 1
+ ],
+ [
+ "0x1905E8fdc8483B075f41043212Bfc17Cef0F3D27",
+ 1
+ ],
+ [
+ "0x78B45FF8ffaEA7257996767d40674E717051Ce5E",
+ 1
+ ],
+ [
+ "0xc16D2e43904d5359F6c2f2E9b5417f09b84702C1",
+ 1
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/scripts/nft/early-stakers/earlyStakerNft.deploy.js b/scripts/nft/early-stakers/earlyStakerNft.deploy.js
new file mode 100644
index 0000000..7678717
--- /dev/null
+++ b/scripts/nft/early-stakers/earlyStakerNft.deploy.js
@@ -0,0 +1,88 @@
+// npx hardhat run scripts/nft/early-stakers/earlyStakerNft.deploy.js --network songbird
+
+const data = require("./claimers.json");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+
+const metadataContractName = "EarlyStakerMetadata";
+const nftContractName = "EarlyStakerNft";
+const claimerContractName = "MerkleClaimerERC721";
+
+// metadata
+const description = "Commemorative NFT for early CHIRP stakers on SGB Chat.";
+const externalUrl = "https://sgb.chat";
+const image = "ipfs://bafybeic3fpbvtqj6kqpu77vy56efkasgbaviguc3qm4jgy3dy7fuk7fire/early-staker-nft-sgb-chat.png";
+const mdName = "Early CHIRP Staker";
+const video = "ipfs://bafybeibajqsxbuihg4jxsmlnees2gytagp4gqwxr2sielhe2bcbgrjbi2y/early-staker-nft-sgb-chat-2.mp4";
+
+const nftName = "Early CHIRP Staker";
+const nftSymbol = "earlyCHIRP";
+
+// create merkle tree
+tree = StandardMerkleTree.of(data.claimers, ["address", "uint256"]); // TODO: Make sure you have the right data in claimers.json
+const merkleRoot = String(tree.root);
+
+console.log("Merkle root: " + merkleRoot);
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy metadata contract
+ console.log("Deploying " + metadataContractName + " contract");
+ const metadataContract = await ethers.getContractFactory(metadataContractName);
+ const metadataInstance = await metadataContract.deploy(
+ description,
+ externalUrl,
+ image,
+ mdName,
+ video
+ );
+ await metadataInstance.deployed();
+
+ console.log(metadataContractName + " contract address:", metadataInstance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + metadataInstance.address + ' "' + description + '" "' + externalUrl + '" "' + image + '" "' + mdName + '" "' + video + '"');
+
+ // deploy NFT contract
+ console.log("Deploying " + nftContractName + " contract");
+ const nftContract = await ethers.getContractFactory(nftContractName);
+ const nftInstance = await nftContract.deploy(
+ metadataInstance.address,
+ nftName,
+ nftSymbol
+ );
+ await nftInstance.deployed();
+
+ console.log(nftContractName + " contract address:", nftInstance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + nftInstance.address + " " + metadataInstance.address + ' "' + nftName + '" "' + nftSymbol + '"');
+
+ // deploy claimer contract
+ console.log("Deploying " + claimerContractName + " contract");
+ const claimerContract = await ethers.getContractFactory(claimerContractName);
+ const claimerInstance = await claimerContract.deploy(
+ nftInstance.address,
+ merkleRoot
+ );
+ await claimerInstance.deployed();
+
+ console.log(claimerContractName + " contract address:", claimerInstance.address);
+
+ // add minter address to nft contract
+ console.log("Adding minter address to " + nftContractName + " contract");
+ await nftInstance.changeMinterAddress(claimerInstance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + claimerInstance.address + " " + nftInstance.address + " " + merkleRoot);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/nft/early-stakers/generateClaimersJson.js b/scripts/nft/early-stakers/generateClaimersJson.js
new file mode 100644
index 0000000..4e3d73b
--- /dev/null
+++ b/scripts/nft/early-stakers/generateClaimersJson.js
@@ -0,0 +1,49 @@
+// Run: node scripts/nft/early-stakers/generateClaimersJson.js
+const fs = require('fs');
+
+// Path to the CSV file
+const csvFilePath = 'scripts/nft/early-stakers/addresses.csv'; // @TODO: Change the path
+
+// Read the CSV file
+fs.readFile(csvFilePath, 'utf8', (err, data) => {
+ if (err) {
+ console.error('Error reading the CSV file:', err);
+ return;
+ }
+
+ // Split the data by newline to get individual addresses
+ const addresses = data.trim().split('\n');
+
+ // Array to store claimers
+ const claimers = [];
+
+ // Process each address
+ addresses.forEach((address) => {
+ const trimmedAddress = address.trim();
+
+ // Validate ETH address format
+ if (/^(0x)?[0-9a-fA-F]{40}$/.test(trimmedAddress)) {
+ // Append the address to the claimers array
+ claimers.push([trimmedAddress, 1]);
+ } else {
+ console.log(`Invalid ETH address: ${trimmedAddress}`);
+ }
+ });
+
+ // Create the JSON object
+ const jsonData = {
+ claimers: claimers,
+ };
+
+ // Write the JSON object to a file
+ fs.writeFile(
+ 'scripts/nft/early-stakers/claimers.json', // @TODO: Change the path
+ JSON.stringify(jsonData, null, 2), (err) => {
+ if (err) {
+ console.error('Error writing JSON file:', err);
+ return;
+ }
+ console.log('JSON file created successfully!');
+ }
+ );
+});
diff --git a/scripts/nft/early-stakers/onlyMetadata.deploy.js b/scripts/nft/early-stakers/onlyMetadata.deploy.js
new file mode 100644
index 0000000..c18afd8
--- /dev/null
+++ b/scripts/nft/early-stakers/onlyMetadata.deploy.js
@@ -0,0 +1,46 @@
+// npx hardhat run scripts/nft/early-stakers/onlyMetadata.deploy.js --network polygonMumbai
+
+const contractName = "EarlyStakerMetadata";
+
+const description = "Commemorative NFT for early SGB Chat stakers.";
+const externalUrl = "https://sgb.chat";
+const image = "ipfs://bafybeic3fpbvtqj6kqpu77vy56efkasgbaviguc3qm4jgy3dy7fuk7fire/early-staker-nft-sgb-chat.png";
+const mdName = "Early SGB Chat Staker";
+const video = "ipfs://bafybeibajqsxbuihg4jxsmlnees2gytagp4gqwxr2sielhe2bcbgrjbi2y/early-staker-nft-sgb-chat-2.mp4";
+
+const nftAddress = "0x8e769129F8CAc93d2EF1c46a4d3E6Fa32Bc7b569";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const metadataInstance = await contract.deploy(
+ description,
+ externalUrl,
+ image,
+ mdName,
+ video
+ );
+ await metadataInstance.deployed();
+
+ // add metadata address to the NFT contract
+ const nftContract = await ethers.getContractFactory("EarlyStakerNft");
+ const nftInstance = await nftContract.attach(nftAddress);
+ await nftInstance.changeMetadataAddress(metadataInstance.address);
+
+ console.log(contractName + " contract address:", metadataInstance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + metadataInstance.address + ' "' + description + '" "' + externalUrl + '" "' + image + '" "' + mdName + '" "' + video + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/other/manager/addManager.js b/scripts/other/manager/addManager.js
new file mode 100644
index 0000000..d765b6c
--- /dev/null
+++ b/scripts/other/manager/addManager.js
@@ -0,0 +1,55 @@
+// npx hardhat run scripts/other/manager/addManager.js --network base
+
+const managerAddress = "";
+
+const contractAddresses = [
+ "", // Post NFT
+ "", // Post metadata
+ "", // Post minter
+ "", // Post stats
+
+ "", // Launchpad
+ "", // Launchpad stats middleware
+ "", // Launchpad stats
+
+ "", // Keys
+ "", // Keys stats
+
+ "", // Activity points
+];
+
+async function main() {
+ const [ owner ] = await ethers.getSigners();
+
+ console.log("Running script with the account:", owner.address);
+ console.log("Account balance:", (await owner.getBalance()).toString());
+
+ const intrfc = new ethers.utils.Interface([
+ "function addManager(address manager_) external"
+ ]);
+
+ for (let i = 0; i < contractAddresses.length; i++) {
+ const contractAddress = contractAddresses[i];
+ console.log(`Adding manager address ${managerAddress} to ${contractAddress}`);
+
+ try {
+ const contract = new ethers.Contract(contractAddress, intrfc, owner);
+ const tx = await contract.addManager(managerAddress);
+ await tx.wait();
+ } catch (error) {
+ console.log(error.code);
+ continue;
+ }
+
+ console.log(`Manager added to ${contractAddress}`);
+ }
+
+ console.log("Done");
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/1_metadata.deploy.js b/scripts/post/IggyPostNft1155/1_metadata.deploy.js
new file mode 100644
index 0000000..974259d
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/1_metadata.deploy.js
@@ -0,0 +1,39 @@
+// 1. Deploy metadata contract
+// npx hardhat run scripts/post/IggyPostNft1155/1_metadata.deploy.js --network modeTestnet
+
+const contractName = "IggyPostMetadata";
+
+const mdName = "ModeChat Post";
+const description = "ModeChat.xyz is the first decentralized social network on Mode Network. Go visit here: https://modechat.xyz/";
+const url = "https://modechat.xyz/post/";
+const tldAddress = "";
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(mdName, description, url, tldAddress, sfsAddress, sfsNftTokenId);
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + mdName + '" "' + description + '" "' + url + '" ' + tldAddress + " " + sfsAddress + ' "' + sfsNftTokenId + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/1_metadataStaticColor.deploy.js b/scripts/post/IggyPostNft1155/1_metadataStaticColor.deploy.js
new file mode 100644
index 0000000..fdac8b4
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/1_metadataStaticColor.deploy.js
@@ -0,0 +1,40 @@
+// 1. Deploy metadata contract
+// npx hardhat run scripts/post/IggyPostNft1155/1_metadataStaticColor.deploy.js --network modeTestnet
+
+const contractName = "IggyPostMetadataStaticColor";
+
+const colorCode = "#DFFE00";
+const mdName = "ModeChat Post";
+const description = "ModeChat.xyz is the first decentralized social network on Mode Network. Go visit here: https://modechat.xyz/";
+const url = "https://modechat.xyz/post/";
+const tldAddress = "";
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(colorCode, mdName, description, url, tldAddress, sfsAddress, sfsNftTokenId);
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + colorCode + '" "' + mdName + '" "' + description + '" "' + url + '" ' + tldAddress + " " + sfsAddress + ' "' + sfsNftTokenId + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/2_iggyPostNft1155.deploy.js b/scripts/post/IggyPostNft1155/2_iggyPostNft1155.deploy.js
new file mode 100644
index 0000000..ceb84b2
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/2_iggyPostNft1155.deploy.js
@@ -0,0 +1,46 @@
+// 2. Deploy NFT contract
+// npx hardhat run scripts/post/IggyPostNft1155/2_iggyPostNft1155.deploy.js --network modeTestnet
+
+const contractName = "IggyPostNft1155";
+
+const defaultPrice = ethers.utils.parseEther("0.00001"); // TODO: change price!!!
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+const metadataAddress = "0x633Ae857445cF0cd02B21C6a3033C7CE74fB32c2";
+const collectionName = "ModeChat Posts";
+const collectionSymbol = "MODECHATPOST";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ defaultPrice,
+ sfsAddress,
+ sfsNftTokenId,
+ metadataAddress,
+ collectionName,
+ collectionSymbol
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + defaultPrice + '" ' + sfsAddress + ' "' + sfsNftTokenId + '" ' + metadataAddress + ' "' + collectionName + '" "' + collectionSymbol + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/3_minter.deploy.js b/scripts/post/IggyPostNft1155/3_minter.deploy.js
new file mode 100644
index 0000000..115662a
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/3_minter.deploy.js
@@ -0,0 +1,66 @@
+// 3. Deploy minter contract
+// npx hardhat run scripts/post/IggyPostNft1155/3_minter.deploy.js --network modeTestnet
+
+const contractName = "IggyPostMinter";
+
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+const daoAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // distributor contract
+const devAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // iggy address
+const postAddress = "0x06A7Ab7Bb68b0ad6eB7688C5781E60BE6AFc658d";
+
+const daoFee = 2000; // 10%
+const devFee = 0; // 10%
+const refFee = 1000; // 10%
+
+const postInterface = new ethers.utils.Interface([
+ "function ownerChangeMinterAddress(address _newMinterAddress) external"
+]);
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ sfsAddress,
+ daoAddress,
+ devAddress,
+ sfsNftTokenId,
+ postAddress,
+ daoFee,
+ devFee,
+ refFee
+ );
+
+ await instance.deployed();
+
+ console.log("Contract deployed to:", instance.address);
+
+ console.log("Changing minter address in post contract...");
+
+ // change minter address in post contract
+ const postContract = new ethers.Contract(postAddress, postInterface, deployer);
+ await postContract.ownerChangeMinterAddress(instance.address);
+
+ console.log("Done!");
+
+ // verify contracts
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + sfsAddress + " " + daoAddress + " " + devAddress + ' "' + sfsNftTokenId + '" ' + postAddress + ' "' + daoFee + '" "' + devFee + '" "' + refFee + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/4_stats.deploy.js b/scripts/post/IggyPostNft1155/4_stats.deploy.js
new file mode 100644
index 0000000..babc020
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/4_stats.deploy.js
@@ -0,0 +1,67 @@
+// 4. Deploy stats contract
+// npx hardhat run scripts/post/IggyPostNft1155/4_stats.deploy.js --network modeTestnet
+
+const contractName = "IggyPostStats";
+
+const sfsAddress = (network.name == "modeTestnet") ? "0xBBd707815a7F7eb6897C7686274AFabd7B579Ff6" : "0x8680CEaBcb9b56913c519c069Add6Bc3494B7020";
+const sfsNftTokenId = 0; // TODO: Enter SFS NFT token ID!!!
+
+if (sfsNftTokenId == 0) {
+ console.log("Please enter SFS NFT token ID!!!");
+ return;
+}
+const minterAddress = "0x5e54CebB2612744cB56547bC7CC41466ad7ac557";
+const shouldStatsBeEnabled = true;
+
+const minterInterface = new ethers.utils.Interface([
+ "function changeStatsAddress(address _statsAddress) external",
+ "function statsEnabled() external view returns (bool)",
+ "function toggleStatsEnabled() external"
+]);
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(sfsAddress, sfsNftTokenId, minterAddress);
+
+ await instance.deployed();
+
+ console.log(contractName + " contract deployed to:", instance.address);
+
+ console.log("Changing stats address in minter contract...");
+
+ // add stats address to minter contract
+ const minterContract = new ethers.Contract(minterAddress, minterInterface, deployer);
+ const changeStatsAddrTx = await minterContract.changeStatsAddress(instance.address);
+ await changeStatsAddrTx.wait();
+
+ console.log("Done!");
+
+ console.log("Changing statsEnabled in minter contract...");
+
+ // enable/disable stats
+ const isStatsEnabled = await minterContract.statsEnabled();
+
+ if (isStatsEnabled && !shouldStatsBeEnabled) {
+ await minterContract.toggleStatsEnabled();
+ } else if (!isStatsEnabled && shouldStatsBeEnabled) {
+ await minterContract.toggleStatsEnabled();
+ }
+
+ console.log("Done!");
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + sfsAddress + ' "' + sfsNftTokenId + '" ' + minterAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/calls.js b/scripts/post/IggyPostNft1155/calls.js
new file mode 100644
index 0000000..7ab9a01
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/calls.js
@@ -0,0 +1,113 @@
+// npx hardhat run scripts/post/IggyPostNft1155/calls.js --network songbird
+
+const postAddress = "0xE33F27496A9cE75313f6d1FA2BA95657Fc904387";
+const minterAddress = "0x9e9905FA405A5FC7Ee2DEB94CbAc089B4FE6f0Ef";
+const metadataAddress = "0xdADFC61225BC17785E185FD6F88619e42D996472";
+const statsAddress = "0xE2AfE33f16519e31c6FFE5eEb333A0887a44D2BC";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Calling methods with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ const postInterface = new ethers.utils.Interface([
+ "function ownerChangeDefaultPrice(uint256 _newDefaultPrice) external",
+ "function ownerChangeMinterAddress(address _newMinterAddress) external",
+ "function minterAddress() view external returns(address)",
+ "function mint(string memory, address, address, string memory, uint256) external returns(uint256)"
+ ]);
+
+ const metadataInterface = new ethers.utils.Interface([
+ "function changeDescription(string calldata _description) external"
+ ]);
+
+ const minterInterface = new ethers.utils.Interface([
+ "function getCurrentChatEthRatio() public view returns(uint256)", // v2 specific function
+ "function changeChatEthRatio(uint256 _chatEthRatio) external", // v2 specific function
+ "function paused() public view returns(bool)",
+ "function togglePaused() external",
+ "function transferOwnership(address newOwner) external"
+ ]);
+
+ const statsInterface = new ethers.utils.Interface([
+ "function minterAddress() view external returns(address)",
+ "function setMinterAddress(address _minterAddress) external"
+ ]);
+
+ const postContract = new ethers.Contract(postAddress, postInterface, deployer);
+ const metadataContract = new ethers.Contract(metadataAddress, metadataInterface, deployer);
+ const minterContract = new ethers.Contract(minterAddress, minterInterface, deployer);
+ const statsContract = new ethers.Contract(statsAddress, statsInterface, deployer);
+
+ // GET CURRENT CHAT ETH RATIO
+ //const currentChatEthRatio = await minterContract.getCurrentChatEthRatio();
+ //console.log("Current chat eth ratio: " + currentChatEthRatio);
+
+ // CHANGE CHAT ETH RATIO
+ //await minterContract.changeChatEthRatio(10);
+
+ // CHANGE DEFAULT PRICE
+ //await postContract.ownerChangeDefaultPrice(ethers.utils.parseEther("99"));
+
+ // MINTER: TOGGLE PAUSED
+ //await minterContract.togglePaused();
+
+ // check if paused
+ /*
+ const paused = await minterContract.paused();
+ console.log("Paused: " + paused);
+ */
+
+ // CHANGE MINTER
+
+ /*
+
+ const minterBefore = await postContract.minterAddress();
+ console.log("Minter before: " + minterBefore);
+
+ //await postContract.ownerChangeMinterAddress(minterAddress);
+
+ const minterAfter = await postContract.minterAddress();
+ console.log("Minter after: " + minterAfter);
+
+ // MINT NFT
+
+ const mintTx = await postContract.mint(
+ "kjzl6cwe1jw14atwguxr7gatk5b4ekzk10jono427hh2d3v8zyquwhw8puqq130", // post ID
+ "0xb29050965a5ac70ab487aa47546cdcbc97dae45d", // post author
+ "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2", // nft recipient
+ "test 456", // text preview
+ 1 // quantity
+ );
+
+ const mintReceipt = await mintTx.wait();
+ console.log("Mint receipt: ");
+ console.log(mintReceipt);
+
+ */
+
+ // CHANGE METADATA DESCRIPTION
+ /*
+ await metadataContract.changeDescription(
+ "Description text"
+ );
+ */
+
+ // CHANGE STATS ADDRESS
+ const statsMinterAddressBefore = await statsContract.minterAddress();
+ console.log("Stats minter address before: " + statsMinterAddressBefore);
+
+ //await statsContract.setMinterAddress(minterAddress);
+
+ const statsMinterAddressAfter = await statsContract.minterAddress();
+ console.log("Stats minter address after: " + statsMinterAddressAfter);
+
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/post/IggyPostNft1155/minterV2.deploy.js b/scripts/post/IggyPostNft1155/minterV2.deploy.js
new file mode 100644
index 0000000..d8efdc3
--- /dev/null
+++ b/scripts/post/IggyPostNft1155/minterV2.deploy.js
@@ -0,0 +1,135 @@
+// Deploy minter V2 contract
+// npx hardhat run scripts/post/IggyPostNft1155/minterV2.deploy.js --network polygonMumbai
+// It will automatically set different fees (if needed).
+// It will also automatically add the minter to the ChatTokenMinter contract and change the minter address in the post contract.
+// If any of these actions fail, you must do them manually.
+
+const contractName = "IggyPostMinterV2";
+
+const chatTokenMinterAddress = "0x2C6A9F47a2B1BA7976ACd14CDd8f6f35d27C1e28";
+const daoAddress = "0xb29050965a5ac70ab487aa47546cdcbc97dae45d"; // DAO or web3 community which owns the frontend
+const devAddress = "0xb29050965a5ac70ab487aa47546cdcbc97dae45d"; // person or entity that is doing the development
+const devFeeUpdaterAddress = "0xb29050965a5ac70ab487aa47546cdcbc97dae45d"; // the address that can change dev fee (can be a multisig)
+const postAddress = "0x63FE8216a66737CFE474DF3949F9081EbD4Bd800";
+const chatEthRatio = 10; // 1 ETH/SGB = 10 CHAT
+const chatRewardsDuration = 60 * 60 * 24 * 30 * 11; // 30 days * 12 months = 1 year
+
+// stats contract
+const statsEnabled = false; // have it enabled by default so that users can see minted posts on their profile
+const statsAddress = "";
+
+// set fees separately (only set if needed)
+let daoFee = 0; // = 450; // 4.5%
+let devFee = 0; // = 900; // 9%
+let referrerFee; // = 200; // = 200; // 2%
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ chatTokenMinterAddress,
+ daoAddress,
+ devAddress,
+ devFeeUpdaterAddress,
+ postAddress,
+ chatEthRatio,
+ chatRewardsDuration
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ await instance.deployed();
+
+ console.log("Deploy transaction mined!");
+
+ console.log("Add post minter contract address as a minter in the ChatTokenMinter contract");
+
+ // add as minter to ChatTokenMinter contract
+ const chatTokenMinterContract = await ethers.getContractFactory("ChatTokenMinter");
+ const chatTokenMinterInstance = await chatTokenMinterContract.attach(chatTokenMinterAddress);
+ const tx1 = await chatTokenMinterInstance.addMinter(instance.address);
+ await tx1.wait();
+
+ // change minter address in post contract
+ console.log("Change minter address in post contract");
+ console.log("MAKE SURE THE POST CONTRACT IS OWNED BY THIS DEPLOYER! (Otherwise make the transaction manually)");
+
+ const postContract = await ethers.getContractFactory("IggyPostNft1155");
+ const postContractInstance = await postContract.attach(postAddress);
+
+ const postContractOwner = await postContractInstance.owner();
+ console.log("Post contract owner:", postContractOwner);
+
+ if (String(postContractOwner).toLowerCase() == String(deployer.address).toLowerCase()) {
+ const tx2 = await postContractInstance.ownerChangeMinterAddress(instance.address);
+ await tx2.wait();
+ } else {
+ console.log("Post contract is not owned by this deployer. Please change the minter address manually.");
+ }
+
+ // change post minter address in stats contract
+ if (statsEnabled && statsAddress) {
+ console.log("Change post minter address in stats contract");
+
+ const statsContract = await ethers.getContractFactory("IggyPostStats");
+ const statsContractInstance = await statsContract.attach(statsAddress);
+
+ // setMinterAddress
+ const tx2b = await statsContractInstance.setMinterAddress(instance.address);
+ await tx2b.wait();
+
+ // check if statsEnabled in smart contract is true
+ const statsEnabledInContract = await instance.statsEnabled();
+
+ if (!statsEnabledInContract) {
+ console.log("Stats enabled in contract is false. Enabling it now...");
+ const tx2c = await instance.toggleStatsEnabled();
+ await tx2c.wait();
+ }
+
+ // change stats address in post minter contract
+ console.log("Change stats address in post minter contract");
+ const tx2d = await instance.changeStatsAddress(statsAddress);
+ await tx2d.wait();
+ }
+
+ // set dao fee
+ if (daoFee > 0) {
+ console.log("Setting DAO fee...");
+ const tx4 = await instance.changeDaoFee(daoFee);
+ await tx4.wait();
+ console.log("DAO fee set!");
+ }
+
+ // set dev fee
+ if (devFee > 0) {
+ console.log("Setting dev fee...");
+ const tx5 = await instance.changeDevFee(devFee);
+ await tx5.wait();
+ console.log("Dev fee set!");
+ }
+
+ // set referrer fee
+ if (referrerFee > 0) {
+ console.log("Setting referrer fee...");
+ const tx6 = await instance.changeReferrerFee(referrerFee);
+ await tx6.wait();
+ console.log("Referrer fee set!");
+ }
+
+ // verify contract
+ console.log("Wait a minute and then run this command to verify contract on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + chatTokenMinterAddress + " " + daoAddress + " " + devAddress + " " + devFeeUpdaterAddress + " " + postAddress + ' "' + chatEthRatio + '" "' + chatRewardsDuration + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/staking/addStakingAddressToPostMinter.js b/scripts/staking/addStakingAddressToPostMinter.js
new file mode 100644
index 0000000..279f937
--- /dev/null
+++ b/scripts/staking/addStakingAddressToPostMinter.js
@@ -0,0 +1,29 @@
+// npx hardhat run scripts/staking/addStakingAddressToPostMinter.js --network songbird
+
+const stakingContractAddress = "0xCA9749778327CD67700d3a777731a712330beB9A"; // staking contract address
+const postMinterAddress = "0xeC5Af9F794B9f26bB62Cd951088445c95EAF428D"; // post minter contract address
+
+async function main() {
+ // create post minter contract instance
+ const postMinterContract = await ethers.getContractFactory("IggyPostMinterV2");
+ const postMinterContractInstance = await postMinterContract.attach(postMinterAddress);
+
+ // fetch staking address before
+ const stakingAddressBefore = await postMinterContractInstance.stakingAddress();
+ console.log("Staking address before:", stakingAddressBefore);
+
+ // set staking address
+ const tx = await postMinterContractInstance.changeStakingAddress(stakingContractAddress);
+ await tx.wait();
+
+ // fetch staking address after
+ const stakingAddressAfter = await postMinterContractInstance.stakingAddress();
+ console.log("Staking address after:", stakingAddressAfter);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/staking/iggyStakingRewards.deploy.js b/scripts/staking/iggyStakingRewards.deploy.js
new file mode 100644
index 0000000..ab4679b
--- /dev/null
+++ b/scripts/staking/iggyStakingRewards.deploy.js
@@ -0,0 +1,35 @@
+// npx hardhat run scripts/staking/iggyStakingRewards.deploy.js --network polygonMumbai
+const contractName = "IggyStakingRewards";
+
+const assetAddress = "0xF874f79eBfB8FEe898a289C4cAa5dc4383873431"; // token to stake
+const wethAddress = "0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889"; // wrapped native coin (WETH, WSGB, WBNB, etc.)
+const tokenName = "Iggy Governance Token";
+const symbol = "IGT";
+const claimRewardsMinimum = ethers.utils.parseEther("0.001"); // 10 SGB/ETH minimum total reward for a given week (if not met, rewards are rolled over to the next week)
+const minDeposit = ethers.utils.parseEther("0.001"); // 0.001 LP tokens minimum deposit to stake
+const periodLength = 604800; // 7 days
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ assetAddress, wethAddress, tokenName, symbol, claimRewardsMinimum, minDeposit, periodLength
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + assetAddress + " " + wethAddress + ' "' + tokenName + '" "' + symbol + '" "' + claimRewardsMinimum + '" "' + minDeposit + '" "' + periodLength + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/stats/1_stats.deploy.js b/scripts/stats/1_stats.deploy.js
new file mode 100644
index 0000000..70cadba
--- /dev/null
+++ b/scripts/stats/1_stats.deploy.js
@@ -0,0 +1,28 @@
+// 1. Deploy LaunchpadStats contract.
+// npx hardhat run scripts/stats/1_stats.deploy.js --network modeTestnet
+
+const contractName = "Stats";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy();
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/stats/2_statsMiddleware.deploy.js b/scripts/stats/2_statsMiddleware.deploy.js
new file mode 100644
index 0000000..697f846
--- /dev/null
+++ b/scripts/stats/2_statsMiddleware.deploy.js
@@ -0,0 +1,38 @@
+// 2. Deploy StatsMiddleware contract.
+// npx hardhat run scripts/stats/2_statsMiddleware.deploy.js --network modeTestnet
+
+const contractName = "StatsMiddleware";
+
+const statsAddress = "0xabf9960132818049340253C3Ca0551F92Db856d7";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(statsAddress);
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // create a Stats contract instance
+ const statsInstance = await ethers.getContractAt("Stats", statsAddress);
+
+ // set middleware address (setStatsWriterAddress)
+ console.log("Setting middleware address...");
+ const tx2 = await statsInstance.setStatsWriterAddress(instance.address);
+ await tx2.wait();
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + statsAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/stats/calls.js b/scripts/stats/calls.js
new file mode 100644
index 0000000..19b1032
--- /dev/null
+++ b/scripts/stats/calls.js
@@ -0,0 +1,42 @@
+// npx hardhat run scripts/stats/calls.js --network zkfair
+
+const statsMiddlewareAddress = "0x3Fa0EaC3058828Cc4BA97F51A33597C695bF6F9e";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Calling methods with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // create stats middleware contract
+ const statsMiddlewareContract = await ethers.getContractAt("StatsMiddleware", statsMiddlewareAddress);
+
+ // get stats contract address
+ const statsAddress = await statsMiddlewareContract.statsAddress();
+ console.log("Stats address:", statsAddress);
+
+ // create stats contract
+ const statsContract = await ethers.getContractAt("Stats", statsAddress);
+
+ // Check if this address is a writer
+ const swapAddress = "0xe69FD53b8C0F2F764cFe5929CAb5e213c0328b42";
+ const isWriter = await statsMiddlewareContract.writers(swapAddress);
+ console.log("Is writer: ", isWriter);
+
+ // Add this address to the Stats middleware contract
+ /*
+ console.log("Adding this address to the stats middleware contract:");
+ const tx1 = await statsMiddlewareContract.addWriter(swapAddress);
+ await tx1.wait();
+ */
+
+ console.log("Done!");
+
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/swap/IggySwapRouter.deploy.js b/scripts/swap/IggySwapRouter.deploy.js
new file mode 100644
index 0000000..0fba99e
--- /dev/null
+++ b/scripts/swap/IggySwapRouter.deploy.js
@@ -0,0 +1,60 @@
+// npx hardhat run scripts/swap/IggySwapRouter.deploy.js --network taikoJolnir
+
+const contractName = "IggySwapRouter";
+
+const iggyAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2"; // mandatory
+const routerAddress = "0xE4f7776c753aF46D2aa23e3348d17548C86DC47D"; // mandatory
+const frontendAddress = ethers.constants.AddressZero; // optional
+const stakingAddress = ethers.constants.AddressZero; // optional
+const statsAddress = ethers.constants.AddressZero; // stats middleware address (optional)
+
+const swapFee = 80; // 0.8%
+const stakingShare = 4000; // bps
+const frontendShare = 4000; // bps
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ frontendAddress,
+ iggyAddress,
+ routerAddress,
+ stakingAddress,
+ statsAddress,
+ swapFee,
+ stakingShare,
+ frontendShare
+ );
+
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add this address to the Stats middleware contract
+ if (statsAddress != ethers.constants.AddressZero) {
+ console.log("Adding this address to the stats middleware contract:");
+ const statsContract = await ethers.getContractFactory("StatsMiddleware");
+ const statsInstance = await statsContract.attach(statsAddress);
+ const tx1 = await statsInstance.addWriter(instance.address);
+ await tx1.wait();
+ console.log("Done!");
+ }
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log(
+ "npx hardhat verify --network " + network.name + " " + instance.address + " " + frontendAddress + " " +
+ iggyAddress + " " + routerAddress + " " + stakingAddress + " " + statsAddress + ' "' + swapFee + '" "' + stakingShare + '" "' + frontendShare + '"'
+ );
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/swap/IggySwapRouterSolidly.deploy.js b/scripts/swap/IggySwapRouterSolidly.deploy.js
new file mode 100644
index 0000000..754f4d1
--- /dev/null
+++ b/scripts/swap/IggySwapRouterSolidly.deploy.js
@@ -0,0 +1,60 @@
+// npx hardhat run scripts/swap/IggySwapRouterSolidly.deploy.js --network base
+
+const contractName = "IggySwapRouterSolidly";
+
+const frontendAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2";
+const iggyAddress = "0x6771F33Cfd8C6FC0A1766331f715f5d2E1d4E0e2";
+const routerAddress = "0xE11b93B61f6291d35c5a2beA0A9fF169080160cF";
+const stakingAddress = "0x0000000000000000000000000000000000000000"; // zero address
+const statsAddress = ""; // stats middleware address
+const wethAddress = "0x4200000000000000000000000000000000000006";
+
+const swapFee = 80; // 0.8%
+const stakingShare = 0; // 80%
+const frontendShare = 5000; // 50% of what's left after staking share and referral share are taken out
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ frontendAddress,
+ iggyAddress,
+ routerAddress,
+ stakingAddress,
+ statsAddress,
+ wethAddress,
+ swapFee,
+ stakingShare,
+ frontendShare
+ );
+
+ await instance.deployed();
+
+ console.log(contractName + " contract address:", instance.address);
+
+ // add this address to the Stats middleware contract
+ console.log("Adding this address to the stats middleware contract:");
+ const statsContract = await ethers.getContractFactory("StatsMiddleware");
+ const statsInstance = await statsContract.attach(statsAddress);
+ const tx1 = await statsInstance.addWriter(instance.address);
+ await tx1.wait();
+ console.log("Done!");
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log(
+ "npx hardhat verify --network " + network.name + " " + instance.address + " " + frontendAddress + " " +
+ iggyAddress + " " + routerAddress + " " + stakingAddress + " " + statsAddress + " " + wethAddress + ' "' + swapFee + '" "' + stakingShare + '" "' + frontendShare + '"'
+ );
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/token/ChatToken/chatToken.deploy.js b/scripts/token/ChatToken/chatToken.deploy.js
new file mode 100644
index 0000000..20a2621
--- /dev/null
+++ b/scripts/token/ChatToken/chatToken.deploy.js
@@ -0,0 +1,31 @@
+// npx hardhat run scripts/token/ChatToken/chatToken.deploy.js --network songbird
+
+const contractName = "ChatToken";
+
+const tokenName = "CHIRP";
+const symbol = "CHIRP";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ tokenName, symbol
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait a minute and then run this command to verify contracts on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + ' "' + tokenName + '" "' + symbol + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/token/ChatTokenClaimActivityPoints/chatTokenClaimActivityPoints.deploy.js b/scripts/token/ChatTokenClaimActivityPoints/chatTokenClaimActivityPoints.deploy.js
new file mode 100644
index 0000000..87ca82c
--- /dev/null
+++ b/scripts/token/ChatTokenClaimActivityPoints/chatTokenClaimActivityPoints.deploy.js
@@ -0,0 +1,53 @@
+// npx hardhat run scripts/token/ChatTokenClaimActivityPoints/chatTokenClaimActivityPoints.deploy.js --network polygonMumbai
+// This script deploys the ChatTokenClaimActivityPoints contract and sets it as a minter in the ChatTokenMinter contract.
+// If setting the minter address fails, do it manually by calling the addMinter function in the ChatTokenMinter contract.
+
+const contractName = "ChatTokenClaimActivityPoints";
+
+const chatTokenMinterAddress = "0x2C6A9F47a2B1BA7976ACd14CDd8f6f35d27C1e28"; // TODO
+const activityPointsAddress = "0x7d20A0E75B1ac519f500a51351bcb01A07fE3D7d"; // TODO
+const chatEthRatio = 1_000_000; // TODO: 1 ETH = 1,000 CHAT
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ chatTokenMinterAddress,
+ activityPointsAddress,
+ chatEthRatio
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait for deploy transaction to be mined...");
+
+ await instance.deployed();
+
+ console.log("Deploy transaction mined!");
+
+ console.log("Add ChatTokenClaimActivityPoints contract address as the Minter in the ChatTokenMinter contract");
+
+ const chatTokenMinterContract = await ethers.getContractFactory("ChatTokenMinter");
+ const chatTokenMinterInstance = await chatTokenMinterContract.attach(chatTokenMinterAddress);
+
+ await chatTokenMinterInstance.addMinter(instance.address);
+
+ console.log("Done!");
+
+ console.log("Lastly, verify the Minter contract on block explorer");
+
+ console.log("Wait a minute and then run this command to verify contract on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + chatTokenMinterAddress + " " + activityPointsAddress + ' "' + chatEthRatio + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/token/ChatTokenClaimDomains/chatTokenClaimDomains.deploy.js b/scripts/token/ChatTokenClaimDomains/chatTokenClaimDomains.deploy.js
new file mode 100644
index 0000000..3098368
--- /dev/null
+++ b/scripts/token/ChatTokenClaimDomains/chatTokenClaimDomains.deploy.js
@@ -0,0 +1,55 @@
+// npx hardhat run scripts/token/ChatTokenClaimDomains/chatTokenClaimDomains.deploy.js --network songbird
+// This script deploys the ChatTokenClaimDomains contract and sets it as a minter in the ChatTokenMinter contract.
+// If setting the minter address fails, do it manually by calling the addMinter function in the ChatTokenMinter contract.
+
+const contractName = "ChatTokenClaimDomains";
+
+const chatTokenMinterAddress = "0x31CfDF366dd9753b8443B6fc3c59598415697131"; // TODO: Update this address
+const tldAddress = "0xBDACF94dDCAB51c39c2dD50BffEe60Bb8021949a"; // TODO: Update this address
+const chatReward = ethers.utils.parseEther("1337"); // TODO: 1 domain = 1337 CHAT tokens
+const maxIdEligible = 1520; // TODO: The first X number of domains (by ID) are eligible for claiming
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ chatTokenMinterAddress,
+ tldAddress,
+ chatReward,
+ maxIdEligible
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait for deploy transaction to be mined...");
+
+ await instance.deployed();
+
+ console.log("Deploy transaction mined!");
+
+ console.log("Add ChatTokenClaimDomains contract address as the Minter in the ChatTokenMinter contract");
+
+ const chatTokenMinterContract = await ethers.getContractFactory("ChatTokenMinter");
+ const chatTokenMinterInstance = await chatTokenMinterContract.attach(chatTokenMinterAddress);
+
+ await chatTokenMinterInstance.addMinter(instance.address);
+
+ console.log("Done!");
+
+ console.log("Lastly, verify the Minter contract on block explorer");
+
+ console.log("Wait a minute and then run this command to verify contract on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + chatTokenMinterAddress + " " + tldAddress + ' "' + chatReward + '" "' + maxIdEligible + '"');
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/scripts/token/ChatTokenMinter/chatTokenMinter.deploy.js b/scripts/token/ChatTokenMinter/chatTokenMinter.deploy.js
new file mode 100644
index 0000000..e1249a8
--- /dev/null
+++ b/scripts/token/ChatTokenMinter/chatTokenMinter.deploy.js
@@ -0,0 +1,47 @@
+// npx hardhat run scripts/token/ChatTokenMinter/chatTokenMinter.deploy.js --network songbird
+// This script deploys the ChatTokenMinter contract and sets it as the minter in the ChatToken contract.
+// If setting the minter address fails, do it manually by calling the setMinter function in the ChatToken contract.
+
+const contractName = "ChatTokenMinter";
+
+const chatTokenAddress = "0x81aDd7359f2B95276F8542f2a0acD7ECD2Ae9349";
+
+async function main() {
+ const [deployer] = await ethers.getSigners();
+
+ console.log("Deploying contracts with the account:", deployer.address);
+ console.log("Account balance:", (await deployer.getBalance()).toString());
+
+ // deploy contract
+ const contract = await ethers.getContractFactory(contractName);
+ const instance = await contract.deploy(
+ chatTokenAddress
+ );
+
+ console.log(contractName + " contract address:", instance.address);
+
+ console.log("Wait for deploy transaction to be mined...");
+
+ await instance.deployed();
+
+ console.log("Set minter contract address as the Minter in the ChatToken contract");
+
+ const chatTokenContract = await ethers.getContractFactory("ChatToken");
+ const chatTokenInstance = await chatTokenContract.attach(chatTokenAddress);
+
+ await chatTokenInstance.setMinter(instance.address);
+
+ console.log("Done!");
+
+ console.log("Lastly, verify the Minter contract on block explorer");
+
+ console.log("Wait a minute and then run this command to verify contract on block explorer:");
+ console.log("npx hardhat verify --network " + network.name + " " + instance.address + " " + chatTokenAddress);
+}
+
+main()
+ .then(() => process.exit(0))
+ .catch((error) => {
+ console.error(error);
+ process.exit(1);
+ });
\ No newline at end of file
diff --git a/test/activity-points/activityPoints.test.js b/test/activity-points/activityPoints.test.js
new file mode 100644
index 0000000..cf36a10
--- /dev/null
+++ b/test/activity-points/activityPoints.test.js
@@ -0,0 +1,162 @@
+// npx hardhat test test/activity-points/activityPoints.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("Activity Points", function () {
+ let activityPointsContract;
+ let statsContract;
+
+ const multiplier = 100;
+
+ let owner;
+ let user1;
+ let user2;
+
+ beforeEach(async function () {
+ [ owner, user1, user2, feeReceiver ] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ await statsContract.addWriter(owner.address);
+
+ const ActivityPoints = await ethers.getContractFactory("ActivityPoints");
+ activityPointsContract = await ActivityPoints.deploy(
+ statsContract.address,
+ ethers.constants.AddressZero,
+ ethers.constants.AddressZero,
+ multiplier,
+ sfsContract.address,
+ sfsNftTokenId
+ );
+ await activityPointsContract.deployed();
+ });
+
+ it("shows activity points for users", async function () {
+ user1points1 = await activityPointsContract.getPoints(user1.address);
+ expect(user1points1).to.equal(0);
+ console.log("user1 points1:", Number(user1points1));
+
+ user2points1 = await activityPointsContract.getPoints(user2.address);
+ expect(user2points1).to.equal(0);
+ console.log("user2 points1:", Number(user2points1));
+
+ // add wei spent to user1 via the stats contract
+ const weiSpent = ethers.utils.parseEther("0.1337");
+ await statsContract.addWeiSpent(user1.address, weiSpent);
+
+ user1points2 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points2:", ethers.utils.formatEther(user1points2) + " points");
+ expect(user1points2).to.equal(weiSpent.mul(multiplier));
+
+ user2points2 = await activityPointsContract.getPoints(user2.address);
+ console.log("user2 points2:", ethers.utils.formatEther(user2points2) + " points");
+ expect(user2points2).to.equal(0);
+
+ // add bonus points to user1 via the activity points contract
+ const bonusPoints = ethers.utils.parseEther("0.69");
+ await activityPointsContract.addBonusPoints(user1.address, bonusPoints);
+
+ user1points3 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points3:", ethers.utils.formatEther(user1points3) + " points");
+ expect(user1points3).to.equal(weiSpent.mul(multiplier).add(bonusPoints));
+
+ user2points3 = await activityPointsContract.getPoints(user2.address);
+ console.log("user2 points3:", ethers.utils.formatEther(user2points3) + " points");
+ expect(user2points3).to.equal(0);
+
+ // add bonus points to user2 via the activity points contract
+ const bonusPoints2 = ethers.utils.parseEther("4.2069");
+ await activityPointsContract.addBonusPoints(user2.address, bonusPoints2);
+
+ user1points4 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points4:", ethers.utils.formatEther(user1points4) + " points");
+ expect(user1points4).to.equal(weiSpent.mul(multiplier).add(bonusPoints));
+
+ user2points4 = await activityPointsContract.getPoints(user2.address);
+ console.log("user2 points4:", ethers.utils.formatEther(user2points4) + " points");
+ expect(user2points4).to.equal(bonusPoints2);
+
+ // remove 0.06 bonus points from user1 via the activity points contract
+ const bonusPoints3 = ethers.utils.parseEther("0.06");
+ await activityPointsContract.removeBonusPoints(user1.address, bonusPoints3);
+
+ // remove 0.0069 bonus points from user2 via the activity points contract
+ const bonusPoints4 = ethers.utils.parseEther("0.0069");
+ await activityPointsContract.removeBonusPoints(user2.address, bonusPoints4);
+
+ user1points5 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points5:", ethers.utils.formatEther(user1points5) + " points");
+ expect(user1points5).to.equal(weiSpent.mul(multiplier).add(bonusPoints).sub(bonusPoints3));
+
+ user2points5 = await activityPointsContract.getPoints(user2.address);
+ console.log("user2 points5:", ethers.utils.formatEther(user2points5) + " points");
+ expect(user2points5).to.equal(bonusPoints2.sub(bonusPoints4));
+
+ // add bonus wei spent to user1 via the activity points contract
+ const weiSpent2 = ethers.utils.parseEther("0.42");
+ await activityPointsContract.addBonusWei(user1.address, weiSpent2);
+
+ user1points6 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points6:", ethers.utils.formatEther(user1points6) + " points");
+ expect(user1points6).to.equal(weiSpent.mul(multiplier).add(bonusPoints).sub(bonusPoints3).add(weiSpent2.mul(multiplier)));
+
+ // remove the added bonus wei spent from user1 via the activity points contract
+ await activityPointsContract.removeBonusWei(user1.address, weiSpent2);
+
+ user1points7 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points7:", ethers.utils.formatEther(user1points7) + " points");
+ expect(user1points7).to.equal(weiSpent.mul(multiplier).add(bonusPoints).sub(bonusPoints3));
+
+ // fail at removing more bonus wei spent than user1 has
+ const weiSpent3 = ethers.utils.parseEther("1000");
+ await expect(activityPointsContract.removeBonusWei(user1.address, weiSpent3)).to.be.revertedWith("ActivityPoints: not enough bonus wei");
+
+ // fail at removing more bonus points than user1 has
+ await expect(activityPointsContract.removeBonusPoints(user1.address, weiSpent3)).to.be.revertedWith("ActivityPoints: not enough bonus points");
+
+ // check multiplier
+ const multiplierBefore = await activityPointsContract.multiplier();
+ console.log("Multiplier before:", Number(multiplierBefore));
+
+ // owner change multiplier
+ const newMultiplier = 1000;
+ await activityPointsContract.setMultiplier(newMultiplier);
+
+ const multiplierAfter = await activityPointsContract.multiplier();
+ console.log("Multiplier after:", Number(multiplierAfter));
+ expect(multiplierAfter).to.equal(newMultiplier);
+
+ // check points after multiplier change
+ user1points8 = await activityPointsContract.getPoints(user1.address);
+ console.log("user1 points8:", ethers.utils.formatEther(user1points8) + " points");
+ const multiplierDiff = newMultiplier / multiplier;
+ expect(user1points8).to.equal(weiSpent.mul(multiplier).add(bonusPoints).sub(bonusPoints3).mul(multiplierDiff));
+ });
+
+});
\ No newline at end of file
diff --git a/test/distributor/revenueDistributor.test.js b/test/distributor/revenueDistributor.test.js
new file mode 100644
index 0000000..00d4644
--- /dev/null
+++ b/test/distributor/revenueDistributor.test.js
@@ -0,0 +1,248 @@
+// npx hardhat test test/distributor/revenueDistributor.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("RevenueDistributor", function () {
+ let distributorContract;
+
+ let owner;
+ let user1;
+ let user2;
+ let recipient1;
+ let recipient2;
+ let recipient3;
+ let recipient4;
+ let recipient5;
+ let recipient6;
+
+ const recipient1Percent = ethers.utils.parseEther("0.05"); // 5%
+ const recipient2Percent = ethers.utils.parseEther("0.15"); // 15%
+ const recipient3Percent = ethers.utils.parseEther("0.25"); // 25%
+ const recipient4Percent = ethers.utils.parseEther("0.05"); // 5%
+ const recipient5Percent = ethers.utils.parseEther("0.30"); // 30%
+ const recipient6Percent = ethers.utils.parseEther("0.20"); // 20%
+
+ beforeEach(async function () {
+ [
+ owner, user1, user2, recipient1, recipient2, recipient3, recipient4, recipient5, recipient6
+ ] = await ethers.getSigners();
+
+ const RevenueDistributor = await ethers.getContractFactory("RevenueDistributor");
+ distributorContract = await RevenueDistributor.deploy();
+ await distributorContract.deployed();
+
+ // add recipients
+ await distributorContract.addRecipient(recipient1.address, "Recipient 1", recipient1Percent);
+ await distributorContract.addRecipient(recipient2.address, "Recipient 2", recipient2Percent);
+ await distributorContract.addRecipient(recipient3.address, "Recipient 3", recipient3Percent);
+ await distributorContract.addRecipient(recipient4.address, "Recipient 4", recipient4Percent);
+ await distributorContract.addRecipient(recipient5.address, "Recipient 5", recipient5Percent);
+ await distributorContract.addRecipient(recipient6.address, "Recipient 6", recipient6Percent);
+ });
+
+ it("reverts when trying to add one more recipient and the total is larger than 100%", async function () {
+ await expect(
+ distributorContract.addRecipient(user1.address, "Recipient 7", ethers.utils.parseEther("0.01"))
+ ).to.be.revertedWith("RevenueDistributor: percentage total must be less than or equal to 100%");
+ });
+
+ it("reverts when trying to add existing recipient", async function () {
+ // remove last recipient
+ await distributorContract.removeLastRecipient();
+
+ // add recipient1 again
+ await expect(
+ distributorContract.addRecipient(recipient1.address, "Recipient 1 again", ethers.utils.parseEther("0.01"))
+ ).to.be.revertedWith("RevenueDistributor: recipient already in the list");
+ });
+
+ it("distributes funds to recipients", async function () {
+ const initialBalance = ethers.utils.parseEther("10000");
+
+ // check balances of recipients before the distribution
+ expect(await ethers.provider.getBalance(recipient1.address)).to.equal(initialBalance);
+ expect(await ethers.provider.getBalance(recipient2.address)).to.equal(initialBalance);
+ expect(await ethers.provider.getBalance(recipient3.address)).to.equal(initialBalance);
+ expect(await ethers.provider.getBalance(recipient4.address)).to.equal(initialBalance);
+ expect(await ethers.provider.getBalance(recipient5.address)).to.equal(initialBalance);
+ expect(await ethers.provider.getBalance(recipient6.address)).to.equal(initialBalance);
+
+ // user1 sends 100 ETH to the contract
+ const user1BalanceBefore = await ethers.provider.getBalance(user1.address);
+ expect(await ethers.provider.getBalance(user1.address)).to.equal(initialBalance);
+
+ expect(await ethers.provider.getBalance(distributorContract.address)).to.equal(0);
+
+ const tx = await user1.sendTransaction({
+ to: distributorContract.address,
+ value: ethers.utils.parseEther("100"),
+ });
+ const receipt = await tx.wait();
+
+ calculateGasCosts("distributes funds to recipients", receipt);
+
+ const user1BalanceAfter = await ethers.provider.getBalance(user1.address);
+ expect(user1BalanceAfter).to.be.closeTo(user1BalanceBefore.sub(ethers.utils.parseEther("100")), ethers.utils.parseEther("0.1"));
+
+ // check balances of recipients after the distribution
+ const r1bal = await ethers.provider.getBalance(recipient1.address);
+ console.log(ethers.utils.formatEther(r1bal));
+
+ expect(await ethers.provider.getBalance(recipient1.address)).to.equal(initialBalance.add(recipient1Percent.mul(100)));
+ expect(await ethers.provider.getBalance(recipient2.address)).to.equal(initialBalance.add(recipient2Percent.mul(100)));
+ expect(await ethers.provider.getBalance(recipient3.address)).to.equal(initialBalance.add(recipient3Percent.mul(100)));
+ expect(await ethers.provider.getBalance(recipient4.address)).to.equal(initialBalance.add(recipient4Percent.mul(100)));
+ expect(await ethers.provider.getBalance(recipient5.address)).to.equal(initialBalance.add(recipient5Percent.mul(100)));
+ expect(await ethers.provider.getBalance(recipient6.address)).to.equal(initialBalance.add(recipient6Percent.mul(100)));
+ });
+
+ it("removes a recipient and adds a new one, and then updates the percentage of the new one", async function () {
+ const isRecipientBefore = await distributorContract.isRecipient(recipient3.address);
+ expect(isRecipientBefore).to.equal(true);
+
+ await distributorContract.removeRecipientByAddress(recipient3.address);
+
+ const isRecipientAfter = await distributorContract.isRecipient(recipient3.address);
+ expect(isRecipientAfter).to.equal(false);
+
+ await distributorContract.addRecipient(user1.address, "Recipient 7", ethers.utils.parseEther("0.01"));
+
+ // fails at update for recipient 7 because the percentage is larger than 25%
+ await expect(
+ distributorContract.updateRecipientByAddress(user1.address, user1.address, "Recipient 7", ethers.utils.parseEther("0.26"))
+ ).to.be.revertedWith("RevenueDistributor: percentage total must be less than or equal to 100%");
+
+ // succeeds at update for recipient 7 because the percentage is 25%
+ await distributorContract.updateRecipientByAddress(user1.address, user1.address, "Recipient 7 new", ethers.utils.parseEther("0.25"));
+
+ // check recipient's percentage and label via getRecipient
+ const recipient7 = await distributorContract.getRecipient(user1.address);
+ console.log(ethers.utils.formatEther(recipient7.percentage));
+ expect(recipient7.percentage).to.equal(ethers.utils.parseEther("0.25"));
+ expect(recipient7.label).to.equal("Recipient 7 new");
+ });
+
+ it("removes last recipient and adds a bunch of new ones with 1% share each", async function () {
+ await distributorContract.removeLastRecipient();
+
+ // getRecipientsLength before adding new recipients
+ const recipientsLengthBefore = await distributorContract.getRecipientsLength();
+ console.log("recipientsLengthBefore: " + recipientsLengthBefore);
+
+ const limit = 20;
+ let signers = await ethers.getSigners();
+
+ // loop thorugh fillerAddresses and add them as recipients with 1% share each
+ for (let i = 9; i < limit; i++) {
+ //console.log("Adding filler " + i);
+ //console.log(signers[i].address);
+ await distributorContract.addRecipient(signers[i].address, "Filler " + i, ethers.utils.parseEther("0.01"));
+ }
+
+ // getRecipientsLength after adding new recipients
+ const recipientsLengthAfter = await distributorContract.getRecipientsLength();
+ console.log("recipientsLengthAfter: " + recipientsLengthAfter);
+
+ const tx = await user1.sendTransaction({
+ to: distributorContract.address,
+ value: ethers.utils.parseEther("100"),
+ });
+ const receipt = await tx.wait();
+
+ calculateGasCosts("distributes funds to recipients", receipt);
+
+ // getRecipients and print them in console log
+ const recipients = await distributorContract.getRecipients();
+ //console.log(recipients);
+
+ // removeLastRecipient
+ await distributorContract.removeLastRecipient();
+ expect(await distributorContract.getRecipientsLength()).to.equal(recipientsLengthAfter - 1);
+
+ // removeAllRecipients
+ await distributorContract.removeAllRecipients();
+ expect(await distributorContract.getRecipientsLength()).to.equal(0);
+ });
+
+ it("updates recipient by index", async function () {
+ // check recipient's percentage and label via getRecipient
+ const recipient3before = await distributorContract.getRecipient(recipient3.address);
+ expect(recipient3before.percentage).to.equal(recipient3Percent);
+ expect(recipient3before.label).to.equal("Recipient 3");
+
+ // update recipient's percentage and label via updateRecipientByIndex
+ await distributorContract.updateRecipientByIndex(2, recipient3.address, "Recipient 3 new", ethers.utils.parseEther("0.05"));
+
+ // check recipient's percentage and label via getRecipient
+ const recipient3Updated = await distributorContract.getRecipient(recipient3.address);
+ expect(recipient3Updated.percentage).to.equal(ethers.utils.parseEther("0.05"));
+ expect(recipient3Updated.label).to.equal("Recipient 3 new");
+
+ // check contract balance before the distribution
+ expect(await ethers.provider.getBalance(distributorContract.address)).to.equal(0);
+
+ // make a distribution
+ const tx = await user1.sendTransaction({
+ to: distributorContract.address,
+ value: ethers.utils.parseEther("100"),
+ });
+
+ // check contract balance after the distribution (20% should be left in the contract, because recipient3's percentage was changed from 25% to 5%)
+ expect(await ethers.provider.getBalance(distributorContract.address)).to.equal(ethers.utils.parseEther("20"));
+
+ // withdraw remaining ETH from the contract via withdrawEth
+ await distributorContract.withdrawEth();
+
+ // check contract balance after the withdrawal (should be 0)
+ expect(await ethers.provider.getBalance(distributorContract.address)).to.equal(0);
+ });
+
+ it("adds user1 as manager", async function () {
+ // check if user 1 is a manager (isManager)
+ const isManagerBefore = await distributorContract.isManager(user1.address);
+ expect(isManagerBefore).to.equal(false);
+
+ // revert when user1 is trying to add a new recipient
+ await expect(
+ distributorContract.connect(user1).addRecipient(user2.address, "Recipient 7", ethers.utils.parseEther("0.01"))
+ ).to.be.revertedWith("OwnableWithManagers: caller is not a manager or owner");
+
+ // add user1 as manager via addManager
+ await distributorContract.addManager(user1.address);
+
+ // check if user 1 is a manager (isManager)
+ const isManagerAfter = await distributorContract.isManager(user1.address);
+ expect(isManagerAfter).to.equal(true);
+
+ // remove the last recipient via user1
+ await distributorContract.connect(user1).removeLastRecipient();
+
+ // add a new recipient via user1
+ await distributorContract.connect(user1).addRecipient(user2.address, "Recipient 7", ethers.utils.parseEther("0.01"));
+
+ // remove user1 as manager via removeManagerByAddress
+ await distributorContract.removeManagerByAddress(user1.address);
+
+ // check if user 1 is a manager (isManager)
+ const isManagerAfterRemove = await distributorContract.isManager(user1.address);
+ expect(isManagerAfterRemove).to.equal(false);
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/keys/friendKeys.test.js b/test/keys/friendKeys.test.js
new file mode 100644
index 0000000..a3eb9e9
--- /dev/null
+++ b/test/keys/friendKeys.test.js
@@ -0,0 +1,267 @@
+// npx hardhat test test/keys/friendKeys.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("FriendKeys", function () {
+ let mockPunkTldContract;
+ let statsMiddlewareContract;
+ let friendKeysContract;
+
+ let owner;
+ let feeReceiver;
+ let user1;
+ let user2; // domain holder
+
+ const domainName = "test"; // test.tld domain
+
+ const protocolFeePercent = ethers.utils.parseEther("0.05");
+ const domainHolderFeePercent = ethers.utils.parseEther("0.05");
+ const ratio = ethers.utils.parseEther("133769"); // ETH for 16000 keys
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, feeReceiver, user1, user2] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(user2.address, domainName);
+ await mockPunkTldContract.deployed();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ const StatsMiddleware = await ethers.getContractFactory("StatsMiddleware");
+ statsMiddlewareContract = await StatsMiddleware.deploy(sfsContract.address, sfsNftTokenId, statsContract.address);
+ await statsMiddlewareContract.deployed();
+
+ await statsContract.setStatsWriterAddress(statsMiddlewareContract.address);
+
+ const FriendKeys = await ethers.getContractFactory("FriendKeys");
+ friendKeysContract = await FriendKeys.deploy(
+ mockPunkTldContract.address,
+ feeReceiver.address,
+ statsMiddlewareContract.address,
+ protocolFeePercent,
+ domainHolderFeePercent,
+ ratio
+ );
+ await friendKeysContract.deployed();
+
+ await statsMiddlewareContract.addWriter(friendKeysContract.address);
+ });
+
+ it("buys and sell 3 keys sequentially", async function () {
+ // function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256)
+ // get sell price for 1 domain
+ const sellPrice = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice: " + ethers.utils.formatEther(sellPrice) + " ETH");
+
+ // get domain holder address
+ const domainHolderAddress = await mockPunkTldContract.getDomainHolder(domainName);
+ expect(domainHolderAddress).to.equal(user2.address);
+
+ // get domain holder balance
+ const domainHolderBalanceBefore = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceBefore: " + ethers.utils.formatEther(domainHolderBalanceBefore) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceBefore = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceBefore: " + ethers.utils.formatEther(feeReceiverBalanceBefore) + " ETH");
+
+ // get user1 balance
+ const user1BalanceBefore = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceBefore: " + ethers.utils.formatEther(user1BalanceBefore) + " ETH");
+
+ // get smart contract balance
+ const smartContractBalanceBefore = await ethers.provider.getBalance(friendKeysContract.address);
+ console.log("smartContractBalanceBefore: " + ethers.utils.formatEther(smartContractBalanceBefore) + " ETH");
+
+ // get buy price (should be 0 for the 1 share)
+ const buyPriceBefore = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceBefore: " + ethers.utils.formatEther(buyPriceBefore) + " ETH");
+ //expect(buyPriceBefore).to.equal(ethers.utils.parseEther("0"));
+
+ // buy 1 share
+ const tx = await friendKeysContract.connect(user1).buyKeys(
+ domainName, 1, ethers.constants.AddressZero, { value: buyPriceBefore }
+ );
+ const receipt = await tx.wait();
+ //calculateGasCosts("buyKeys", receipt);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter1 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter1: " + ethers.utils.formatEther(domainHolderBalanceAfter1) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter1 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter1: " + ethers.utils.formatEther(feeReceiverBalanceAfter1) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter1 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter1: " + ethers.utils.formatEther(user1BalanceAfter1) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter1 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter1: " + ethers.utils.formatEther(buyPriceAfter1) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPriceBefore = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPriceBefore: " + ethers.utils.formatEther(sellPriceBefore) + " ETH");
+
+ // buy 1 share
+ const tx2 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, ethers.constants.AddressZero, { value: buyPriceAfter1 });
+ const receipt2 = await tx2.wait();
+ //calculateGasCosts("buyKeys", receipt2);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter2 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter2: " + ethers.utils.formatEther(domainHolderBalanceAfter2) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter2 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter2: " + ethers.utils.formatEther(feeReceiverBalanceAfter2) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter2 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter2: " + ethers.utils.formatEther(user1BalanceAfter2) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter2 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter2: " + ethers.utils.formatEther(buyPriceAfter2) + " ETH");
+
+ // buy 1 share
+ const tx3 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, ethers.constants.AddressZero, { value: buyPriceAfter2 });
+ const receipt3 = await tx3.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter3 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter3: " + ethers.utils.formatEther(domainHolderBalanceAfter3) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter3 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter3: " + ethers.utils.formatEther(feeReceiverBalanceAfter3) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter3 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter3: " + ethers.utils.formatEther(user1BalanceAfter3) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter3 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter3: " + ethers.utils.formatEther(buyPriceAfter3) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice1 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice1: " + ethers.utils.formatEther(sellPrice1) + " ETH");
+
+ // sell 1 share
+ const tx4 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt4 = await tx4.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter4 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter4: " + ethers.utils.formatEther(domainHolderBalanceAfter4) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter4 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter4: " + ethers.utils.formatEther(feeReceiverBalanceAfter4) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter4 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter4: " + ethers.utils.formatEther(user1BalanceAfter4) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice2 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice2: " + ethers.utils.formatEther(sellPrice2) + " ETH");
+
+ // sell 1 share
+ const tx5 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt5 = await tx5.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter5 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter5: " + ethers.utils.formatEther(domainHolderBalanceAfter5) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter5 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter5: " + ethers.utils.formatEther(feeReceiverBalanceAfter5) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter5 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter5: " + ethers.utils.formatEther(user1BalanceAfter5) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice3 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice3: " + ethers.utils.formatEther(sellPrice3) + " ETH");
+
+ // sell 1 share
+ const tx6 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt6 = await tx6.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter6 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter6: " + ethers.utils.formatEther(domainHolderBalanceAfter6) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter6 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter6: " + ethers.utils.formatEther(feeReceiverBalanceAfter6) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter6 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter6: " + ethers.utils.formatEther(user1BalanceAfter6) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice4 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice4: " + ethers.utils.formatEther(sellPrice4) + " ETH");
+
+ // get smart contract balance
+ const smartContractBalance4 = await ethers.provider.getBalance(friendKeysContract.address);
+ console.log("smartContractBalance4: " + ethers.utils.formatEther(smartContractBalance4) + " ETH");
+
+ // mapping(string => uint256) public keysSupply;
+ // get key supply
+ const keySupply = await friendKeysContract.keysSupply(domainName);
+ console.log("keySupply: " + keySupply);
+
+ // expect to revert when trying to sell the last share as user2
+ await expect(friendKeysContract.connect(user2).sellKeys(domainName, 1, ethers.constants.AddressZero)).to.be.revertedWith("Cannot sell the last key");
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/keys/friendKeysErc20.test.js b/test/keys/friendKeysErc20.test.js
new file mode 100644
index 0000000..90ce203
--- /dev/null
+++ b/test/keys/friendKeysErc20.test.js
@@ -0,0 +1,286 @@
+// npx hardhat test test/keys/friendKeysErc20.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("FriendKeys ERC-20", function () {
+ let mockPunkTldContract;
+ let statsMiddlewareContract;
+ let friendKeysContract;
+
+ let tokenContract; // ERC-20 token for buying/selling keys
+ const tokenSymbol = "TTK";
+
+ let owner;
+ let feeReceiver;
+ let user1;
+ let user2; // domain holder
+
+ const domainName = "test"; // test.tld domain
+
+ const protocolFeePercent = ethers.utils.parseEther("0.05");
+ const domainHolderFeePercent = ethers.utils.parseEther("0.05");
+ const ratio = ethers.utils.parseEther("133769"); // ETH for 16000 keys
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, feeReceiver, user1, user2] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockErc20TokenDecimals = await ethers.getContractFactory("MockErc20TokenDecimals");
+ tokenContract = await MockErc20TokenDecimals.deploy("Test token", tokenSymbol, 18);
+ await tokenContract.deployed();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(user2.address, domainName);
+ await mockPunkTldContract.deployed();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ const statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ const StatsMiddleware = await ethers.getContractFactory("StatsMiddleware");
+ statsMiddlewareContract = await StatsMiddleware.deploy(sfsContract.address, sfsNftTokenId, statsContract.address);
+ await statsMiddlewareContract.deployed(sfsContract.address, sfsNftTokenId);
+
+ await statsContract.setStatsWriterAddress(statsMiddlewareContract.address);
+
+ const FriendKeys = await ethers.getContractFactory("FriendKeysErc20");
+ friendKeysContract = await FriendKeys.deploy(
+ mockPunkTldContract.address,
+ tokenContract.address,
+ feeReceiver.address,
+ statsMiddlewareContract.address,
+ protocolFeePercent,
+ domainHolderFeePercent,
+ ratio
+ );
+ await friendKeysContract.deployed();
+
+ await statsMiddlewareContract.addWriter(friendKeysContract.address);
+
+ // mint tokens for user1
+ await tokenContract.mint(user1.address, ethers.utils.parseEther("1000"));
+ });
+
+ it("buys and sell 3 keys sequentially (with ERC-20 token)", async function () {
+ // function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256)
+ // get sell price for 1 domain
+ const sellPrice = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice: " + ethers.utils.formatEther(sellPrice) + " " + tokenSymbol);
+
+ // get domain holder address
+ const domainHolderAddress = await mockPunkTldContract.getDomainHolder(domainName);
+ expect(domainHolderAddress).to.equal(user2.address);
+
+ // get domain holder token balance
+ const domainHolderBalanceBefore = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceBefore: " + ethers.utils.formatEther(domainHolderBalanceBefore) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceBefore = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceBefore: " + ethers.utils.formatEther(feeReceiverBalanceBefore) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceBefore = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceBefore: " + ethers.utils.formatEther(user1BalanceBefore) + " " + tokenSymbol);
+
+ // get smart contract balance
+ const smartContractBalanceBefore = await tokenContract.balanceOf(friendKeysContract.address);
+ console.log("smartContractBalanceBefore: " + ethers.utils.formatEther(smartContractBalanceBefore) + " " + tokenSymbol);
+
+ // get buy price (should be 0 for the 1 share)
+ const buyPriceBefore = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceBefore: " + ethers.utils.formatEther(buyPriceBefore) + " " + tokenSymbol);
+ //expect(buyPriceBefore).to.equal(ethers.utils.parseEther("0"));
+
+ // user1 gives token approval to friend keys smart contract
+ await tokenContract.connect(user1).approve(
+ friendKeysContract.address,
+ ethers.constants.MaxUint256 // max approval
+ );
+
+ // buy 1 share
+ const tx = await friendKeysContract.connect(user1).buyKeys(
+ domainName,
+ 1, // keys amount
+ ethers.constants.AddressZero, // referrer
+ );
+ const receipt = await tx.wait();
+ //calculateGasCosts("buyKeys", receipt);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter1 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter1: " + ethers.utils.formatEther(domainHolderBalanceAfter1) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter1 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter1: " + ethers.utils.formatEther(feeReceiverBalanceAfter1) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter1 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter1: " + ethers.utils.formatEther(user1BalanceAfter1) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter1 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter1: " + ethers.utils.formatEther(buyPriceAfter1) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPriceBefore = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPriceBefore: " + ethers.utils.formatEther(sellPriceBefore) + " " + tokenSymbol);
+
+ // buy 1 share
+ const tx2 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt2 = await tx2.wait();
+ //calculateGasCosts("buyKeys", receipt2);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter2 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter2: " + ethers.utils.formatEther(domainHolderBalanceAfter2) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter2 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter2: " + ethers.utils.formatEther(feeReceiverBalanceAfter2) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter2 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter2: " + ethers.utils.formatEther(user1BalanceAfter2) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter2 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter2: " + ethers.utils.formatEther(buyPriceAfter2) + " " + tokenSymbol);
+
+ // buy 1 share
+ const tx3 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt3 = await tx3.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter3 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter3: " + ethers.utils.formatEther(domainHolderBalanceAfter3) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter3 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter3: " + ethers.utils.formatEther(feeReceiverBalanceAfter3) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter3 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter3: " + ethers.utils.formatEther(user1BalanceAfter3) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter3 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter3: " + ethers.utils.formatEther(buyPriceAfter3) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice1 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice1: " + ethers.utils.formatEther(sellPrice1) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx4 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt4 = await tx4.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter4 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter4: " + ethers.utils.formatEther(domainHolderBalanceAfter4) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter4 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter4: " + ethers.utils.formatEther(feeReceiverBalanceAfter4) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter4 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter4: " + ethers.utils.formatEther(user1BalanceAfter4) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice2 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice2: " + ethers.utils.formatEther(sellPrice2) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx5 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt5 = await tx5.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter5 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter5: " + ethers.utils.formatEther(domainHolderBalanceAfter5) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter5 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter5: " + ethers.utils.formatEther(feeReceiverBalanceAfter5) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter5 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter5: " + ethers.utils.formatEther(user1BalanceAfter5) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice3 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice3: " + ethers.utils.formatEther(sellPrice3) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx6 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, ethers.constants.AddressZero);
+ const receipt6 = await tx6.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter6 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter6: " + ethers.utils.formatEther(domainHolderBalanceAfter6) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter6 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter6: " + ethers.utils.formatEther(feeReceiverBalanceAfter6) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter6 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter6: " + ethers.utils.formatEther(user1BalanceAfter6) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice4 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice4: " + ethers.utils.formatEther(sellPrice4) + " " + tokenSymbol);
+
+ // get smart contract balance
+ const smartContractBalance4 = await tokenContract.balanceOf(friendKeysContract.address);
+ console.log("smartContractBalance4: " + ethers.utils.formatEther(smartContractBalance4) + " " + tokenSymbol);
+
+ // mapping(string => uint256) public keysSupply;
+ // get key supply
+ const keySupply = await friendKeysContract.keysSupply(domainName);
+ console.log("keySupply: " + keySupply);
+
+ // expect to revert when trying to sell the last share as user2
+ await expect(friendKeysContract.connect(user2).sellKeys(domainName, 1, ethers.constants.AddressZero)).to.be.revertedWith("Cannot sell the last key");
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/keys/friendKeysErc20Referrer.test.js b/test/keys/friendKeysErc20Referrer.test.js
new file mode 100644
index 0000000..21a9ac3
--- /dev/null
+++ b/test/keys/friendKeysErc20Referrer.test.js
@@ -0,0 +1,315 @@
+// npx hardhat test test/keys/friendKeysErc20Referrer.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("FriendKeys ERC-20 with referrer", function () {
+ let mockPunkTldContract;
+ let statsMiddlewareContract;
+ let friendKeysContract;
+
+ let tokenContract; // ERC-20 token for buying/selling keys
+ const tokenSymbol = "TTK";
+
+ let owner;
+ let feeReceiver;
+ let user1;
+ let user2; // domain holder
+ let referrer;
+
+ const domainName = "test"; // test.tld domain
+
+ const protocolFeePercent = ethers.utils.parseEther("0.05");
+ const domainHolderFeePercent = ethers.utils.parseEther("0.05");
+ const ratio = ethers.utils.parseEther("133769"); // ETH for 16000 keys
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, feeReceiver, user1, user2, referrer] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockErc20TokenDecimals = await ethers.getContractFactory("MockErc20TokenDecimals");
+ tokenContract = await MockErc20TokenDecimals.deploy("Test token", tokenSymbol, 18);
+ await tokenContract.deployed();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(user2.address, domainName);
+ await mockPunkTldContract.deployed();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ const statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ const StatsMiddleware = await ethers.getContractFactory("StatsMiddleware");
+ statsMiddlewareContract = await StatsMiddleware.deploy(sfsContract.address, sfsNftTokenId, statsContract.address);
+ await statsMiddlewareContract.deployed();
+
+ await statsContract.setStatsWriterAddress(statsMiddlewareContract.address);
+
+ const FriendKeys = await ethers.getContractFactory("FriendKeysErc20");
+ friendKeysContract = await FriendKeys.deploy(
+ mockPunkTldContract.address,
+ tokenContract.address,
+ feeReceiver.address,
+ statsMiddlewareContract.address,
+ protocolFeePercent,
+ domainHolderFeePercent,
+ ratio
+ );
+ await friendKeysContract.deployed();
+
+ await statsMiddlewareContract.addWriter(friendKeysContract.address);
+
+ // mint tokens for user1
+ await tokenContract.mint(user1.address, ethers.utils.parseEther("1000"));
+ });
+
+ it("buys and sell 3 keys sequentially (with ERC-20 token and referrer)", async function () {
+ // function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256)
+ // get sell price for 1 domain
+ const sellPrice = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice: " + ethers.utils.formatEther(sellPrice) + " " + tokenSymbol);
+
+ // get domain holder address
+ const domainHolderAddress = await mockPunkTldContract.getDomainHolder(domainName);
+ expect(domainHolderAddress).to.equal(user2.address);
+
+ // get domain holder token balance
+ const domainHolderBalanceBefore = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceBefore: " + ethers.utils.formatEther(domainHolderBalanceBefore) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceBefore = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceBefore: " + ethers.utils.formatEther(feeReceiverBalanceBefore) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceBefore = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceBefore: " + ethers.utils.formatEther(user1BalanceBefore) + " " + tokenSymbol);
+
+ // get smart contract balance
+ const smartContractBalanceBefore = await tokenContract.balanceOf(friendKeysContract.address);
+ console.log("smartContractBalanceBefore: " + ethers.utils.formatEther(smartContractBalanceBefore) + " " + tokenSymbol);
+
+ // get referrer balance
+ const referrerBalanceBefore = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceBefore: " + ethers.utils.formatEther(referrerBalanceBefore) + " " + tokenSymbol);
+
+ // get buy price (should be 0 for the 1 share)
+ const buyPriceBefore = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceBefore: " + ethers.utils.formatEther(buyPriceBefore) + " " + tokenSymbol);
+ //expect(buyPriceBefore).to.equal(ethers.utils.parseEther("0"));
+
+ // user1 gives token approval to friend keys smart contract
+ await tokenContract.connect(user1).approve(
+ friendKeysContract.address,
+ ethers.constants.MaxUint256 // max approval
+ );
+
+ // buy 1 share
+ const tx = await friendKeysContract.connect(user1).buyKeys(
+ domainName,
+ 1, // keys amount
+ referrer.address // referrer
+ );
+ const receipt = await tx.wait();
+ //calculateGasCosts("buyKeys", receipt);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter1 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter1: " + ethers.utils.formatEther(domainHolderBalanceAfter1) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter1 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter1: " + ethers.utils.formatEther(feeReceiverBalanceAfter1) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter1 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter1: " + ethers.utils.formatEther(user1BalanceAfter1) + " " + tokenSymbol);
+
+ // get referrer balance after1
+ const referrerBalanceAfter1 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter1: " + ethers.utils.formatEther(referrerBalanceAfter1) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter1 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter1: " + ethers.utils.formatEther(buyPriceAfter1) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPriceBefore = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPriceBefore: " + ethers.utils.formatEther(sellPriceBefore) + " " + tokenSymbol);
+
+ // buy 1 share
+ const tx2 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, referrer.address);
+ const receipt2 = await tx2.wait();
+ //calculateGasCosts("buyKeys", receipt2);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter2 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter2: " + ethers.utils.formatEther(domainHolderBalanceAfter2) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter2 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter2: " + ethers.utils.formatEther(feeReceiverBalanceAfter2) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter2 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter2: " + ethers.utils.formatEther(user1BalanceAfter2) + " " + tokenSymbol);
+
+ // get referrer balance after2
+ const referrerBalanceAfter2 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter2: " + ethers.utils.formatEther(referrerBalanceAfter2) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter2 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter2: " + ethers.utils.formatEther(buyPriceAfter2) + " " + tokenSymbol);
+
+ // buy 1 share
+ const tx3 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, referrer.address);
+ const receipt3 = await tx3.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter3 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter3: " + ethers.utils.formatEther(domainHolderBalanceAfter3) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter3 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter3: " + ethers.utils.formatEther(feeReceiverBalanceAfter3) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter3 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter3: " + ethers.utils.formatEther(user1BalanceAfter3) + " " + tokenSymbol);
+
+ // get referrer balance after3
+ const referrerBalanceAfter3 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter3: " + ethers.utils.formatEther(referrerBalanceAfter3) + " " + tokenSymbol);
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter3 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter3: " + ethers.utils.formatEther(buyPriceAfter3) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice1 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice1: " + ethers.utils.formatEther(sellPrice1) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx4 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt4 = await tx4.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter4 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter4: " + ethers.utils.formatEther(domainHolderBalanceAfter4) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter4 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter4: " + ethers.utils.formatEther(feeReceiverBalanceAfter4) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter4 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter4: " + ethers.utils.formatEther(user1BalanceAfter4) + " " + tokenSymbol);
+
+ // get referrer balance after4
+ const referrerBalanceAfter4 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter4: " + ethers.utils.formatEther(referrerBalanceAfter4) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice2 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice2: " + ethers.utils.formatEther(sellPrice2) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx5 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt5 = await tx5.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter5 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter5: " + ethers.utils.formatEther(domainHolderBalanceAfter5) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter5 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter5: " + ethers.utils.formatEther(feeReceiverBalanceAfter5) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter5 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter5: " + ethers.utils.formatEther(user1BalanceAfter5) + " " + tokenSymbol);
+
+ // get referrer balance after5
+ const referrerBalanceAfter5 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter5: " + ethers.utils.formatEther(referrerBalanceAfter5) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice3 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice3: " + ethers.utils.formatEther(sellPrice3) + " " + tokenSymbol);
+
+ // sell 1 share
+ const tx6 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt6 = await tx6.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter6 = await tokenContract.balanceOf(domainHolderAddress);
+ console.log("domainHolderBalanceAfter6: " + ethers.utils.formatEther(domainHolderBalanceAfter6) + " " + tokenSymbol);
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter6 = await tokenContract.balanceOf(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter6: " + ethers.utils.formatEther(feeReceiverBalanceAfter6) + " " + tokenSymbol);
+
+ // get user1 balance
+ const user1BalanceAfter6 = await tokenContract.balanceOf(user1.address);
+ console.log("user1BalanceAfter6: " + ethers.utils.formatEther(user1BalanceAfter6) + " " + tokenSymbol);
+
+ // get referrer balance after6
+ const referrerBalanceAfter6 = await tokenContract.balanceOf(referrer.address);
+ console.log("referrerBalanceAfter6: " + ethers.utils.formatEther(referrerBalanceAfter6) + " " + tokenSymbol);
+
+ // get sell price for 1 domain
+ const sellPrice4 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice4: " + ethers.utils.formatEther(sellPrice4) + " " + tokenSymbol);
+
+ // get smart contract balance
+ const smartContractBalance4 = await tokenContract.balanceOf(friendKeysContract.address);
+ console.log("smartContractBalance4: " + ethers.utils.formatEther(smartContractBalance4) + " " + tokenSymbol);
+
+ // mapping(string => uint256) public keysSupply;
+ // get key supply
+ const keySupply = await friendKeysContract.keysSupply(domainName);
+ console.log("keySupply: " + keySupply);
+
+ // expect to revert when trying to sell the last share as user2
+ await expect(friendKeysContract.connect(user2).sellKeys(domainName, 1, referrer.address)).to.be.revertedWith("Cannot sell the last key");
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/keys/friendKeysReferrer.test.js b/test/keys/friendKeysReferrer.test.js
new file mode 100644
index 0000000..5d494fc
--- /dev/null
+++ b/test/keys/friendKeysReferrer.test.js
@@ -0,0 +1,299 @@
+// npx hardhat test test/keys/friendKeysReferrer.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("FriendKeys WITH REFERRER", function () {
+ let mockPunkTldContract;
+ let statsMiddlewareContract;
+ let friendKeysContract;
+
+ let owner;
+ let feeReceiver;
+ let user1;
+ let user2; // domain holder
+ let referrer;
+
+ const domainName = "test"; // test.tld domain
+
+ const protocolFeePercent = ethers.utils.parseEther("0.05");
+ const domainHolderFeePercent = ethers.utils.parseEther("0.05");
+ const ratio = ethers.utils.parseEther("133769"); // ETH for 16000 keys
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, feeReceiver, user1, user2, referrer] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(user2.address, domainName);
+ await mockPunkTldContract.deployed();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ const statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ const StatsMiddleware = await ethers.getContractFactory("StatsMiddleware");
+ statsMiddlewareContract = await StatsMiddleware.deploy(sfsContract.address, sfsNftTokenId, statsContract.address);
+ await statsMiddlewareContract.deployed();
+
+ await statsContract.setStatsWriterAddress(statsMiddlewareContract.address);
+
+ const FriendKeys = await ethers.getContractFactory("FriendKeys");
+ friendKeysContract = await FriendKeys.deploy(
+ mockPunkTldContract.address,
+ feeReceiver.address,
+ statsMiddlewareContract.address,
+ protocolFeePercent,
+ domainHolderFeePercent,
+ ratio
+ );
+ await friendKeysContract.deployed();
+
+ await statsMiddlewareContract.addWriter(friendKeysContract.address);
+ });
+
+ it("buys and sell 3 keys sequentially WITH REFERRER", async function () {
+ // function getSellPriceAfterFee(string memory domainName, uint256 amount) public view returns (uint256)
+ // get sell price for 1 domain
+ const sellPrice = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice: " + ethers.utils.formatEther(sellPrice) + " ETH");
+
+ // get domain holder address
+ const domainHolderAddress = await mockPunkTldContract.getDomainHolder(domainName);
+ expect(domainHolderAddress).to.equal(user2.address);
+
+ // get domain holder balance
+ const domainHolderBalanceBefore = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceBefore: " + ethers.utils.formatEther(domainHolderBalanceBefore) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceBefore = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceBefore: " + ethers.utils.formatEther(feeReceiverBalanceBefore) + " ETH");
+
+ // get user1 balance
+ const user1BalanceBefore = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceBefore: " + ethers.utils.formatEther(user1BalanceBefore) + " ETH");
+
+ // get smart contract balance
+ const smartContractBalanceBefore = await ethers.provider.getBalance(friendKeysContract.address);
+ console.log("smartContractBalanceBefore: " + ethers.utils.formatEther(smartContractBalanceBefore) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceBefore = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceBefore: " + ethers.utils.formatEther(referrerBalanceBefore) + " ETH");
+
+ // get buy price (should be 0 for the 1 share)
+ const buyPriceBefore = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceBefore: " + ethers.utils.formatEther(buyPriceBefore) + " ETH");
+ //expect(buyPriceBefore).to.equal(ethers.utils.parseEther("0"));
+
+ // buy 1 share
+ const tx = await friendKeysContract.connect(user1).buyKeys(
+ domainName,
+ 1,
+ referrer.address,
+ { value: buyPriceBefore }
+ );
+ const receipt = await tx.wait();
+ //calculateGasCosts("buyKeys", receipt);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter1 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter1: " + ethers.utils.formatEther(domainHolderBalanceAfter1) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter1 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter1: " + ethers.utils.formatEther(feeReceiverBalanceAfter1) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter1 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter1: " + ethers.utils.formatEther(user1BalanceAfter1) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter1 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter1: " + ethers.utils.formatEther(referrerBalanceAfter1) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter1 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter1: " + ethers.utils.formatEther(buyPriceAfter1) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPriceBefore = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPriceBefore: " + ethers.utils.formatEther(sellPriceBefore) + " ETH");
+
+ // buy 1 share
+ const tx2 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, referrer.address, { value: buyPriceAfter1 });
+ const receipt2 = await tx2.wait();
+ //calculateGasCosts("buyKeys", receipt2);
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter2 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter2: " + ethers.utils.formatEther(domainHolderBalanceAfter2) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter2 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter2: " + ethers.utils.formatEther(feeReceiverBalanceAfter2) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter2 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter2: " + ethers.utils.formatEther(user1BalanceAfter2) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter2 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter2: " + ethers.utils.formatEther(referrerBalanceAfter2) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter2 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter2: " + ethers.utils.formatEther(buyPriceAfter2) + " ETH");
+
+ // buy 1 share
+ const tx3 = await friendKeysContract.connect(user1).buyKeys(domainName, 1, referrer.address, { value: buyPriceAfter2 });
+ const receipt3 = await tx3.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter3 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter3: " + ethers.utils.formatEther(domainHolderBalanceAfter3) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter3 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter3: " + ethers.utils.formatEther(feeReceiverBalanceAfter3) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter3 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter3: " + ethers.utils.formatEther(user1BalanceAfter3) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter3 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter3: " + ethers.utils.formatEther(referrerBalanceAfter3) + " ETH");
+
+ // get buy price after (should not be 0 anymore)
+ const buyPriceAfter3 = await friendKeysContract.getBuyPriceAfterFee(domainName, 1);
+ console.log("buyPriceAfter3: " + ethers.utils.formatEther(buyPriceAfter3) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice1 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice1: " + ethers.utils.formatEther(sellPrice1) + " ETH");
+
+ // sell 1 share
+ const tx4 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt4 = await tx4.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter4 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter4: " + ethers.utils.formatEther(domainHolderBalanceAfter4) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter4 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter4: " + ethers.utils.formatEther(feeReceiverBalanceAfter4) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter4 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter4: " + ethers.utils.formatEther(user1BalanceAfter4) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter4 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter4: " + ethers.utils.formatEther(referrerBalanceAfter4) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice2 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice2: " + ethers.utils.formatEther(sellPrice2) + " ETH");
+
+ // sell 1 share
+ const tx5 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt5 = await tx5.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter5 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter5: " + ethers.utils.formatEther(domainHolderBalanceAfter5) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter5 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter5: " + ethers.utils.formatEther(feeReceiverBalanceAfter5) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter5 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter5: " + ethers.utils.formatEther(user1BalanceAfter5) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter5 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter5: " + ethers.utils.formatEther(referrerBalanceAfter5) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice3 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice3: " + ethers.utils.formatEther(sellPrice3) + " ETH");
+
+ // sell 1 share
+ const tx6 = await friendKeysContract.connect(user1).sellKeys(domainName, 1, referrer.address);
+ const receipt6 = await tx6.wait();
+
+ console.log("----");
+
+ // get domain holder balance
+ const domainHolderBalanceAfter6 = await ethers.provider.getBalance(domainHolderAddress);
+ console.log("domainHolderBalanceAfter6: " + ethers.utils.formatEther(domainHolderBalanceAfter6) + " ETH");
+
+ // get fee receiver balance
+ const feeReceiverBalanceAfter6 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter6: " + ethers.utils.formatEther(feeReceiverBalanceAfter6) + " ETH");
+
+ // get user1 balance
+ const user1BalanceAfter6 = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter6: " + ethers.utils.formatEther(user1BalanceAfter6) + " ETH");
+
+ // get referrer balance
+ const referrerBalanceAfter6 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter6: " + ethers.utils.formatEther(referrerBalanceAfter6) + " ETH");
+
+ // get sell price for 1 domain
+ const sellPrice4 = await friendKeysContract.getSellPriceAfterFee(domainName, 1);
+ console.log("sellPrice4: " + ethers.utils.formatEther(sellPrice4) + " ETH");
+
+ // get smart contract balance
+ const smartContractBalance4 = await ethers.provider.getBalance(friendKeysContract.address);
+ console.log("smartContractBalance4: " + ethers.utils.formatEther(smartContractBalance4) + " ETH");
+
+ // mapping(string => uint256) public keysSupply;
+ // get key supply
+ const keySupply = await friendKeysContract.keysSupply(domainName);
+ console.log("keySupply: " + keySupply);
+
+ // expect to revert when trying to sell the last share as user2
+ await expect(friendKeysContract.connect(user2).sellKeys(domainName, 1, referrer.address)).to.be.revertedWith("Cannot sell the last key");
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/launchpad/NftMetadataOnchainMultipleImages.test.js b/test/launchpad/NftMetadataOnchainMultipleImages.test.js
new file mode 100644
index 0000000..b88633f
--- /dev/null
+++ b/test/launchpad/NftMetadataOnchainMultipleImages.test.js
@@ -0,0 +1,88 @@
+// npx hardhat test test/launchpad/NftMetadataOnchainMultipleImages.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("NftMetadataOnchainMultipleImages", function () {
+ let metadataContract;
+
+ // NFT contract data
+ const nftDescription = "Iggy NFT is a new unique NFT project";
+ const nftImage = "https://bafkreihn7v7ugcu4yjnapsha3tij4cq7qatj2wjofpvxlkp6s4sl5nujn4.ipfs.w3s.link/";
+ const nftMetadataName = "Iggy";
+ const externalUrl = "https://iggy.social";
+
+ const images = [
+ "https://bafybeidim3mkbj4obd3cbf7fcrolioo2zpeha3xbzg2jvuajrtmvjk5lnq.ipfs.w3s.link/et_puisBK_2.jpg",
+ "https://bafybeicbrgmgugbmk7g2nofbndysbq4pwezm65t6yjlyo7j32q3hgsxtxm.ipfs.w3s.link/et_puisBK_3.jpg",
+ "https://bafybeif2gszl6ts4hmjdpdribflwakqtx5mljdfpqmobizrgjbumaksdwy.ipfs.w3s.link/et_puisBK_4.jpg",
+ "https://bafybeigahpskfomwq3kfbklcdtatz3g5pun5rwatjje3i2rwye6ktanszq.ipfs.w3s.link/et_puisBK_5.jpg",
+ "https://bafybeihgmzfxbtigxzzcvmfpr6atmkgvr54muzxq3ew3minfx6ofugwvhi.ipfs.w3s.link/et_puisBK_6.jpg",
+ "https://bafybeif4rte6ltbcc3gfwvneqwwrxvoif26zjmcuhoyksxdcnpjjjpbsxm.ipfs.w3s.link/et_puisBK_7.jpg",
+ "https://bafybeid5sn5wfmijo4ixlcvzqnnmpjqucqi7pf6mnr5upf7tg3zsiarzhy.ipfs.w3s.link/et_puisBK_8.jpg",
+ "https://bafybeibpgwuchjkm42hwpbrujeglpp77diw4o6f27kjpihbrhfrndxfvzu.ipfs.w3s.link/et_puisBK_9.jpg",
+ "https://bafybeigw6zhwvnmiy6t6dxhtjvfnmy2uv5iib7g465ii2pp7otjmbitmsq.ipfs.w3s.link/et_puisBK_10.jpg",
+ "https://bafybeibjtypyvp5gjvldbbo7z4jnq52qw6if6cqgwwn32r526dygzdoyf4.ipfs.w3s.link/et_puisBK_11.jpg",
+ "https://bafybeicamgpbio5fghlau4t6cajxemttsjmf3mcsueoen2ehybgdemm7jm.ipfs.w3s.link/et_puisBK_12.jpg",
+ "https://bafybeigcgk54wvsrcqwk6rzwqxfri27u4th3rukcuhfhztp2dfncgtexiy.ipfs.w3s.link/et_puisBK_13.jpg",
+ "https://bafybeiewptq3ylx32gckjxaxpg4gioro5hz2r74a6ut54wwkqzeas4dwxa.ipfs.w3s.link/et_puisBK_14.jpg",
+ "https://bafybeibxscafzx2zizv3fcbewk7g4scvbfwd7pwyay2fpcw76ixov2dquy.ipfs.w3s.link/et_puisBK_15.jpg",
+ "https://bafybeiahyzchhowf3chkbs26y3u743kz7eh4idogpbmad66lk7iulsondi.ipfs.w3s.link/et_puisBK_16.jpg",
+ "https://bafybeic5wboyolq736quq4i3dm2qg7rei35ujst6nywwgrlxawrllz3sie.ipfs.dweb.link/et_puisBK_17.jpg",
+ "https://bafybeiawebroia6j7qglx4dhlsev4slqtgu2mqjnepdi6ka6jtjmj762qy.ipfs.w3s.link/et_puisBK_18.jpg",
+ "https://bafybeibscztchiurjtq2tzrjh5jtzpkoygmqsrmrqdat34o3amxsfkkzm4.ipfs.w3s.link/et_puisBK_19.jpg",
+ "https://bafybeicneamma5bz2byzynk5re3rwklgglcmlk2xbqp3xcda3nd4sypmu4.ipfs.w3s.link/et_puisBK_20.jpg",
+ "https://bafybeidznwxrql55ig4rumlv2tovfzcu2iaja66vy6lscfufeyxrbmgax4.ipfs.w3s.link/et_puisBK_21.jpg",
+ "https://bafybeicom6nyaarxnxqk5kz66uxidhd2accwzi6oyxzgfzyrcvnfmrn7lq.ipfs.w3s.link/et_puisBK_22.jpg",
+ "https://bafybeid2yip54zx4opmv2hzing6qxalzsugj5jhxjndktvwzpiaxnywemi.ipfs.w3s.link/et_puisBK_23.jpg",
+ "https://bafybeihrxuqdzcdmxwkeggzm62rbjezgmeyyiya5xorwjmktrxyshw3neq.ipfs.w3s.link/et_puisBK_24.jpg",
+ "https://bafybeifnw66wjbngkbybcqc4bes2f7pujt3r7qe6koqml3cyuo2xaiztoq.ipfs.w3s.link/et_puisBK_25.jpg",
+ "https://bafybeihnlgdwhzityuro4jbcljd2eubqtpqc3o4qkbbolypp2wjhveqc3y.ipfs.w3s.link/et_puisBK_26.jpg"
+ ];
+
+ beforeEach(async function () {
+ const NftMetadata = await ethers.getContractFactory("NftMetadataOnchainMultipleImages");
+
+ metadataContract = await NftMetadata.deploy(
+ nftImage, // collectionPreviewImage
+ nftDescription, // description
+ externalUrl, // externalUrl
+ nftImage, // image
+ nftMetadataName // name
+ );
+
+ await metadataContract.deployed();
+
+ // add images to contract via addImageToCollection() function
+ for (let i = 0; i < images.length; i++) {
+ const tx = await metadataContract.addImageToCollection(images[i]);
+ await tx.wait();
+ }
+ });
+
+ it("get metadata", async function () {
+
+ const firstImage = await metadataContract.images(metadataContract.address);
+ console.log("firstImage: " + firstImage);
+
+ //const imagesArray = await metadataContract.getImages();
+ //console.log("images: " + imagesArray);
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/launchpad/bonding721.test.js b/test/launchpad/bonding721.test.js
new file mode 100644
index 0000000..11172b1
--- /dev/null
+++ b/test/launchpad/bonding721.test.js
@@ -0,0 +1,519 @@
+// npx hardhat test test/launchpad/bonding721.test.js
+
+const { expect } = require("chai");
+const { ethers } = require("hardhat");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("IggyLaunchpad721Bonding", function () {
+ let directoryContract;
+ let launchpadContract;
+ let statsContract;
+ let statsMiddlewareContract;
+ let metadataContract;
+
+ let factoryOwner;
+ let feeReceiver;
+ let nftContractOwner;
+ let user1;
+ let user2;
+
+ const feePercent = ethers.utils.parseEther("0.05");
+ const priceLaunch = ethers.utils.parseEther("69"); // price for launching a new NFT contract
+ const ratio = ethers.utils.parseEther("6969"); // ratio for the bonding curve
+
+ // NFT contract data
+ const nftDescription = "Iggy NFT is a new unique NFT project";
+ const nftImage = "https://iggy.social/assets/img/preview.jpg";
+ const nftMetadataName = "Iggy";
+ const nftName = "Iggy NFT";
+ const nftSymbol = "IGGY";
+ const nftUniqueId = "justsomethingunique";
+
+ beforeEach(async function () {
+ [factoryOwner, feeReceiver, nftContractOwner, user1, user2, referrer] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const Stats = await ethers.getContractFactory("Stats");
+ statsContract = await Stats.deploy(sfsContract.address, sfsNftTokenId);
+ await statsContract.deployed();
+
+ const StatsMiddleware = await ethers.getContractFactory("StatsMiddleware");
+ statsMiddlewareContract = await StatsMiddleware.deploy(sfsContract.address, sfsNftTokenId, statsContract.address);
+ await statsMiddlewareContract.deployed();
+
+ // add middleware contract address to launchpad stats contract
+ await statsContract.setStatsWriterAddress(statsMiddlewareContract.address);
+
+ const NftMetadata = await ethers.getContractFactory("NftMetadata");
+ metadataContract = await NftMetadata.deploy();
+ await metadataContract.deployed();
+
+ const NftDirectory = await ethers.getContractFactory("NftDirectory");
+ directoryContract = await NftDirectory.deploy();
+ await directoryContract.deployed();
+
+ const IggyLaunchpad721Bonding = await ethers.getContractFactory("IggyLaunchpad721Bonding");
+ launchpadContract = await IggyLaunchpad721Bonding.deploy(
+ metadataContract.address,
+ feeReceiver.address,
+ directoryContract.address,
+ statsMiddlewareContract.address,
+ feePercent,
+ priceLaunch
+ );
+ await launchpadContract.deployed();
+
+ // add launchpad contract as writer in the NFT directory contract
+ await directoryContract.addWriter(launchpadContract.address);
+
+ // add launchpad contract address to stats middleware contract as writer
+ await statsMiddlewareContract.addWriter(launchpadContract.address);
+ });
+
+ it("creates a new NFT contract via launchpad and mints&burns a bunch of NFTs", async function () {
+ // get user1 balance before
+ const user1BalanceBefore = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceBefore: " + ethers.utils.formatEther(user1BalanceBefore) + " ETH");
+
+ // get fee receiver balance before
+ const feeReceiverBalanceBefore = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceBefore: " + ethers.utils.formatEther(feeReceiverBalanceBefore) + " ETH");
+
+ // launch a new NFT contract
+ const tx = await launchpadContract.connect(user1).launch(
+ user1.address, // NFT contract owner
+ ethers.constants.AddressZero, // referrer
+ nftDescription,
+ nftImage,
+ nftMetadataName,
+ nftName,
+ nftSymbol,
+ nftUniqueId,
+ ratio,
+ { value: priceLaunch }
+ );
+ const receipt = await tx.wait();
+ //calculateGasCosts("launch", receipt);
+
+ // get user1 balance after
+ const user1BalanceAfter = await ethers.provider.getBalance(user1.address);
+ console.log("user1BalanceAfter: " + ethers.utils.formatEther(user1BalanceAfter) + " ETH");
+
+ // get fee receiver balance after
+ const feeReceiverBalanceAfter = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("feeReceiverBalanceAfter: " + ethers.utils.formatEther(feeReceiverBalanceAfter) + " ETH");
+
+ // get NFT contract address
+ const nftContractAddress = await launchpadContract.getNftContractAddress(nftUniqueId);
+ console.log("nftContractAddress: " + nftContractAddress);
+
+ // create NFT contract instance
+ const Nft721Bonding = await ethers.getContractFactory("Nft721Bonding");
+ const nftContract = await Nft721Bonding.attach(nftContractAddress);
+
+ console.log("-- Mint NFTs #1 & #2 --");
+
+ // get NFT total supply
+ const nftTotalSupply1 = await nftContract.totalSupply();
+ console.log("Total Supply 1: " + nftTotalSupply1);
+
+ // get burn price 1
+ const burnPrice1 = await nftContract.getBurnPrice();
+ console.log("Burn Price 1: " + ethers.utils.formatEther(burnPrice1) + " ETH");
+
+ // get mint price 1
+ const mintPrice1 = await nftContract.getMintPrice();
+ console.log("Mint Price 1: " + ethers.utils.formatEther(mintPrice1) + " ETH");
+
+ // mint 1 NFT
+ const tx1 = await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice1 }
+ );
+ const receipt1 = await tx1.wait();
+ //calculateGasCosts("mint1", receipt1);
+
+ // get user1 balance 1
+ const user1Balance1 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 1: " + ethers.utils.formatEther(user1Balance1) + " ETH");
+
+ // get fee receiver balance 1
+ const feeReceiverBalance1 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 1: " + ethers.utils.formatEther(feeReceiverBalance1) + " ETH");
+
+ // get user2 balance 1
+ const user2Balance1 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 1: " + ethers.utils.formatEther(user2Balance1) + " ETH");
+
+ // get NFT contract balance 1
+ const nftContractBalance1 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 1: " + ethers.utils.formatEther(nftContractBalance1) + " ETH");
+
+ // check the owner of the NFT #1 (should be the NFT contract owner)
+ const nftOwner1 = await nftContract.ownerOf(1);
+ expect(nftOwner1).to.equal(user1.address);
+
+ // check the owner of the NFT #2 (should be the user2)
+ const nftOwner2 = await nftContract.ownerOf(2);
+ expect(nftOwner2).to.equal(user2.address);
+
+ console.log("-- Burn NFT #2 --");
+
+ // get NFT total supply
+ const nftTotalSupply2 = await nftContract.totalSupply();
+ console.log("Total Supply 2: " + nftTotalSupply2);
+
+ // get burn price 2
+ const burnPrice2 = await nftContract.getBurnPrice();
+ console.log("Burn Price 2: " + ethers.utils.formatEther(burnPrice2) + " ETH");
+
+ // get mint price 2
+ const mintPrice2 = await nftContract.getMintPrice();
+ console.log("Mint Price 2: " + ethers.utils.formatEther(mintPrice2) + " ETH");
+
+ // revert if user1 tries to burn NFT #2 (user2 owns it)
+ await expect(nftContract.connect(user1).burn(2)).to.be.revertedWith("Nft721Bonding: caller is not owner nor approved");
+
+ // burn NFT #2
+ const tx2 = await nftContract.connect(user2).burn(
+ 2 // token ID #2 (user2 owns it, while user1 owns token ID #1)
+ );
+ const receipt2 = await tx2.wait();
+ //calculateGasCosts("burn", receipt2);
+
+ // get user1 balance 2
+ const user1Balance2 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 2: " + ethers.utils.formatEther(user1Balance2) + " ETH");
+
+ // get fee receiver balance 2
+ const feeReceiverBalance2 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 2: " + ethers.utils.formatEther(feeReceiverBalance2) + " ETH");
+
+ // get user2 balance 2
+ const user2Balance2 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 2: " + ethers.utils.formatEther(user2Balance2) + " ETH");
+
+ // get NFT contract balance 2
+ const nftContractBalance2 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 2: " + ethers.utils.formatEther(nftContractBalance2) + " ETH");
+
+ console.log("-- Mint NFT #3 --");
+
+ // get NFT total supply
+ const nftTotalSupply3 = await nftContract.totalSupply();
+ console.log("Total Supply 3: " + nftTotalSupply3);
+
+ // get burn price 3
+ const burnPrice3 = await nftContract.getBurnPrice();
+ console.log("Burn Price 3: " + ethers.utils.formatEther(burnPrice3) + " ETH");
+
+ // get mint price 3
+ const mintPrice3 = await nftContract.getMintPrice();
+ console.log("Mint Price 3: " + ethers.utils.formatEther(mintPrice3) + " ETH");
+
+ // mint NFT #3
+ const tx3 = await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice3 }
+ );
+ const receipt3 = await tx3.wait();
+ //calculateGasCosts("mint3", receipt3);
+
+ // get user1 balance 3
+ const user1Balance3 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 3: " + ethers.utils.formatEther(user1Balance3) + " ETH");
+
+ // get fee receiver balance 3
+ const feeReceiverBalance3 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 3: " + ethers.utils.formatEther(feeReceiverBalance3) + " ETH");
+
+ // get user2 balance 3
+ const user2Balance3 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 3: " + ethers.utils.formatEther(user2Balance3) + " ETH");
+
+ // get NFT contract balance 3
+ const nftContractBalance3 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 3: " + ethers.utils.formatEther(nftContractBalance3) + " ETH");
+
+ console.log("-- Mint NFT #4 --");
+
+ // get NFT total supply
+ const nftTotalSupply4 = await nftContract.totalSupply();
+ console.log("Total Supply 4: " + nftTotalSupply4);
+
+ // get burn price 4
+ const burnPrice4 = await nftContract.getBurnPrice();
+ console.log("Burn Price 4: " + ethers.utils.formatEther(burnPrice4) + " ETH");
+
+ // get mint price 4
+ const mintPrice4 = await nftContract.getMintPrice();
+ console.log("Mint Price 4: " + ethers.utils.formatEther(mintPrice4) + " ETH");
+
+ // mint NFT #4
+ const tx4 = await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice4 }
+ );
+ const receipt4 = await tx4.wait();
+ //calculateGasCosts("mint4", receipt4);
+
+ // get user1 balance 4
+ const user1Balance4 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 4: " + ethers.utils.formatEther(user1Balance4) + " ETH");
+
+ // get fee receiver balance 4
+ const feeReceiverBalance4 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 4: " + ethers.utils.formatEther(feeReceiverBalance4) + " ETH");
+
+ // get user2 balance 4
+ const user2Balance4 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 4: " + ethers.utils.formatEther(user2Balance4) + " ETH");
+
+ // get NFT contract balance 4
+ const nftContractBalance4 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 4: " + ethers.utils.formatEther(nftContractBalance4) + " ETH");
+
+ console.log("-- Mint NFT #5 --");
+
+ // get NFT total supply
+ const nftTotalSupply5 = await nftContract.totalSupply();
+ console.log("Total Supply 5: " + nftTotalSupply5);
+
+ // get burn price 5
+ const burnPrice5 = await nftContract.getBurnPrice();
+ console.log("Burn Price 5: " + ethers.utils.formatEther(burnPrice5) + " ETH");
+
+ // get mint price 5
+ const mintPrice5 = await nftContract.getMintPrice();
+ console.log("Mint Price 5: " + ethers.utils.formatEther(mintPrice5) + " ETH");
+
+ // mint NFT #5
+ const tx5 = await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice5 }
+ );
+ const receipt5 = await tx5.wait();
+ //calculateGasCosts("mint5", receipt5);
+
+ // get user1 balance 5
+ const user1Balance5 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 5: " + ethers.utils.formatEther(user1Balance5) + " ETH");
+
+ // get fee receiver balance 5
+ const feeReceiverBalance5 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 5: " + ethers.utils.formatEther(feeReceiverBalance5) + " ETH");
+
+ // get user2 balance 5
+ const user2Balance5 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 5: " + ethers.utils.formatEther(user2Balance5) + " ETH");
+
+ // get NFT contract balance 5
+ const nftContractBalance5 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 5: " + ethers.utils.formatEther(nftContractBalance5) + " ETH");
+
+ console.log("-- Mint NFT #6 --");
+
+ // get NFT total supply
+ const nftTotalSupply6 = await nftContract.totalSupply();
+ console.log("Total Supply 6: " + nftTotalSupply6);
+
+ // get burn price 6
+ const burnPrice6 = await nftContract.getBurnPrice();
+ console.log("Burn Price 6: " + ethers.utils.formatEther(burnPrice6) + " ETH");
+
+ // get mint price 6
+ const mintPrice6 = await nftContract.getMintPrice();
+ console.log("Mint Price 6: " + ethers.utils.formatEther(mintPrice6) + " ETH");
+
+ // mint NFT #6
+ const tx6 = await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice6 }
+ );
+ const receipt6 = await tx6.wait();
+ //calculateGasCosts("mint6", receipt6);
+
+ // get user1 balance 6
+ const user1Balance6 = await ethers.provider.getBalance(user1.address);
+ console.log("user1 balance 6: " + ethers.utils.formatEther(user1Balance6) + " ETH");
+
+ // get fee receiver balance 6
+ const feeReceiverBalance6 = await ethers.provider.getBalance(feeReceiver.address);
+ console.log("fee receiver balance 6: " + ethers.utils.formatEther(feeReceiverBalance6) + " ETH");
+
+ // get user2 balance 6
+ const user2Balance6 = await ethers.provider.getBalance(user2.address);
+ console.log("user2 balance 6: " + ethers.utils.formatEther(user2Balance6) + " ETH");
+
+ // get NFT contract balance 6
+ const nftContractBalance6 = await ethers.provider.getBalance(nftContractAddress);
+ console.log("NFT contract balance 6: " + ethers.utils.formatEther(nftContractBalance6) + " ETH");
+
+ // change NFT & collection image via metadata contract (use setMdTypeAndUrlOrImage function)
+ await metadataContract.connect(user1).setMdTypeAndUrlOrImage(
+ nftContractAddress,
+ 0, // mdType: 0 (static image URL)
+ "https://iggy.social/assets/img/preview.jpg",
+ "https://iggy.social/assets/img/preview.jpg"
+ );
+
+ // check minting fee before
+ const mintingFeePercentageBefore = await nftContract.mintingFeePercentage();
+ console.log("mintingFeePercentageBefore: " + Number(ethers.utils.formatEther(mintingFeePercentageBefore))*100 + "%");
+
+ // fail to change minting fee via setMintingFeePercentage function in the NFT contract if new fee is too high (over 5%)
+ await expect(nftContract.connect(user1).setMintingFeePercentage(ethers.utils.parseEther("0.06"))).to.be.revertedWith("Nft721Bonding: fee must be lower than 5%");
+
+ // set minting fee to 1% via setMintingFeePercentage function in the NFT contract
+ await nftContract.connect(user1).setMintingFeePercentage(ethers.utils.parseEther("0.01"));
+
+ // check minting fee after
+ const mintingFeePercentageAfter = await nftContract.mintingFeePercentage();
+ console.log("mintingFeePercentageAfter: " + Number(ethers.utils.formatEther(mintingFeePercentageAfter))*100 + "%");
+ expect(mintingFeePercentageAfter).to.equal(ethers.utils.parseEther("0.01"));
+ expect(mintingFeePercentageAfter).to.be.lt(mintingFeePercentageBefore);
+
+ });
+
+ it("creates a few new NFT contracts via launchpad and queries them", async function () {
+
+ // check referrer balance
+ const referrerBalanceBefore = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceBefore: " + ethers.utils.formatEther(referrerBalanceBefore) + " ETH");
+
+ await launchpadContract.connect(user1).launch(
+ user1.address, // NFT contract owner
+ referrer.address, // referrer
+ nftDescription,
+ nftImage,
+ nftMetadataName,
+ nftName,
+ nftSymbol,
+ nftUniqueId,
+ ratio,
+ { value: priceLaunch }
+ );
+
+ // referrer balance after 1
+ const referrerBalanceAfter1 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter1: " + ethers.utils.formatEther(referrerBalanceAfter1) + " ETH");
+
+ await launchpadContract.connect(user1).launch(
+ user2.address, // NFT contract owner
+ ethers.constants.AddressZero, // referrer
+ nftDescription,
+ nftImage,
+ nftMetadataName,
+ nftName,
+ nftSymbol,
+ nftUniqueId + "2",
+ ratio,
+ { value: priceLaunch }
+ );
+
+ // referrer balance after 2
+ const referrerBalanceAfter2 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter2: " + ethers.utils.formatEther(referrerBalanceAfter2) + " ETH");
+
+ await launchpadContract.connect(user1).launch(
+ user1.address, // NFT contract owner
+ referrer.address, // referrer
+ nftDescription,
+ nftImage,
+ nftMetadataName,
+ nftName,
+ nftSymbol,
+ nftUniqueId + "3",
+ ratio,
+ { value: priceLaunch }
+ );
+
+ // referrer balance after 3
+ const referrerBalanceAfter3 = await ethers.provider.getBalance(referrer.address);
+ console.log("referrerBalanceAfter3: " + ethers.utils.formatEther(referrerBalanceAfter3) + " ETH");
+
+ // call getLastNftContracts
+
+ const lastNftContracts3 = await launchpadContract.getLastNftContracts(3);
+ console.log(lastNftContracts3);
+
+ const lastNftContracts2 = await launchpadContract.getLastNftContracts(2);
+ console.log(lastNftContracts2);
+
+ const lastNftContracts5 = await launchpadContract.getLastNftContracts(5);
+ console.log(lastNftContracts5);
+
+ const lastFeaturedContracts5 = await launchpadContract.getFeaturedNftContracts(5);
+ console.log(lastFeaturedContracts5);
+
+ // addNftAddressToFeatured
+ await directoryContract.addNftAddressToFeatured(lastNftContracts3[1]);
+
+ const lastFeaturedContracts6 = await launchpadContract.getFeaturedNftContracts(6);
+ console.log(lastFeaturedContracts6);
+
+
+ });
+
+ it("get only mint price data", async function () {
+
+ // launch a new NFT contract
+ const tx = await launchpadContract.connect(user1).launch(
+ user1.address, // NFT contract owner
+ ethers.constants.AddressZero, // referrer
+ nftDescription,
+ nftImage,
+ nftMetadataName,
+ nftName,
+ nftSymbol,
+ nftUniqueId,
+ ratio,
+ { value: priceLaunch }
+ );
+
+ // get NFT contract address
+ const nftContractAddress = await launchpadContract.getNftContractAddress(nftUniqueId);
+ console.log("nftContractAddress: " + nftContractAddress);
+
+ // create NFT contract instance
+ const Nft721Bonding = await ethers.getContractFactory("Nft721Bonding");
+ const nftContract = await Nft721Bonding.attach(nftContractAddress);
+
+ const numberOfMintPriceData = 25;
+
+ for (let i = 0; i < numberOfMintPriceData; i++) {
+ let mintPrice = await nftContract.getMintPrice();
+ console.log(ethers.utils.formatEther(mintPrice));
+
+ // mint NFT
+ await nftContract.connect(user2).mint(
+ user2.address,
+ { value: mintPrice }
+ );
+ }
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/merkle/merkleClaimerERC721.test.js b/test/merkle/merkleClaimerERC721.test.js
new file mode 100644
index 0000000..cfca192
--- /dev/null
+++ b/test/merkle/merkleClaimerERC721.test.js
@@ -0,0 +1,118 @@
+// npx hardhat test test/merkle/merkleClaimerERC721.test.js
+
+const { expect } = require("chai");
+const { StandardMerkleTree } = require("@openzeppelin/merkle-tree");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("MerkleClaimerERC721", function () {
+ let nftContract;
+ let merkleClaimerContract;
+
+ let owner;
+ let user1;
+ let user2;
+ let user3;
+ let user4;
+
+ let claimers;
+ let tree; // merkle tree
+
+ beforeEach(async function () {
+ [owner, user1, user2, user3, user4] = await ethers.getSigners();
+
+ // deploy nft contract
+ const MockErc721WithMinter = await ethers.getContractFactory("MockErc721WithMinter");
+ nftContract = await MockErc721WithMinter.deploy("Iggy NFT", "IGGY");
+ await nftContract.deployed();
+
+ // claimers
+ claimers = [
+ [owner.address, 1],
+ [user1.address, 1],
+ [user3.address, 1]
+ ];
+
+ // create merkle tree
+ tree = StandardMerkleTree.of(claimers, ["address", "uint256"]);
+
+ console.log("Merkle root: " + tree.root);
+
+ // deploy nft minter
+ const MerkleClaimerERC721 = await ethers.getContractFactory("MerkleClaimerERC721");
+ merkleClaimerContract = await MerkleClaimerERC721.deploy(nftContract.address, tree.root);
+ await merkleClaimerContract.deployed();
+
+ // add minter address to nft contract
+ await nftContract.setMinterAddress(merkleClaimerContract.address);
+
+ });
+
+ it("can claim NFT if listed among claimers", async function () {
+ // check owner's NFT balance before claim
+ const ownerNftBalanceBefore = await nftContract.balanceOf(owner.address);
+ expect(ownerNftBalanceBefore).to.equal(0);
+
+ // owner claims NFT
+ const indexOwner = claimers.findIndex(item => item[0] === owner.address);
+ //console.log("indexOwner: " + indexOwner);
+
+ const proofOwner = tree.getProof(indexOwner);
+ //console.log("proofOwner: " + proofOwner);
+
+ const txOwner = await merkleClaimerContract.connect(owner).claim(owner.address, proofOwner);
+ const receiptOwner = await txOwner.wait();
+ calculateGasCosts("claimNFT owner", receiptOwner);
+
+ // check owner's NFT balance after claim
+ const ownerNftBalanceAfter = await nftContract.balanceOf(owner.address);
+ expect(ownerNftBalanceAfter).to.equal(1);
+
+ // check user1's NFT balance before claim
+ const user1NftBalanceBefore = await nftContract.balanceOf(user1.address);
+ expect(user1NftBalanceBefore).to.equal(0);
+
+ // user1 claims NFT
+ const indexUser1 = claimers.findIndex(item => item[0] === user1.address);
+ //console.log("indexUser1: " + indexUser1);
+
+ const proofUser1 = tree.getProof(indexUser1);
+ //console.log("proofUser1: " + proofUser1);
+
+ const txUser1 = await merkleClaimerContract.connect(user1).claim(user1.address, proofUser1);
+ const receiptUser1 = await txUser1.wait();
+ calculateGasCosts("claimNFT user1", receiptUser1);
+
+ // check user1's NFT balance after claim
+ const user1NftBalanceAfter = await nftContract.balanceOf(user1.address);
+ expect(user1NftBalanceAfter).to.equal(1);
+
+ // check user2's NFT balance before claim
+ const user2NftBalanceBefore = await nftContract.balanceOf(user2.address);
+ expect(user2NftBalanceBefore).to.equal(0);
+
+ // user2 fails to claim NFT (uses wrong proof)
+ await expect(merkleClaimerContract.connect(user2).claim(user2.address, proofUser1)).to.be.revertedWith("MerkleClaimerERC721: Invalid proof");
+
+ // check user2's NFT balance after claim
+ const user2NftBalanceAfter = await nftContract.balanceOf(user2.address);
+ expect(user2NftBalanceAfter).to.equal(0);
+
+ });
+
+
+});
\ No newline at end of file
diff --git a/test/post/iggyPostMinterV2.test.js b/test/post/iggyPostMinterV2.test.js
new file mode 100644
index 0000000..a25b3ac
--- /dev/null
+++ b/test/post/iggyPostMinterV2.test.js
@@ -0,0 +1,1013 @@
+// npx hardhat test test/post/iggyPostMinterV2.test.js
+
+const { expect } = require("chai");
+const { ethers } = require("hardhat");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("IggyPostMinterV2", function () {
+ let iggyPostContract;
+ let metadataContract;
+ let minterContract;
+ let mockPunkTldContract;
+ let chatTokenContract;
+ let chatTokenMinterContract;
+
+ let owner;
+ let dao;
+ let author;
+ let user1;
+ let user2;
+ let dev;
+ let devFeeUpdaterAddress;
+ let referrer;
+
+ const chatEthRatio = 1000;
+ const chatRewardsDuration = 60 * 60 * 24 * 7 * 52; // 52 weeks (1 year)
+
+ const daoFee = 450; // 4.5%
+ const devFee = 900; // 9%
+ const referrerFee = 200; // 2%
+
+ const defaultPrice = ethers.utils.parseEther("1");
+ const postId = "testjkdnw6t6dq37gg7";
+ const textPreview = "This is a test post";
+ const quantityOne = 1;
+ const quantityMultiple = 3;
+
+ const mdName = "Iggy";
+ const symbol = "IGGYPOST";
+ const mdDescription = "Mint an Iggy Social post as an NFT";
+ const mdUrl = "https://iggy-social-frontend.vercel.app/post/";
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, dao, author, user1, user2, dev, devFeeUpdaterAddress, referrer, feeReceiver] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(referrer.address, "referrer");
+ await mockPunkTldContract.deployed();
+
+ const IggyMetadata = await ethers.getContractFactory("IggyPostMetadata");
+ metadataContract = await IggyMetadata.deploy(
+ mdName, mdDescription, mdUrl, mockPunkTldContract.address, sfsContract.address, sfsNftTokenId
+ );
+ await metadataContract.deployed();
+
+ const IggyPost = await ethers.getContractFactory("IggyPostNft1155");
+ iggyPostContract = await IggyPost.deploy(
+ defaultPrice,
+ sfsContract.address,
+ sfsNftTokenId,
+ metadataContract.address,
+ mdName,
+ symbol
+ );
+
+ await iggyPostContract.deployed();
+
+ // deploy ChatToken
+ const ChatToken = await ethers.getContractFactory("ChatToken");
+ chatTokenContract = await ChatToken.deploy("Chat Token", "CHAT");
+ await chatTokenContract.deployed();
+
+ // deploy ChatTokenMinter
+ const ChatTokenMinter = await ethers.getContractFactory("ChatTokenMinter");
+ chatTokenMinterContract = await ChatTokenMinter.deploy(chatTokenContract.address);
+ await chatTokenMinterContract.deployed();
+
+ // add minter to ChatToken
+ await chatTokenContract.setMinter(chatTokenMinterContract.address);
+
+ // iggy post minter
+ const IggyMinter = await ethers.getContractFactory("IggyPostMinterV2");
+ minterContract = await IggyMinter.deploy(
+ chatTokenMinterContract.address,
+ dao.address, // dao address
+ dev.address, // dev address
+ dev.address, // dev fee updater
+ iggyPostContract.address,
+ chatEthRatio,
+ chatRewardsDuration
+ );
+ await minterContract.deployed();
+
+ await chatTokenMinterContract.addMinter(minterContract.address);
+
+ await iggyPostContract.ownerChangeMinterAddress(minterContract.address);
+ });
+
+ // list of tests
+ // owner change default price
+ it("Owner can change default price", async function () {
+ // check price before
+ const oldDefaultPrice = await iggyPostContract.defaultPrice();
+ expect(oldDefaultPrice).to.equal(defaultPrice);
+
+ const newDefaultPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.ownerChangeDefaultPrice(newDefaultPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("ownerChangeDefaultPrice", receipt);
+
+ // check price after
+ const newDefaultPriceAfter = await iggyPostContract.defaultPrice();
+ expect(newDefaultPriceAfter).to.equal(newDefaultPrice);
+ });
+
+ // author set post price
+ it("Author can set price for their post", async function () {
+ // check price before
+ const oldPostPrice = await iggyPostContract.getPostPrice(postId, author.address);
+ expect(oldPostPrice).to.equal(defaultPrice);
+
+ const newPostPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.connect(author).authorSetPostPrice(postId, newPostPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("authorSetPostPrice", receipt);
+
+ // check price after
+ const newPostPriceAfter = await iggyPostContract.getPostPrice(postId, author.address);
+ expect(newPostPriceAfter).to.equal(newPostPrice);
+ });
+
+ // author set default price
+ it("Author can set default price for their posts", async function () {
+ // check price before
+ const oldDefaultPostPrice = await iggyPostContract.getPostPrice("default", author.address);
+ expect(oldDefaultPostPrice).to.equal(defaultPrice);
+
+ const newDefaultPostPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.connect(author).authorSetDefaultPrice(newDefaultPostPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("authorSetDefaultPrice", receipt);
+
+ // check price after
+ const newDefaultPostPriceAfter = await iggyPostContract.getPostPrice("default", author.address);
+ expect(newDefaultPostPriceAfter).to.equal(newDefaultPostPrice);
+ });
+
+ // mint 1 nft at default price
+ it("can mint 1 nft at default price", async function () {
+ const tokenId = 1;
+
+ console.log("CHECKPOINT 1");
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ console.log("CHECKPOINT 2");
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ // get NFT metadata
+ const nftMetadata = await iggyPostContract.uri(tokenId);
+ console.log("NFT metadata: ", nftMetadata);
+
+ // fail at fetching uri for non-existent token
+ await expect(iggyPostContract.uri(23)).to.be.revertedWith("IggyPost: Token id does not exist");
+
+ // fetch Post data (token id 1) and assert that text preview in post data is correct
+ const postData = await iggyPostContract.getPost(tokenId);
+ expect(postData.textPreview).to.equal(textPreview);
+
+ // owner changes text preview of post with token id 1
+ const newTextPreview = "New text preview";
+ await iggyPostContract.ownerChangeTextPreview(tokenId, newTextPreview);
+
+ // fetch Post data (token id 1) and assert that text preview in post data is now changed
+ const postDataAfter = await iggyPostContract.getPost(tokenId);
+ expect(postDataAfter.textPreview).to.equal(newTextPreview);
+
+ });
+
+ // user fails to mint through the post contract because only the minter contract can mint
+ it("fails to mint through the post contract", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the post contract
+ await expect(
+ iggyPostContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ textPreview, // text preview
+ "", // image
+ quantityOne // quantity
+ )
+ ).to.be.revertedWith("IggyPost: Only minter can mint");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ // mint multiple nfts at default price
+ it("can mint multiple nfts at default price", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: defaultPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(devFee).div(10000)));
+
+ });
+
+ // mint 1 nft with user1 and 1 nft with user2 at default price
+ it("can mint 1 nft with user1 and 1 nft with user2 at default price", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+
+ // check user2 balance before
+ const user2BalanceBefore = await iggyPostContract.balanceOf(user2.address, tokenId);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftDefaultPriceUser1", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ // mint through the minter contract
+ const tx2 = await minterContract.connect(user2).mint(
+ postId, // post ID
+ author.address, // post author
+ user2.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("mintOneNftDefaultPriceUser2", receipt2);
+
+ // check user2 balance after
+ const user2BalanceAfter = await iggyPostContract.balanceOf(user2.address, tokenId);
+ expect(user2BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter2 = await author.getBalance();
+ expect(authorEthBalanceAfter2).to.equal(authorEthBalanceAfter.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter2 = await dao.getBalance();
+ expect(daoEthBalanceAfter2).to.equal(daoEthBalanceAfter.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter2 = await dev.getBalance();
+ expect(devEthBalanceAfter2).to.equal(devEthBalanceAfter.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint 1 nft at author default price
+ it("can mint 1 nft at author default price", async function () {
+ const tokenId = 1;
+
+ // author set default price
+ const authorDefaultPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetDefaultPrice(authorDefaultPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: authorDefaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftAuthorDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(authorDefaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(authorDefaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(authorDefaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint multiple nfts at author default price
+ it("can mint multiple nfts at author default price", async function () {
+ const tokenId = 1;
+
+ // author set default price
+ const authorDefaultPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetDefaultPrice(authorDefaultPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: authorDefaultPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsAuthorDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(authorDefaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000).mul(quantityMultiple)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(authorDefaultPrice.mul(daoFee).div(10000).mul(quantityMultiple)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(authorDefaultPrice.mul(devFee).div(10000).mul(quantityMultiple)));
+
+ });
+
+ // mint 1 nft at post price
+ it("can mint 1 nft at post price", async function () {
+ const tokenId = 1;
+
+ // author set post price
+ const postPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetPostPrice(postId, postPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: postPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftPostPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(postPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(postPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(postPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint multiple nfts at post price
+ it("can mint multiple nfts at post price", async function () {
+ const tokenId = 1;
+
+ // author set post price
+ const postPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetPostPrice(postId, postPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: postPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsPostPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(postPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000).mul(quantityMultiple)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(postPrice.mul(daoFee).div(10000).mul(quantityMultiple)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(postPrice.mul(devFee).div(10000).mul(quantityMultiple)));
+
+ });
+
+ // author set mint time to 1 day, user1 mints an NFT within that time at default price
+ it("can mint 1 nft at default price within mint time", async function () {
+ const tokenId = 1;
+
+ // author set mint time to 1 day
+ const mintTime = 86400;
+ await iggyPostContract.connect(author).authorSetMintTime(postId, mintTime);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftWithinMintTime", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // author set mint time to 1 day, user1 fails at minting an NFT after that time has passed
+ it("cannot mint an nft after mint time has passed", async function () {
+ const tokenId = 1;
+
+ // author set mint time to 1 day
+ const mintTime = 86400;
+ await iggyPostContract.connect(author).authorSetMintTime(postId, mintTime);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // successful mint through the minter contract (important: minting time starts from this moment on)
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ // fast forward time by 1 day
+ await network.provider.send("evm_increaseTime", [mintTime+100]);
+
+ // fails to mint after the minting time has passed
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("IggyPost: Minting deadline has passed");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // user fails to mint 1 nft at default price because the contract is paused
+ it("cannot mint 1 nft at default price because the contract is paused", async function () {
+ // owner pause minting in the minter contract
+ await minterContract.togglePaused();
+
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // fails to mint because the contract is paused
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("Minting paused");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ // user fails to mint an NFT because the preview text is too long
+ it("cannot mint an nft because the preview text is too long", async function () {
+ const tokenId = 1;
+
+ const textPreviewLong = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet";
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // fails to mint because the preview text is too long
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreviewLong, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("IggyPost: Text preview is too long");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ // checks chat token balances at various points in time (CHAT rewards are decreasing linearly)
+ it("checks chat token balances at various points in time (CHAT rewards are decreasing linearly)", async function () {
+ // get fixed chatEthRatio from the minter contract
+ const fixedChatEthRatio = await minterContract.chatEthRatio();
+ console.log("fixedChatEthRatio", fixedChatEthRatio, "CHAT/ETH");
+
+ // get chatRewardsDuration from the minter contract
+ const _chatRewardsDuration = await minterContract.chatRewardsDuration();
+ console.log("chatRewardsDuration", _chatRewardsDuration.toString(), "seconds");
+ expect(_chatRewardsDuration).to.equal(chatRewardsDuration);
+
+ // get chatRewardsEnd from the minter contract
+ const _chatRewardsEnd = await minterContract.chatRewardsEnd();
+ console.log("chatRewardsEnd", _chatRewardsEnd.toString(), "seconds");
+
+ // get current timestamp in seconds
+ const currentTimestamp = Math.floor(Date.now() / 1000);
+ console.log("currentTimestamp", currentTimestamp.toString(), "seconds");
+
+ // getCurrentChatEthRatio in the minter contract
+ const currentChatEthRatio = await minterContract.getCurrentChatEthRatio();
+ console.log("currentChatEthRatio", ethers.utils.formatEther(currentChatEthRatio), "CHAT/ETH");
+
+ // check user1 CHAT balance 1
+ const user1ChatBalance1 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance1).to.equal(0);
+
+ // user1: mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftDefaultPrice", receipt);
+
+ // check user1 CHAT balance 2
+ const user1ChatBalance2 = await chatTokenContract.balanceOf(user1.address);
+ console.log("user1ChatBalance2", ethers.utils.formatEther(user1ChatBalance2), "CHAT");
+
+ console.log("--------------------");
+ console.log("AFTER 1 WEEK");
+
+ // wait 1 week
+ await ethers.provider.send("evm_increaseTime", [86400*7]);
+
+ // check user2 CHAT balance 1
+ const user2ChatBalance1 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance1).to.equal(0);
+
+ // user2: mint through the minter contract
+ const tx2 = await minterContract.connect(user2).mint(
+ postId, // post ID
+ author.address, // post author
+ user2.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("mintOneNftDefaultPrice", receipt2);
+
+ // check user2 CHAT balance 2
+ const user2ChatBalance2 = await chatTokenContract.balanceOf(user2.address);
+ console.log("user2ChatBalance2", ethers.utils.formatEther(user2ChatBalance2), "CHAT");
+
+ console.log("--------------------");
+ console.log("AFTER 1 YEAR");
+
+ // wait 1 year
+ await ethers.provider.send("evm_increaseTime", [86400*365]);
+
+ // check user1 CHAT balance 3
+ const user1ChatBalance3 = await chatTokenContract.balanceOf(user1.address);
+ console.log("user1ChatBalance3", ethers.utils.formatEther(user1ChatBalance3), "CHAT");
+ // expect user1ChatBalance3 is the same as user1ChatBalance2
+ expect(user1ChatBalance3).to.equal(user1ChatBalance2);
+
+ // user1: mint through the minter contract
+ const tx3 = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ // check user1 CHAT balance 4
+ const user1ChatBalance4 = await chatTokenContract.balanceOf(user1.address);
+ console.log("user1ChatBalance4", ethers.utils.formatEther(user1ChatBalance4), "CHAT");
+ // expect user1ChatBalance4 is the same as user1ChatBalance3 because the rewards have ended
+ expect(user1ChatBalance4).to.equal(user1ChatBalance3);
+
+ // getCurrentChatEthRatio in the minter contract should return 0
+ const currentChatEthRatio2 = await minterContract.getCurrentChatEthRatio();
+ console.log("currentChatEthRatio2", ethers.utils.formatEther(currentChatEthRatio2), "CHAT/ETH");
+ expect(currentChatEthRatio2).to.equal(0);
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/post/iggyPostNft1155.test.js b/test/post/iggyPostNft1155.test.js
new file mode 100644
index 0000000..f58f0c7
--- /dev/null
+++ b/test/post/iggyPostNft1155.test.js
@@ -0,0 +1,911 @@
+// npx hardhat test test/post/iggyPostNft1155.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("IggyPostNft1155", function () {
+ let iggyPostContract;
+ let metadataContract;
+ let minterContract;
+ let mockPunkTldContract;
+
+ let owner;
+ let dao;
+ let author;
+ let user1;
+ let user2;
+ let dev;
+ let referrer;
+
+ const defaultAddressBalance = ethers.utils.parseEther("10000");
+
+ const daoFee = 2000; // 20%
+ const devFee = 1000; // 10%
+ const referrerFee = 1000; // 10%
+
+ const defaultPrice = ethers.utils.parseEther("1");
+ const postId = "testjkdnw6t6dq37gg7";
+ const textPreview = "This is a test post";
+ const quantityOne = 1;
+ const quantityMultiple = 3;
+
+ const mdName = "Iggy";
+ const symbol = "IGGYPOST";
+ const mdDescription = "Mint an Iggy Social post as an NFT";
+ const mdUrl = "https://iggy-social-frontend.vercel.app/post/";
+
+ //const provider = waffle.provider;
+
+ beforeEach(async function () {
+ [owner, dao, author, user1, user2, dev, referrer, feeReceiver] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(referrer.address, "referrer");
+ await mockPunkTldContract.deployed();
+
+ const IggyMetadata = await ethers.getContractFactory("IggyPostMetadata");
+ metadataContract = await IggyMetadata.deploy(
+ mdName, mdDescription, mdUrl, mockPunkTldContract.address, sfsContract.address, sfsNftTokenId
+ );
+ await metadataContract.deployed();
+
+ const IggyPost = await ethers.getContractFactory("IggyPostNft1155");
+ iggyPostContract = await IggyPost.deploy(
+ defaultPrice,
+ sfsContract.address,
+ sfsNftTokenId,
+ metadataContract.address,
+ mdName,
+ symbol
+ );
+
+ await iggyPostContract.deployed();
+
+ const IggyMinter = await ethers.getContractFactory("IggyPostMinter");
+ minterContract = await IggyMinter.deploy(
+ sfsContract.address,
+ dao.address,
+ dev.address,
+ sfsNftTokenId,
+ iggyPostContract.address,
+ daoFee,
+ devFee,
+ referrerFee
+ );
+
+ await minterContract.deployed();
+
+ await iggyPostContract.ownerChangeMinterAddress(minterContract.address);
+ });
+
+ // list of tests
+ // owner change default price
+ it("Owner can change default price", async function () {
+ // check price before
+ const oldDefaultPrice = await iggyPostContract.defaultPrice();
+ expect(oldDefaultPrice).to.equal(defaultPrice);
+
+ const newDefaultPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.ownerChangeDefaultPrice(newDefaultPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("ownerChangeDefaultPrice", receipt);
+
+ // check price after
+ const newDefaultPriceAfter = await iggyPostContract.defaultPrice();
+ expect(newDefaultPriceAfter).to.equal(newDefaultPrice);
+ });
+
+ // author set post price
+ it("Author can set price for their post", async function () {
+ // check price before
+ const oldPostPrice = await iggyPostContract.getPostPrice(postId, author.address);
+ expect(oldPostPrice).to.equal(defaultPrice);
+
+ const newPostPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.connect(author).authorSetPostPrice(postId, newPostPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("authorSetPostPrice", receipt);
+
+ // check price after
+ const newPostPriceAfter = await iggyPostContract.getPostPrice(postId, author.address);
+ expect(newPostPriceAfter).to.equal(newPostPrice);
+ });
+
+ // author set default price
+ it("Author can set default price for their posts", async function () {
+ // check price before
+ const oldDefaultPostPrice = await iggyPostContract.getPostPrice("default", author.address);
+ expect(oldDefaultPostPrice).to.equal(defaultPrice);
+
+ const newDefaultPostPrice = ethers.utils.parseEther("2");
+
+ const tx = await iggyPostContract.connect(author).authorSetDefaultPrice(newDefaultPostPrice);
+ const receipt = await tx.wait();
+ calculateGasCosts("authorSetDefaultPrice", receipt);
+
+ // check price after
+ const newDefaultPostPriceAfter = await iggyPostContract.getPostPrice("default", author.address);
+ expect(newDefaultPostPriceAfter).to.equal(newDefaultPostPrice);
+ });
+
+ // mint 1 nft at default price
+ it("can mint 1 nft at default price", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ //return;
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ // get NFT metadata
+ const nftMetadata = await iggyPostContract.uri(tokenId);
+ console.log("NFT metadata: ", nftMetadata);
+
+ // fail at fetching uri for non-existent token
+ await expect(iggyPostContract.uri(23)).to.be.revertedWith("IggyPost: Token id does not exist");
+
+ // fetch Post data (token id 1) and assert that text preview in post data is correct
+ const postData = await iggyPostContract.getPost(tokenId);
+ expect(postData.textPreview).to.equal(textPreview);
+
+ // owner changes text preview of post with token id 1
+ const newTextPreview = "New text preview";
+ await iggyPostContract.ownerChangeTextPreview(tokenId, newTextPreview);
+
+ // fetch Post data (token id 1) and assert that text preview in post data is now changed
+ const postDataAfter = await iggyPostContract.getPost(tokenId);
+ expect(postDataAfter.textPreview).to.equal(newTextPreview);
+
+ });
+
+ // user fails to mint through the post contract because only the minter contract can mint
+ it("fails to mint through the post contract", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the post contract
+ await expect(
+ iggyPostContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ textPreview, // text preview
+ "", // image
+ quantityOne // quantity
+ )
+ ).to.be.revertedWith("IggyPost: Only minter can mint");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ // mint multiple nfts at default price
+ it("can mint multiple nfts at default price", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: defaultPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(quantityMultiple).mul(devFee).div(10000)));
+
+ });
+
+ // mint 1 nft with user1 and 1 nft with user2 at default price
+ it("can mint 1 nft with user1 and 1 nft with user2 at default price", async function () {
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+
+ // check user2 balance before
+ const user2BalanceBefore = await iggyPostContract.balanceOf(user2.address, tokenId);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftDefaultPriceUser1", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ // mint through the minter contract
+ const tx2 = await minterContract.connect(user2).mint(
+ postId, // post ID
+ author.address, // post author
+ user2.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("mintOneNftDefaultPriceUser2", receipt2);
+
+ // check user2 balance after
+ const user2BalanceAfter = await iggyPostContract.balanceOf(user2.address, tokenId);
+ expect(user2BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter2 = await author.getBalance();
+ expect(authorEthBalanceAfter2).to.equal(authorEthBalanceAfter.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter2 = await dao.getBalance();
+ expect(daoEthBalanceAfter2).to.equal(daoEthBalanceAfter.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter2 = await dev.getBalance();
+ expect(devEthBalanceAfter2).to.equal(devEthBalanceAfter.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint 1 nft at author default price
+ it("can mint 1 nft at author default price", async function () {
+ const tokenId = 1;
+
+ // author set default price
+ const authorDefaultPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetDefaultPrice(authorDefaultPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: authorDefaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftAuthorDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(authorDefaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(authorDefaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(authorDefaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint multiple nfts at author default price
+ it("can mint multiple nfts at author default price", async function () {
+ const tokenId = 1;
+
+ // author set default price
+ const authorDefaultPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetDefaultPrice(authorDefaultPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: authorDefaultPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsAuthorDefaultPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(authorDefaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000).mul(quantityMultiple)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(authorDefaultPrice.mul(daoFee).div(10000).mul(quantityMultiple)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(authorDefaultPrice.mul(devFee).div(10000).mul(quantityMultiple)));
+
+ });
+
+ // mint 1 nft at post price
+ it("can mint 1 nft at post price", async function () {
+ const tokenId = 1;
+
+ // author set post price
+ const postPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetPostPrice(postId, postPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: postPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftPostPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(postPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(postPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(postPrice.mul(devFee).div(10000)));
+
+ });
+
+ // mint multiple nfts at post price
+ it("can mint multiple nfts at post price", async function () {
+ const tokenId = 1;
+
+ // author set post price
+ const postPrice = ethers.utils.parseEther("0.1");
+ await iggyPostContract.connect(author).authorSetPostPrice(postId, postPrice);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityMultiple, // quantity
+ {
+ value: postPrice.mul(quantityMultiple)
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintMultipleNftsPostPrice", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityMultiple);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(postPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000).mul(quantityMultiple)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(postPrice.mul(daoFee).div(10000).mul(quantityMultiple)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(postPrice.mul(devFee).div(10000).mul(quantityMultiple)));
+
+ });
+
+ // author set mint time to 1 day, user1 mints an NFT within that time at default price
+ it("can mint 1 nft at default price within mint time", async function () {
+ const tokenId = 1;
+
+ // author set mint time to 1 day
+ const mintTime = 86400;
+ await iggyPostContract.connect(author).authorSetMintTime(postId, mintTime);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // mint through the minter contract
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ const receipt = await tx.wait();
+ calculateGasCosts("mintOneNftWithinMintTime", receipt);
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // author set mint time to 1 day, user1 fails at minting an NFT after that time has passed
+ it("cannot mint an nft after mint time has passed", async function () {
+ const tokenId = 1;
+
+ // author set mint time to 1 day
+ const mintTime = 86400;
+ await iggyPostContract.connect(author).authorSetMintTime(postId, mintTime);
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // successful mint through the minter contract (important: minting time starts from this moment on)
+ const tx = await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ // fast forward time by 1 day
+ await network.provider.send("evm_increaseTime", [mintTime+100]);
+
+ // fails to mint after the minting time has passed
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("IggyPost: Minting deadline has passed");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(quantityOne);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore.add(defaultPrice.mul(10000 - daoFee - devFee - referrerFee).div(10000)));
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore.add(defaultPrice.mul(daoFee).div(10000)));
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore.add(defaultPrice.mul(devFee).div(10000)));
+
+ });
+
+ // user fails to mint 1 nft at default price because the contract is paused
+ it("cannot mint 1 nft at default price because the contract is paused", async function () {
+ // owner pause minting in the minter contract
+ await minterContract.togglePaused();
+
+ const tokenId = 1;
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // fails to mint because the contract is paused
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreview, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("Minting paused");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ // user fails to mint an NFT because the preview text is too long
+ it("cannot mint an nft because the preview text is too long", async function () {
+ const tokenId = 1;
+
+ const textPreviewLong = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. Donec auctor, nisl eget ultricies lacinia, nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet";
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // get author's ETH balance before
+ const authorEthBalanceBefore = await author.getBalance();
+
+ // check dao ETH balance before
+ const daoEthBalanceBefore = await dao.getBalance();
+
+ // check dev ETH balance before
+ const devEthBalanceBefore = await dev.getBalance();
+
+ // fails to mint because the preview text is too long
+ await expect(
+ minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreviewLong, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ )
+ ).to.be.revertedWith("IggyPost: Text preview is too long");
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(0);
+
+ // check author ETH balance after
+ const authorEthBalanceAfter = await author.getBalance();
+ expect(authorEthBalanceAfter).to.equal(authorEthBalanceBefore);
+
+ // check dao ETH balance after
+ const daoEthBalanceAfter = await dao.getBalance();
+ expect(daoEthBalanceAfter).to.equal(daoEthBalanceBefore);
+
+ // check dev ETH balance after
+ const devEthBalanceAfter = await dev.getBalance();
+ expect(devEthBalanceAfter).to.equal(devEthBalanceBefore);
+
+ });
+
+ it("mints a post NFT with emoji in text", async function () {
+ const tokenId = 1;
+
+ const textPreviewEmoji = "Loremicies lacinia. 😅 Nisl nisl aliquam nisl, eget aliquam nisl nisl sit amet nisl. 🤣";
+
+ // check user1 balance before
+ const user1BalanceBefore = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceBefore).to.equal(0);
+
+ // fails to mint because the preview text is too long
+ await minterContract.connect(user1).mint(
+ postId, // post ID
+ author.address, // post author
+ user1.address, // NFT receiver
+ referrer.address, // referrer
+ textPreviewEmoji, // text preview
+ "", // image
+ quantityOne, // quantity
+ {
+ value: defaultPrice
+ }
+ );
+
+ // check user1 balance after
+ const user1BalanceAfter = await iggyPostContract.balanceOf(user1.address, tokenId);
+ expect(user1BalanceAfter).to.equal(1);
+
+ // get NFT metadata
+ const nftMetadata = await iggyPostContract.uri(tokenId);
+ console.log("NFT metadata: ", nftMetadata);
+
+ // fetch Post data (token id 1) and assert that text preview in post data is correct
+ const postData = await iggyPostContract.getPost(tokenId);
+ expect(postData.textPreview).to.equal(textPreviewEmoji);
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/swap/iggySwap.polygon.fork.test.js b/test/swap/iggySwap.polygon.fork.test.js
new file mode 100644
index 0000000..2dcfe74
--- /dev/null
+++ b/test/swap/iggySwap.polygon.fork.test.js
@@ -0,0 +1,363 @@
+// test on a forked mainnet (polygon)
+// 1. First run the forked localhost node: npx hardhat node --fork https://rpc.ankr.com/polygon // must be Polygon Mainnet
+// 2. Then run the tests in a different tab: npx hardhat test test/swap/iggySwap.polygon.fork.test.js --network localhost
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+xdescribe("Iggy Swap tests (on a forked mainnet)", function () {
+ let routerAddress = "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff"; // quickswap router
+ let wethAddress = "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"; // wmatic
+ let daiAddress = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; // dai on polygon
+ let aaveAddress = "0xd6df932a45c0f255f85145f286ea0b292b21c90b"; // aave on polygon
+
+ let iggySwapRouterContract;
+
+ let owner;
+ let frontend;
+ let iggy;
+ let user1;
+ let user2;
+ let referrer;
+ let staking; // staking contract address
+
+ let swapFee = 80; // 0.8%
+ let stakingShare = 0; // 0%
+ let frontendShare = 5000; // 50% (after referral fee and staking fee are deducted)
+
+ beforeEach(async function () {
+ [owner, frontend, iggy, user1, user2, referrer, staking] = await ethers.getSigners();
+
+ // deploy IggySwapRouter
+ const IggySwapRouter = await ethers.getContractFactory("IggySwapRouter");
+ iggySwapRouterContract = await IggySwapRouter.deploy(
+ frontend.address,
+ iggy.address,
+ routerAddress,
+ staking.address,
+ ethers.constants.AddressZero,
+ swapFee,
+ stakingShare,
+ frontendShare
+ );
+ await iggySwapRouterContract.deployed();
+ });
+
+ it("test swapping via Iggy Swap", async function () {
+ console.log("--------- First swap (path: MATIC -> DAI) ---------");
+
+ // check owner's ETH balance before swap
+ const ownerEthBalanceBefore = await owner.getBalance();
+ console.log("Owner's MATIC balance before swap:", ethers.utils.formatUnits(ownerEthBalanceBefore, "ether"), "MATIC");
+ expect(ownerEthBalanceBefore).to.be.gt(ethers.utils.parseUnits("9990", "ether"));
+
+ // check frontend's DAI balance before swap
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+ const frontendDaiBalanceBefore = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance before swap:", ethers.utils.formatUnits(frontendDaiBalanceBefore, "ether"), "DAI");
+ expect(frontendDaiBalanceBefore).to.be.eq(0);
+
+ // check iggy's DAI balance before swap
+ const iggyDaiBalanceBefore = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance before swap:", ethers.utils.formatUnits(iggyDaiBalanceBefore, "ether"), "DAI");
+ expect(iggyDaiBalanceBefore).to.be.eq(0);
+
+ // set amount in and path for the MATIC -> DAI swap
+ const amountIn = ethers.utils.parseUnits("400", "ether"); // 400 MATIC
+ console.log("Amount of MATIC to swap:", ethers.utils.formatUnits(amountIn, "ether"), "MATIC");
+
+ const path = [wethAddress, daiAddress]; // path to swap eth for dai
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut = await iggySwapRouterContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (iggy contract):", ethers.utils.formatUnits(amountsOut[path.length-1], "ether"), "DAI");
+ expect(amountsOut[path.length-1]).to.be.gt(0);
+
+ // check getAmountsOut first before swap (via router)
+ const routerContract = await ethers.getContractAt("contracts/swap/IggySwapRouter.sol:IUniswapV2Router02", routerAddress);
+ const amountsOut2 = await routerContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (router):", ethers.utils.formatUnits(amountsOut2[path.length-1], "ether"), "DAI");
+ expect(amountsOut2[path.length-1]).to.be.gt(0);
+
+ // check owner's DAI balance before swap
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before swap:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+ expect(ownerDaiBalanceBefore).to.equal(0);
+
+ // get price impact
+ const priceImpact = await iggySwapRouterContract.getPriceImpact(path[0], path[path.length-1], amountIn);
+ console.log(priceImpact);
+ console.log("Price impact:", Number(priceImpact), "bps");
+
+ // swapExactETHForTokens (swap ETH for DAI)
+ const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut = amountsOut[path.length-1].sub(amountsOut[path.length-1].div(100)); // 1% slippage
+ console.log("Min DAI to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut, "ether"), "DAI");
+
+ const tx = await iggySwapRouterContract.swapExactETHForTokens(
+ minAmountOut,
+ path,
+ owner.address,
+ deadline,
+ {
+ value: amountIn,
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Swap (MATIC -> DAI)", receipt);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.be.gt(0);
+
+ // check frontend's DAI balance after swap
+ const frontendDaiBalanceAfter = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance after swap:", ethers.utils.formatUnits(frontendDaiBalanceAfter, "ether"), "DAI");
+ expect(frontendDaiBalanceAfter).to.be.gt(0);
+
+ // check iggy's DAI balance after swap
+ const iggyDaiBalanceAfter = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance after swap:", ethers.utils.formatUnits(iggyDaiBalanceAfter, "ether"), "DAI");
+ expect(iggyDaiBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter).to.be.lt(ownerEthBalanceBefore);
+
+ console.log("--------- second swap (path: DAI -> MATIC -> AAVE) ---------");
+
+ // check owner's AAVE balance before second swap
+ const aaveContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", aaveAddress);
+ const ownerAaveBalanceBefore = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance before swap:", ethers.utils.formatUnits(ownerAaveBalanceBefore, "ether"), "AAVE");
+ expect(ownerAaveBalanceBefore).to.equal(0);
+
+ // set amount in and path for the DAI -> AAVE swap
+ const amountIn3 = ethers.utils.parseUnits("200", "ether"); // 200 DAI
+ console.log("Amount of DAI to swap:", ethers.utils.formatUnits(amountIn3, "ether"), "DAI");
+
+ const path3 = [daiAddress, wethAddress, aaveAddress]; // path to swap dai for aave
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut3 = await iggySwapRouterContract.getAmountsOut(amountIn3, path3);
+ console.log("Amount of AAVE to receive:", ethers.utils.formatUnits(amountsOut3[path3.length-1], "ether"), "AAVE");
+ expect(amountsOut3[path3.length-1]).to.be.gt(0);
+
+ // set allowance for DAI
+ await daiContract.approve(iggySwapRouterContract.address, amountIn3);
+
+ // get price impact
+ const priceImpact2 = await iggySwapRouterContract.getPriceImpact(path3[0], path3[path.length-1], amountIn3);
+ console.log(priceImpact2);
+ console.log("Price impact 2:", Number(priceImpact2), "bps");
+
+ // swapExactTokensForTokens (swap DAI for AAVE)
+ const deadline3 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut3 = amountsOut3[path3.length-1].sub(amountsOut3[path3.length-1].div(100)); // 1% slippage
+ console.log("Min AAVE to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut3, "ether"), "AAVE");
+
+ const tx3 = await iggySwapRouterContract.swapExactTokensForTokens(
+ amountIn3,
+ minAmountOut3,
+ path3,
+ owner.address,
+ deadline3,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt3 = await tx3.wait();
+ calculateGasCosts("Swap (DAI -> AAVE)", receipt3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter3 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter3, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter3).to.be.lt(ownerEthBalanceBefore);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter3 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter3, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter3).to.equal(ownerDaiBalanceAfter.sub(amountIn3));
+
+ console.log("--------- third swap (path: AAVE -> MATIC) ---------");
+
+ // set amount in and path for the AAVE -> MATIC swap
+ const amountIn4 = ethers.utils.parseUnits("0.01", "ether"); // 1 AAVE
+ console.log("Amount of AAVE to swap:", ethers.utils.formatUnits(amountIn4, "ether"), "AAVE");
+
+ const path4 = [aaveAddress, wethAddress]; // path to swap aave for matic
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut4 = await iggySwapRouterContract.getAmountsOut(amountIn4, path4);
+ console.log("Amount of MATIC to receive:", ethers.utils.formatUnits(amountsOut4[path4.length-1], "ether"), "MATIC");
+ expect(amountsOut4[path4.length-1]).to.be.gt(0);
+
+ // set allowance for AAVE
+ await aaveContract.approve(iggySwapRouterContract.address, amountIn4);
+
+ // get price impact
+ const priceImpact3 = await iggySwapRouterContract.getPriceImpact(path4[0], path4[path.length-1], amountIn3);
+ console.log(priceImpact3);
+ console.log("Price impact 3:", Number(priceImpact3), "bps");
+
+ // swapExactTokensForETH (swap AAVE for MATIC)
+ const deadline4 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut4 = amountsOut4[path4.length-1].sub(amountsOut4[path4.length-1].div(100)); // 1% slippage
+ console.log("Min MATIC to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut4, "ether"), "MATIC");
+
+ const tx4 = await iggySwapRouterContract.swapExactTokensForETH(
+ amountIn4,
+ minAmountOut4,
+ path4,
+ owner.address,
+ deadline4,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt4 = await tx4.wait();
+ calculateGasCosts("Swap (AAVE -> MATIC)", receipt4);
+
+ // check owner's MATIC balance after swap
+ const ownerEthBalanceAfter4 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter4, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter4).to.be.gt(ownerEthBalanceAfter3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter4 = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter4, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter4).to.equal(ownerAaveBalanceAfter.sub(amountIn4));
+
+ });
+
+ it("adds liquidity to the MATIC/DAI pool and then removes it", async function() {
+ console.log("------ ADDING LIQUIDITY ------ ");
+
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+
+ // get LP token balance (call getLpTokenAddress from the iggySwapRouterContract to get LP token address first)
+ const lpTokenAddress = await iggySwapRouterContract.getLpTokenAddress(daiAddress, wethAddress);
+ console.log("LP token address:", lpTokenAddress);
+
+ const lpTokenContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", lpTokenAddress);
+ const lpTokenBalanceBefore = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance before adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceBefore, "ether"), "LP tokens");
+
+ // check DAI balance before adding liquidity
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+
+ const amountInDesired = ethers.utils.parseUnits("10", "ether"); // 10 DAI
+ const amountInMin = amountInDesired.sub(amountInDesired.div(10)); // 10% slippage
+
+ // give DAI allowance to the iggySwapRouterContract
+ await daiContract.approve(iggySwapRouterContract.address, amountInDesired);
+
+ // call addLiquidityETH function in a read-only way to get the amount of MATIC to send
+ const result = await iggySwapRouterContract.callStatic.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: ethers.utils.parseUnits("100", "ether"), // some amount of ETH (doesn't matter, but needs to be big enough)
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ console.log("Amount of MATIC to send:", ethers.utils.formatUnits(result[1], "ether"), "MATIC");
+
+ // addLiquidityETH (write)
+ const tx = await iggySwapRouterContract.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: result[1].add(result[1].div(10)), // result[1] + 10% slippage
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Add liquidity", receipt);
+
+ // check DAI balance after adding liquidity
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.equal(ownerDaiBalanceBefore.sub(amountInDesired));
+
+ // check LP token balance after adding liquidity
+ const lpTokenBalanceAfter = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter).to.be.gt(lpTokenBalanceBefore);
+
+ console.log("------ REMOVING LIQUIDITY ------ ");
+
+ // calculate amount of DAI and ETH to receive
+ const amountEthDai = await iggySwapRouterContract.calculateETHAndTokensToReceive(lpTokenContract.address, lpTokenBalanceAfter);
+ console.log("Amount of ETH and DAI to receive:", ethers.utils.formatEther(amountEthDai[0]), "ETH,", ethers.utils.formatEther(amountEthDai[1]), "DAI");
+
+ // give LP token allowance to the iggySwapRouterContract
+ await lpTokenContract.approve(iggySwapRouterContract.address, lpTokenBalanceAfter);
+
+ // removeLiquidityETH (write)
+ const tx2 = await iggySwapRouterContract.removeLiquidityETH(
+ daiAddress,
+ lpTokenBalanceAfter, // amount LP tokens to remove
+ 0, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("Remove liquidity", receipt2);
+
+ // check DAI balance after removing liquidity
+ const ownerDaiBalanceAfter2 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after removing liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter2, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter2).to.be.gt(ownerDaiBalanceAfter);
+ console.log("Difference:", ethers.utils.formatEther(ownerDaiBalanceAfter2.sub(ownerDaiBalanceAfter)), "DAI");
+
+ // check LP token balance after removing liquidity
+ const lpTokenBalanceAfter2 = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after removing liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter2, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter2).to.equal(0);
+
+ });
+
+});
diff --git a/test/swap/iggySwapSolidly.optimism.fork.test.js b/test/swap/iggySwapSolidly.optimism.fork.test.js
new file mode 100644
index 0000000..481af4f
--- /dev/null
+++ b/test/swap/iggySwapSolidly.optimism.fork.test.js
@@ -0,0 +1,363 @@
+// test on a forked mainnet (polygon)
+// 1. First run the forked localhost node: npx hardhat node --fork https://rpc.ankr.com/op // must be Optimism Mainnet
+// 2. Then run the tests in a different tab: npx hardhat test test/swap/iggySwapSolidly.optimism.fork.test.js --network localhost
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+xdescribe("Iggy Swap Solidly tests (on a forked Optimism mainnet)", function () {
+ let routerAddress = "0x9c12939390052919aF3155f41Bf4160Fd3666A6f"; // velodrome router
+ let wethAddress = "0x4200000000000000000000000000000000000006"; // weth
+ let daiAddress = "0x7f5c764cbc14f9669b88837ca1490cca17c31607"; // usdc on optimism
+ let aaveAddress = "0x4200000000000000000000000000000000000042"; // op on optimism
+
+ let iggySwapRouterContract;
+
+ let owner;
+ let frontend;
+ let iggy;
+ let user1;
+ let user2;
+ let referrer;
+ let staking; // staking contract address
+
+ let swapFee = 80; // 0.8%
+ let stakingShare = 0; // 0%
+ let frontendShare = 5000; // 50% (after referral fee and staking fee are deducted)
+
+ beforeEach(async function () {
+ [owner, frontend, iggy, user1, user2, referrer, staking] = await ethers.getSigners();
+
+ // deploy IggySwapRouter
+ const IggySwapRouter = await ethers.getContractFactory("IggySwapRouter");
+ iggySwapRouterContract = await IggySwapRouter.deploy(
+ frontend.address,
+ iggy.address,
+ routerAddress,
+ staking.address,
+ ethers.constants.AddressZero,
+ swapFee,
+ stakingShare,
+ frontendShare
+ );
+ await iggySwapRouterContract.deployed();
+ });
+
+ it("test swapping via Iggy Swap", async function () {
+ console.log("--------- First swap (path: MATIC -> DAI) ---------");
+
+ // check owner's ETH balance before swap
+ const ownerEthBalanceBefore = await owner.getBalance();
+ console.log("Owner's MATIC balance before swap:", ethers.utils.formatUnits(ownerEthBalanceBefore, "ether"), "MATIC");
+ expect(ownerEthBalanceBefore).to.be.gt(ethers.utils.parseUnits("9990", "ether"));
+
+ // check frontend's DAI balance before swap
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+ const frontendDaiBalanceBefore = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance before swap:", ethers.utils.formatUnits(frontendDaiBalanceBefore, "ether"), "DAI");
+ expect(frontendDaiBalanceBefore).to.be.eq(0);
+
+ // check iggy's DAI balance before swap
+ const iggyDaiBalanceBefore = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance before swap:", ethers.utils.formatUnits(iggyDaiBalanceBefore, "ether"), "DAI");
+ expect(iggyDaiBalanceBefore).to.be.eq(0);
+
+ // set amount in and path for the MATIC -> DAI swap
+ const amountIn = ethers.utils.parseUnits("400", "ether"); // 400 MATIC
+ console.log("Amount of MATIC to swap:", ethers.utils.formatUnits(amountIn, "ether"), "MATIC");
+
+ const path = [wethAddress, daiAddress]; // path to swap eth for dai
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut = await iggySwapRouterContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (iggy contract):", ethers.utils.formatUnits(amountsOut[path.length-1], "ether"), "DAI");
+ expect(amountsOut[path.length-1]).to.be.gt(0);
+
+ // check getAmountsOut first before swap (via router)
+ const routerContract = await ethers.getContractAt("contracts/swap/IggySwapRouter.sol:IUniswapV2Router02", routerAddress);
+ const amountsOut2 = await routerContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (router):", ethers.utils.formatUnits(amountsOut2[path.length-1], "ether"), "DAI");
+ expect(amountsOut2[path.length-1]).to.be.gt(0);
+
+ // check owner's DAI balance before swap
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before swap:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+ expect(ownerDaiBalanceBefore).to.equal(0);
+
+ // get price impact
+ const priceImpact = await iggySwapRouterContract.getPriceImpact(path[0], path[path.length-1], amountIn);
+ console.log(priceImpact);
+ console.log("Price impact:", Number(priceImpact), "bps");
+
+ // swapExactETHForTokens (swap ETH for DAI)
+ const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut = amountsOut[path.length-1].sub(amountsOut[path.length-1].div(100)); // 1% slippage
+ console.log("Min DAI to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut, "ether"), "DAI");
+
+ const tx = await iggySwapRouterContract.swapExactETHForTokens(
+ minAmountOut,
+ path,
+ owner.address,
+ deadline,
+ {
+ value: amountIn,
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Swap (MATIC -> DAI)", receipt);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.be.gt(0);
+
+ // check frontend's DAI balance after swap
+ const frontendDaiBalanceAfter = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance after swap:", ethers.utils.formatUnits(frontendDaiBalanceAfter, "ether"), "DAI");
+ expect(frontendDaiBalanceAfter).to.be.gt(0);
+
+ // check iggy's DAI balance after swap
+ const iggyDaiBalanceAfter = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance after swap:", ethers.utils.formatUnits(iggyDaiBalanceAfter, "ether"), "DAI");
+ expect(iggyDaiBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter).to.be.lt(ownerEthBalanceBefore);
+
+ console.log("--------- second swap (path: DAI -> MATIC -> AAVE) ---------");
+
+ // check owner's AAVE balance before second swap
+ const aaveContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", aaveAddress);
+ const ownerAaveBalanceBefore = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance before swap:", ethers.utils.formatUnits(ownerAaveBalanceBefore, "ether"), "AAVE");
+ expect(ownerAaveBalanceBefore).to.equal(0);
+
+ // set amount in and path for the DAI -> AAVE swap
+ const amountIn3 = ethers.utils.parseUnits("200", "ether"); // 200 DAI
+ console.log("Amount of DAI to swap:", ethers.utils.formatUnits(amountIn3, "ether"), "DAI");
+
+ const path3 = [daiAddress, wethAddress, aaveAddress]; // path to swap dai for aave
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut3 = await iggySwapRouterContract.getAmountsOut(amountIn3, path3);
+ console.log("Amount of AAVE to receive:", ethers.utils.formatUnits(amountsOut3[path3.length-1], "ether"), "AAVE");
+ expect(amountsOut3[path3.length-1]).to.be.gt(0);
+
+ // set allowance for DAI
+ await daiContract.approve(iggySwapRouterContract.address, amountIn3);
+
+ // get price impact
+ const priceImpact2 = await iggySwapRouterContract.getPriceImpact(path3[0], path3[path.length-1], amountIn3);
+ console.log(priceImpact2);
+ console.log("Price impact 2:", Number(priceImpact2), "bps");
+
+ // swapExactTokensForTokens (swap DAI for AAVE)
+ const deadline3 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut3 = amountsOut3[path3.length-1].sub(amountsOut3[path3.length-1].div(100)); // 1% slippage
+ console.log("Min AAVE to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut3, "ether"), "AAVE");
+
+ const tx3 = await iggySwapRouterContract.swapExactTokensForTokens(
+ amountIn3,
+ minAmountOut3,
+ path3,
+ owner.address,
+ deadline3,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt3 = await tx3.wait();
+ calculateGasCosts("Swap (DAI -> AAVE)", receipt3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter3 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter3, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter3).to.be.lt(ownerEthBalanceBefore);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter3 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter3, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter3).to.equal(ownerDaiBalanceAfter.sub(amountIn3));
+
+ console.log("--------- third swap (path: AAVE -> MATIC) ---------");
+
+ // set amount in and path for the AAVE -> MATIC swap
+ const amountIn4 = ethers.utils.parseUnits("0.01", "ether"); // 1 AAVE
+ console.log("Amount of AAVE to swap:", ethers.utils.formatUnits(amountIn4, "ether"), "AAVE");
+
+ const path4 = [aaveAddress, wethAddress]; // path to swap aave for matic
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut4 = await iggySwapRouterContract.getAmountsOut(amountIn4, path4);
+ console.log("Amount of MATIC to receive:", ethers.utils.formatUnits(amountsOut4[path4.length-1], "ether"), "MATIC");
+ expect(amountsOut4[path4.length-1]).to.be.gt(0);
+
+ // set allowance for AAVE
+ await aaveContract.approve(iggySwapRouterContract.address, amountIn4);
+
+ // get price impact
+ const priceImpact3 = await iggySwapRouterContract.getPriceImpact(path4[0], path4[path.length-1], amountIn3);
+ console.log(priceImpact3);
+ console.log("Price impact 3:", Number(priceImpact3), "bps");
+
+ // swapExactTokensForETH (swap AAVE for MATIC)
+ const deadline4 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut4 = amountsOut4[path4.length-1].sub(amountsOut4[path4.length-1].div(100)); // 1% slippage
+ console.log("Min MATIC to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut4, "ether"), "MATIC");
+
+ const tx4 = await iggySwapRouterContract.swapExactTokensForETH(
+ amountIn4,
+ minAmountOut4,
+ path4,
+ owner.address,
+ deadline4,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt4 = await tx4.wait();
+ calculateGasCosts("Swap (AAVE -> MATIC)", receipt4);
+
+ // check owner's MATIC balance after swap
+ const ownerEthBalanceAfter4 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter4, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter4).to.be.gt(ownerEthBalanceAfter3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter4 = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter4, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter4).to.equal(ownerAaveBalanceAfter.sub(amountIn4));
+
+ });
+
+ it("adds liquidity to the MATIC/DAI pool and then removes it", async function() {
+ console.log("------ ADDING LIQUIDITY ------ ");
+
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+
+ // get LP token balance (call getLpTokenAddress from the iggySwapRouterContract to get LP token address first)
+ const lpTokenAddress = await iggySwapRouterContract.getLpTokenAddress(daiAddress, wethAddress);
+ console.log("LP token address:", lpTokenAddress);
+
+ const lpTokenContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", lpTokenAddress);
+ const lpTokenBalanceBefore = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance before adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceBefore, "ether"), "LP tokens");
+
+ // check DAI balance before adding liquidity
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+
+ const amountInDesired = ethers.utils.parseUnits("10", "ether"); // 10 DAI
+ const amountInMin = amountInDesired.sub(amountInDesired.div(10)); // 10% slippage
+
+ // give DAI allowance to the iggySwapRouterContract
+ await daiContract.approve(iggySwapRouterContract.address, amountInDesired);
+
+ // call addLiquidityETH function in a read-only way to get the amount of MATIC to send
+ const result = await iggySwapRouterContract.callStatic.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: ethers.utils.parseUnits("100", "ether"), // some amount of ETH (doesn't matter, but needs to be big enough)
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ console.log("Amount of MATIC to send:", ethers.utils.formatUnits(result[1], "ether"), "MATIC");
+
+ // addLiquidityETH (write)
+ const tx = await iggySwapRouterContract.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: result[1].add(result[1].div(10)), // result[1] + 10% slippage
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Add liquidity", receipt);
+
+ // check DAI balance after adding liquidity
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.equal(ownerDaiBalanceBefore.sub(amountInDesired));
+
+ // check LP token balance after adding liquidity
+ const lpTokenBalanceAfter = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter).to.be.gt(lpTokenBalanceBefore);
+
+ console.log("------ REMOVING LIQUIDITY ------ ");
+
+ // calculate amount of DAI and ETH to receive
+ const amountEthDai = await iggySwapRouterContract.calculateETHAndTokensToReceive(lpTokenContract.address, lpTokenBalanceAfter);
+ console.log("Amount of ETH and DAI to receive:", ethers.utils.formatEther(amountEthDai[0]), "ETH,", ethers.utils.formatEther(amountEthDai[1]), "DAI");
+
+ // give LP token allowance to the iggySwapRouterContract
+ await lpTokenContract.approve(iggySwapRouterContract.address, lpTokenBalanceAfter);
+
+ // removeLiquidityETH (write)
+ const tx2 = await iggySwapRouterContract.removeLiquidityETH(
+ daiAddress,
+ lpTokenBalanceAfter, // amount LP tokens to remove
+ 0, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("Remove liquidity", receipt2);
+
+ // check DAI balance after removing liquidity
+ const ownerDaiBalanceAfter2 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after removing liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter2, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter2).to.be.gt(ownerDaiBalanceAfter);
+ console.log("Difference:", ethers.utils.formatEther(ownerDaiBalanceAfter2.sub(ownerDaiBalanceAfter)), "DAI");
+
+ // check LP token balance after removing liquidity
+ const lpTokenBalanceAfter2 = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after removing liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter2, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter2).to.equal(0);
+
+ });
+
+});
diff --git a/test/swap/iggySwapSolidly.polygon.fork.test.js b/test/swap/iggySwapSolidly.polygon.fork.test.js
new file mode 100644
index 0000000..725388a
--- /dev/null
+++ b/test/swap/iggySwapSolidly.polygon.fork.test.js
@@ -0,0 +1,364 @@
+// test on a forked mainnet (polygon)
+// 1. First run the forked localhost node: npx hardhat node --fork https://rpc.ankr.com/polygon // must be Polygon Mainnet
+// 2. Then run the tests in a different tab: npx hardhat test test/swap/iggySwapSolidly.polygon.fork.test.js --network localhost
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+xdescribe("Iggy Swap Solidly tests (on a forked Polygon mainnet)", function () {
+ let routerAddress = "0xda822340F5E8216C277DBF66627648Ff5D57b527"; // quickswap router
+ let wethAddress = "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270"; // wmatic
+ let daiAddress = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; // dai on polygon
+ let aaveAddress = "0xd6df932a45c0f255f85145f286ea0b292b21c90b"; // aave on polygon
+
+ let iggySwapRouterContract;
+
+ let owner;
+ let frontend;
+ let iggy;
+ let user1;
+ let user2;
+ let referrer;
+ let staking; // staking contract address
+
+ let swapFee = 80; // 0.8%
+ let stakingShare = 0; // 0%
+ let frontendShare = 5000; // 50% (after referral fee and staking fee are deducted)
+
+ beforeEach(async function () {
+ [owner, frontend, iggy, user1, user2, referrer, staking] = await ethers.getSigners();
+
+ // deploy IggySwapRouter
+ const IggySwapRouter = await ethers.getContractFactory("IggySwapRouterSolidly");
+ iggySwapRouterContract = await IggySwapRouter.deploy(
+ frontend.address,
+ iggy.address,
+ routerAddress,
+ staking.address,
+ ethers.constants.AddressZero,
+ wethAddress,
+ swapFee,
+ stakingShare,
+ frontendShare
+ );
+ await iggySwapRouterContract.deployed();
+ });
+
+ it("test swapping via Iggy Swap", async function () {
+ console.log("--------- First swap (path: MATIC -> DAI) ---------");
+
+ // check owner's ETH balance before swap
+ const ownerEthBalanceBefore = await owner.getBalance();
+ console.log("Owner's MATIC balance before swap:", ethers.utils.formatUnits(ownerEthBalanceBefore, "ether"), "MATIC");
+ expect(ownerEthBalanceBefore).to.be.gt(ethers.utils.parseUnits("9990", "ether"));
+
+ // check frontend's DAI balance before swap
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+ const frontendDaiBalanceBefore = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance before swap:", ethers.utils.formatUnits(frontendDaiBalanceBefore, "ether"), "DAI");
+ expect(frontendDaiBalanceBefore).to.be.eq(0);
+
+ // check iggy's DAI balance before swap
+ const iggyDaiBalanceBefore = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance before swap:", ethers.utils.formatUnits(iggyDaiBalanceBefore, "ether"), "DAI");
+ expect(iggyDaiBalanceBefore).to.be.eq(0);
+
+ // set amount in and path for the MATIC -> DAI swap
+ const amountIn = ethers.utils.parseUnits("400", "ether"); // 400 MATIC
+ console.log("Amount of MATIC to swap:", ethers.utils.formatUnits(amountIn, "ether"), "MATIC");
+
+ const path = [wethAddress, daiAddress]; // path to swap eth for dai
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut = await iggySwapRouterContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (iggy contract):", ethers.utils.formatUnits(amountsOut[path.length-1], "ether"), "DAI");
+ expect(amountsOut[path.length-1]).to.be.gt(0);
+
+ // check getAmountsOut first before swap (via router)
+ const routerContract = await ethers.getContractAt("contracts/swap/IggySwapRouter.sol:IUniswapV2Router02", routerAddress);
+ const amountsOut2 = await routerContract.getAmountsOut(amountIn, path);
+ console.log("Amount of DAI to receive (router):", ethers.utils.formatUnits(amountsOut2[path.length-1], "ether"), "DAI");
+ expect(amountsOut2[path.length-1]).to.be.gt(0);
+
+ // check owner's DAI balance before swap
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before swap:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+ expect(ownerDaiBalanceBefore).to.equal(0);
+
+ // get price impact
+ const priceImpact = await iggySwapRouterContract.getPriceImpact(path[0], path[path.length-1], amountIn);
+ console.log(priceImpact);
+ console.log("Price impact:", Number(priceImpact), "bps");
+
+ // swapExactETHForTokens (swap ETH for DAI)
+ const deadline = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut = amountsOut[path.length-1].sub(amountsOut[path.length-1].div(100)); // 1% slippage
+ console.log("Min DAI to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut, "ether"), "DAI");
+
+ const tx = await iggySwapRouterContract.swapExactETHForTokens(
+ minAmountOut,
+ path,
+ owner.address,
+ deadline,
+ {
+ value: amountIn,
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Swap (MATIC -> DAI)", receipt);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.be.gt(0);
+
+ // check frontend's DAI balance after swap
+ const frontendDaiBalanceAfter = await daiContract.balanceOf(frontend.address);
+ console.log("Frontend's DAI balance after swap:", ethers.utils.formatUnits(frontendDaiBalanceAfter, "ether"), "DAI");
+ expect(frontendDaiBalanceAfter).to.be.gt(0);
+
+ // check iggy's DAI balance after swap
+ const iggyDaiBalanceAfter = await daiContract.balanceOf(iggy.address);
+ console.log("Iggy's DAI balance after swap:", ethers.utils.formatUnits(iggyDaiBalanceAfter, "ether"), "DAI");
+ expect(iggyDaiBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter).to.be.lt(ownerEthBalanceBefore);
+
+ console.log("--------- second swap (path: DAI -> MATIC -> AAVE) ---------");
+
+ // check owner's AAVE balance before second swap
+ const aaveContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", aaveAddress);
+ const ownerAaveBalanceBefore = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance before swap:", ethers.utils.formatUnits(ownerAaveBalanceBefore, "ether"), "AAVE");
+ expect(ownerAaveBalanceBefore).to.equal(0);
+
+ // set amount in and path for the DAI -> AAVE swap
+ const amountIn3 = ethers.utils.parseUnits("200", "ether"); // 200 DAI
+ console.log("Amount of DAI to swap:", ethers.utils.formatUnits(amountIn3, "ether"), "DAI");
+
+ const path3 = [daiAddress, wethAddress, aaveAddress]; // path to swap dai for aave
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut3 = await iggySwapRouterContract.getAmountsOut(amountIn3, path3);
+ console.log("Amount of AAVE to receive:", ethers.utils.formatUnits(amountsOut3[path3.length-1], "ether"), "AAVE");
+ expect(amountsOut3[path3.length-1]).to.be.gt(0);
+
+ // set allowance for DAI
+ await daiContract.approve(iggySwapRouterContract.address, amountIn3);
+
+ // get price impact
+ const priceImpact2 = await iggySwapRouterContract.getPriceImpact(path3[0], path3[path.length-1], amountIn3);
+ console.log(priceImpact2);
+ console.log("Price impact 2:", Number(priceImpact2), "bps");
+
+ // swapExactTokensForTokens (swap DAI for AAVE)
+ const deadline3 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut3 = amountsOut3[path3.length-1].sub(amountsOut3[path3.length-1].div(100)); // 1% slippage
+ console.log("Min AAVE to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut3, "ether"), "AAVE");
+
+ const tx3 = await iggySwapRouterContract.swapExactTokensForTokens(
+ amountIn3,
+ minAmountOut3,
+ path3,
+ owner.address,
+ deadline3,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt3 = await tx3.wait();
+ calculateGasCosts("Swap (DAI -> AAVE)", receipt3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter).to.be.gt(0);
+
+ // check owner's ETH balance after swap
+ const ownerEthBalanceAfter3 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter3, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter3).to.be.lt(ownerEthBalanceBefore);
+
+ // check owner's DAI balance after swap
+ const ownerDaiBalanceAfter3 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after swap:", ethers.utils.formatUnits(ownerDaiBalanceAfter3, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter3).to.equal(ownerDaiBalanceAfter.sub(amountIn3));
+
+ console.log("--------- third swap (path: AAVE -> MATIC) ---------");
+
+ // set amount in and path for the AAVE -> MATIC swap
+ const amountIn4 = ethers.utils.parseUnits("0.01", "ether"); // 1 AAVE
+ console.log("Amount of AAVE to swap:", ethers.utils.formatUnits(amountIn4, "ether"), "AAVE");
+
+ const path4 = [aaveAddress, wethAddress]; // path to swap aave for matic
+
+ // check getAmountsOut first before swap (via iggySwapRouterContract)
+ const amountsOut4 = await iggySwapRouterContract.getAmountsOut(amountIn4, path4);
+ console.log("Amount of MATIC to receive:", ethers.utils.formatUnits(amountsOut4[path4.length-1], "ether"), "MATIC");
+ expect(amountsOut4[path4.length-1]).to.be.gt(0);
+
+ // set allowance for AAVE
+ await aaveContract.approve(iggySwapRouterContract.address, amountIn4);
+
+ // get price impact
+ const priceImpact3 = await iggySwapRouterContract.getPriceImpact(path4[0], path4[path.length-1], amountIn3);
+ console.log(priceImpact3);
+ console.log("Price impact 3:", Number(priceImpact3), "bps");
+
+ // swapExactTokensForETH (swap AAVE for MATIC)
+ const deadline4 = Math.floor(Date.now() / 1000) + 60 * 20; // 20 minutes from the current Unix time
+ const minAmountOut4 = amountsOut4[path4.length-1].sub(amountsOut4[path4.length-1].div(100)); // 1% slippage
+ console.log("Min MATIC to receive (deduct 1% slippage):", ethers.utils.formatUnits(minAmountOut4, "ether"), "MATIC");
+
+ const tx4 = await iggySwapRouterContract.swapExactTokensForETH(
+ amountIn4,
+ minAmountOut4,
+ path4,
+ owner.address,
+ deadline4,
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt4 = await tx4.wait();
+ calculateGasCosts("Swap (AAVE -> MATIC)", receipt4);
+
+ // check owner's MATIC balance after swap
+ const ownerEthBalanceAfter4 = await owner.getBalance();
+ console.log("Owner's MATIC balance after swap:", ethers.utils.formatUnits(ownerEthBalanceAfter4, "ether"), "MATIC");
+ expect(ownerEthBalanceAfter4).to.be.gt(ownerEthBalanceAfter3);
+
+ // check owner's AAVE balance after swap
+ const ownerAaveBalanceAfter4 = await aaveContract.balanceOf(owner.address);
+ console.log("Owner's AAVE balance after swap:", ethers.utils.formatUnits(ownerAaveBalanceAfter4, "ether"), "AAVE");
+ expect(ownerAaveBalanceAfter4).to.equal(ownerAaveBalanceAfter.sub(amountIn4));
+
+ });
+
+ it("adds liquidity to the MATIC/DAI pool and then removes it", async function() {
+ console.log("------ ADDING LIQUIDITY ------ ");
+
+ const daiContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", daiAddress);
+
+ // get LP token balance (call getLpTokenAddress from the iggySwapRouterContract to get LP token address first)
+ const lpTokenAddress = await iggySwapRouterContract.getLpTokenAddress(daiAddress, wethAddress);
+ console.log("LP token address:", lpTokenAddress);
+
+ const lpTokenContract = await ethers.getContractAt("@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20", lpTokenAddress);
+ const lpTokenBalanceBefore = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance before adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceBefore, "ether"), "LP tokens");
+
+ // check DAI balance before adding liquidity
+ const ownerDaiBalanceBefore = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance before adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceBefore, "ether"), "DAI");
+
+ const amountInDesired = ethers.utils.parseUnits("10", "ether"); // 10 DAI
+ const amountInMin = amountInDesired.sub(amountInDesired.div(10)); // 10% slippage
+
+ // give DAI allowance to the iggySwapRouterContract
+ await daiContract.approve(iggySwapRouterContract.address, amountInDesired);
+
+ // call addLiquidityETH function in a read-only way to get the amount of MATIC to send
+ const result = await iggySwapRouterContract.callStatic.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: ethers.utils.parseUnits("100", "ether"), // some amount of ETH (doesn't matter, but needs to be big enough)
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ console.log("Amount of MATIC to send:", ethers.utils.formatUnits(result[1], "ether"), "MATIC");
+
+ // addLiquidityETH (write)
+ const tx = await iggySwapRouterContract.addLiquidityETH(
+ daiAddress,
+ amountInDesired, // amount DAI desired
+ amountInMin, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ value: result[1].add(result[1].div(10)), // result[1] + 10% slippage
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt = await tx.wait();
+ calculateGasCosts("Add liquidity", receipt);
+
+ // check DAI balance after adding liquidity
+ const ownerDaiBalanceAfter = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after adding liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter).to.equal(ownerDaiBalanceBefore.sub(amountInDesired));
+
+ // check LP token balance after adding liquidity
+ const lpTokenBalanceAfter = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after adding liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter).to.be.gt(lpTokenBalanceBefore);
+
+ console.log("------ REMOVING LIQUIDITY ------ ");
+
+ // calculate amount of DAI and ETH to receive
+ const amountEthDai = await iggySwapRouterContract.calculateETHAndTokensToReceive(lpTokenContract.address, lpTokenBalanceAfter);
+ console.log("Amount of ETH and DAI to receive:", ethers.utils.formatEther(amountEthDai[0]), "ETH,", ethers.utils.formatEther(amountEthDai[1]), "DAI");
+
+ // give LP token allowance to the iggySwapRouterContract
+ await lpTokenContract.approve(iggySwapRouterContract.address, lpTokenBalanceAfter);
+
+ // removeLiquidityETH (write)
+ const tx2 = await iggySwapRouterContract.removeLiquidityETH(
+ daiAddress,
+ lpTokenBalanceAfter, // amount LP tokens to remove
+ 0, // amount DAI min
+ 0, // amount ETH min
+ owner.address, // recipient (to)
+ Math.floor(Date.now() / 1000) + 60 * 20, // deadline: 20 minutes from the current Unix time
+ {
+ gasPrice: ethers.utils.parseUnits("500", "gwei"),
+ gasLimit: 500000
+ }
+ );
+ const receipt2 = await tx2.wait();
+ calculateGasCosts("Remove liquidity", receipt2);
+
+ // check DAI balance after removing liquidity
+ const ownerDaiBalanceAfter2 = await daiContract.balanceOf(owner.address);
+ console.log("Owner's DAI balance after removing liquidity:", ethers.utils.formatUnits(ownerDaiBalanceAfter2, "ether"), "DAI");
+ expect(ownerDaiBalanceAfter2).to.be.gt(ownerDaiBalanceAfter);
+ console.log("Difference:", ethers.utils.formatEther(ownerDaiBalanceAfter2.sub(ownerDaiBalanceAfter)), "DAI");
+
+ // check LP token balance after removing liquidity
+ const lpTokenBalanceAfter2 = await lpTokenContract.balanceOf(owner.address);
+ console.log("Owner's LP token balance after removing liquidity:", ethers.utils.formatUnits(lpTokenBalanceAfter2, "ether"), "LP tokens");
+ expect(lpTokenBalanceAfter2).to.equal(0);
+
+ });
+
+});
diff --git a/test/token/chatTokenClaimActivityPoints.test.js b/test/token/chatTokenClaimActivityPoints.test.js
new file mode 100644
index 0000000..38e7ed6
--- /dev/null
+++ b/test/token/chatTokenClaimActivityPoints.test.js
@@ -0,0 +1,150 @@
+// npx hardhat test test/token/chatTokenClaimActivityPoints.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("ChatTokenClaimActivityPoints", function () {
+ let chatTokenContract;
+ let chatTokenMinterContract;
+ let iggyPostStatsContract;
+ let chatTokenClaimActivityPoints;
+
+ let owner;
+ let user1;
+ let user2;
+ let user3;
+
+ const chatEthRatio = 1000;
+
+ const user1mintedWei = ethers.utils.parseEther("1.337");
+ const user2mintedWei = ethers.utils.parseEther("4.2069");
+
+ beforeEach(async function () {
+ [owner, user1, user2, user3, feeReceiver] = await ethers.getSigners();
+
+ const MockSFS = await ethers.getContractFactory("MockSFS");
+ const sfsContract = await MockSFS.deploy();
+
+ const SfsNftInitialize = await ethers.getContractFactory("SfsNftInitialize");
+ const sfsNftInitializeContract = await SfsNftInitialize.deploy(sfsContract.address, feeReceiver.address);
+
+ const sfsNftTokenId = await sfsNftInitializeContract.sfsNftTokenId();
+
+ // deploy ChatToken
+ const ChatToken = await ethers.getContractFactory("ChatToken");
+ chatTokenContract = await ChatToken.deploy("Chat Token", "CHAT");
+ await chatTokenContract.deployed();
+
+ // deploy ChatTokenMinter
+ const ChatTokenMinter = await ethers.getContractFactory("ChatTokenMinter");
+ chatTokenMinterContract = await ChatTokenMinter.deploy(chatTokenContract.address);
+ await chatTokenMinterContract.deployed();
+
+ // add minter to ChatToken
+ await chatTokenContract.setMinter(chatTokenMinterContract.address);
+
+ // deploy IggyPostStats
+ const IggyPostStats = await ethers.getContractFactory("IggyPostStats");
+ iggyPostStatsContract = await IggyPostStats.deploy(
+ sfsContract.address, // SFS address
+ sfsNftTokenId, // SFS NFT token ID
+ owner.address // set owner as minter
+ );
+ await iggyPostStatsContract.deployed();
+
+ // deploy ActivityPoints
+ const ActivityPoints = await ethers.getContractFactory("ActivityPoints");
+ const activityPointsContract = await ActivityPoints.deploy(
+ ethers.constants.AddressZero, // no token address
+ iggyPostStatsContract.address, // IggyPostStats address
+ ethers.constants.AddressZero, // no token address
+ 1,
+ sfsContract.address, // SFS address
+ sfsNftTokenId // SFS NFT token ID
+ );
+
+ // deploy ChatTokenClaimActivityPoints
+ const ChatTokenClaimActivityPoints = await ethers.getContractFactory("ChatTokenClaimActivityPoints");
+ chatTokenClaimActivityPoints = await ChatTokenClaimActivityPoints.deploy(
+ chatTokenMinterContract.address, // ChatTokenMinter address
+ activityPointsContract.address, // IggyPostStats address
+ chatEthRatio // how many tokens per ETH spent will user get (1000 CHAT per ETH)
+ );
+
+ // add ChatTokenClaimActivityPoints address as minter in ChatTokenMinter
+ await chatTokenMinterContract.addMinter(chatTokenClaimActivityPoints.address);
+
+ // add some data in the stats contract (addMintedWei for user1 and user2)
+ await iggyPostStatsContract.addMintedWei(user1.address, user1mintedWei);
+ await iggyPostStatsContract.addMintedWei(user2.address, user2mintedWei);
+ });
+
+ it("can claim CHAT tokens based on data from the stats contract", async function () {
+ // user1: check CHAT balance 1
+ const user1ChatBalance1 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance1).to.equal(0);
+
+ // user1: check claim preview 1
+ const user1ClaimPreview1 = await chatTokenClaimActivityPoints.claimPreview(user1.address);
+ expect(user1ClaimPreview1).to.equal(user1mintedWei.mul(chatEthRatio));
+ console.log("user1 claim preview: ", ethers.utils.formatEther(user1ClaimPreview1), " CHAT");
+
+ // user1: claim CHAT tokens
+ const user1ClaimTx = await chatTokenClaimActivityPoints.connect(user1).claim();
+ const receiptUser1ClaimTx = await user1ClaimTx.wait();
+ calculateGasCosts("user1 claim", receiptUser1ClaimTx);
+
+ // user1: check CHAT balance 2
+ const user1ChatBalance2 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance2).to.equal(user1mintedWei.mul(chatEthRatio));
+
+ // user1: check claim preview 2
+ const user1ClaimPreview2 = await chatTokenClaimActivityPoints.claimPreview(user1.address);
+ expect(user1ClaimPreview2).to.equal(0);
+
+ // user1: fail to claim CHAT tokens again
+ await expect(chatTokenClaimActivityPoints.connect(user1).claim()).to.be.revertedWith("ChatTokenClaimActivityPoints: user already claimed");
+
+ // user2: check CHAT balance 1
+ const user2ChatBalance1 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance1).to.equal(0);
+
+ // user2: check claim preview 1
+ const user2ClaimPreview1 = await chatTokenClaimActivityPoints.claimPreview(user2.address);
+ expect(user2ClaimPreview1).to.equal(user2mintedWei.mul(chatEthRatio));
+ console.log("user2 claim preview: ", ethers.utils.formatEther(user2ClaimPreview1), " CHAT");
+
+ // user2: claim CHAT tokens
+ const user2ClaimTx = await chatTokenClaimActivityPoints.connect(user2).claim();
+ const receiptUser2ClaimTx = await user2ClaimTx.wait();
+ calculateGasCosts("user2 claim", receiptUser2ClaimTx);
+
+ // user2: check CHAT balance 2
+ const user2ChatBalance2 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance2).to.equal(user2mintedWei.mul(chatEthRatio));
+
+ // user2: check claim preview 2
+ const user2ClaimPreview2 = await chatTokenClaimActivityPoints.claimPreview(user2.address);
+ expect(user2ClaimPreview2).to.equal(0);
+
+ // user2: fail to claim CHAT tokens again
+ await expect(chatTokenClaimActivityPoints.connect(user2).claim()).to.be.revertedWith("ChatTokenClaimActivityPoints: user already claimed");
+
+ });
+
+});
\ No newline at end of file
diff --git a/test/token/chatTokenClaimDomains.test.js b/test/token/chatTokenClaimDomains.test.js
new file mode 100644
index 0000000..8670e39
--- /dev/null
+++ b/test/token/chatTokenClaimDomains.test.js
@@ -0,0 +1,133 @@
+// npx hardhat test test/token/chatTokenClaimDomains.test.js
+
+const { expect } = require("chai");
+
+function calculateGasCosts(testName, receipt) {
+ console.log(testName + " gasUsed: " + receipt.gasUsed);
+
+ // coin prices in USD
+ const matic = 1.5;
+ const eth = 1500;
+
+ const gasCostMatic = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("500", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostEthereum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("50", "gwei")) * Number(receipt.gasUsed)), "ether");
+ const gasCostArbitrum = ethers.utils.formatUnits(String(Number(ethers.utils.parseUnits("1.25", "gwei")) * Number(receipt.gasUsed)), "ether");
+
+ console.log(testName + " gas cost (Ethereum): $" + String(Number(gasCostEthereum)*eth));
+ console.log(testName + " gas cost (Arbitrum): $" + String(Number(gasCostArbitrum)*eth));
+ console.log(testName + " gas cost (Polygon): $" + String(Number(gasCostMatic)*matic));
+}
+
+describe("ChatTokenClaimDomains", function () {
+ let chatTokenContract;
+ let chatTokenMinterContract;
+ let mockPunkTldContract;
+ let chatTokenClaimDomainsContract;
+
+ let owner;
+ let user1;
+ let user2;
+ let user3;
+
+ const domain1 = "user1a";
+ const domain2 = "user1b";
+ const domain3 = "user2a";
+ const domain4 = "user2b";
+
+ const chatReward = ethers.utils.parseEther("1337");
+
+ beforeEach(async function () {
+ [owner, user1, user2, user3] = await ethers.getSigners();
+
+ // deploy ChatToken
+ const ChatToken = await ethers.getContractFactory("ChatToken");
+ chatTokenContract = await ChatToken.deploy("Chat Token", "CHAT");
+ await chatTokenContract.deployed();
+
+ // deploy ChatTokenMinter
+ const ChatTokenMinter = await ethers.getContractFactory("ChatTokenMinter");
+ chatTokenMinterContract = await ChatTokenMinter.deploy(chatTokenContract.address);
+ await chatTokenMinterContract.deployed();
+
+ // add minter to ChatToken
+ await chatTokenContract.setMinter(chatTokenMinterContract.address);
+
+ // deploy MockPunkTld
+ const MockPunkTld = await ethers.getContractFactory("MockPunkTld");
+ mockPunkTldContract = await MockPunkTld.deploy(user3.address, "user3");
+ await mockPunkTldContract.deployed();
+
+ // deploy ChatTokenClaimDomains
+ const ChatTokenClaimDomains = await ethers.getContractFactory("ChatTokenClaimDomains");
+ chatTokenClaimDomainsContract = await ChatTokenClaimDomains.deploy(
+ chatTokenMinterContract.address, // ChatTokenMinter address
+ mockPunkTldContract.address, // TLD address
+ chatReward, // chat rewards per domain
+ 100 // max domain NFT ID eligible for airdrop (aka snapshot)
+ );
+
+ // add ChatTokenClaimDomains address as minter in ChatTokenMinter
+ await chatTokenMinterContract.addMinter(chatTokenClaimDomainsContract.address);
+
+ // register some domains
+ await mockPunkTldContract.register(domain1, user1.address);
+ await mockPunkTldContract.register(domain2, user1.address);
+ await mockPunkTldContract.register(domain3, user2.address);
+ await mockPunkTldContract.register(domain4, user2.address);
+ });
+
+ it("can claim CHAT airdrop for a domain", async function () {
+ // user1: check CHAT balance 1
+ const user1ChatBalance1 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance1).to.equal(0);
+
+ const tx = await chatTokenClaimDomainsContract.connect(user1).claim(domain1);
+ const receipt = await tx.wait();
+ calculateGasCosts("claimDomain user1a", receipt);
+
+ // user1: check CHAT balance 2
+ const user1ChatBalance2 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance2).to.equal(chatReward);
+
+ // fail to claim the same domain again
+ await expect(chatTokenClaimDomainsContract.connect(user1).claim(domain1)).to.be.revertedWith(
+ "ChatTokenClaimDomains: domain already claimed"
+ );
+
+ // fail to claim non-existing domain
+ await expect(chatTokenClaimDomainsContract.connect(user1).claim("non-existing")).to.be.revertedWith(
+ "ChatTokenClaimDomains: domain not registered"
+ );
+
+ // user1: claim another domain
+ await chatTokenClaimDomainsContract.connect(user1).claim(domain2);
+
+ // user1: check CHAT balance 3
+ const user1ChatBalance3 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance3).to.equal(chatReward.mul(2));
+
+ // user2: check CHAT balance 1
+ const user2ChatBalance1 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance1).to.equal(0);
+
+ // user2: claim domain
+ await chatTokenClaimDomainsContract.connect(user2).claim(domain3);
+
+ // user2: check CHAT balance 2
+ const user2ChatBalance2 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance2).to.equal(chatReward);
+
+ // user1: claim for user2
+ await chatTokenClaimDomainsContract.connect(user1).claim(domain4);
+
+ // user2: check CHAT balance 3
+ const user2ChatBalance3 = await chatTokenContract.balanceOf(user2.address);
+ expect(user2ChatBalance3).to.equal(chatReward.mul(2));
+
+ // user1: check CHAT balance 4
+ const user1ChatBalance4 = await chatTokenContract.balanceOf(user1.address);
+ expect(user1ChatBalance4).to.equal(user1ChatBalance3); // unchanged
+ });
+
+
+});
\ No newline at end of file