mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-12 14:09:25 +00:00
Compare commits
2 Commits
ff-0.14
...
653b0e0bbc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
653b0e0bbc | ||
|
|
d78c92bc3e |
4986
Cargo.lock
generated
4986
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,427 +0,0 @@
|
|||||||
Attribution-ShareAlike 4.0 International
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
|
||||||
does not provide legal services or legal advice. Distribution of
|
|
||||||
Creative Commons public licenses does not create a lawyer-client or
|
|
||||||
other relationship. Creative Commons makes its licenses and related
|
|
||||||
information available on an "as-is" basis. Creative Commons gives no
|
|
||||||
warranties regarding its licenses, any material licensed under their
|
|
||||||
terms and conditions, or any related information. Creative Commons
|
|
||||||
disclaims all liability for damages resulting from their use to the
|
|
||||||
fullest extent possible.
|
|
||||||
|
|
||||||
Using Creative Commons Public Licenses
|
|
||||||
|
|
||||||
Creative Commons public licenses provide a standard set of terms and
|
|
||||||
conditions that creators and other rights holders may use to share
|
|
||||||
original works of authorship and other material subject to copyright
|
|
||||||
and certain other rights specified in the public license below. The
|
|
||||||
following considerations are for informational purposes only, are not
|
|
||||||
exhaustive, and do not form part of our licenses.
|
|
||||||
|
|
||||||
Considerations for licensors: Our public licenses are
|
|
||||||
intended for use by those authorized to give the public
|
|
||||||
permission to use material in ways otherwise restricted by
|
|
||||||
copyright and certain other rights. Our licenses are
|
|
||||||
irrevocable. Licensors should read and understand the terms
|
|
||||||
and conditions of the license they choose before applying it.
|
|
||||||
Licensors should also secure all rights necessary before
|
|
||||||
applying our licenses so that the public can reuse the
|
|
||||||
material as expected. Licensors should clearly mark any
|
|
||||||
material not subject to the license. This includes other CC-
|
|
||||||
licensed material, or material used under an exception or
|
|
||||||
limitation to copyright. More considerations for licensors:
|
|
||||||
wiki.creativecommons.org/Considerations_for_licensors
|
|
||||||
|
|
||||||
Considerations for the public: By using one of our public
|
|
||||||
licenses, a licensor grants the public permission to use the
|
|
||||||
licensed material under specified terms and conditions. If
|
|
||||||
the licensor's permission is not necessary for any reason--for
|
|
||||||
example, because of any applicable exception or limitation to
|
|
||||||
copyright--then that use is not regulated by the license. Our
|
|
||||||
licenses grant only permissions under copyright and certain
|
|
||||||
other rights that a licensor has authority to grant. Use of
|
|
||||||
the licensed material may still be restricted for other
|
|
||||||
reasons, including because others have copyright or other
|
|
||||||
rights in the material. A licensor may make special requests,
|
|
||||||
such as asking that all changes be marked or described.
|
|
||||||
Although not required by our licenses, you are encouraged to
|
|
||||||
respect those requests where reasonable. More considerations
|
|
||||||
for the public:
|
|
||||||
wiki.creativecommons.org/Considerations_for_licensees
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons Attribution-ShareAlike 4.0 International Public
|
|
||||||
License
|
|
||||||
|
|
||||||
By exercising the Licensed Rights (defined below), You accept and agree
|
|
||||||
to be bound by the terms and conditions of this Creative Commons
|
|
||||||
Attribution-ShareAlike 4.0 International Public License ("Public
|
|
||||||
License"). To the extent this Public License may be interpreted as a
|
|
||||||
contract, You are granted the Licensed Rights in consideration of Your
|
|
||||||
acceptance of these terms and conditions, and the Licensor grants You
|
|
||||||
such rights in consideration of benefits the Licensor receives from
|
|
||||||
making the Licensed Material available under these terms and
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
|
|
||||||
Section 1 -- Definitions.
|
|
||||||
|
|
||||||
a. Adapted Material means material subject to Copyright and Similar
|
|
||||||
Rights that is derived from or based upon the Licensed Material
|
|
||||||
and in which the Licensed Material is translated, altered,
|
|
||||||
arranged, transformed, or otherwise modified in a manner requiring
|
|
||||||
permission under the Copyright and Similar Rights held by the
|
|
||||||
Licensor. For purposes of this Public License, where the Licensed
|
|
||||||
Material is a musical work, performance, or sound recording,
|
|
||||||
Adapted Material is always produced where the Licensed Material is
|
|
||||||
synched in timed relation with a moving image.
|
|
||||||
|
|
||||||
b. Adapter's License means the license You apply to Your Copyright
|
|
||||||
and Similar Rights in Your contributions to Adapted Material in
|
|
||||||
accordance with the terms and conditions of this Public License.
|
|
||||||
|
|
||||||
c. BY-SA Compatible License means a license listed at
|
|
||||||
creativecommons.org/compatiblelicenses, approved by Creative
|
|
||||||
Commons as essentially the equivalent of this Public License.
|
|
||||||
|
|
||||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
|
||||||
closely related to copyright including, without limitation,
|
|
||||||
performance, broadcast, sound recording, and Sui Generis Database
|
|
||||||
Rights, without regard to how the rights are labeled or
|
|
||||||
categorized. For purposes of this Public License, the rights
|
|
||||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
|
||||||
Rights.
|
|
||||||
|
|
||||||
e. Effective Technological Measures means those measures that, in the
|
|
||||||
absence of proper authority, may not be circumvented under laws
|
|
||||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
|
||||||
Treaty adopted on December 20, 1996, and/or similar international
|
|
||||||
agreements.
|
|
||||||
|
|
||||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
|
||||||
any other exception or limitation to Copyright and Similar Rights
|
|
||||||
that applies to Your use of the Licensed Material.
|
|
||||||
|
|
||||||
g. License Elements means the license attributes listed in the name
|
|
||||||
of a Creative Commons Public License. The License Elements of this
|
|
||||||
Public License are Attribution and ShareAlike.
|
|
||||||
|
|
||||||
h. Licensed Material means the artistic or literary work, database,
|
|
||||||
or other material to which the Licensor applied this Public
|
|
||||||
License.
|
|
||||||
|
|
||||||
i. Licensed Rights means the rights granted to You subject to the
|
|
||||||
terms and conditions of this Public License, which are limited to
|
|
||||||
all Copyright and Similar Rights that apply to Your use of the
|
|
||||||
Licensed Material and that the Licensor has authority to license.
|
|
||||||
|
|
||||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
|
||||||
under this Public License.
|
|
||||||
|
|
||||||
k. Share means to provide material to the public by any means or
|
|
||||||
process that requires permission under the Licensed Rights, such
|
|
||||||
as reproduction, public display, public performance, distribution,
|
|
||||||
dissemination, communication, or importation, and to make material
|
|
||||||
available to the public including in ways that members of the
|
|
||||||
public may access the material from a place and at a time
|
|
||||||
individually chosen by them.
|
|
||||||
|
|
||||||
l. Sui Generis Database Rights means rights other than copyright
|
|
||||||
resulting from Directive 96/9/EC of the European Parliament and of
|
|
||||||
the Council of 11 March 1996 on the legal protection of databases,
|
|
||||||
as amended and/or succeeded, as well as other essentially
|
|
||||||
equivalent rights anywhere in the world.
|
|
||||||
|
|
||||||
m. You means the individual or entity exercising the Licensed Rights
|
|
||||||
under this Public License. Your has a corresponding meaning.
|
|
||||||
|
|
||||||
|
|
||||||
Section 2 -- Scope.
|
|
||||||
|
|
||||||
a. License grant.
|
|
||||||
|
|
||||||
1. Subject to the terms and conditions of this Public License,
|
|
||||||
the Licensor hereby grants You a worldwide, royalty-free,
|
|
||||||
non-sublicensable, non-exclusive, irrevocable license to
|
|
||||||
exercise the Licensed Rights in the Licensed Material to:
|
|
||||||
|
|
||||||
a. reproduce and Share the Licensed Material, in whole or
|
|
||||||
in part; and
|
|
||||||
|
|
||||||
b. produce, reproduce, and Share Adapted Material.
|
|
||||||
|
|
||||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
|
||||||
Exceptions and Limitations apply to Your use, this Public
|
|
||||||
License does not apply, and You do not need to comply with
|
|
||||||
its terms and conditions.
|
|
||||||
|
|
||||||
3. Term. The term of this Public License is specified in Section
|
|
||||||
6(a).
|
|
||||||
|
|
||||||
4. Media and formats; technical modifications allowed. The
|
|
||||||
Licensor authorizes You to exercise the Licensed Rights in
|
|
||||||
all media and formats whether now known or hereafter created,
|
|
||||||
and to make technical modifications necessary to do so. The
|
|
||||||
Licensor waives and/or agrees not to assert any right or
|
|
||||||
authority to forbid You from making technical modifications
|
|
||||||
necessary to exercise the Licensed Rights, including
|
|
||||||
technical modifications necessary to circumvent Effective
|
|
||||||
Technological Measures. For purposes of this Public License,
|
|
||||||
simply making modifications authorized by this Section 2(a)
|
|
||||||
(4) never produces Adapted Material.
|
|
||||||
|
|
||||||
5. Downstream recipients.
|
|
||||||
|
|
||||||
a. Offer from the Licensor -- Licensed Material. Every
|
|
||||||
recipient of the Licensed Material automatically
|
|
||||||
receives an offer from the Licensor to exercise the
|
|
||||||
Licensed Rights under the terms and conditions of this
|
|
||||||
Public License.
|
|
||||||
|
|
||||||
b. Additional offer from the Licensor -- Adapted Material.
|
|
||||||
Every recipient of Adapted Material from You
|
|
||||||
automatically receives an offer from the Licensor to
|
|
||||||
exercise the Licensed Rights in the Adapted Material
|
|
||||||
under the conditions of the Adapter's License You apply.
|
|
||||||
|
|
||||||
c. No downstream restrictions. You may not offer or impose
|
|
||||||
any additional or different terms or conditions on, or
|
|
||||||
apply any Effective Technological Measures to, the
|
|
||||||
Licensed Material if doing so restricts exercise of the
|
|
||||||
Licensed Rights by any recipient of the Licensed
|
|
||||||
Material.
|
|
||||||
|
|
||||||
6. No endorsement. Nothing in this Public License constitutes or
|
|
||||||
may be construed as permission to assert or imply that You
|
|
||||||
are, or that Your use of the Licensed Material is, connected
|
|
||||||
with, or sponsored, endorsed, or granted official status by,
|
|
||||||
the Licensor or others designated to receive attribution as
|
|
||||||
provided in Section 3(a)(1)(A)(i).
|
|
||||||
|
|
||||||
b. Other rights.
|
|
||||||
|
|
||||||
1. Moral rights, such as the right of integrity, are not
|
|
||||||
licensed under this Public License, nor are publicity,
|
|
||||||
privacy, and/or other similar personality rights; however, to
|
|
||||||
the extent possible, the Licensor waives and/or agrees not to
|
|
||||||
assert any such rights held by the Licensor to the limited
|
|
||||||
extent necessary to allow You to exercise the Licensed
|
|
||||||
Rights, but not otherwise.
|
|
||||||
|
|
||||||
2. Patent and trademark rights are not licensed under this
|
|
||||||
Public License.
|
|
||||||
|
|
||||||
3. To the extent possible, the Licensor waives any right to
|
|
||||||
collect royalties from You for the exercise of the Licensed
|
|
||||||
Rights, whether directly or through a collecting society
|
|
||||||
under any voluntary or waivable statutory or compulsory
|
|
||||||
licensing scheme. In all other cases the Licensor expressly
|
|
||||||
reserves any right to collect such royalties.
|
|
||||||
|
|
||||||
|
|
||||||
Section 3 -- License Conditions.
|
|
||||||
|
|
||||||
Your exercise of the Licensed Rights is expressly made subject to the
|
|
||||||
following conditions.
|
|
||||||
|
|
||||||
a. Attribution.
|
|
||||||
|
|
||||||
1. If You Share the Licensed Material (including in modified
|
|
||||||
form), You must:
|
|
||||||
|
|
||||||
a. retain the following if it is supplied by the Licensor
|
|
||||||
with the Licensed Material:
|
|
||||||
|
|
||||||
i. identification of the creator(s) of the Licensed
|
|
||||||
Material and any others designated to receive
|
|
||||||
attribution, in any reasonable manner requested by
|
|
||||||
the Licensor (including by pseudonym if
|
|
||||||
designated);
|
|
||||||
|
|
||||||
ii. a copyright notice;
|
|
||||||
|
|
||||||
iii. a notice that refers to this Public License;
|
|
||||||
|
|
||||||
iv. a notice that refers to the disclaimer of
|
|
||||||
warranties;
|
|
||||||
|
|
||||||
v. a URI or hyperlink to the Licensed Material to the
|
|
||||||
extent reasonably practicable;
|
|
||||||
|
|
||||||
b. indicate if You modified the Licensed Material and
|
|
||||||
retain an indication of any previous modifications; and
|
|
||||||
|
|
||||||
c. indicate the Licensed Material is licensed under this
|
|
||||||
Public License, and include the text of, or the URI or
|
|
||||||
hyperlink to, this Public License.
|
|
||||||
|
|
||||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
|
||||||
reasonable manner based on the medium, means, and context in
|
|
||||||
which You Share the Licensed Material. For example, it may be
|
|
||||||
reasonable to satisfy the conditions by providing a URI or
|
|
||||||
hyperlink to a resource that includes the required
|
|
||||||
information.
|
|
||||||
|
|
||||||
3. If requested by the Licensor, You must remove any of the
|
|
||||||
information required by Section 3(a)(1)(A) to the extent
|
|
||||||
reasonably practicable.
|
|
||||||
|
|
||||||
b. ShareAlike.
|
|
||||||
|
|
||||||
In addition to the conditions in Section 3(a), if You Share
|
|
||||||
Adapted Material You produce, the following conditions also apply.
|
|
||||||
|
|
||||||
1. The Adapter's License You apply must be a Creative Commons
|
|
||||||
license with the same License Elements, this version or
|
|
||||||
later, or a BY-SA Compatible License.
|
|
||||||
|
|
||||||
2. You must include the text of, or the URI or hyperlink to, the
|
|
||||||
Adapter's License You apply. You may satisfy this condition
|
|
||||||
in any reasonable manner based on the medium, means, and
|
|
||||||
context in which You Share Adapted Material.
|
|
||||||
|
|
||||||
3. You may not offer or impose any additional or different terms
|
|
||||||
or conditions on, or apply any Effective Technological
|
|
||||||
Measures to, Adapted Material that restrict exercise of the
|
|
||||||
rights granted under the Adapter's License You apply.
|
|
||||||
|
|
||||||
|
|
||||||
Section 4 -- Sui Generis Database Rights.
|
|
||||||
|
|
||||||
Where the Licensed Rights include Sui Generis Database Rights that
|
|
||||||
apply to Your use of the Licensed Material:
|
|
||||||
|
|
||||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
|
||||||
to extract, reuse, reproduce, and Share all or a substantial
|
|
||||||
portion of the contents of the database;
|
|
||||||
|
|
||||||
b. if You include all or a substantial portion of the database
|
|
||||||
contents in a database in which You have Sui Generis Database
|
|
||||||
Rights, then the database in which You have Sui Generis Database
|
|
||||||
Rights (but not its individual contents) is Adapted Material,
|
|
||||||
|
|
||||||
including for purposes of Section 3(b); and
|
|
||||||
c. You must comply with the conditions in Section 3(a) if You Share
|
|
||||||
all or a substantial portion of the contents of the database.
|
|
||||||
|
|
||||||
For the avoidance of doubt, this Section 4 supplements and does not
|
|
||||||
replace Your obligations under this Public License where the Licensed
|
|
||||||
Rights include other Copyright and Similar Rights.
|
|
||||||
|
|
||||||
|
|
||||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
|
||||||
|
|
||||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
|
||||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
|
||||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
|
||||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
|
||||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
|
||||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
|
||||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
|
||||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
|
||||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
|
||||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
|
||||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
|
||||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
|
||||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
|
||||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
|
||||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
|
||||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
|
||||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
|
||||||
|
|
||||||
c. The disclaimer of warranties and limitation of liability provided
|
|
||||||
above shall be interpreted in a manner that, to the extent
|
|
||||||
possible, most closely approximates an absolute disclaimer and
|
|
||||||
waiver of all liability.
|
|
||||||
|
|
||||||
|
|
||||||
Section 6 -- Term and Termination.
|
|
||||||
|
|
||||||
a. This Public License applies for the term of the Copyright and
|
|
||||||
Similar Rights licensed here. However, if You fail to comply with
|
|
||||||
this Public License, then Your rights under this Public License
|
|
||||||
terminate automatically.
|
|
||||||
|
|
||||||
b. Where Your right to use the Licensed Material has terminated under
|
|
||||||
Section 6(a), it reinstates:
|
|
||||||
|
|
||||||
1. automatically as of the date the violation is cured, provided
|
|
||||||
it is cured within 30 days of Your discovery of the
|
|
||||||
violation; or
|
|
||||||
|
|
||||||
2. upon express reinstatement by the Licensor.
|
|
||||||
|
|
||||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
|
||||||
right the Licensor may have to seek remedies for Your violations
|
|
||||||
of this Public License.
|
|
||||||
|
|
||||||
c. For the avoidance of doubt, the Licensor may also offer the
|
|
||||||
Licensed Material under separate terms or conditions or stop
|
|
||||||
distributing the Licensed Material at any time; however, doing so
|
|
||||||
will not terminate this Public License.
|
|
||||||
|
|
||||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
|
||||||
License.
|
|
||||||
|
|
||||||
|
|
||||||
Section 7 -- Other Terms and Conditions.
|
|
||||||
|
|
||||||
a. The Licensor shall not be bound by any additional or different
|
|
||||||
terms or conditions communicated by You unless expressly agreed.
|
|
||||||
|
|
||||||
b. Any arrangements, understandings, or agreements regarding the
|
|
||||||
Licensed Material not stated herein are separate from and
|
|
||||||
independent of the terms and conditions of this Public License.
|
|
||||||
|
|
||||||
|
|
||||||
Section 8 -- Interpretation.
|
|
||||||
|
|
||||||
a. For the avoidance of doubt, this Public License does not, and
|
|
||||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
|
||||||
conditions on any use of the Licensed Material that could lawfully
|
|
||||||
be made without permission under this Public License.
|
|
||||||
|
|
||||||
b. To the extent possible, if any provision of this Public License is
|
|
||||||
deemed unenforceable, it shall be automatically reformed to the
|
|
||||||
minimum extent necessary to make it enforceable. If the provision
|
|
||||||
cannot be reformed, it shall be severed from this Public License
|
|
||||||
without affecting the enforceability of the remaining terms and
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
c. No term or condition of this Public License will be waived and no
|
|
||||||
failure to comply consented to unless expressly agreed to by the
|
|
||||||
Licensor.
|
|
||||||
|
|
||||||
d. Nothing in this Public License constitutes or may be interpreted
|
|
||||||
as a limitation upon, or waiver of, any privileges and immunities
|
|
||||||
that apply to the Licensor or You, including from the legal
|
|
||||||
processes of any jurisdiction or authority.
|
|
||||||
|
|
||||||
|
|
||||||
=======================================================================
|
|
||||||
|
|
||||||
Creative Commons is not a party to its public
|
|
||||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
|
||||||
its public licenses to material it publishes and in those instances
|
|
||||||
will be considered the “Licensor.” The text of the Creative Commons
|
|
||||||
public licenses is dedicated to the public domain under the CC0 Public
|
|
||||||
Domain Dedication. Except for the limited purpose of indicating that
|
|
||||||
material is shared under a Creative Commons public license or as
|
|
||||||
otherwise permitted by the Creative Commons policies published at
|
|
||||||
creativecommons.org/policies, Creative Commons does not authorize the
|
|
||||||
use of the trademark "Creative Commons" or any other trademark or logo
|
|
||||||
of Creative Commons without its prior written consent including,
|
|
||||||
without limitation, in connection with any unauthorized modifications
|
|
||||||
to any of its public licenses or any other arrangements,
|
|
||||||
understandings, or agreements concerning use of licensed material. For
|
|
||||||
the avoidance of doubt, this paragraph does not form part of the
|
|
||||||
public licenses.
|
|
||||||
|
|
||||||
Creative Commons may be contacted at creativecommons.org.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Trail of Bits Ethereum Contracts Audit, June 2025
|
|
||||||
|
|
||||||
This audit included:
|
|
||||||
- Our Schnorr contract and associated library (/networks/ethereum/schnorr)
|
|
||||||
- Our Ethereum primitives library (/processor/ethereum/primitives)
|
|
||||||
- Our Deployer contract and associated library (/processor/ethereum/deployer)
|
|
||||||
- Our ERC20 library (/processor/ethereum/erc20)
|
|
||||||
- Our Router contract and associated library (/processor/ethereum/router)
|
|
||||||
|
|
||||||
It is encompassing up to commit 4e0c58464fc4673623938335f06e2e9ea96ca8dd.
|
|
||||||
|
|
||||||
Please see
|
|
||||||
https://github.com/trailofbits/publications/blob/30c4fa3ebf39ff8e4d23ba9567344ec9691697b5/reviews/2025-04-serai-dex-security-review.pdf
|
|
||||||
for provenance.
|
|
||||||
@@ -17,12 +17,12 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand_core = "0.9"
|
rand_core = "0.6"
|
||||||
|
|
||||||
subtle = "^2.4"
|
subtle = "^2.4"
|
||||||
|
|
||||||
ff = { version = "0.14.0-pre.0", features = ["bits"] }
|
ff = { version = "0.13", features = ["bits"] }
|
||||||
group = "0.14.0-pre.0"
|
group = "0.13"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["std", "arithmetic", "bits"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["std", "arithmetic", "bits"] }
|
||||||
|
|||||||
@@ -133,6 +133,6 @@ allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
|||||||
allow-git = [
|
allow-git = [
|
||||||
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
"https://github.com/rust-lang-nursery/lazy-static.rs",
|
||||||
"https://github.com/serai-dex/substrate-bip39",
|
"https://github.com/serai-dex/substrate-bip39",
|
||||||
"https://github.com/serai-dex/substrate",
|
"https://github.com/serai-dex/polkadot-sdk",
|
||||||
"https://github.com/kayabaNerve/pasta_curves",
|
"https://github.com/kayabaNerve/pasta_curves",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ tower = "0.5"
|
|||||||
serde_json = { version = "1", default-features = false }
|
serde_json = { version = "1", default-features = false }
|
||||||
simple-request = { path = "../../../common/request", version = "0.1", default-features = false }
|
simple-request = { path = "../../../common/request", version = "0.1", default-features = false }
|
||||||
|
|
||||||
alloy-json-rpc = { version = "0.14", default-features = false }
|
alloy-json-rpc = { version = "0.9", default-features = false }
|
||||||
alloy-transport = { version = "0.14", default-features = false }
|
alloy-transport = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["tls"]
|
default = ["tls"]
|
||||||
|
|||||||
@@ -29,14 +29,14 @@ rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
|||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["ecdsa"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["ecdsa"] }
|
||||||
|
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
alloy-sol-types = { version = "1", default-features = false }
|
alloy-sol-types = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-rpc-client = { version = "0.14", default-features = false }
|
alloy-rpc-client = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
alloy-node-bindings = { version = "0.14", default-features = false }
|
alloy-node-bindings = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
tokio = { version = "1", default-features = false, features = ["macros"] }
|
tokio = { version = "1", default-features = false, features = ["macros"] }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use subtle::Choice;
|
use subtle::Choice;
|
||||||
use group::{ff::PrimeField, Group};
|
use group::ff::PrimeField;
|
||||||
use k256::{
|
use k256::{
|
||||||
elliptic_curve::{
|
elliptic_curve::{
|
||||||
ops::Reduce,
|
ops::Reduce,
|
||||||
@@ -22,10 +22,6 @@ impl PublicKey {
|
|||||||
/// bounds such as parity).
|
/// bounds such as parity).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(A: ProjectivePoint) -> Option<PublicKey> {
|
pub fn new(A: ProjectivePoint) -> Option<PublicKey> {
|
||||||
if bool::from(A.is_identity()) {
|
|
||||||
None?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let affine = A.to_affine();
|
let affine = A.to_affine();
|
||||||
|
|
||||||
// Only allow even keys to save a word within Ethereum
|
// Only allow even keys to save a word within Ethereum
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ mod abi {
|
|||||||
pub(crate) use TestSchnorr::*;
|
pub(crate) use TestSchnorr::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_test() -> (AnvilInstance, Arc<RootProvider>, Address) {
|
async fn setup_test() -> (AnvilInstance, Arc<RootProvider<SimpleRequest>>, Address) {
|
||||||
let anvil = Anvil::new().spawn();
|
let anvil = Anvil::new().spawn();
|
||||||
|
|
||||||
let provider = Arc::new(RootProvider::new(
|
let provider = Arc::new(RootProvider::new(
|
||||||
@@ -61,7 +61,7 @@ async fn setup_test() -> (AnvilInstance, Arc<RootProvider>, Address) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn call_verify(
|
async fn call_verify(
|
||||||
provider: &RootProvider,
|
provider: &RootProvider<SimpleRequest>,
|
||||||
address: Address,
|
address: Address,
|
||||||
public_key: &PublicKey,
|
public_key: &PublicKey,
|
||||||
message: &[u8],
|
message: &[u8],
|
||||||
@@ -80,8 +80,10 @@ async fn call_verify(
|
|||||||
.abi_encode()
|
.abi_encode()
|
||||||
.into(),
|
.into(),
|
||||||
));
|
));
|
||||||
let bytes = provider.call(call).await.unwrap();
|
let bytes = provider.call(&call).await.unwrap();
|
||||||
abi::verifyCall::abi_decode_returns(&bytes).unwrap()
|
let res = abi::verifyCall::abi_decode_returns(&bytes, true).unwrap();
|
||||||
|
|
||||||
|
res._0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use crate::{Signature, tests::test_key};
|
|||||||
fn ecrecover(message: Scalar, odd_y: bool, r: Scalar, s: Scalar) -> Option<[u8; 20]> {
|
fn ecrecover(message: Scalar, odd_y: bool, r: Scalar, s: Scalar) -> Option<[u8; 20]> {
|
||||||
let sig = ecdsa::Signature::from_scalars(r, s).ok()?;
|
let sig = ecdsa::Signature::from_scalars(r, s).ok()?;
|
||||||
let message: [u8; 32] = message.to_repr().into();
|
let message: [u8; 32] = message.to_repr().into();
|
||||||
alloy_core::primitives::Signature::from_signature_and_parity(sig, odd_y)
|
alloy_core::primitives::PrimitiveSignature::from_signature_and_parity(sig, odd_y)
|
||||||
.recover_address_from_prehash(&alloy_core::primitives::B256::from(message))
|
.recover_address_from_prehash(&alloy_core::primitives::B256::from(message))
|
||||||
.ok()
|
.ok()
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
|
|||||||
@@ -27,11 +27,6 @@ pub(crate) fn test_key() -> (Scalar, PublicKey) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_identity_key() {
|
|
||||||
assert!(PublicKey::new(ProjectivePoint::IDENTITY).is_none());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_odd_key() {
|
fn test_odd_key() {
|
||||||
// We generate a valid key to ensure there's not some distinct reason this key is invalid
|
// We generate a valid key to ensure there's not some distinct reason this key is invalid
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ frost = { package = "modular-frost", path = "../../crypto/frost", default-featur
|
|||||||
|
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["std"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
alloy-rlp = { version = "0.3", default-features = false }
|
alloy-rlp = { version = "0.3", default-features = false }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-transport = { version = "0.14", default-features = false }
|
alloy-transport = { version = "0.9", default-features = false }
|
||||||
alloy-simple-request-transport = { path = "../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
alloy-simple-request-transport = { path = "../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
alloy-rpc-client = { version = "0.14", default-features = false }
|
alloy-rpc-client = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
serai-client = { path = "../../substrate/client", default-features = false, features = ["ethereum"] }
|
serai-client = { path = "../../substrate/client", default-features = false, features = ["ethereum"] }
|
||||||
|
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-sol-types = { version = "1", default-features = false }
|
alloy-sol-types = { version = "0.8", default-features = false }
|
||||||
alloy-sol-macro = { version = "1", default-features = false }
|
alloy-sol-macro = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-consensus = { version = "0.14", default-features = false }
|
alloy-consensus = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-transport = { version = "0.14", default-features = false }
|
alloy-transport = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
||||||
|
|
||||||
@@ -34,9 +35,8 @@ ethereum-primitives = { package = "serai-processor-ethereum-primitives", path =
|
|||||||
build-solidity-contracts = { path = "../../../networks/ethereum/build-contracts", default-features = false }
|
build-solidity-contracts = { path = "../../../networks/ethereum/build-contracts", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
alloy-rpc-client = { version = "0.9", default-features = false }
|
||||||
alloy-rpc-client = { version = "0.14", default-features = false }
|
alloy-node-bindings = { version = "0.9", default-features = false }
|
||||||
alloy-node-bindings = { version = "0.14", default-features = false }
|
|
||||||
|
|
||||||
tokio = { version = "1.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use alloy_sol_types::SolCall;
|
|||||||
|
|
||||||
use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
|
use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
|
||||||
use alloy_transport::{TransportErrorKind, RpcError};
|
use alloy_transport::{TransportErrorKind, RpcError};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::{Provider, RootProvider};
|
use alloy_provider::{Provider, RootProvider};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -43,7 +44,7 @@ const INITCODE: &[u8] = {
|
|||||||
/// of the EVM. It then supports retrieving the deployed contracts addresses (which aren't
|
/// of the EVM. It then supports retrieving the deployed contracts addresses (which aren't
|
||||||
/// deterministic) using a single call.
|
/// deterministic) using a single call.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Deployer(Arc<RootProvider>);
|
pub struct Deployer(Arc<RootProvider<SimpleRequest>>);
|
||||||
impl Deployer {
|
impl Deployer {
|
||||||
/// Obtain the transaction to deploy this contract, already signed.
|
/// Obtain the transaction to deploy this contract, already signed.
|
||||||
///
|
///
|
||||||
@@ -118,7 +119,7 @@ impl Deployer {
|
|||||||
///
|
///
|
||||||
/// This will return `None` if the Deployer has yet to be deployed on-chain.
|
/// This will return `None` if the Deployer has yet to be deployed on-chain.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
provider: Arc<RootProvider>,
|
provider: Arc<RootProvider<SimpleRequest>>,
|
||||||
) -> Result<Option<Self>, RpcError<TransportErrorKind>> {
|
) -> Result<Option<Self>, RpcError<TransportErrorKind>> {
|
||||||
let address = Self::address();
|
let address = Self::address();
|
||||||
let code = provider.get_code_at(address).await?;
|
let code = provider.get_code_at(address).await?;
|
||||||
@@ -137,14 +138,16 @@ impl Deployer {
|
|||||||
let call = TransactionRequest::default().to(Self::address()).input(TransactionInput::new(
|
let call = TransactionRequest::default().to(Self::address()).input(TransactionInput::new(
|
||||||
abi::Deployer::deploymentsCall::new((init_code_hash.into(),)).abi_encode().into(),
|
abi::Deployer::deploymentsCall::new((init_code_hash.into(),)).abi_encode().into(),
|
||||||
));
|
));
|
||||||
let bytes = self.0.call(call).await?;
|
let bytes = self.0.call(&call).await?;
|
||||||
let deployment = abi::Deployer::deploymentsCall::abi_decode_returns(&bytes).map_err(|e| {
|
let deployment = abi::Deployer::deploymentsCall::abi_decode_returns(&bytes, true)
|
||||||
TransportErrorKind::Custom(
|
.map_err(|e| {
|
||||||
format!("node returned a non-address for function returning address: {e:?}").into(),
|
TransportErrorKind::Custom(
|
||||||
)
|
format!("node returned a non-address for function returning address: {e:?}").into(),
|
||||||
})?;
|
)
|
||||||
|
})?
|
||||||
|
._0;
|
||||||
|
|
||||||
if deployment == Address::ZERO {
|
if **deployment == [0; 20] {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
Ok(Some(deployment))
|
Ok(Some(deployment))
|
||||||
|
|||||||
@@ -76,9 +76,9 @@ async fn test_deployer() {
|
|||||||
let call = TransactionRequest::default()
|
let call = TransactionRequest::default()
|
||||||
.to(Deployer::address())
|
.to(Deployer::address())
|
||||||
.input(TransactionInput::new(deploy_tx.tx().input.clone()));
|
.input(TransactionInput::new(deploy_tx.tx().input.clone()));
|
||||||
let call_err = provider.call(call).await.unwrap_err();
|
let call_err = provider.call(&call).await.unwrap_err();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
call_err.as_error_resp().unwrap().as_decoded_interface_error::<DeployerErrors>().unwrap(),
|
call_err.as_error_resp().unwrap().as_decoded_error::<DeployerErrors>(true).unwrap(),
|
||||||
DeployerErrors::PriorDeployed(PriorDeployed {}),
|
DeployerErrors::PriorDeployed(PriorDeployed {}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -97,9 +97,9 @@ async fn test_deployer() {
|
|||||||
let call = TransactionRequest::default()
|
let call = TransactionRequest::default()
|
||||||
.to(Deployer::address())
|
.to(Deployer::address())
|
||||||
.input(TransactionInput::new(deploy_tx.tx().input.clone()));
|
.input(TransactionInput::new(deploy_tx.tx().input.clone()));
|
||||||
let call_err = provider.call(call).await.unwrap_err();
|
let call_err = provider.call(&call).await.unwrap_err();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
call_err.as_error_resp().unwrap().as_decoded_interface_error::<DeployerErrors>().unwrap(),
|
call_err.as_error_resp().unwrap().as_decoded_error::<DeployerErrors>(true).unwrap(),
|
||||||
DeployerErrors::DeploymentFailed(DeploymentFailed {}),
|
DeployerErrors::DeploymentFailed(DeploymentFailed {}),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,15 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-sol-types = { version = "1", default-features = false }
|
alloy-sol-types = { version = "0.8", default-features = false }
|
||||||
alloy-sol-macro = { version = "1", default-features = false }
|
alloy-sol-macro = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-transport = { version = "0.14", default-features = false }
|
alloy-transport = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use alloy_sol_types::{SolInterface, SolEvent};
|
|||||||
|
|
||||||
use alloy_rpc_types_eth::{Log, Filter, TransactionTrait};
|
use alloy_rpc_types_eth::{Log, Filter, TransactionTrait};
|
||||||
use alloy_transport::{TransportErrorKind, RpcError};
|
use alloy_transport::{TransportErrorKind, RpcError};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::{Provider, RootProvider};
|
use alloy_provider::{Provider, RootProvider};
|
||||||
|
|
||||||
use ethereum_primitives::LogIndex;
|
use ethereum_primitives::LogIndex;
|
||||||
@@ -93,7 +94,7 @@ impl Erc20 {
|
|||||||
// Yielding THE top-level transfer would require tracing the transaction execution and isn't
|
// Yielding THE top-level transfer would require tracing the transaction execution and isn't
|
||||||
// worth the effort.
|
// worth the effort.
|
||||||
async fn top_level_transfer(
|
async fn top_level_transfer(
|
||||||
provider: &RootProvider,
|
provider: &RootProvider<SimpleRequest>,
|
||||||
erc20: Address,
|
erc20: Address,
|
||||||
transaction_hash: [u8; 32],
|
transaction_hash: [u8; 32],
|
||||||
transfer_logs: &[Log],
|
transfer_logs: &[Log],
|
||||||
@@ -111,13 +112,15 @@ impl Erc20 {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let Ok(call) = IERC20Calls::abi_decode(transaction.inner.input()) else {
|
// Don't validate the encoding as this can't be re-encoded to an identical bytestring due
|
||||||
|
// to the additional data appended after the call itself
|
||||||
|
let Ok(call) = IERC20Calls::abi_decode(transaction.inner.input(), false) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extract the top-level call's from/to/value
|
// Extract the top-level call's from/to/value
|
||||||
let (from, to, value) = match call {
|
let (from, to, value) = match call {
|
||||||
IERC20Calls::transfer(transferCall { to, value }) => (transaction.inner.signer(), to, value),
|
IERC20Calls::transfer(transferCall { to, value }) => (transaction.from, to, value),
|
||||||
IERC20Calls::transferFrom(transferFromCall { from, to, value }) => (from, to, value),
|
IERC20Calls::transferFrom(transferFromCall { from, to, value }) => (from, to, value),
|
||||||
// Treat any other function selectors as unrecognized
|
// Treat any other function selectors as unrecognized
|
||||||
_ => return Ok(None),
|
_ => return Ok(None),
|
||||||
@@ -146,7 +149,7 @@ impl Erc20 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read the data appended after
|
// Read the data appended after
|
||||||
let data = if let Ok(call) = SeraiIERC20Calls::abi_decode(transaction.inner.input()) {
|
let data = if let Ok(call) = SeraiIERC20Calls::abi_decode(transaction.inner.input(), true) {
|
||||||
match call {
|
match call {
|
||||||
SeraiIERC20Calls::transferWithInInstruction01BB244A8A(
|
SeraiIERC20Calls::transferWithInInstruction01BB244A8A(
|
||||||
transferWithInInstructionCall { inInstruction, .. },
|
transferWithInInstructionCall { inInstruction, .. },
|
||||||
@@ -177,7 +180,7 @@ impl Erc20 {
|
|||||||
///
|
///
|
||||||
/// The `transfers` in the result are unordered. The `logs` are sorted by index.
|
/// The `transfers` in the result are unordered. The `logs` are sorted by index.
|
||||||
pub async fn top_level_transfers_unordered(
|
pub async fn top_level_transfers_unordered(
|
||||||
provider: &RootProvider,
|
provider: &RootProvider<SimpleRequest>,
|
||||||
blocks: RangeInclusive<u64>,
|
blocks: RangeInclusive<u64>,
|
||||||
erc20: Address,
|
erc20: Address,
|
||||||
to: Address,
|
to: Address,
|
||||||
|
|||||||
@@ -12,30 +12,4 @@ fn selector_collisions() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn abi_decode_panic() {
|
|
||||||
use alloy_sol_types::SolInterface;
|
|
||||||
|
|
||||||
/*
|
|
||||||
The following code panics with alloy-core 0.8, when the validate flag (commented out) is set to
|
|
||||||
`false`. This flag was removed with alloy-core 1.0, leaving the default behavior of
|
|
||||||
`abi_decode` to be `validate = false`. This test was added to ensure when we removed our
|
|
||||||
practice of `validate = true`, we didn't open ourselves up this as a DoS risk.
|
|
||||||
*/
|
|
||||||
assert!(crate::SeraiIERC20Calls::abi_decode(
|
|
||||||
&alloy_core::primitives::hex::decode(concat!(
|
|
||||||
"a9059cbb",
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
||||||
"000000000000000000000000000000000000000000000000000000000000006f",
|
|
||||||
"ffffffffff000000000000000000000000000000000000000000000000000023",
|
|
||||||
"000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
|
||||||
"ffffff0000000000000000000000000000000000000000000000000000000000",
|
|
||||||
))
|
|
||||||
.unwrap(),
|
|
||||||
// false
|
|
||||||
)
|
|
||||||
.is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is primarily tested via serai-processor-ethereum-router
|
// This is primarily tested via serai-processor-ethereum-router
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
k256 = { version = "^0.13.1", default-features = false, features = ["std", "arithmetic"] }
|
k256 = { version = "^0.13.1", default-features = false, features = ["std", "arithmetic"] }
|
||||||
|
|
||||||
alloy-primitives = { version = "1", default-features = false }
|
alloy-primitives = { version = "0.8", default-features = false }
|
||||||
alloy-consensus = { version = "0.14", default-features = false, features = ["k256"] }
|
alloy-consensus = { version = "0.9", default-features = false, features = ["k256"] }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use ::borsh::{BorshSerialize, BorshDeserialize};
|
|||||||
use group::ff::PrimeField;
|
use group::ff::PrimeField;
|
||||||
use k256::Scalar;
|
use k256::Scalar;
|
||||||
|
|
||||||
use alloy_primitives::Signature;
|
use alloy_primitives::PrimitiveSignature;
|
||||||
use alloy_consensus::{SignableTransaction, Signed, TxLegacy};
|
use alloy_consensus::{SignableTransaction, Signed, TxLegacy};
|
||||||
|
|
||||||
mod borsh;
|
mod borsh;
|
||||||
@@ -68,7 +68,8 @@ pub fn deterministically_sign(tx: TxLegacy) -> Signed<TxLegacy> {
|
|||||||
let s = Scalar::ONE;
|
let s = Scalar::ONE;
|
||||||
let r_bytes: [u8; 32] = r.to_repr().into();
|
let r_bytes: [u8; 32] = r.to_repr().into();
|
||||||
let s_bytes: [u8; 32] = s.to_repr().into();
|
let s_bytes: [u8; 32] = s.to_repr().into();
|
||||||
let signature = Signature::from_scalars_and_parity(r_bytes.into(), s_bytes.into(), false);
|
let signature =
|
||||||
|
PrimitiveSignature::from_scalars_and_parity(r_bytes.into(), s_bytes.into(), false);
|
||||||
|
|
||||||
let res = tx.into_signed(signature);
|
let res = tx.into_signed(signature);
|
||||||
debug_assert!(res.recover_signer().is_ok());
|
debug_assert!(res.recover_signer().is_ok());
|
||||||
|
|||||||
@@ -22,18 +22,19 @@ borsh = { version = "1", default-features = false, features = ["std", "derive",
|
|||||||
group = { version = "0.13", default-features = false }
|
group = { version = "0.13", default-features = false }
|
||||||
k256 = { version = "0.13", default-features = false, features = ["std", "arithmetic"] }
|
k256 = { version = "0.13", default-features = false, features = ["std", "arithmetic"] }
|
||||||
|
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-sol-types = { version = "1", default-features = false }
|
alloy-sol-types = { version = "0.8", default-features = false }
|
||||||
alloy-sol-macro = { version = "1", default-features = false }
|
alloy-sol-macro = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
alloy-consensus = { version = "0.14", default-features = false }
|
alloy-consensus = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-transport = { version = "0.14", default-features = false }
|
alloy-transport = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
revm = { version = "22", default-features = false, features = ["std"] }
|
revm = { version = "19", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
ethereum-schnorr = { package = "ethereum-schnorr-contract", path = "../../../networks/ethereum/schnorr", default-features = false }
|
ethereum-schnorr = { package = "ethereum-schnorr-contract", path = "../../../networks/ethereum/schnorr", default-features = false }
|
||||||
|
|
||||||
@@ -51,19 +52,18 @@ build-solidity-contracts = { path = "../../../networks/ethereum/build-contracts"
|
|||||||
|
|
||||||
syn = { version = "2", default-features = false, features = ["proc-macro"] }
|
syn = { version = "2", default-features = false, features = ["proc-macro"] }
|
||||||
|
|
||||||
syn-solidity = { version = "1", default-features = false }
|
syn-solidity = { version = "0.8", default-features = false }
|
||||||
alloy-sol-macro-input = { version = "1", default-features = false }
|
alloy-sol-macro-input = { version = "0.8", default-features = false }
|
||||||
alloy-sol-macro-expander = { version = "1", default-features = false }
|
alloy-sol-macro-expander = { version = "0.8", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
rand_core = { version = "0.6", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
k256 = { version = "0.13", default-features = false, features = ["std"] }
|
k256 = { version = "0.13", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
alloy-provider = { version = "0.9", default-features = false, features = ["debug-api", "trace-api"] }
|
||||||
alloy-provider = { version = "0.14", default-features = false, features = ["debug-api", "trace-api"] }
|
alloy-rpc-client = { version = "0.9", default-features = false }
|
||||||
alloy-rpc-client = { version = "0.14", default-features = false }
|
alloy-node-bindings = { version = "0.9", default-features = false }
|
||||||
alloy-node-bindings = { version = "0.14", default-features = false }
|
|
||||||
|
|
||||||
tokio = { version = "1.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1.0", default-features = false, features = ["rt-multi-thread", "macros"] }
|
||||||
|
|
||||||
|
|||||||
@@ -148,9 +148,8 @@ contract Router is IRouterWithoutCollisions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @dev Verify a signature of the calldata, placed immediately after the function selector. The
|
* @dev Verify a signature of the calldata, placed immediately after the function selector. The
|
||||||
* calldata should be signed with the chain ID taking the place of the signature's challenge, and
|
* calldata should be signed with the nonce taking the place of the signature's commitment to
|
||||||
* the signature's response replaced by the contract's address shifted into the high bits with
|
* its nonce, and the signature solution zeroed.
|
||||||
* the contract's nonce as the low bits.
|
|
||||||
*/
|
*/
|
||||||
/// @param key The key to verify the signature with
|
/// @param key The key to verify the signature with
|
||||||
function verifySignature(bytes32 key)
|
function verifySignature(bytes32 key)
|
||||||
@@ -186,10 +185,6 @@ contract Router is IRouterWithoutCollisions {
|
|||||||
// Read _nextNonce into memory as the nonce we'll use
|
// Read _nextNonce into memory as the nonce we'll use
|
||||||
nonceUsed = _nextNonce;
|
nonceUsed = _nextNonce;
|
||||||
|
|
||||||
// We overwrite the signature response with the Router contract's address concatenated with the
|
|
||||||
// nonce. This is safe until the nonce exceeds 2**96, which is infeasible to do on-chain
|
|
||||||
uint256 signatureResponseOverwrite = (uint256(uint160(address(this))) << 96) | nonceUsed;
|
|
||||||
|
|
||||||
// Declare memory to copy the signature out to
|
// Declare memory to copy the signature out to
|
||||||
bytes32 signatureC;
|
bytes32 signatureC;
|
||||||
bytes32 signatureS;
|
bytes32 signatureS;
|
||||||
@@ -203,8 +198,8 @@ contract Router is IRouterWithoutCollisions {
|
|||||||
|
|
||||||
// Overwrite the signature challenge with the chain ID
|
// Overwrite the signature challenge with the chain ID
|
||||||
mstore(add(message, 36), chainID)
|
mstore(add(message, 36), chainID)
|
||||||
// Overwrite the signature response with the contract's address, nonce
|
// Overwrite the signature response with the nonce
|
||||||
mstore(add(message, 68), signatureResponseOverwrite)
|
mstore(add(message, 68), nonceUsed)
|
||||||
|
|
||||||
// Calculate the message hash
|
// Calculate the message hash
|
||||||
messageHash := keccak256(add(message, 32), messageLen)
|
messageHash := keccak256(add(message, 32), messageLen)
|
||||||
|
|||||||
@@ -1,130 +1,26 @@
|
|||||||
use core::convert::Infallible;
|
|
||||||
|
|
||||||
use k256::{Scalar, ProjectivePoint};
|
use k256::{Scalar, ProjectivePoint};
|
||||||
|
|
||||||
use alloy_core::primitives::{Address, U256, Bytes};
|
use alloy_core::primitives::{Address, U160, U256};
|
||||||
use alloy_sol_types::SolCall;
|
use alloy_sol_types::SolCall;
|
||||||
|
|
||||||
use revm::{
|
use revm::{
|
||||||
primitives::hardfork::SpecId,
|
primitives::*,
|
||||||
bytecode::Bytecode,
|
interpreter::{gas::*, opcode::InstructionTables, *},
|
||||||
state::AccountInfo,
|
db::{emptydb::EmptyDB, in_memory_db::InMemoryDB},
|
||||||
database::{empty_db::EmptyDB, in_memory_db::InMemoryDB},
|
Handler, Context, EvmBuilder, Evm,
|
||||||
interpreter::{
|
|
||||||
gas::calculate_initial_tx_gas,
|
|
||||||
interpreter_action::{CallInputs, CallOutcome},
|
|
||||||
interpreter::EthInterpreter,
|
|
||||||
Interpreter,
|
|
||||||
},
|
|
||||||
handler::{
|
|
||||||
instructions::EthInstructions, PrecompileProvider, EthPrecompiles, EthFrame, MainnetHandler,
|
|
||||||
},
|
|
||||||
context::{
|
|
||||||
result::{EVMError, InvalidTransaction, ExecutionResult},
|
|
||||||
evm::{EvmData, Evm},
|
|
||||||
context::Context,
|
|
||||||
*,
|
|
||||||
},
|
|
||||||
inspector::{Inspector, InspectorHandler},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use ethereum_schnorr::{PublicKey, Signature};
|
use ethereum_schnorr::{PublicKey, Signature};
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
// The specification this uses
|
|
||||||
const SPEC_ID: SpecId = SpecId::CANCUN;
|
|
||||||
|
|
||||||
// The chain ID used for gas estimation
|
// The chain ID used for gas estimation
|
||||||
const CHAIN_ID: U256 = U256::from_be_slice(&[1]);
|
const CHAIN_ID: U256 = U256::from_be_slice(&[1]);
|
||||||
|
|
||||||
type RevmContext = Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Journal<InMemoryDB>, ()>;
|
|
||||||
|
|
||||||
fn precompiles() -> EthPrecompiles {
|
|
||||||
let mut precompiles = EthPrecompiles::default();
|
|
||||||
PrecompileProvider::<RevmContext>::set_spec(&mut precompiles, SPEC_ID);
|
|
||||||
precompiles
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Instead of attempting to solve the halting problem, we assume all CALLs take the worst-case
|
|
||||||
amount of gas (as we do have bounds on the gas they're allowed to take). This assumption is
|
|
||||||
implemented via an revm Inspector.
|
|
||||||
|
|
||||||
The Inspector is allowed to override the CALL directly. We don't do this due to the amount of
|
|
||||||
side effects a CALL has. Instead, we override the result.
|
|
||||||
|
|
||||||
In the case the ERC20 is called, we additionally have it return `true` (as expected for compliant
|
|
||||||
ERC20s, and as will trigger the worst-case gas consumption by the Router itself). This is done by
|
|
||||||
hooking `call_end`.
|
|
||||||
*/
|
|
||||||
pub(crate) struct WorstCaseCallInspector {
|
|
||||||
erc20: Option<Address>,
|
|
||||||
call_depth: usize,
|
|
||||||
unused_gas: u64,
|
|
||||||
override_immediate_call_return_value: bool,
|
|
||||||
}
|
|
||||||
impl Inspector<RevmContext> for WorstCaseCallInspector {
|
|
||||||
fn call(&mut self, _context: &mut RevmContext, _inputs: &mut CallInputs) -> Option<CallOutcome> {
|
|
||||||
self.call_depth += 1;
|
|
||||||
// Don't override the CALL immediately for prior described reasons
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_end(
|
|
||||||
&mut self,
|
|
||||||
_context: &mut RevmContext,
|
|
||||||
inputs: &CallInputs,
|
|
||||||
outcome: &mut CallOutcome,
|
|
||||||
) {
|
|
||||||
self.call_depth -= 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Mark the amount of gas left unused, for us to later assume will be used in practice.
|
|
||||||
|
|
||||||
This only runs if the call-depth is 1 (so only the Router-made calls have their gas so
|
|
||||||
tracked), and if it's not to a precompile. This latter condition isn't solely because we can
|
|
||||||
perfectly model precompiles (which wouldn't be worth the complexity) yet because the Router
|
|
||||||
does call precompiles (ecrecover) and accordingly has to model the gas of that correctly.
|
|
||||||
*/
|
|
||||||
if (self.call_depth == 1) && (!precompiles().contains(&inputs.target_address)) {
|
|
||||||
let unused_gas = inputs.gas_limit - outcome.result.gas.spent();
|
|
||||||
self.unused_gas += unused_gas;
|
|
||||||
|
|
||||||
// Now that the CALL is over, flag we should normalize the values on the stack
|
|
||||||
self.override_immediate_call_return_value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If ERC20, provide the expected ERC20 return data
|
|
||||||
if Some(inputs.target_address) == self.erc20 {
|
|
||||||
outcome.result.output = true.abi_encode().into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step(&mut self, interpreter: &mut Interpreter, _context: &mut RevmContext) {
|
|
||||||
if self.override_immediate_call_return_value {
|
|
||||||
// We fix this result to having succeeded, which triggers the most-expensive pathing within
|
|
||||||
// the Router contract itself (some paths return early if a CALL fails)
|
|
||||||
let return_value = interpreter.stack.pop().unwrap();
|
|
||||||
assert!((return_value == U256::ZERO) || (return_value == U256::ONE));
|
|
||||||
assert!(
|
|
||||||
interpreter.stack.push(U256::ONE),
|
|
||||||
"stack capacity couldn't fit item after popping an item"
|
|
||||||
);
|
|
||||||
self.override_immediate_call_return_value = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The object used for estimating gas.
|
/// The object used for estimating gas.
|
||||||
///
|
///
|
||||||
/// Due to `execute` heavily branching, we locally simulate calls with revm.
|
/// Due to `execute` heavily branching, we locally simulate calls with revm.
|
||||||
pub(crate) type GasEstimator = Evm<
|
pub(crate) type GasEstimator = Evm<'static, (), InMemoryDB>;
|
||||||
RevmContext,
|
|
||||||
WorstCaseCallInspector,
|
|
||||||
EthInstructions<EthInterpreter, RevmContext>,
|
|
||||||
EthPrecompiles,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl Router {
|
impl Router {
|
||||||
const SMART_CONTRACT_NONCE_STORAGE_SLOT: U256 = U256::from_be_slice(&[0]);
|
const SMART_CONTRACT_NONCE_STORAGE_SLOT: U256 = U256::from_be_slice(&[0]);
|
||||||
@@ -151,11 +47,11 @@ impl Router {
|
|||||||
the correct set of prices for the network they're operating on.
|
the correct set of prices for the network they're operating on.
|
||||||
*/
|
*/
|
||||||
/// The gas used by `confirmSeraiKey`.
|
/// The gas used by `confirmSeraiKey`.
|
||||||
pub const CONFIRM_NEXT_SERAI_KEY_GAS: u64 = 57_753;
|
pub const CONFIRM_NEXT_SERAI_KEY_GAS: u64 = 57_736;
|
||||||
/// The gas used by `updateSeraiKey`.
|
/// The gas used by `updateSeraiKey`.
|
||||||
pub const UPDATE_SERAI_KEY_GAS: u64 = 60_062;
|
pub const UPDATE_SERAI_KEY_GAS: u64 = 60_045;
|
||||||
/// The gas used by `escapeHatch`.
|
/// The gas used by `escapeHatch`.
|
||||||
pub const ESCAPE_HATCH_GAS: u64 = 61_111;
|
pub const ESCAPE_HATCH_GAS: u64 = 61_094;
|
||||||
|
|
||||||
/// The key to use when performing gas estimations.
|
/// The key to use when performing gas estimations.
|
||||||
///
|
///
|
||||||
@@ -218,35 +114,120 @@ impl Router {
|
|||||||
db
|
db
|
||||||
};
|
};
|
||||||
|
|
||||||
Evm {
|
// Create a custom handler so we can assume every CALL is the worst-case
|
||||||
data: EvmData {
|
let handler = {
|
||||||
ctx: RevmContext::new(db, SPEC_ID)
|
let mut instructions = InstructionTables::<'_, _>::new_plain::<CancunSpec>();
|
||||||
.modify_cfg_chained(|cfg| {
|
instructions.update_boxed(revm::interpreter::opcode::CALL, {
|
||||||
cfg.chain_id = CHAIN_ID.try_into().unwrap();
|
move |call_op, interpreter, host: &mut Context<_, _>| {
|
||||||
})
|
let (address_called, value, return_addr, return_len) = {
|
||||||
.modify_tx_chained(|tx: &mut TxEnv| {
|
let stack = &mut interpreter.stack;
|
||||||
tx.gas_limit = u64::MAX;
|
|
||||||
tx.kind = self.address.into();
|
let address = stack.peek(1).unwrap();
|
||||||
}),
|
let value = stack.peek(2).unwrap();
|
||||||
inspector: WorstCaseCallInspector {
|
let return_addr = stack.peek(5).unwrap();
|
||||||
erc20,
|
let return_len = stack.peek(6).unwrap();
|
||||||
call_depth: 0,
|
|
||||||
unused_gas: 0,
|
(
|
||||||
override_immediate_call_return_value: false,
|
address,
|
||||||
},
|
value,
|
||||||
},
|
usize::try_from(return_addr).unwrap(),
|
||||||
instruction: EthInstructions::default(),
|
usize::try_from(return_len).unwrap(),
|
||||||
precompiles: precompiles(),
|
)
|
||||||
}
|
};
|
||||||
|
let address_called =
|
||||||
|
Address::from(U160::from_be_slice(&address_called.to_be_bytes::<32>()[12 ..]));
|
||||||
|
|
||||||
|
// Have the original call op incur its costs as programmed
|
||||||
|
call_op(interpreter, host);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Unfortunately, the call opcode executed only sets itself up, it doesn't handle the
|
||||||
|
entire inner call for us. We manually do so here by shimming the intended result. The
|
||||||
|
other option, on this path chosen, would be to shim the call-frame execution ourselves
|
||||||
|
and only then manipulate the result.
|
||||||
|
|
||||||
|
Ideally, we wouldn't override CALL, yet STOP/RETURN (the tail of the CALL) to avoid all
|
||||||
|
of this. Those overrides weren't being successfully hit in initial experiments, and
|
||||||
|
while this solution does appear overly complicated, it's sufficiently tested to justify
|
||||||
|
itself.
|
||||||
|
|
||||||
|
revm does cost the entire gas limit during the call setup. After the call completes,
|
||||||
|
it refunds whatever was unused. Since we manually complete the call here ourselves,
|
||||||
|
but don't implement that refund logic as we want the worst-case scenario, we do
|
||||||
|
successfully implement complete costing of the gas limit.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Perform the call value transfer, which also marks the recipient as warm
|
||||||
|
assert!(host
|
||||||
|
.evm
|
||||||
|
.inner
|
||||||
|
.journaled_state
|
||||||
|
.transfer(
|
||||||
|
&interpreter.contract.target_address,
|
||||||
|
&address_called,
|
||||||
|
value,
|
||||||
|
&mut host.evm.inner.db
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.is_none());
|
||||||
|
|
||||||
|
// Clear the call-to-be
|
||||||
|
debug_assert!(matches!(interpreter.next_action, InterpreterAction::Call { .. }));
|
||||||
|
interpreter.next_action = InterpreterAction::None;
|
||||||
|
interpreter.instruction_result = InstructionResult::Continue;
|
||||||
|
|
||||||
|
// Clear the existing return data
|
||||||
|
interpreter.return_data_buffer.clear();
|
||||||
|
|
||||||
|
/*
|
||||||
|
If calling an ERC20, trigger the return data's worst-case by returning `true`
|
||||||
|
(as expected by compliant ERC20s). Else return none, as we expect none or won't bother
|
||||||
|
copying/decoding the return data.
|
||||||
|
|
||||||
|
This doesn't affect calls to ecrecover as those use STATICCALL and this overrides CALL
|
||||||
|
alone.
|
||||||
|
*/
|
||||||
|
if Some(address_called) == erc20 {
|
||||||
|
interpreter.return_data_buffer = true.abi_encode().into();
|
||||||
|
}
|
||||||
|
// Also copy the return data into memory
|
||||||
|
let return_len = return_len.min(interpreter.return_data_buffer.len());
|
||||||
|
let needed_memory_size = return_addr + return_len;
|
||||||
|
if interpreter.shared_memory.len() < needed_memory_size {
|
||||||
|
assert!(interpreter.resize_memory(needed_memory_size));
|
||||||
|
}
|
||||||
|
interpreter
|
||||||
|
.shared_memory
|
||||||
|
.slice_mut(return_addr, return_len)
|
||||||
|
.copy_from_slice(&interpreter.return_data_buffer[.. return_len]);
|
||||||
|
|
||||||
|
// Finally, push the result of the call onto the stack
|
||||||
|
interpreter.stack.push(U256::from(1)).unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let mut handler = Handler::mainnet::<CancunSpec>();
|
||||||
|
handler.set_instruction_table(instructions);
|
||||||
|
|
||||||
|
handler
|
||||||
|
};
|
||||||
|
|
||||||
|
EvmBuilder::default()
|
||||||
|
.with_db(db)
|
||||||
|
.with_handler(handler)
|
||||||
|
.modify_cfg_env(|cfg| {
|
||||||
|
cfg.chain_id = CHAIN_ID.try_into().unwrap();
|
||||||
|
})
|
||||||
|
.modify_tx_env(|tx| {
|
||||||
|
tx.gas_limit = u64::MAX;
|
||||||
|
tx.transact_to = self.address.into();
|
||||||
|
})
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The worst-case gas cost for a legacy transaction which executes this batch.
|
/// The worst-case gas cost for a legacy transaction which executes this batch.
|
||||||
pub fn execute_gas_and_fee(
|
///
|
||||||
&self,
|
/// This assumes the fee will be non-zero.
|
||||||
coin: Coin,
|
pub fn execute_gas(&self, coin: Coin, fee_per_gas: U256, outs: &OutInstructions) -> u64 {
|
||||||
fee_per_gas: U256,
|
|
||||||
outs: &OutInstructions,
|
|
||||||
) -> (u64, U256) {
|
|
||||||
// Unfortunately, we can't cache this in self, despite the following code being written such
|
// Unfortunately, we can't cache this in self, despite the following code being written such
|
||||||
// that a common EVM instance could be used, as revm's types aren't Send/Sync and we expect the
|
// that a common EVM instance could be used, as revm's types aren't Send/Sync and we expect the
|
||||||
// Router to be send/sync
|
// Router to be send/sync
|
||||||
@@ -255,17 +236,17 @@ impl Router {
|
|||||||
Coin::Erc20(erc20) => Some(erc20),
|
Coin::Erc20(erc20) => Some(erc20),
|
||||||
});
|
});
|
||||||
|
|
||||||
let shimmed_fee = match coin {
|
let fee = match coin {
|
||||||
Coin::Ether => {
|
Coin::Ether => {
|
||||||
// Use a fee of 1 so the fee payment is recognized as positive-value, if the fee is
|
// Use a fee of 1 so the fee payment is recognized as positive-value
|
||||||
// non-zero
|
let fee = U256::from(1);
|
||||||
let fee = if fee_per_gas == U256::ZERO { U256::ZERO } else { U256::ONE };
|
|
||||||
|
|
||||||
// Set a balance of the amount sent out to ensure we don't error on that premise
|
// Set a balance of the amount sent out to ensure we don't error on that premise
|
||||||
gas_estimator.data.ctx.modify_db(|db| {
|
{
|
||||||
|
let db = gas_estimator.db_mut();
|
||||||
let account = db.load_account(self.address).unwrap();
|
let account = db.load_account(self.address).unwrap();
|
||||||
account.info.balance = fee + outs.0.iter().map(|out| out.amount).sum::<U256>();
|
account.info.balance = fee + outs.0.iter().map(|out| out.amount).sum::<U256>();
|
||||||
});
|
}
|
||||||
|
|
||||||
fee
|
fee
|
||||||
}
|
}
|
||||||
@@ -278,7 +259,7 @@ impl Router {
|
|||||||
// Use a nonce of 1
|
// Use a nonce of 1
|
||||||
ProjectivePoint::GENERATOR,
|
ProjectivePoint::GENERATOR,
|
||||||
&public_key,
|
&public_key,
|
||||||
&Self::execute_message(CHAIN_ID, self.address, 1, coin, shimmed_fee, outs.clone()),
|
&Self::execute_message(CHAIN_ID, 1, coin, fee, outs.clone()),
|
||||||
);
|
);
|
||||||
let s = Scalar::ONE + (c * private_key);
|
let s = Scalar::ONE + (c * private_key);
|
||||||
let sig = Signature::new(c, s).unwrap();
|
let sig = Signature::new(c, s).unwrap();
|
||||||
@@ -290,7 +271,8 @@ impl Router {
|
|||||||
consistent use of nonce #1 shows storage read/writes aren't being persisted. They're solely
|
consistent use of nonce #1 shows storage read/writes aren't being persisted. They're solely
|
||||||
returned upon execution in a `state` field we ignore.
|
returned upon execution in a `state` field we ignore.
|
||||||
*/
|
*/
|
||||||
gas_estimator.data.ctx.modify_tx(|tx| {
|
{
|
||||||
|
let tx = gas_estimator.tx_mut();
|
||||||
tx.caller = Address::from({
|
tx.caller = Address::from({
|
||||||
/*
|
/*
|
||||||
We assume the transaction sender is not the destination of any `OutInstruction`, making
|
We assume the transaction sender is not the destination of any `OutInstruction`, making
|
||||||
@@ -309,82 +291,55 @@ impl Router {
|
|||||||
tx.data = abi::executeCall::new((
|
tx.data = abi::executeCall::new((
|
||||||
abi::Signature::from(&sig),
|
abi::Signature::from(&sig),
|
||||||
Address::from(coin),
|
Address::from(coin),
|
||||||
shimmed_fee,
|
fee,
|
||||||
outs.0.clone(),
|
outs.0.clone(),
|
||||||
))
|
))
|
||||||
.abi_encode()
|
.abi_encode()
|
||||||
.into();
|
.into();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Execute the transaction
|
// Execute the transaction
|
||||||
let mut gas = match MainnetHandler::<
|
let mut gas = match gas_estimator.transact().unwrap().result {
|
||||||
_,
|
|
||||||
EVMError<Infallible, InvalidTransaction>,
|
|
||||||
EthFrame<_, _, _>,
|
|
||||||
>::default()
|
|
||||||
.inspect_run(&mut gas_estimator)
|
|
||||||
.unwrap()
|
|
||||||
.result
|
|
||||||
{
|
|
||||||
ExecutionResult::Success { gas_used, gas_refunded, .. } => {
|
ExecutionResult::Success { gas_used, gas_refunded, .. } => {
|
||||||
assert_eq!(gas_refunded, 0);
|
assert_eq!(gas_refunded, 0);
|
||||||
gas_used
|
gas_used
|
||||||
}
|
}
|
||||||
res => panic!("estimated execute transaction failed: {res:?}"),
|
res => panic!("estimated execute transaction failed: {res:?}"),
|
||||||
};
|
};
|
||||||
gas += gas_estimator.into_inspector().unused_gas;
|
|
||||||
|
|
||||||
/*
|
// The transaction uses gas based on the amount of non-zero bytes in the calldata, which is
|
||||||
The transaction pays an initial gas fee which is dependent on the length of the calldata and
|
// variable to the fee, which is variable to the gad used. This iterates until parity
|
||||||
the amount of non-zero bytes in the calldata. This is variable to the fee, which was prior
|
|
||||||
shimmed to be `1`.
|
|
||||||
|
|
||||||
Here, we calculate the actual fee, and update the initial gas fee accordingly. We then update
|
|
||||||
the fee again, until the initial gas fee stops increasing.
|
|
||||||
*/
|
|
||||||
let initial_gas = |fee, sig| {
|
let initial_gas = |fee, sig| {
|
||||||
let gas = calculate_initial_tx_gas(
|
let gas = calculate_initial_tx_gas(
|
||||||
SPEC_ID,
|
SpecId::CANCUN,
|
||||||
&abi::executeCall::new((sig, Address::from(coin), fee, outs.0.clone())).abi_encode(),
|
&abi::executeCall::new((sig, Address::from(coin), fee, outs.0.clone())).abi_encode(),
|
||||||
false,
|
false,
|
||||||
0,
|
&[],
|
||||||
0,
|
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
assert_eq!(gas.floor_gas, 0);
|
assert_eq!(gas.floor_gas, 0);
|
||||||
gas.initial_gas
|
gas.initial_gas
|
||||||
};
|
};
|
||||||
let mut current_initial_gas = initial_gas(shimmed_fee, abi::Signature::from(&sig));
|
let mut current_initial_gas = initial_gas(fee, abi::Signature::from(&sig));
|
||||||
// Remove the current initial gas from the transaction's gas
|
|
||||||
gas -= current_initial_gas;
|
|
||||||
loop {
|
loop {
|
||||||
// Calculate the would-be fee
|
let fee = fee_per_gas * U256::from(gas);
|
||||||
let fee = fee_per_gas * U256::from(gas + current_initial_gas);
|
|
||||||
// Calculate the would-be gas for this fee
|
|
||||||
let new_initial_gas =
|
let new_initial_gas =
|
||||||
initial_gas(fee, abi::Signature { c: [0xff; 32].into(), s: [0xff; 32].into() });
|
initial_gas(fee, abi::Signature { c: [0xff; 32].into(), s: [0xff; 32].into() });
|
||||||
// If the values are equal, or if it went down, return
|
|
||||||
/*
|
|
||||||
The gas will decrease if the new fee has more zero bytes in its encoding. Further
|
|
||||||
iterations are unhelpful as they'll simply loop infinitely for some inputs. Accordingly, we
|
|
||||||
return the current fee (which is for a very slightly higher gas rate) with the decreased
|
|
||||||
gas to ensure this algorithm terminates.
|
|
||||||
*/
|
|
||||||
if current_initial_gas >= new_initial_gas {
|
if current_initial_gas >= new_initial_gas {
|
||||||
return (gas + new_initial_gas, fee);
|
return gas;
|
||||||
}
|
}
|
||||||
// Update what the current initial gas is
|
|
||||||
|
gas += new_initial_gas - current_initial_gas;
|
||||||
current_initial_gas = new_initial_gas;
|
current_initial_gas = new_initial_gas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The estimated gas for this `OutInstruction`.
|
/// The estimated fee for this `OutInstruction`.
|
||||||
///
|
///
|
||||||
/// This does not model the quadratic costs incurred when in a batch, nor other misc costs such
|
/// This does not model the quadratic costs incurred when in a batch, nor other misc costs such
|
||||||
/// as the potential to cause one less zero byte in the fee's encoding. This is intended to
|
/// as the potential to cause one less zero byte in the fee's encoding. This is intended to
|
||||||
/// produce a per-`OutInstruction` value which can be ratioed against others to decide the fee to
|
/// produce a per-`OutInstruction` fee to deduct from each `OutInstruction`, before all
|
||||||
/// deduct from each `OutInstruction`, before all `OutInstruction`s incur an amortized fee of
|
/// `OutInstruction`s incur an amortized fee of what remains for the batch itself.
|
||||||
/// what remains for the batch itself.
|
|
||||||
pub fn execute_out_instruction_gas_estimate(
|
pub fn execute_out_instruction_gas_estimate(
|
||||||
&mut self,
|
&mut self,
|
||||||
coin: Coin,
|
coin: Coin,
|
||||||
@@ -393,12 +348,11 @@ impl Router {
|
|||||||
#[allow(clippy::map_entry)] // clippy doesn't realize the multiple mutable borrows
|
#[allow(clippy::map_entry)] // clippy doesn't realize the multiple mutable borrows
|
||||||
if !self.empty_execute_gas.contains_key(&coin) {
|
if !self.empty_execute_gas.contains_key(&coin) {
|
||||||
// This can't be de-duplicated across ERC20s due to the zero bytes in the address
|
// This can't be de-duplicated across ERC20s due to the zero bytes in the address
|
||||||
let (gas, _fee) = self.execute_gas_and_fee(coin, U256::from(0), &OutInstructions(vec![]));
|
let gas = self.execute_gas(coin, U256::from(0), &OutInstructions(vec![]));
|
||||||
self.empty_execute_gas.insert(coin, gas);
|
self.empty_execute_gas.insert(coin, gas);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (gas, _fee) =
|
let gas = self.execute_gas(coin, U256::from(0), &OutInstructions(vec![instruction]));
|
||||||
self.execute_gas_and_fee(coin, U256::from(0), &OutInstructions(vec![instruction]));
|
|
||||||
gas - self.empty_execute_gas[&coin]
|
gas - self.empty_execute_gas[&coin]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use alloy_consensus::TxLegacy;
|
|||||||
|
|
||||||
use alloy_rpc_types_eth::{BlockId, Log, Filter, TransactionInput, TransactionRequest};
|
use alloy_rpc_types_eth::{BlockId, Log, Filter, TransactionInput, TransactionRequest};
|
||||||
use alloy_transport::{TransportErrorKind, RpcError};
|
use alloy_transport::{TransportErrorKind, RpcError};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::{Provider, RootProvider};
|
use alloy_provider::{Provider, RootProvider};
|
||||||
|
|
||||||
use scale::Encode;
|
use scale::Encode;
|
||||||
@@ -47,7 +48,6 @@ mod _irouter_abi {
|
|||||||
#[expect(warnings)]
|
#[expect(warnings)]
|
||||||
#[expect(needless_pass_by_value)]
|
#[expect(needless_pass_by_value)]
|
||||||
#[expect(clippy::all)]
|
#[expect(clippy::all)]
|
||||||
#[expect(clippy::unused_self)]
|
|
||||||
#[expect(clippy::ignored_unit_patterns)]
|
#[expect(clippy::ignored_unit_patterns)]
|
||||||
#[expect(clippy::redundant_closure_for_method_calls)]
|
#[expect(clippy::redundant_closure_for_method_calls)]
|
||||||
mod _router_abi {
|
mod _router_abi {
|
||||||
@@ -236,7 +236,7 @@ pub struct Escape {
|
|||||||
/// A view of the Router for Serai.
|
/// A view of the Router for Serai.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Router {
|
pub struct Router {
|
||||||
provider: Arc<RootProvider>,
|
provider: Arc<RootProvider<SimpleRequest>>,
|
||||||
address: Address,
|
address: Address,
|
||||||
empty_execute_gas: HashMap<Coin, u64>,
|
empty_execute_gas: HashMap<Coin, u64>,
|
||||||
}
|
}
|
||||||
@@ -272,7 +272,7 @@ impl Router {
|
|||||||
/// This performs an on-chain lookup for the first deployed Router constructed with this public
|
/// This performs an on-chain lookup for the first deployed Router constructed with this public
|
||||||
/// key. This lookup is of a constant amount of calls and does not read any logs.
|
/// key. This lookup is of a constant amount of calls and does not read any logs.
|
||||||
pub async fn new(
|
pub async fn new(
|
||||||
provider: Arc<RootProvider>,
|
provider: Arc<RootProvider<SimpleRequest>>,
|
||||||
initial_serai_key: &PublicKey,
|
initial_serai_key: &PublicKey,
|
||||||
) -> Result<Option<Self>, RpcError<TransportErrorKind>> {
|
) -> Result<Option<Self>, RpcError<TransportErrorKind>> {
|
||||||
let Some(deployer) = Deployer::new(provider.clone()).await? else {
|
let Some(deployer) = Deployer::new(provider.clone()).await? else {
|
||||||
@@ -292,22 +292,13 @@ impl Router {
|
|||||||
self.address
|
self.address
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the signature data signed in place of the actual signature.
|
|
||||||
fn signature_data(chain_id: U256, router_address: Address, nonce: u64) -> abi::Signature {
|
|
||||||
let mut s = [0; 32];
|
|
||||||
s[.. 20].copy_from_slice(router_address.as_slice());
|
|
||||||
s[24 ..].copy_from_slice(&nonce.to_be_bytes());
|
|
||||||
abi::Signature { c: chain_id.into(), s: s.into() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the message to be signed in order to confirm the next key for Serai.
|
/// Get the message to be signed in order to confirm the next key for Serai.
|
||||||
pub fn confirm_next_serai_key_message(
|
pub fn confirm_next_serai_key_message(chain_id: U256, nonce: u64) -> Vec<u8> {
|
||||||
chain_id: U256,
|
abi::confirmNextSeraiKeyCall::new((abi::Signature {
|
||||||
router_address: Address,
|
c: chain_id.into(),
|
||||||
nonce: u64,
|
s: U256::try_from(nonce).unwrap().into(),
|
||||||
) -> Vec<u8> {
|
},))
|
||||||
abi::confirmNextSeraiKeyCall::new((Self::signature_data(chain_id, router_address, nonce),))
|
.abi_encode()
|
||||||
.abi_encode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a transaction to confirm the next key representing Serai.
|
/// Construct a transaction to confirm the next key representing Serai.
|
||||||
@@ -322,14 +313,9 @@ impl Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the message to be signed in order to update the key for Serai.
|
/// Get the message to be signed in order to update the key for Serai.
|
||||||
pub fn update_serai_key_message(
|
pub fn update_serai_key_message(chain_id: U256, nonce: u64, key: &PublicKey) -> Vec<u8> {
|
||||||
chain_id: U256,
|
|
||||||
router_address: Address,
|
|
||||||
nonce: u64,
|
|
||||||
key: &PublicKey,
|
|
||||||
) -> Vec<u8> {
|
|
||||||
abi::updateSeraiKeyCall::new((
|
abi::updateSeraiKeyCall::new((
|
||||||
Self::signature_data(chain_id, router_address, nonce),
|
abi::Signature { c: chain_id.into(), s: U256::try_from(nonce).unwrap().into() },
|
||||||
key.eth_repr().into(),
|
key.eth_repr().into(),
|
||||||
))
|
))
|
||||||
.abi_encode()
|
.abi_encode()
|
||||||
@@ -385,14 +371,13 @@ impl Router {
|
|||||||
/// Get the message to be signed in order to execute a series of `OutInstruction`s.
|
/// Get the message to be signed in order to execute a series of `OutInstruction`s.
|
||||||
pub fn execute_message(
|
pub fn execute_message(
|
||||||
chain_id: U256,
|
chain_id: U256,
|
||||||
router_address: Address,
|
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
coin: Coin,
|
coin: Coin,
|
||||||
fee: U256,
|
fee: U256,
|
||||||
outs: OutInstructions,
|
outs: OutInstructions,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
abi::executeCall::new((
|
abi::executeCall::new((
|
||||||
Self::signature_data(chain_id, router_address, nonce),
|
abi::Signature { c: chain_id.into(), s: U256::try_from(nonce).unwrap().into() },
|
||||||
Address::from(coin),
|
Address::from(coin),
|
||||||
fee,
|
fee,
|
||||||
outs.0,
|
outs.0,
|
||||||
@@ -414,14 +399,12 @@ impl Router {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the message to be signed in order to trigger the escape hatch.
|
/// Get the message to be signed in order to trigger the escape hatch.
|
||||||
pub fn escape_hatch_message(
|
pub fn escape_hatch_message(chain_id: U256, nonce: u64, escape_to: Address) -> Vec<u8> {
|
||||||
chain_id: U256,
|
abi::escapeHatchCall::new((
|
||||||
router_address: Address,
|
abi::Signature { c: chain_id.into(), s: U256::try_from(nonce).unwrap().into() },
|
||||||
nonce: u64,
|
escape_to,
|
||||||
escape_to: Address,
|
))
|
||||||
) -> Vec<u8> {
|
.abi_encode()
|
||||||
abi::escapeHatchCall::new((Self::signature_data(chain_id, router_address, nonce), escape_to))
|
|
||||||
.abi_encode()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a transaction to trigger the escape hatch.
|
/// Construct a transaction to trigger the escape hatch.
|
||||||
@@ -590,7 +573,7 @@ impl Router {
|
|||||||
if log.topics().first() != Some(&Transfer::SIGNATURE_HASH) {
|
if log.topics().first() != Some(&Transfer::SIGNATURE_HASH) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let Ok(transfer) = Transfer::decode_log(&log.inner.clone()) else { continue };
|
let Ok(transfer) = Transfer::decode_log(&log.inner.clone(), true) else { continue };
|
||||||
// Check if this aligns with the InInstruction
|
// Check if this aligns with the InInstruction
|
||||||
if (transfer.from == in_instruction.from) &&
|
if (transfer.from == in_instruction.from) &&
|
||||||
(transfer.to == self.address) &&
|
(transfer.to == self.address) &&
|
||||||
@@ -760,11 +743,11 @@ impl Router {
|
|||||||
) -> Result<Option<PublicKey>, RpcError<TransportErrorKind>> {
|
) -> Result<Option<PublicKey>, RpcError<TransportErrorKind>> {
|
||||||
let call =
|
let call =
|
||||||
TransactionRequest::default().to(self.address).input(TransactionInput::new(call.into()));
|
TransactionRequest::default().to(self.address).input(TransactionInput::new(call.into()));
|
||||||
let bytes = self.provider.call(call).block(block).await?;
|
let bytes = self.provider.call(&call).block(block).await?;
|
||||||
// This is fine as both key calls share a return type
|
// This is fine as both key calls share a return type
|
||||||
let res = abi::nextSeraiKeyCall::abi_decode_returns(&bytes)
|
let res = abi::nextSeraiKeyCall::abi_decode_returns(&bytes, true)
|
||||||
.map_err(|e| TransportErrorKind::Custom(format!("failed to decode key: {e:?}").into()))?;
|
.map_err(|e| TransportErrorKind::Custom(format!("failed to decode key: {e:?}").into()))?;
|
||||||
let eth_repr = <[u8; 32]>::from(res);
|
let eth_repr = <[u8; 32]>::from(res._0);
|
||||||
Ok(if eth_repr == [0; 32] {
|
Ok(if eth_repr == [0; 32] {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@@ -795,10 +778,10 @@ impl Router {
|
|||||||
let call = TransactionRequest::default()
|
let call = TransactionRequest::default()
|
||||||
.to(self.address)
|
.to(self.address)
|
||||||
.input(TransactionInput::new(abi::nextNonceCall::new(()).abi_encode().into()));
|
.input(TransactionInput::new(abi::nextNonceCall::new(()).abi_encode().into()));
|
||||||
let bytes = self.provider.call(call).block(block).await?;
|
let bytes = self.provider.call(&call).block(block).await?;
|
||||||
let res = abi::nextNonceCall::abi_decode_returns(&bytes)
|
let res = abi::nextNonceCall::abi_decode_returns(&bytes, true)
|
||||||
.map_err(|e| TransportErrorKind::Custom(format!("failed to decode nonce: {e:?}").into()))?;
|
.map_err(|e| TransportErrorKind::Custom(format!("failed to decode nonce: {e:?}").into()))?;
|
||||||
Ok(u64::try_from(res).map_err(|_| {
|
Ok(u64::try_from(res._0).map_err(|_| {
|
||||||
TransportErrorKind::Custom("nonce returned exceeded 2**64".to_string().into())
|
TransportErrorKind::Custom("nonce returned exceeded 2**64".to_string().into())
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
@@ -811,10 +794,10 @@ impl Router {
|
|||||||
let call = TransactionRequest::default()
|
let call = TransactionRequest::default()
|
||||||
.to(self.address)
|
.to(self.address)
|
||||||
.input(TransactionInput::new(abi::escapedToCall::new(()).abi_encode().into()));
|
.input(TransactionInput::new(abi::escapedToCall::new(()).abi_encode().into()));
|
||||||
let bytes = self.provider.call(call).block(block).await?;
|
let bytes = self.provider.call(&call).block(block).await?;
|
||||||
let res = abi::escapedToCall::abi_decode_returns(&bytes).map_err(|e| {
|
let res = abi::escapedToCall::abi_decode_returns(&bytes, true).map_err(|e| {
|
||||||
TransportErrorKind::Custom(format!("failed to decode the address escaped to: {e:?}").into())
|
TransportErrorKind::Custom(format!("failed to decode the address escaped to: {e:?}").into())
|
||||||
})?;
|
})?;
|
||||||
Ok(if res == Address::ZERO { None } else { Some(res) })
|
Ok(if res._0 == Address([0; 20].into()) { None } else { Some(res._0) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use alloy_consensus::TxLegacy;
|
|||||||
use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
|
use alloy_rpc_types_eth::{TransactionInput, TransactionRequest};
|
||||||
use alloy_provider::Provider;
|
use alloy_provider::Provider;
|
||||||
|
|
||||||
use revm::{primitives::hardfork::SpecId, interpreter::gas::calculate_initial_tx_gas};
|
use revm::{primitives::SpecId, interpreter::gas::calculate_initial_tx_gas};
|
||||||
|
|
||||||
use crate::tests::Test;
|
use crate::tests::Test;
|
||||||
|
|
||||||
@@ -65,13 +65,13 @@ async fn test_create_address() {
|
|||||||
let call =
|
let call =
|
||||||
TransactionRequest::default().to(address).input(TransactionInput::new(input.clone().into()));
|
TransactionRequest::default().to(address).input(TransactionInput::new(input.clone().into()));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&test.provider.call(call.clone()).await.unwrap().as_ref()[12 ..],
|
&test.provider.call(&call).await.unwrap().as_ref()[12 ..],
|
||||||
address.create(nonce).as_slice(),
|
address.create(nonce).as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check the function is constant-gas
|
// Check the function is constant-gas
|
||||||
let gas_used = test.provider.estimate_gas(call).await.unwrap();
|
let gas_used = test.provider.estimate_gas(&call).await.unwrap();
|
||||||
let initial_gas = calculate_initial_tx_gas(SpecId::CANCUN, &input, false, 0, 0, 0).initial_gas;
|
let initial_gas = calculate_initial_tx_gas(SpecId::CANCUN, &input, false, &[], 0).initial_gas;
|
||||||
let this_call = gas_used - initial_gas;
|
let this_call = gas_used - initial_gas;
|
||||||
if gas.is_none() {
|
if gas.is_none() {
|
||||||
gas = Some(this_call);
|
gas = Some(this_call);
|
||||||
|
|||||||
@@ -86,13 +86,13 @@ impl Erc20 {
|
|||||||
let call = TransactionRequest::default().to(self.0).input(TransactionInput::new(
|
let call = TransactionRequest::default().to(self.0).input(TransactionInput::new(
|
||||||
abi::TestERC20::balanceOfCall::new((account,)).abi_encode().into(),
|
abi::TestERC20::balanceOfCall::new((account,)).abi_encode().into(),
|
||||||
));
|
));
|
||||||
U256::abi_decode(&test.provider.call(call).await.unwrap()).unwrap()
|
U256::abi_decode(&test.provider.call(&call).await.unwrap(), true).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn router_approval(&self, test: &Test, account: Address) -> U256 {
|
pub(crate) async fn router_approval(&self, test: &Test, account: Address) -> U256 {
|
||||||
let call = TransactionRequest::default().to(self.0).input(TransactionInput::new(
|
let call = TransactionRequest::default().to(self.0).input(TransactionInput::new(
|
||||||
abi::TestERC20::allowanceCall::new((test.router.address(), account)).abi_encode().into(),
|
abi::TestERC20::allowanceCall::new((test.router.address(), account)).abi_encode().into(),
|
||||||
));
|
));
|
||||||
U256::abi_decode(&test.provider.call(call).await.unwrap()).unwrap()
|
U256::abi_decode(&test.provider.call(&call).await.unwrap(), true).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,7 @@ use crate::tests::*;
|
|||||||
|
|
||||||
impl Test {
|
impl Test {
|
||||||
pub(crate) fn escape_hatch_tx(&self, escape_to: Address) -> TxLegacy {
|
pub(crate) fn escape_hatch_tx(&self, escape_to: Address) -> TxLegacy {
|
||||||
let msg = Router::escape_hatch_message(
|
let msg = Router::escape_hatch_message(self.chain_id, self.state.next_nonce, escape_to);
|
||||||
self.chain_id,
|
|
||||||
self.router.address(),
|
|
||||||
self.state.next_nonce,
|
|
||||||
escape_to,
|
|
||||||
);
|
|
||||||
let sig = sign(self.state.key.unwrap(), &msg);
|
let sig = sign(self.state.key.unwrap(), &msg);
|
||||||
let mut tx = self.router.escape_hatch(escape_to, &sig);
|
let mut tx = self.router.escape_hatch(escape_to, &sig);
|
||||||
tx.gas_limit = Router::ESCAPE_HATCH_GAS + 5_000;
|
tx.gas_limit = Router::ESCAPE_HATCH_GAS + 5_000;
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ struct CalldataAgnosticGas;
|
|||||||
impl CalldataAgnosticGas {
|
impl CalldataAgnosticGas {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn calculate(input: &[u8], mut constant_zero_bytes: usize, gas_used: u64) -> u64 {
|
fn calculate(input: &[u8], mut constant_zero_bytes: usize, gas_used: u64) -> u64 {
|
||||||
use revm::{primitives::hardfork::SpecId, interpreter::gas::calculate_initial_tx_gas};
|
use revm::{primitives::SpecId, interpreter::gas::calculate_initial_tx_gas};
|
||||||
|
|
||||||
let mut without_variable_zero_bytes = Vec::with_capacity(input.len());
|
let mut without_variable_zero_bytes = Vec::with_capacity(input.len());
|
||||||
for byte in input {
|
for byte in input {
|
||||||
@@ -76,9 +76,9 @@ impl CalldataAgnosticGas {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
gas_used +
|
gas_used +
|
||||||
(calculate_initial_tx_gas(SpecId::CANCUN, &without_variable_zero_bytes, false, 0, 0, 0)
|
(calculate_initial_tx_gas(SpecId::CANCUN, &without_variable_zero_bytes, false, &[], 0)
|
||||||
.initial_gas -
|
.initial_gas -
|
||||||
calculate_initial_tx_gas(SpecId::CANCUN, input, false, 0, 0, 0).initial_gas)
|
calculate_initial_tx_gas(SpecId::CANCUN, input, false, &[], 0).initial_gas)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ struct RouterState {
|
|||||||
struct Test {
|
struct Test {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
anvil: AnvilInstance,
|
anvil: AnvilInstance,
|
||||||
provider: Arc<RootProvider>,
|
provider: Arc<RootProvider<SimpleRequest>>,
|
||||||
chain_id: U256,
|
chain_id: U256,
|
||||||
router: Router,
|
router: Router,
|
||||||
state: RouterState,
|
state: RouterState,
|
||||||
@@ -173,16 +173,12 @@ impl Test {
|
|||||||
let call = TransactionRequest::default()
|
let call = TransactionRequest::default()
|
||||||
.to(self.router.address())
|
.to(self.router.address())
|
||||||
.input(TransactionInput::new(tx.input));
|
.input(TransactionInput::new(tx.input));
|
||||||
let call_err = self.provider.call(call).await.unwrap_err();
|
let call_err = self.provider.call(&call).await.unwrap_err();
|
||||||
call_err.as_error_resp().unwrap().as_decoded_interface_error::<IRouterErrors>().unwrap()
|
call_err.as_error_resp().unwrap().as_decoded_error::<IRouterErrors>(true).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_next_serai_key_tx(&self) -> TxLegacy {
|
fn confirm_next_serai_key_tx(&self) -> TxLegacy {
|
||||||
let msg = Router::confirm_next_serai_key_message(
|
let msg = Router::confirm_next_serai_key_message(self.chain_id, self.state.next_nonce);
|
||||||
self.chain_id,
|
|
||||||
self.router.address(),
|
|
||||||
self.state.next_nonce,
|
|
||||||
);
|
|
||||||
let sig = sign(self.state.next_key.unwrap(), &msg);
|
let sig = sign(self.state.next_key.unwrap(), &msg);
|
||||||
|
|
||||||
self.router.confirm_next_serai_key(&sig)
|
self.router.confirm_next_serai_key(&sig)
|
||||||
@@ -231,12 +227,7 @@ impl Test {
|
|||||||
fn update_serai_key_tx(&self) -> ((Scalar, PublicKey), TxLegacy) {
|
fn update_serai_key_tx(&self) -> ((Scalar, PublicKey), TxLegacy) {
|
||||||
let next_key = test_key();
|
let next_key = test_key();
|
||||||
|
|
||||||
let msg = Router::update_serai_key_message(
|
let msg = Router::update_serai_key_message(self.chain_id, self.state.next_nonce, &next_key.1);
|
||||||
self.chain_id,
|
|
||||||
self.router.address(),
|
|
||||||
self.state.next_nonce,
|
|
||||||
&next_key.1,
|
|
||||||
);
|
|
||||||
let sig = sign(self.state.key.unwrap(), &msg);
|
let sig = sign(self.state.key.unwrap(), &msg);
|
||||||
|
|
||||||
(next_key, self.router.update_serai_key(&next_key.1, &sig))
|
(next_key, self.router.update_serai_key(&next_key.1, &sig))
|
||||||
@@ -284,7 +275,6 @@ impl Test {
|
|||||||
) -> ([u8; 32], TxLegacy) {
|
) -> ([u8; 32], TxLegacy) {
|
||||||
let msg = Router::execute_message(
|
let msg = Router::execute_message(
|
||||||
self.chain_id,
|
self.chain_id,
|
||||||
self.router.address(),
|
|
||||||
self.state.next_nonce,
|
self.state.next_nonce,
|
||||||
coin,
|
coin,
|
||||||
fee,
|
fee,
|
||||||
@@ -478,10 +468,11 @@ async fn test_update_serai_key() {
|
|||||||
|
|
||||||
// But we shouldn't be able to update the key to None
|
// But we shouldn't be able to update the key to None
|
||||||
{
|
{
|
||||||
let router_address_u256: U256 = test.router.address().into_word().into();
|
|
||||||
let s: U256 = (router_address_u256 << 96) | U256::from(test.state.next_nonce);
|
|
||||||
let msg = crate::abi::updateSeraiKeyCall::new((
|
let msg = crate::abi::updateSeraiKeyCall::new((
|
||||||
crate::abi::Signature { c: test.chain_id.into(), s: s.into() },
|
crate::abi::Signature {
|
||||||
|
c: test.chain_id.into(),
|
||||||
|
s: U256::try_from(test.state.next_nonce).unwrap().into(),
|
||||||
|
},
|
||||||
[0; 32].into(),
|
[0; 32].into(),
|
||||||
))
|
))
|
||||||
.abi_encode();
|
.abi_encode();
|
||||||
@@ -549,8 +540,8 @@ async fn test_empty_execute() {
|
|||||||
test.confirm_next_serai_key().await;
|
test.confirm_next_serai_key().await;
|
||||||
|
|
||||||
{
|
{
|
||||||
let (gas, fee) =
|
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &[].as_slice().into());
|
||||||
test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &[].as_slice().into());
|
let fee = U256::from(gas);
|
||||||
|
|
||||||
let () = test
|
let () = test
|
||||||
.provider
|
.provider
|
||||||
@@ -583,15 +574,15 @@ async fn test_empty_execute() {
|
|||||||
TransactionRequest::default().to(token).input(TransactionInput::new(vec![].into()));
|
TransactionRequest::default().to(token).input(TransactionInput::new(vec![].into()));
|
||||||
// Check it returns the expected result
|
// Check it returns the expected result
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
test.provider.call(call.clone()).await.unwrap().as_ref(),
|
test.provider.call(&call).await.unwrap().as_ref(),
|
||||||
U256::from(1).abi_encode().as_slice()
|
U256::from(1).abi_encode().as_slice()
|
||||||
);
|
);
|
||||||
// Check it has the expected gas cost (16 is documented in `return_true_code`)
|
// Check it has the expected gas cost (16 is documented in `return_true_code`)
|
||||||
assert_eq!(test.provider.estimate_gas(call).await.unwrap(), 21_000 + 16);
|
assert_eq!(test.provider.estimate_gas(&call).await.unwrap(), 21_000 + 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (gas, fee) =
|
let gas = test.router.execute_gas(Coin::Erc20(token), U256::from(0), &[].as_slice().into());
|
||||||
test.router.execute_gas_and_fee(Coin::Erc20(token), U256::from(0), &[].as_slice().into());
|
let fee = U256::from(0);
|
||||||
let (_tx, gas_used) = test.execute(Coin::Erc20(token), fee, [].as_slice().into(), vec![]).await;
|
let (_tx, gas_used) = test.execute(Coin::Erc20(token), fee, [].as_slice().into(), vec![]).await;
|
||||||
const UNUSED_GAS: u64 = Router::GAS_FOR_ERC20_CALL - 16;
|
const UNUSED_GAS: u64 = Router::GAS_FOR_ERC20_CALL - 16;
|
||||||
assert_eq!(gas_used + UNUSED_GAS, gas);
|
assert_eq!(gas_used + UNUSED_GAS, gas);
|
||||||
@@ -609,7 +600,8 @@ async fn test_eth_address_out_instruction() {
|
|||||||
let out_instructions =
|
let out_instructions =
|
||||||
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &out_instructions);
|
||||||
|
let fee = U256::from(gas);
|
||||||
|
|
||||||
let () = test
|
let () = test
|
||||||
.provider
|
.provider
|
||||||
@@ -646,7 +638,8 @@ async fn test_erc20_address_out_instruction() {
|
|||||||
let out_instructions =
|
let out_instructions =
|
||||||
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
OutInstructions::from([(SeraiEthereumAddress::Address(rand_address), amount_out)].as_slice());
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(coin, U256::from(1), &out_instructions);
|
let gas = test.router.execute_gas(coin, U256::from(1), &out_instructions);
|
||||||
|
let fee = U256::from(gas);
|
||||||
|
|
||||||
// Mint to the Router the necessary amount of the ERC20
|
// Mint to the Router the necessary amount of the ERC20
|
||||||
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
||||||
@@ -681,7 +674,8 @@ async fn test_eth_code_out_instruction() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
let gas = test.router.execute_gas(Coin::Ether, U256::from(1), &out_instructions);
|
||||||
|
let fee = U256::from(gas);
|
||||||
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
||||||
|
|
||||||
// We use call-traces here to determine how much gas was allowed but unused due to the complexity
|
// We use call-traces here to determine how much gas was allowed but unused due to the complexity
|
||||||
@@ -706,34 +700,6 @@ async fn test_eth_code_out_instruction() {
|
|||||||
assert_eq!(test.provider.get_code_at(deployed).await.unwrap().to_vec(), true.abi_encode());
|
assert_eq!(test.provider.get_code_at(deployed).await.unwrap().to_vec(), true.abi_encode());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_eth_code_out_instruction_reverts() {
|
|
||||||
let mut test = Test::new().await;
|
|
||||||
test.confirm_next_serai_key().await;
|
|
||||||
let () = test
|
|
||||||
.provider
|
|
||||||
.raw_request("anvil_setBalance".into(), (test.router.address(), 1_000_000))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// [REVERT], which will cause `executeArbitraryCode`'s call to CREATE to fail
|
|
||||||
let code = vec![0xfd];
|
|
||||||
let amount_out = U256::from(0);
|
|
||||||
let out_instructions = OutInstructions::from(
|
|
||||||
[(
|
|
||||||
SeraiEthereumAddress::Contract(ContractDeployment::new(50_000, code.clone()).unwrap()),
|
|
||||||
amount_out,
|
|
||||||
)]
|
|
||||||
.as_slice(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(1), &out_instructions);
|
|
||||||
let (tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
|
||||||
|
|
||||||
let unused_gas = test.gas_unused_by_calls(&tx).await;
|
|
||||||
assert_eq!(gas_used + unused_gas, gas);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_erc20_code_out_instruction() {
|
async fn test_erc20_code_out_instruction() {
|
||||||
let mut test = Test::new().await;
|
let mut test = Test::new().await;
|
||||||
@@ -749,7 +715,8 @@ async fn test_erc20_code_out_instruction() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(coin, U256::from(1), &out_instructions);
|
let gas = test.router.execute_gas(coin, U256::from(1), &out_instructions);
|
||||||
|
let fee = U256::from(gas);
|
||||||
|
|
||||||
// Mint to the Router the necessary amount of the ERC20
|
// Mint to the Router the necessary amount of the ERC20
|
||||||
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
erc20.mint(&test, test.router.address(), amount_out + fee).await;
|
||||||
@@ -781,11 +748,11 @@ async fn test_result_decoding() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(0), &out_instructions);
|
let gas = test.router.execute_gas(Coin::Ether, U256::from(0), &out_instructions);
|
||||||
|
|
||||||
// We should decode these in the correct order (not `false, true, true`)
|
// We should decode these in the correct order (not `false, true, true`)
|
||||||
let (_tx, gas_used) =
|
let (_tx, gas_used) =
|
||||||
test.execute(Coin::Ether, fee, out_instructions, vec![true, true, false]).await;
|
test.execute(Coin::Ether, U256::from(0), out_instructions, vec![true, true, false]).await;
|
||||||
// We don't check strict equality as we don't know how much gas was used by the reverted call
|
// We don't check strict equality as we don't know how much gas was used by the reverted call
|
||||||
// (even with the trace), solely that it used less than or equal to the limit
|
// (even with the trace), solely that it used less than or equal to the limit
|
||||||
assert!(gas_used <= gas);
|
assert!(gas_used <= gas);
|
||||||
@@ -821,8 +788,9 @@ async fn test_reentrancy() {
|
|||||||
.as_slice(),
|
.as_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(Coin::Ether, U256::from(0), &out_instructions);
|
let gas = test.router.execute_gas(Coin::Ether, U256::from(0), &out_instructions);
|
||||||
let (_tx, gas_used) = test.execute(Coin::Ether, fee, out_instructions, vec![true]).await;
|
let (_tx, gas_used) =
|
||||||
|
test.execute(Coin::Ether, U256::from(0), out_instructions, vec![true]).await;
|
||||||
// Even though this doesn't have failed `OutInstruction`s, our logic is incomplete upon any
|
// Even though this doesn't have failed `OutInstruction`s, our logic is incomplete upon any
|
||||||
// failed internal calls for some reason. That's fine, as the gas yielded is still the worst-case
|
// failed internal calls for some reason. That's fine, as the gas yielded is still the worst-case
|
||||||
// (which this isn't a counter-example to) and is validated to be the worst-case, but is peculiar
|
// (which this isn't a counter-example to) and is validated to be the worst-case, but is peculiar
|
||||||
@@ -831,7 +799,7 @@ async fn test_reentrancy() {
|
|||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn fuzz_test_out_instructions_gas() {
|
async fn fuzz_test_out_instructions_gas() {
|
||||||
for _ in 0 .. 100 {
|
for _ in 0 .. 10 {
|
||||||
let mut test = Test::new().await;
|
let mut test = Test::new().await;
|
||||||
test.confirm_next_serai_key().await;
|
test.confirm_next_serai_key().await;
|
||||||
|
|
||||||
@@ -849,7 +817,7 @@ async fn fuzz_test_out_instructions_gas() {
|
|||||||
code.extend(&ext);
|
code.extend(&ext);
|
||||||
|
|
||||||
out_instructions.push((
|
out_instructions.push((
|
||||||
SeraiEthereumAddress::Contract(ContractDeployment::new(100_000, code).unwrap()),
|
SeraiEthereumAddress::Contract(ContractDeployment::new(100_000, ext).unwrap()),
|
||||||
amount_out,
|
amount_out,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
@@ -886,7 +854,8 @@ async fn fuzz_test_out_instructions_gas() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let fee_per_gas = U256::from(1) + U256::from(OsRng.next_u64() % 10);
|
let fee_per_gas = U256::from(1) + U256::from(OsRng.next_u64() % 10);
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(coin, fee_per_gas, &out_instructions);
|
let gas = test.router.execute_gas(coin, fee_per_gas, &out_instructions);
|
||||||
|
let fee = U256::from(gas) * fee_per_gas;
|
||||||
// All of these should have succeeded
|
// All of these should have succeeded
|
||||||
let (tx, gas_used) =
|
let (tx, gas_used) =
|
||||||
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
||||||
@@ -898,47 +867,3 @@ async fn fuzz_test_out_instructions_gas() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_gas_increases_then_decreases() {
|
|
||||||
/*
|
|
||||||
This specific batch of `OutInstruction`s causes the gas to be initially calculated, and then
|
|
||||||
increase as the proper fee is written in (due to the increased amount of non-zero bytes). But
|
|
||||||
then, as the fee is updated until the final fee no longer increases the gas used, the gas
|
|
||||||
actually goes *back down*. To then derive the fee from this reduced gas causes the gas to go
|
|
||||||
back up.
|
|
||||||
|
|
||||||
A prior version of this library would return the reduced amount of gas fee in this edge case,
|
|
||||||
which only rarely appeared via the fuzz test (yet did once, yielding this). Then, it'd derive
|
|
||||||
the fee from it, and expect the realized transaction to have parity (causing a test failure as
|
|
||||||
it didn't). Now, `execute_gas` is `execute_gas_and_fee`, yielding both the gas which is
|
|
||||||
expected *and the fee for it*. This fee is guaranteed to cost the reported amount of gas,
|
|
||||||
resolving this issue.
|
|
||||||
*/
|
|
||||||
let out_instructions = vec![(
|
|
||||||
SeraiEthereumAddress::Contract(ContractDeployment::new(100240, vec![]).unwrap()),
|
|
||||||
U256::from(1u8),
|
|
||||||
)];
|
|
||||||
|
|
||||||
let mut test = Test::new().await;
|
|
||||||
test.confirm_next_serai_key().await;
|
|
||||||
|
|
||||||
let out_instructions = OutInstructions::from(out_instructions.as_slice());
|
|
||||||
|
|
||||||
let coin = {
|
|
||||||
let () = test
|
|
||||||
.provider
|
|
||||||
.raw_request("anvil_setBalance".into(), (test.router.address(), 1_000_000_000))
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
Coin::Ether
|
|
||||||
};
|
|
||||||
|
|
||||||
let fee_per_gas = U256::from(1);
|
|
||||||
let (gas, fee) = test.router.execute_gas_and_fee(coin, fee_per_gas, &out_instructions);
|
|
||||||
assert!((U256::from(gas) * fee_per_gas) != fee);
|
|
||||||
let (tx, gas_used) =
|
|
||||||
test.execute(coin, fee, out_instructions.clone(), vec![true; out_instructions.0.len()]).await;
|
|
||||||
let unused_gas = test.gas_unused_by_calls(&tx).await;
|
|
||||||
assert_eq!(gas_used + unused_gas, gas);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use std::io;
|
|||||||
use ciphersuite::Secp256k1;
|
use ciphersuite::Secp256k1;
|
||||||
use frost::dkg::ThresholdKeys;
|
use frost::dkg::ThresholdKeys;
|
||||||
|
|
||||||
use alloy_core::primitives::{U256, Address as EthereumAddress};
|
use alloy_core::primitives::U256;
|
||||||
|
|
||||||
use serai_client::networks::ethereum::Address;
|
use serai_client::networks::ethereum::Address;
|
||||||
|
|
||||||
@@ -17,20 +17,8 @@ use crate::{output::OutputId, machine::ClonableTransctionMachine};
|
|||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub(crate) enum Action {
|
pub(crate) enum Action {
|
||||||
SetKey {
|
SetKey { chain_id: U256, nonce: u64, key: PublicKey },
|
||||||
chain_id: U256,
|
Batch { chain_id: U256, nonce: u64, coin: Coin, fee: U256, outs: Vec<(Address, U256)> },
|
||||||
router_address: EthereumAddress,
|
|
||||||
nonce: u64,
|
|
||||||
key: PublicKey,
|
|
||||||
},
|
|
||||||
Batch {
|
|
||||||
chain_id: U256,
|
|
||||||
router_address: EthereumAddress,
|
|
||||||
nonce: u64,
|
|
||||||
coin: Coin,
|
|
||||||
fee: U256,
|
|
||||||
outs: Vec<(Address, U256)>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
@@ -45,28 +33,25 @@ impl Action {
|
|||||||
|
|
||||||
pub(crate) fn message(&self) -> Vec<u8> {
|
pub(crate) fn message(&self) -> Vec<u8> {
|
||||||
match self {
|
match self {
|
||||||
Action::SetKey { chain_id, router_address, nonce, key } => {
|
Action::SetKey { chain_id, nonce, key } => {
|
||||||
Router::update_serai_key_message(*chain_id, *router_address, *nonce, key)
|
Router::update_serai_key_message(*chain_id, *nonce, key)
|
||||||
}
|
|
||||||
Action::Batch { chain_id, router_address, nonce, coin, fee, outs } => {
|
|
||||||
Router::execute_message(
|
|
||||||
*chain_id,
|
|
||||||
*router_address,
|
|
||||||
*nonce,
|
|
||||||
*coin,
|
|
||||||
*fee,
|
|
||||||
OutInstructions::from(outs.as_ref()),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Action::Batch { chain_id, nonce, coin, fee, outs } => Router::execute_message(
|
||||||
|
*chain_id,
|
||||||
|
*nonce,
|
||||||
|
*coin,
|
||||||
|
*fee,
|
||||||
|
OutInstructions::from(outs.as_ref()),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eventuality(&self) -> Eventuality {
|
pub(crate) fn eventuality(&self) -> Eventuality {
|
||||||
Eventuality(match self {
|
Eventuality(match self {
|
||||||
Self::SetKey { chain_id: _, router_address: _, nonce, key } => {
|
Self::SetKey { chain_id: _, nonce, key } => {
|
||||||
Executed::NextSeraiKeySet { nonce: *nonce, key: key.eth_repr() }
|
Executed::NextSeraiKeySet { nonce: *nonce, key: key.eth_repr() }
|
||||||
}
|
}
|
||||||
Self::Batch { chain_id: _, router_address: _, nonce, .. } => {
|
Self::Batch { chain_id: _, nonce, .. } => {
|
||||||
Executed::Batch { nonce: *nonce, message_hash: keccak256(self.message()), results: vec![] }
|
Executed::Batch { nonce: *nonce, message_hash: keccak256(self.message()), results: vec![] }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -104,10 +89,6 @@ impl SignableTransaction for Action {
|
|||||||
reader.read_exact(&mut chain_id)?;
|
reader.read_exact(&mut chain_id)?;
|
||||||
let chain_id = U256::from_be_bytes(chain_id);
|
let chain_id = U256::from_be_bytes(chain_id);
|
||||||
|
|
||||||
let mut router_address = [0; 20];
|
|
||||||
reader.read_exact(&mut router_address)?;
|
|
||||||
let router_address = EthereumAddress::from(router_address);
|
|
||||||
|
|
||||||
let mut nonce = [0; 8];
|
let mut nonce = [0; 8];
|
||||||
reader.read_exact(&mut nonce)?;
|
reader.read_exact(&mut nonce)?;
|
||||||
let nonce = u64::from_le_bytes(nonce);
|
let nonce = u64::from_le_bytes(nonce);
|
||||||
@@ -119,7 +100,7 @@ impl SignableTransaction for Action {
|
|||||||
let key =
|
let key =
|
||||||
PublicKey::from_eth_repr(key).ok_or_else(|| io::Error::other("invalid key in Action"))?;
|
PublicKey::from_eth_repr(key).ok_or_else(|| io::Error::other("invalid key in Action"))?;
|
||||||
|
|
||||||
Action::SetKey { chain_id, router_address, nonce, key }
|
Action::SetKey { chain_id, nonce, key }
|
||||||
}
|
}
|
||||||
1 => {
|
1 => {
|
||||||
let coin = borsh::from_reader(reader)?;
|
let coin = borsh::from_reader(reader)?;
|
||||||
@@ -142,24 +123,22 @@ impl SignableTransaction for Action {
|
|||||||
|
|
||||||
outs.push((address, amount));
|
outs.push((address, amount));
|
||||||
}
|
}
|
||||||
Action::Batch { chain_id, router_address, nonce, coin, fee, outs }
|
Action::Batch { chain_id, nonce, coin, fee, outs }
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
|
fn write(&self, writer: &mut impl io::Write) -> io::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::SetKey { chain_id, router_address, nonce, key } => {
|
Self::SetKey { chain_id, nonce, key } => {
|
||||||
writer.write_all(&[0])?;
|
writer.write_all(&[0])?;
|
||||||
writer.write_all(&chain_id.to_be_bytes::<32>())?;
|
writer.write_all(&chain_id.to_be_bytes::<32>())?;
|
||||||
writer.write_all(router_address.as_slice())?;
|
|
||||||
writer.write_all(&nonce.to_le_bytes())?;
|
writer.write_all(&nonce.to_le_bytes())?;
|
||||||
writer.write_all(&key.eth_repr())
|
writer.write_all(&key.eth_repr())
|
||||||
}
|
}
|
||||||
Self::Batch { chain_id, router_address, nonce, coin, fee, outs } => {
|
Self::Batch { chain_id, nonce, coin, fee, outs } => {
|
||||||
writer.write_all(&[1])?;
|
writer.write_all(&[1])?;
|
||||||
writer.write_all(&chain_id.to_be_bytes::<32>())?;
|
writer.write_all(&chain_id.to_be_bytes::<32>())?;
|
||||||
writer.write_all(router_address.as_slice())?;
|
|
||||||
writer.write_all(&nonce.to_le_bytes())?;
|
writer.write_all(&nonce.to_le_bytes())?;
|
||||||
borsh::BorshSerialize::serialize(coin, writer)?;
|
borsh::BorshSerialize::serialize(coin, writer)?;
|
||||||
writer.write_all(&fee.as_le_bytes())?;
|
writer.write_all(&fee.as_le_bytes())?;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::sync::Arc;
|
|||||||
use alloy_rlp::Encodable;
|
use alloy_rlp::Encodable;
|
||||||
|
|
||||||
use alloy_transport::{TransportErrorKind, RpcError};
|
use alloy_transport::{TransportErrorKind, RpcError};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::RootProvider;
|
use alloy_provider::RootProvider;
|
||||||
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@@ -25,13 +26,13 @@ use crate::{
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct TransactionPublisher<D: Db> {
|
pub(crate) struct TransactionPublisher<D: Db> {
|
||||||
db: D,
|
db: D,
|
||||||
rpc: Arc<RootProvider>,
|
rpc: Arc<RootProvider<SimpleRequest>>,
|
||||||
router: Arc<RwLock<Option<Router>>>,
|
router: Arc<RwLock<Option<Router>>>,
|
||||||
relayer_url: String,
|
relayer_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Db> TransactionPublisher<D> {
|
impl<D: Db> TransactionPublisher<D> {
|
||||||
pub(crate) fn new(db: D, rpc: Arc<RootProvider>, relayer_url: String) -> Self {
|
pub(crate) fn new(db: D, rpc: Arc<RootProvider<SimpleRequest>>, relayer_url: String) -> Self {
|
||||||
Self { db, rpc, router: Arc::new(RwLock::new(None)), relayer_url }
|
Self { db, rpc, router: Arc::new(RwLock::new(None)), relayer_url }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,10 +88,8 @@ impl<D: Db> signers::TransactionPublisher<Transaction> for TransactionPublisher<
|
|||||||
let nonce = tx.0.nonce();
|
let nonce = tx.0.nonce();
|
||||||
// Convert from an Action (an internal representation of a signable event) to a TxLegacy
|
// Convert from an Action (an internal representation of a signable event) to a TxLegacy
|
||||||
let tx = match tx.0 {
|
let tx = match tx.0 {
|
||||||
Action::SetKey { chain_id: _, router_address: _, nonce: _, key } => {
|
Action::SetKey { chain_id: _, nonce: _, key } => router.update_serai_key(&key, &tx.1),
|
||||||
router.update_serai_key(&key, &tx.1)
|
Action::Batch { chain_id: _, nonce: _, coin, fee, outs } => {
|
||||||
}
|
|
||||||
Action::Batch { chain_id: _, router_address: _, nonce: _, coin, fee, outs } => {
|
|
||||||
router.execute(coin, fee, OutInstructions::from(outs.as_ref()), &tx.1)
|
router.execute(coin, fee, OutInstructions::from(outs.as_ref()), &tx.1)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,8 +2,9 @@ use core::future::Future;
|
|||||||
use std::{sync::Arc, collections::HashSet};
|
use std::{sync::Arc, collections::HashSet};
|
||||||
|
|
||||||
use alloy_core::primitives::B256;
|
use alloy_core::primitives::B256;
|
||||||
use alloy_rpc_types_eth::{Header, BlockNumberOrTag};
|
use alloy_rpc_types_eth::{Header, BlockTransactionsKind, BlockNumberOrTag};
|
||||||
use alloy_transport::{RpcError, TransportErrorKind};
|
use alloy_transport::{RpcError, TransportErrorKind};
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::{Provider, RootProvider};
|
use alloy_provider::{Provider, RootProvider};
|
||||||
|
|
||||||
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
|
use serai_client::primitives::{ExternalNetworkId, ExternalCoin, Amount};
|
||||||
@@ -25,7 +26,7 @@ use crate::{
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Rpc<D: Db> {
|
pub(crate) struct Rpc<D: Db> {
|
||||||
pub(crate) db: D,
|
pub(crate) db: D,
|
||||||
pub(crate) provider: Arc<RootProvider>,
|
pub(crate) provider: Arc<RootProvider<SimpleRequest>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Db> ScannerFeed for Rpc<D> {
|
impl<D: Db> ScannerFeed for Rpc<D> {
|
||||||
@@ -48,7 +49,7 @@ impl<D: Db> ScannerFeed for Rpc<D> {
|
|||||||
async move {
|
async move {
|
||||||
let actual_number = self
|
let actual_number = self
|
||||||
.provider
|
.provider
|
||||||
.get_block(BlockNumberOrTag::Finalized.into())
|
.get_block(BlockNumberOrTag::Finalized.into(), BlockTransactionsKind::Hashes)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TransportErrorKind::Custom("there was no finalized block".to_string().into())
|
TransportErrorKind::Custom("there was no finalized block".to_string().into())
|
||||||
@@ -76,7 +77,7 @@ impl<D: Db> ScannerFeed for Rpc<D> {
|
|||||||
async move {
|
async move {
|
||||||
let header = self
|
let header = self
|
||||||
.provider
|
.provider
|
||||||
.get_block(BlockNumberOrTag::Number(number).into())
|
.get_block(BlockNumberOrTag::Number(number).into(), BlockTransactionsKind::Hashes)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TransportErrorKind::Custom(
|
TransportErrorKind::Custom(
|
||||||
@@ -104,7 +105,7 @@ impl<D: Db> ScannerFeed for Rpc<D> {
|
|||||||
} else {
|
} else {
|
||||||
self
|
self
|
||||||
.provider
|
.provider
|
||||||
.get_block((start - 1).into())
|
.get_block((start - 1).into(), BlockTransactionsKind::Hashes)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TransportErrorKind::Custom(
|
TransportErrorKind::Custom(
|
||||||
@@ -119,7 +120,7 @@ impl<D: Db> ScannerFeed for Rpc<D> {
|
|||||||
|
|
||||||
let end_header = self
|
let end_header = self
|
||||||
.provider
|
.provider
|
||||||
.get_block((start + 31).into())
|
.get_block((start + 31).into(), BlockTransactionsKind::Hashes)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TransportErrorKind::Custom(
|
TransportErrorKind::Custom(
|
||||||
@@ -176,7 +177,7 @@ impl<D: Db> ScannerFeed for Rpc<D> {
|
|||||||
while to_check != epoch.prior_end_hash {
|
while to_check != epoch.prior_end_hash {
|
||||||
let to_check_block = self
|
let to_check_block = self
|
||||||
.provider
|
.provider
|
||||||
.get_block(B256::from(to_check).into())
|
.get_block(B256::from(to_check).into(), BlockTransactionsKind::Hashes)
|
||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| {
|
.ok_or_else(|| {
|
||||||
TransportErrorKind::Custom(
|
TransportErrorKind::Custom(
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ impl<D: Db> smart_contract_scheduler::SmartContract<Rpc<D>> for SmartContract {
|
|||||||
) -> (Self::SignableTransaction, EventualityFor<Rpc<D>>) {
|
) -> (Self::SignableTransaction, EventualityFor<Rpc<D>>) {
|
||||||
let action = Action::SetKey {
|
let action = Action::SetKey {
|
||||||
chain_id: self.chain_id,
|
chain_id: self.chain_id,
|
||||||
router_address: if true { todo!("TODO") } else { Default::default() },
|
|
||||||
nonce,
|
nonce,
|
||||||
key: PublicKey::new(new_key).expect("rotating to an invald key"),
|
key: PublicKey::new(new_key).expect("rotating to an invald key"),
|
||||||
};
|
};
|
||||||
@@ -140,7 +139,6 @@ impl<D: Db> smart_contract_scheduler::SmartContract<Rpc<D>> for SmartContract {
|
|||||||
|
|
||||||
res.push(Action::Batch {
|
res.push(Action::Batch {
|
||||||
chain_id: self.chain_id,
|
chain_id: self.chain_id,
|
||||||
router_address: if true { todo!("TODO") } else { Default::default() },
|
|
||||||
nonce,
|
nonce,
|
||||||
coin: coin_to_ethereum_coin(coin),
|
coin: coin_to_ethereum_coin(coin),
|
||||||
fee: U256::try_from(total_gas).unwrap() * fee_per_gas,
|
fee: U256::try_from(total_gas).unwrap() * fee_per_gas,
|
||||||
|
|||||||
@@ -19,10 +19,11 @@ workspace = true
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
k256 = { version = "0.13", default-features = false, features = ["std"] }
|
k256 = { version = "0.13", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
alloy-core = { version = "1", default-features = false }
|
alloy-core = { version = "0.8", default-features = false }
|
||||||
alloy-consensus = { version = "0.14", default-features = false, features = ["std"] }
|
alloy-consensus = { version = "0.9", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
alloy-rpc-types-eth = { version = "0.14", default-features = false }
|
alloy-rpc-types-eth = { version = "0.9", default-features = false }
|
||||||
alloy-provider = { version = "0.14", default-features = false }
|
alloy-simple-request-transport = { path = "../../../networks/ethereum/alloy-simple-request-transport", default-features = false }
|
||||||
|
alloy-provider = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
ethereum-primitives = { package = "serai-processor-ethereum-primitives", path = "../primitives", default-features = false }
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
use k256::{elliptic_curve::sec1::ToEncodedPoint, ProjectivePoint};
|
use k256::{elliptic_curve::sec1::ToEncodedPoint, ProjectivePoint};
|
||||||
|
|
||||||
use alloy_core::{
|
use alloy_core::{
|
||||||
primitives::{Address, U256, Bytes, Signature, TxKind},
|
primitives::{Address, U256, Bytes, PrimitiveSignature, TxKind},
|
||||||
hex::FromHex,
|
hex::FromHex,
|
||||||
};
|
};
|
||||||
use alloy_consensus::{SignableTransaction, TxLegacy, Signed};
|
use alloy_consensus::{SignableTransaction, TxLegacy, Signed};
|
||||||
|
|
||||||
use alloy_rpc_types_eth::TransactionReceipt;
|
use alloy_rpc_types_eth::TransactionReceipt;
|
||||||
|
use alloy_simple_request_transport::SimpleRequest;
|
||||||
use alloy_provider::{Provider, RootProvider};
|
use alloy_provider::{Provider, RootProvider};
|
||||||
|
|
||||||
use ethereum_primitives::{keccak256, deterministically_sign};
|
use ethereum_primitives::{keccak256, deterministically_sign};
|
||||||
@@ -23,7 +24,7 @@ fn address(point: &ProjectivePoint) -> [u8; 20] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Fund an account.
|
/// Fund an account.
|
||||||
pub async fn fund_account(provider: &RootProvider, address: Address, value: U256) {
|
pub async fn fund_account(provider: &RootProvider<SimpleRequest>, address: Address, value: U256) {
|
||||||
let _: () = provider
|
let _: () = provider
|
||||||
.raw_request("anvil_setBalance".into(), [address.to_string(), value.to_string()])
|
.raw_request("anvil_setBalance".into(), [address.to_string(), value.to_string()])
|
||||||
.await
|
.await
|
||||||
@@ -31,7 +32,10 @@ pub async fn fund_account(provider: &RootProvider, address: Address, value: U256
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Publish an already-signed transaction.
|
/// Publish an already-signed transaction.
|
||||||
pub async fn publish_tx(provider: &RootProvider, tx: Signed<TxLegacy>) -> TransactionReceipt {
|
pub async fn publish_tx(
|
||||||
|
provider: &RootProvider<SimpleRequest>,
|
||||||
|
tx: Signed<TxLegacy>,
|
||||||
|
) -> TransactionReceipt {
|
||||||
// Fund the sender's address
|
// Fund the sender's address
|
||||||
fund_account(
|
fund_account(
|
||||||
provider,
|
provider,
|
||||||
@@ -51,7 +55,7 @@ pub async fn publish_tx(provider: &RootProvider, tx: Signed<TxLegacy>) -> Transa
|
|||||||
///
|
///
|
||||||
/// The contract deployment will be done by a random account.
|
/// The contract deployment will be done by a random account.
|
||||||
pub async fn deploy_contract(
|
pub async fn deploy_contract(
|
||||||
provider: &RootProvider,
|
provider: &RootProvider<SimpleRequest>,
|
||||||
file_path: &str,
|
file_path: &str,
|
||||||
constructor_arguments: &[u8],
|
constructor_arguments: &[u8],
|
||||||
) -> Address {
|
) -> Address {
|
||||||
@@ -84,7 +88,7 @@ pub async fn deploy_contract(
|
|||||||
///
|
///
|
||||||
/// This assumes the wallet is funded.
|
/// This assumes the wallet is funded.
|
||||||
pub async fn send(
|
pub async fn send(
|
||||||
provider: &RootProvider,
|
provider: &RootProvider<SimpleRequest>,
|
||||||
wallet: &k256::ecdsa::SigningKey,
|
wallet: &k256::ecdsa::SigningKey,
|
||||||
mut tx: TxLegacy,
|
mut tx: TxLegacy,
|
||||||
) -> TransactionReceipt {
|
) -> TransactionReceipt {
|
||||||
@@ -107,7 +111,7 @@ pub async fn send(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
tx.into_signed(Signature::from(sig)).eip2718_encode(&mut bytes);
|
tx.into_signed(PrimitiveSignature::from(sig)).eip2718_encode(&mut bytes);
|
||||||
let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap();
|
let pending_tx = provider.send_raw_transaction(&bytes).await.unwrap();
|
||||||
pending_tx.get_receipt().await.unwrap()
|
pending_tx.get_receipt().await.unwrap()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "1.86"
|
channel = "1.81"
|
||||||
targets = ["wasm32-unknown-unknown"]
|
targets = ["wasm32-unknown-unknown"]
|
||||||
profile = "minimal"
|
profile = "minimal"
|
||||||
components = ["rust-src", "rustfmt", "clippy"]
|
components = ["rust-src", "rustfmt", "clippy"]
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ scale-info = { version = "2", default-features = false, features = ["derive", "b
|
|||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
serai-primitives = { path = "../primitives", version = "0.1", default-features = false }
|
||||||
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
serai-coins-primitives = { path = "../coins/primitives", version = "0.1", default-features = false }
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ serde_json = { version = "1", optional = true }
|
|||||||
serai-abi = { path = "../abi", version = "0.1" }
|
serai-abi = { path = "../abi", version = "0.1" }
|
||||||
|
|
||||||
multiaddr = { version = "0.18", optional = true }
|
multiaddr = { version = "0.18", optional = true }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", optional = true }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", optional = true }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", optional = true }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", optional = true }
|
||||||
|
|
||||||
async-lock = "3"
|
async-lock = "3"
|
||||||
|
|
||||||
|
|||||||
@@ -22,20 +22,20 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-transaction-payment = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false, features = ["serde"] }
|
serai-primitives = { path = "../../primitives", default-features = false, features = ["serde"] }
|
||||||
coins-primitives = { package = "serai-coins-primitives", path = "../primitives", default-features = false }
|
coins-primitives = { package = "serai-coins-primitives", path = "../primitives", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false, features = ["std"] }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = ["zeroize", "borsh?/std", "serde?/std", "scale/std", "scale-info/std", "sp-runtime/std", "serai-primitives/std"]
|
std = ["zeroize", "borsh?/std", "serde?/std", "scale/std", "scale-info/std", "sp-runtime/std", "serai-primitives/std"]
|
||||||
|
|||||||
@@ -22,15 +22,15 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
scale = { package = "parity-scale-codec", version = "3.6.1", default-features = false }
|
||||||
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
scale-info = { version = "2.5.0", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
frame-benchmarking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, optional = true }
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
||||||
@@ -32,16 +32,16 @@ serai-primitives = { path = "../../primitives", default-features = false }
|
|||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
||||||
|
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
||||||
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
validator-sets-pallet = { package = "serai-validator-sets-pallet", path = "../../validator-sets/pallet", default-features = false }
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", default-features = false }
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ serde = { version = "1", default-features = false, features = ["derive", "alloc"
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false }
|
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../../validator-sets/primitives", default-features = false }
|
||||||
|
|||||||
@@ -24,14 +24,14 @@ bitvec = { version = "1", default-features = false, features = ["alloc"] }
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../primitives", default-features = false }
|
in-instructions-primitives = { package = "serai-in-instructions-primitives", path = "../primitives", default-features = false }
|
||||||
@@ -43,9 +43,9 @@ genesis-liquidity-pallet = { package = "serai-genesis-liquidity-pallet", path =
|
|||||||
emissions-pallet = { package = "serai-emissions-pallet", path = "../../emissions/pallet", default-features = false }
|
emissions-pallet = { package = "serai-emissions-pallet", path = "../../emissions/pallet", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../../economic-security/pallet", default-features = false }
|
economic-security-pallet = { package = "serai-economic-security-pallet", path = "../../economic-security/pallet", default-features = false }
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ serde = { version = "1", default-features = false, features = ["derive", "alloc"
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
coins-primitives = { package = "serai-coins-primitives", path = "../../coins/primitives", default-features = false }
|
coins-primitives = { package = "serai-coins-primitives", path = "../../coins/primitives", default-features = false }
|
||||||
|
|||||||
@@ -31,18 +31,18 @@ ciphersuite = { path = "../../crypto/ciphersuite" }
|
|||||||
embedwards25519 = { path = "../../crypto/evrf/embedwards25519" }
|
embedwards25519 = { path = "../../crypto/evrf/embedwards25519" }
|
||||||
secq256k1 = { path = "../../crypto/evrf/secq256k1" }
|
secq256k1 = { path = "../../crypto/evrf/secq256k1" }
|
||||||
|
|
||||||
libp2p = "0.52"
|
libp2p = "0.54"
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate" }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-keystore = { git = "https://github.com/serai-dex/substrate" }
|
sp-keystore = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-timestamp = { git = "https://github.com/serai-dex/substrate" }
|
sp-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate" }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-blockchain = { git = "https://github.com/serai-dex/substrate" }
|
sp-blockchain = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-api = { git = "https://github.com/serai-dex/substrate" }
|
sp-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-block-builder = { git = "https://github.com/serai-dex/substrate" }
|
sp-block-builder = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate" }
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate" }
|
frame-benchmarking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
serai-runtime = { path = "../runtime", features = ["std"] }
|
serai-runtime = { path = "../runtime", features = ["std"] }
|
||||||
|
|
||||||
@@ -50,32 +50,32 @@ clap = { version = "4", features = ["derive"] }
|
|||||||
|
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
tokio = { version = "1", features = ["sync", "rt-multi-thread"] }
|
tokio = { version = "1", features = ["sync", "rt-multi-thread"] }
|
||||||
jsonrpsee = { version = "0.16", features = ["server"] }
|
jsonrpsee = { version = "0.24", features = ["server"] }
|
||||||
|
|
||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sc-offchain = { git = "https://github.com/serai-dex/substrate" }
|
sc-offchain = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" }
|
sc-transaction-pool = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-transaction-pool-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-transaction-pool-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-basic-authorship = { git = "https://github.com/serai-dex/substrate" }
|
sc-basic-authorship = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-executor = { git = "https://github.com/serai-dex/substrate" }
|
sc-executor = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-service = { git = "https://github.com/serai-dex/substrate" }
|
sc-service = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-client-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-client-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-network-common = { git = "https://github.com/serai-dex/substrate" }
|
sc-network-common = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-network = { git = "https://github.com/serai-dex/substrate" }
|
sc-network = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = [] }
|
||||||
|
|
||||||
sc-consensus = { git = "https://github.com/serai-dex/substrate" }
|
sc-consensus = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-consensus-babe = { git = "https://github.com/serai-dex/substrate" }
|
sc-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-consensus-grandpa = { git = "https://github.com/serai-dex/substrate" }
|
sc-consensus-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-authority-discovery = { git = "https://github.com/serai-dex/substrate" }
|
sc-authority-discovery = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
sc-telemetry = { git = "https://github.com/serai-dex/substrate" }
|
sc-telemetry = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
sc-cli = { git = "https://github.com/serai-dex/substrate" }
|
sc-cli = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, features = ["rocksdb"] }
|
||||||
|
|
||||||
sc-rpc-api = { git = "https://github.com/serai-dex/substrate" }
|
sc-rpc-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/substrate" }
|
substrate-frame-rpc-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/substrate" }
|
pallet-transaction-payment-rpc = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
serai-env = { path = "../../common/env" }
|
serai-env = { path = "../../common/env" }
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ bitcoin-serai = { path = "../../networks/bitcoin", default-features = false, fea
|
|||||||
monero-address = { path = "../../networks/monero/wallet/address", default-features = false, features = ["std"] }
|
monero-address = { path = "../../networks/monero/wallet/address", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
substrate-build-script-utils = { git = "https://github.com/serai-dex/substrate" }
|
substrate-build-script-utils = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ scale-info = { version = "2", default-features = false, features = ["derive"] }
|
|||||||
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
borsh = { version = "1", default-features = false, features = ["derive", "de_strict_order"], optional = true }
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
serde = { version = "1", default-features = false, features = ["derive", "alloc"], optional = true }
|
||||||
|
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
|
||||||
|
|||||||
@@ -24,37 +24,37 @@ hashbrown = { version = "0.15", default-features = false, features = ["default-h
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-offchain = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-offchain = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-version = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-version = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-inherents = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-inherents = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-session = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-session = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-consensus-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-authority-discovery = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-authority-discovery = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-transaction-pool = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-transaction-pool = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-block-builder = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-block-builder = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-executive = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-executive = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-benchmarking = { git = "https://github.com/serai-dex/substrate", default-features = false, optional = true }
|
frame-benchmarking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false, optional = true }
|
||||||
|
|
||||||
serai-primitives = { path = "../primitives", default-features = false }
|
serai-primitives = { path = "../primitives", default-features = false }
|
||||||
serai-abi = { path = "../abi", default-features = false, features = ["serde"] }
|
serai-abi = { path = "../abi", default-features = false, features = ["serde"] }
|
||||||
|
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-authorship = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-authorship = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
pallet-transaction-payment = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-transaction-payment = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
coins-pallet = { package = "serai-coins-pallet", path = "../coins/pallet", default-features = false }
|
coins-pallet = { package = "serai-coins-pallet", path = "../coins/pallet", default-features = false }
|
||||||
dex-pallet = { package = "serai-dex-pallet", path = "../dex/pallet", default-features = false }
|
dex-pallet = { package = "serai-dex-pallet", path = "../dex/pallet", default-features = false }
|
||||||
@@ -69,14 +69,14 @@ in-instructions-pallet = { package = "serai-in-instructions-pallet", path = "../
|
|||||||
|
|
||||||
signals-pallet = { package = "serai-signals-pallet", path = "../signals/pallet", default-features = false }
|
signals-pallet = { package = "serai-signals-pallet", path = "../signals/pallet", default-features = false }
|
||||||
|
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system-rpc-runtime-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system-rpc-runtime-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
substrate-wasm-builder = { git = "https://github.com/serai-dex/substrate" }
|
substrate-wasm-builder = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
std = [
|
std = [
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ workspace = true
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
serai-signals-primitives = { path = "../primitives", default-features = false }
|
serai-signals-primitives = { path = "../primitives", default-features = false }
|
||||||
|
|||||||
@@ -23,20 +23,20 @@ scale-info = { version = "2", default-features = false, features = ["derive", "b
|
|||||||
|
|
||||||
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
|
serde = { version = "1", default-features = false, features = ["derive", "alloc"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-io = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-io = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-api = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-api = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-application-crypto = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-application-crypto = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-runtime = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-session = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-session = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-staking = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-staking = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
frame-system = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-system = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
frame-support = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
frame-support = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
pallet-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
pallet-grandpa = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-grandpa = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../primitives", default-features = false }
|
validator-sets-primitives = { package = "serai-validator-sets-primitives", path = "../primitives", default-features = false }
|
||||||
@@ -45,9 +45,9 @@ coins-pallet = { package = "serai-coins-pallet", path = "../../coins/pallet", de
|
|||||||
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
dex-pallet = { package = "serai-dex-pallet", path = "../../dex/pallet", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pallet-timestamp = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
pallet-timestamp = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
sp-consensus-babe = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-consensus-babe = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
ciphersuite = { path = "../../../crypto/ciphersuite", features = ["ristretto"] }
|
ciphersuite = { path = "../../../crypto/ciphersuite", features = ["ristretto"] }
|
||||||
frost = { package = "modular-frost", path = "../../../crypto/frost", features = ["tests"] }
|
frost = { package = "modular-frost", path = "../../../crypto/frost", features = ["tests"] }
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ serde = { version = "1", default-features = false, features = ["derive", "alloc"
|
|||||||
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive", "max-encoded-len"] }
|
||||||
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
scale-info = { version = "2", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
sp-core = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-core = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
sp-std = { git = "https://github.com/serai-dex/substrate", default-features = false }
|
sp-std = { git = "https://github.com/serai-dex/polkadot-sdk", branch = "serai-next", default-features = false }
|
||||||
|
|
||||||
serai-primitives = { path = "../../primitives", default-features = false }
|
serai-primitives = { path = "../../primitives", default-features = false }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user