Compare commits
90 Commits
@0x/contra
...
feat/proto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4072724866 | ||
|
|
74be3d1d96 | ||
|
|
730b476987 | ||
|
|
84e353aa38 | ||
|
|
151915ec46 | ||
|
|
aa20eaac47 | ||
|
|
ba7599ad39 | ||
|
|
5756d7c563 | ||
|
|
2577caaf5a | ||
|
|
603813191f | ||
|
|
0484519e4d | ||
|
|
275542b6b2 | ||
|
|
f55eaa867b | ||
|
|
6b2856424a | ||
|
|
da757c4700 | ||
|
|
75e6654884 | ||
|
|
87308e7693 | ||
|
|
d5eef93a76 | ||
|
|
a7f23a982e | ||
|
|
9eadc5fc28 | ||
|
|
92ad1a612e | ||
|
|
09413c0e12 | ||
|
|
23788b41d5 | ||
|
|
ccf999a495 | ||
|
|
aa1016ee5f | ||
|
|
423ef57344 | ||
|
|
c18149e82f | ||
|
|
d14aebf724 | ||
|
|
ba719a9631 | ||
|
|
d36034d958 | ||
|
|
7750c57620 | ||
|
|
4d027e11d1 | ||
|
|
470e9a4697 | ||
|
|
7c51412e2f | ||
|
|
b3d1f3cd10 | ||
|
|
389bb77439 | ||
|
|
4327885a00 | ||
|
|
0aef0afbbb | ||
|
|
fa4c3a4f5f | ||
|
|
1d7c527c5c | ||
|
|
cbe3135e4b | ||
|
|
955ad49711 | ||
|
|
8d6f6e76e0 | ||
|
|
9337115650 | ||
|
|
fa45a44fe4 | ||
|
|
c9c7ac8559 | ||
|
|
c881723578 | ||
|
|
c9c30d3a76 | ||
|
|
73dfdb5b69 | ||
|
|
e638268f94 | ||
|
|
0bfd765481 | ||
|
|
1f12893735 | ||
|
|
dd3d9337c4 | ||
|
|
904214f4a8 | ||
|
|
64c090c4b4 | ||
|
|
e24474f152 | ||
|
|
29fa408256 | ||
|
|
1b94cc68af | ||
|
|
f5b4bb3035 | ||
|
|
afd880f28c | ||
|
|
cd14cdd168 | ||
|
|
c8ff53a75f | ||
|
|
6d08add20b | ||
|
|
29f9c725e3 | ||
|
|
a83453f07f | ||
|
|
ae365ce92c | ||
|
|
77a592e891 | ||
|
|
9a1df67d6b | ||
|
|
4b91411faf | ||
|
|
622a542d57 | ||
|
|
cba53a9a50 | ||
|
|
e186f27f63 | ||
|
|
4cd767ecb8 | ||
|
|
f6e85aedf1 | ||
|
|
b3ee294ba5 | ||
|
|
1c242def93 | ||
|
|
f0fe6f2f69 | ||
|
|
f86d555e49 | ||
|
|
b0f2c40463 | ||
|
|
87be6fbb8a | ||
|
|
9141a9d2c8 | ||
|
|
7f75de347e | ||
|
|
329f7761c3 | ||
|
|
0d8e83cd75 | ||
|
|
e5d60b8077 | ||
|
|
ae2fe55efa | ||
|
|
6073607d3e | ||
|
|
389ebb5df8 | ||
|
|
fd9655e9d4 | ||
|
|
6480aaa189 |
@@ -4,12 +4,13 @@ jobs:
|
||||
build:
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
environment:
|
||||
NODE_OPTIONS: '--max-old-space-size=16384'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init --recursive
|
||||
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
|
||||
- run:
|
||||
name: install-yarn
|
||||
@@ -19,7 +20,6 @@ jobs:
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
- setup_remote_docker
|
||||
- run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci
|
||||
- run: yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
test-exchange-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
test-integrations-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
test-contracts-staking-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
test-contracts-extra-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
test-contracts-rest-ganache:
|
||||
resource_class: medium+
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
environment:
|
||||
NODE_OPTIONS: '--max-old-space-size=6442'
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
- image: 0xorg/verdaccio
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
path: ~/.npm/_logs
|
||||
test-doc-generation:
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
@@ -108,8 +108,10 @@ jobs:
|
||||
no_output_timeout: 1200
|
||||
test-rest:
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
environment:
|
||||
RUST_ROUTER: "true"
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
@@ -136,7 +138,7 @@ jobs:
|
||||
resource_class: large
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
steps:
|
||||
- restore_cache:
|
||||
keys:
|
||||
@@ -147,7 +149,7 @@ jobs:
|
||||
- run: yarn diff_md_docs:ci
|
||||
submit-coverage:
|
||||
docker:
|
||||
- image: node:12
|
||||
- image: node:16
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- restore_cache:
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 10
|
||||
node-version: 16
|
||||
- uses: actions/setup-python@v2
|
||||
- name: 'configure git'
|
||||
run: |
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -173,6 +173,15 @@ contracts/zero-ex/test/generated-wrappers/
|
||||
contracts/treasury/generated-wrappers/
|
||||
contracts/treasury/test/generated-wrappers/
|
||||
|
||||
# foundry artifacts
|
||||
contracts/zero-ex/foundry-artifacts/
|
||||
|
||||
# foundry cache
|
||||
contracts/zero-ex/foundry-cache/
|
||||
|
||||
# typechain wrappers
|
||||
contracts/zero-ex/typechain-wrappers/
|
||||
|
||||
# Doc README copy
|
||||
packages/*/docs/README.md
|
||||
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "contracts/zero-ex/contracts/deps/forge-std"]
|
||||
path = contracts/zero-ex/contracts/deps/forge-std
|
||||
url = https://github.com/foundry-rs/forge-std
|
||||
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1650611093,
|
||||
"version": "3.3.29",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "3.3.28",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "3.3.27",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "3.3.26",
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.3.29 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.28 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.27 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.26 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
758
contracts/erc20/contracts/src/v06/WETH9.sol
Normal file
758
contracts/erc20/contracts/src/v06/WETH9.sol
Normal file
@@ -0,0 +1,758 @@
|
||||
// Copyright (C) 2015, 2016, 2017 Dapphub
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// solhint-disable
|
||||
pragma solidity ^0.6.5;
|
||||
|
||||
|
||||
contract WETH9 {
|
||||
string public name = "Wrapped Ether";
|
||||
string public symbol = "WETH";
|
||||
uint8 public decimals = 18;
|
||||
|
||||
event Approval(address indexed _owner, address indexed _spender, uint _value);
|
||||
event Transfer(address indexed _from, address indexed _to, uint _value);
|
||||
event Deposit(address indexed _owner, uint _value);
|
||||
event Withdrawal(address indexed _owner, uint _value);
|
||||
|
||||
mapping (address => uint) public balanceOf;
|
||||
mapping (address => mapping (address => uint)) public allowance;
|
||||
|
||||
receive() external payable {
|
||||
deposit();
|
||||
}
|
||||
function deposit() public payable {
|
||||
balanceOf[msg.sender] += msg.value;
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
function withdraw(uint wad) public {
|
||||
require(balanceOf[msg.sender] >= wad);
|
||||
balanceOf[msg.sender] -= wad;
|
||||
msg.sender.transfer(wad);
|
||||
emit Withdrawal(msg.sender, wad);
|
||||
}
|
||||
|
||||
function totalSupply() public view returns (uint) {
|
||||
return address(this).balance;
|
||||
}
|
||||
|
||||
function approve(address guy, uint wad) public returns (bool) {
|
||||
allowance[msg.sender][guy] = wad;
|
||||
emit Approval(msg.sender, guy, wad);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transfer(address dst, uint wad) public returns (bool) {
|
||||
return transferFrom(msg.sender, dst, wad);
|
||||
}
|
||||
|
||||
function transferFrom(address src, address dst, uint wad)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
require(balanceOf[src] >= wad);
|
||||
|
||||
if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
|
||||
require(allowance[src][msg.sender] >= wad);
|
||||
allowance[src][msg.sender] -= wad;
|
||||
}
|
||||
|
||||
balanceOf[src] -= wad;
|
||||
balanceOf[dst] += wad;
|
||||
|
||||
emit Transfer(src, dst, wad);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
||||
*/
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "3.3.26",
|
||||
"version": "3.3.29",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -51,18 +51,18 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.7.2",
|
||||
"@0x/contracts-gen": "^2.0.43",
|
||||
"@0x/contracts-test-utils": "^5.4.17",
|
||||
"@0x/contracts-utils": "^4.8.7",
|
||||
"@0x/dev-utils": "^4.2.11",
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.20",
|
||||
"@0x/contracts-utils": "^4.8.10",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "12.12.54",
|
||||
@@ -70,7 +70,7 @@
|
||||
"chai-as-promised": "^7.1.0",
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.6.0",
|
||||
"ethereum-types": "^3.7.0",
|
||||
"lodash": "^4.17.11",
|
||||
"make-promises-safe": "^1.1.0",
|
||||
"mocha": "^6.2.0",
|
||||
@@ -79,10 +79,10 @@
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "~0.16.11",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1650611093,
|
||||
"version": "5.4.20",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "5.4.19",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "5.4.18",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "5.4.17",
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.20 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.19 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.18 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.17 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.17",
|
||||
"version": "5.4.20",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -34,28 +34,28 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
|
||||
"devDependencies": {
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"shx": "^0.2.2",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.31",
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/contract-addresses": "^6.11.0",
|
||||
"@0x/dev-utils": "^4.2.11",
|
||||
"@0x/json-schemas": "^6.4.1",
|
||||
"@0x/assert": "^3.0.34",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/contract-addresses": "^6.13.0",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-coverage": "^4.0.42",
|
||||
"@0x/sol-profiler": "^4.1.32",
|
||||
"@0x/sol-trace": "^3.0.42",
|
||||
"@0x/subproviders": "^6.6.2",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"@0x/sol-coverage": "^4.0.45",
|
||||
"@0x/sol-profiler": "^4.1.35",
|
||||
"@0x/sol-trace": "^3.0.45",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/js-combinatorics": "^0.5.29",
|
||||
"@types/lodash": "4.14.104",
|
||||
@@ -67,7 +67,7 @@
|
||||
"chai-bignumber": "^3.0.0",
|
||||
"decimal.js": "^10.2.0",
|
||||
"dirty-chai": "^2.0.1",
|
||||
"ethereum-types": "^3.6.0",
|
||||
"ethereum-types": "^3.7.0",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
"ethers": "~4.0.4",
|
||||
"js-combinatorics": "^0.5.3",
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1650611093,
|
||||
"version": "1.4.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "1.4.11",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "1.4.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "1.4.9",
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.4.12 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.11 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.10 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.4.9 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.4.9",
|
||||
"version": "1.4.12",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -46,14 +46,14 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.7.2",
|
||||
"@0x/contract-addresses": "^6.11.0",
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contract-addresses": "^6.13.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.26",
|
||||
"@0x/contracts-gen": "^2.0.43",
|
||||
"@0x/contracts-erc20": "^3.3.29",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.17",
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/contracts-test-utils": "^5.4.20",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@types/isomorphic-fetch": "^0.0.35",
|
||||
@@ -69,17 +69,17 @@
|
||||
"solhint": "^1.4.1",
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "~0.16.11",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/protocol-utils": "^1.11.0",
|
||||
"@0x/subproviders": "^6.6.2",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"ethereum-types": "^3.6.0",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/protocol-utils": "^11.12.0",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"ethereum-types": "^3.7.0",
|
||||
"ethereumjs-util": "^7.0.10"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1650611093,
|
||||
"version": "4.8.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "4.8.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "4.8.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "4.8.7",
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.8.10 - _April 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.9 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.8 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.8.7 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.8.7",
|
||||
"version": "4.8.10",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -50,15 +50,15 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.7.2",
|
||||
"@0x/contracts-gen": "^2.0.43",
|
||||
"@0x/contracts-test-utils": "^5.4.17",
|
||||
"@0x/dev-utils": "^4.2.11",
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.20",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"@types/bn.js": "^4.11.0",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
@@ -76,14 +76,14 @@
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"bn.js": "^4.11.8",
|
||||
"ethereum-types": "^3.6.0"
|
||||
"ethereum-types": "^3.7.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
[
|
||||
{
|
||||
"version": "0.32.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add support for `BalancerV2Batch` fills in FQT",
|
||||
"pr": 462
|
||||
}
|
||||
],
|
||||
"timestamp": 1650611093
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "0.31.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1646225739,
|
||||
"version": "0.31.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.31.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.32.0 - _April 22, 2022_
|
||||
|
||||
* Add support for `BalancerV2Batch` fills in FQT (#462)
|
||||
|
||||
## v0.31.2 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.31.1 - _March 2, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.31.0 - _February 22, 2022_
|
||||
|
||||
* Add ERC721OrdersFeature, ERC1155OrdersFeature, and ERC165Feature (#429)
|
||||
|
||||
1
contracts/zero-ex/contracts/deps/forge-std
Submodule
1
contracts/zero-ex/contracts/deps/forge-std
Submodule
Submodule contracts/zero-ex/contracts/deps/forge-std added at 1680d7fb3e
@@ -25,6 +25,7 @@ import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinAaveV2.sol";
|
||||
import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBalancerV2.sol";
|
||||
import "./mixins/MixinBalancerV2Batch.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinCoFiX.sol";
|
||||
import "./mixins/MixinCompound.sol";
|
||||
@@ -52,6 +53,7 @@ contract BridgeAdapter is
|
||||
MixinAaveV2,
|
||||
MixinBalancer,
|
||||
MixinBalancerV2,
|
||||
MixinBalancerV2Batch,
|
||||
MixinBancor,
|
||||
MixinCoFiX,
|
||||
MixinCompound,
|
||||
@@ -159,6 +161,11 @@ contract BridgeAdapter is
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
|
||||
boughtAmount = _tradeBalancerV2Batch(
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.KYBER) {
|
||||
boughtAmount = _tradeKyber(
|
||||
sellToken,
|
||||
|
||||
@@ -27,29 +27,30 @@ library BridgeProtocols {
|
||||
// A incrementally increasing, append-only list of protocol IDs.
|
||||
// We don't use an enum so solidity doesn't throw when we pass in a
|
||||
// new protocol ID that hasn't been rolled up yet.
|
||||
uint128 internal constant UNKNOWN = 0;
|
||||
uint128 internal constant CURVE = 1;
|
||||
uint128 internal constant UNISWAPV2 = 2;
|
||||
uint128 internal constant UNISWAP = 3;
|
||||
uint128 internal constant BALANCER = 4;
|
||||
uint128 internal constant KYBER = 5;
|
||||
uint128 internal constant MOONISWAP = 6;
|
||||
uint128 internal constant MSTABLE = 7;
|
||||
uint128 internal constant OASIS = 8;
|
||||
uint128 internal constant SHELL = 9;
|
||||
uint128 internal constant DODO = 10;
|
||||
uint128 internal constant DODOV2 = 11;
|
||||
uint128 internal constant CRYPTOCOM = 12;
|
||||
uint128 internal constant BANCOR = 13;
|
||||
uint128 internal constant COFIX = 14;
|
||||
uint128 internal constant NERVE = 15;
|
||||
uint128 internal constant MAKERPSM = 16;
|
||||
uint128 internal constant BALANCERV2 = 17;
|
||||
uint128 internal constant UNISWAPV3 = 18;
|
||||
uint128 internal constant KYBERDMM = 19;
|
||||
uint128 internal constant CURVEV2 = 20;
|
||||
uint128 internal constant LIDO = 21;
|
||||
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
|
||||
uint128 internal constant AAVEV2 = 23;
|
||||
uint128 internal constant COMPOUND = 24;
|
||||
uint128 internal constant UNKNOWN = 0;
|
||||
uint128 internal constant CURVE = 1;
|
||||
uint128 internal constant UNISWAPV2 = 2;
|
||||
uint128 internal constant UNISWAP = 3;
|
||||
uint128 internal constant BALANCER = 4;
|
||||
uint128 internal constant KYBER = 5;
|
||||
uint128 internal constant MOONISWAP = 6;
|
||||
uint128 internal constant MSTABLE = 7;
|
||||
uint128 internal constant OASIS = 8;
|
||||
uint128 internal constant SHELL = 9;
|
||||
uint128 internal constant DODO = 10;
|
||||
uint128 internal constant DODOV2 = 11;
|
||||
uint128 internal constant CRYPTOCOM = 12;
|
||||
uint128 internal constant BANCOR = 13;
|
||||
uint128 internal constant COFIX = 14;
|
||||
uint128 internal constant NERVE = 15;
|
||||
uint128 internal constant MAKERPSM = 16;
|
||||
uint128 internal constant BALANCERV2 = 17;
|
||||
uint128 internal constant UNISWAPV3 = 18;
|
||||
uint128 internal constant KYBERDMM = 19;
|
||||
uint128 internal constant CURVEV2 = 20;
|
||||
uint128 internal constant LIDO = 21;
|
||||
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
|
||||
uint128 internal constant AAVEV2 = 23;
|
||||
uint128 internal constant COMPOUND = 24;
|
||||
uint128 internal constant BALANCERV2BATCH = 25;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
interface IBalancerV2BatchSwapVault {
|
||||
|
||||
enum SwapKind { GIVEN_IN, GIVEN_OUT }
|
||||
|
||||
struct BatchSwapStep {
|
||||
bytes32 poolId;
|
||||
uint256 assetInIndex;
|
||||
uint256 assetOutIndex;
|
||||
uint256 amount;
|
||||
bytes userData;
|
||||
}
|
||||
|
||||
struct FundManagement {
|
||||
address sender;
|
||||
bool fromInternalBalance;
|
||||
address payable recipient;
|
||||
bool toInternalBalance;
|
||||
}
|
||||
|
||||
function batchSwap(
|
||||
SwapKind kind,
|
||||
BatchSwapStep[] calldata swaps,
|
||||
IERC20TokenV06[] calldata assets,
|
||||
FundManagement calldata funds,
|
||||
int256[] calldata limits,
|
||||
uint256 deadline
|
||||
) external returns (int256[] memory amounts);
|
||||
}
|
||||
|
||||
contract MixinBalancerV2Batch {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
struct BalancerV2BatchBridgeData {
|
||||
IBalancerV2BatchSwapVault vault;
|
||||
IBalancerV2BatchSwapVault.BatchSwapStep[] swapSteps;
|
||||
IERC20TokenV06[] assets;
|
||||
}
|
||||
|
||||
function _tradeBalancerV2Batch(
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data.
|
||||
(
|
||||
IBalancerV2BatchSwapVault vault,
|
||||
IBalancerV2BatchSwapVault.BatchSwapStep[] memory swapSteps,
|
||||
address[] memory assets_
|
||||
) = abi.decode(bridgeData, (IBalancerV2BatchSwapVault, IBalancerV2BatchSwapVault.BatchSwapStep[], address[]));
|
||||
IERC20TokenV06[] memory assets;
|
||||
assembly { assets := assets_ }
|
||||
|
||||
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
|
||||
assets[0].approveIfBelow(address(vault), sellAmount);
|
||||
|
||||
swapSteps[0].amount = sellAmount;
|
||||
int256[] memory limits = new int256[](assets.length);
|
||||
for (uint256 i = 0; i < limits.length; ++i) {
|
||||
limits[i] = type(int256).max;
|
||||
}
|
||||
|
||||
int256[] memory amounts = vault.batchSwap(
|
||||
IBalancerV2BatchSwapVault.SwapKind.GIVEN_IN,
|
||||
swapSteps,
|
||||
assets,
|
||||
IBalancerV2BatchSwapVault.FundManagement({
|
||||
sender: address(this),
|
||||
fromInternalBalance: false,
|
||||
recipient: payable(address(this)),
|
||||
toInternalBalance: false
|
||||
}),
|
||||
limits,
|
||||
block.timestamp + 1
|
||||
);
|
||||
require(amounts[amounts.length - 1] <= 0, 'Unexpected BalancerV2Batch output');
|
||||
return uint256(amounts[amounts.length - 1] * -1);
|
||||
}
|
||||
}
|
||||
30
contracts/zero-ex/contracts/test/foundry/ContractTest.sol
Normal file
30
contracts/zero-ex/contracts/test/foundry/ContractTest.sol
Normal file
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
contract ContractTest is Test {
|
||||
function setUp() public {}
|
||||
|
||||
function testExample() public {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "src/features/OtcOrdersFeature.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/features/UniswapFeature.sol";
|
||||
import "src/features/UniswapV3Feature.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "../utils/DeployZeroEx.sol";
|
||||
|
||||
contract Architecture is DeployZeroEx {
|
||||
|
||||
function testFeatureDeployment()
|
||||
public
|
||||
{
|
||||
deployZeroEx();
|
||||
// If we look up the address of the Uniswap Implementation, it will be empty
|
||||
// as it hasn't yet been registered.
|
||||
emit log_named_address("sellToUniswap implementation", ZERO_EX.getFunctionImplementation(UniswapFeature.sellToUniswap.selector));
|
||||
// Try registering the implementation
|
||||
// Technically the constructor argument is the WETH address, which doesn't yet exist in this environment
|
||||
UniswapFeature uniswapFeature = new UniswapFeature(IEtherTokenV06(address(0)));
|
||||
emit log_named_address("UniswapFeature deployed at", address(uniswapFeature));
|
||||
// As part of the migration, the UniswapFeature registers its own selectors
|
||||
IZERO_EX.migrate(address(uniswapFeature), abi.encodePacked(uniswapFeature.migrate.selector), address(this));
|
||||
// You will observe in the logs the following events
|
||||
// ├─ emit ProxyFunctionUpdated(selector: 0xd9627aa4, oldImpl: 0x0000000000000000000000000000000000000000, newImpl: UniswapFeature: [0xf5a2fe45f4f1308502b1c136b9ef8af136141382])
|
||||
// sellToUniswap(address[],uint256,uint256,bool) = 0xd9627aa4
|
||||
// cast keccak 'sellToUniswap(address[],uint256,uint256,bool)'
|
||||
// > 0xd9627aa4...
|
||||
emit log_named_address("sellToUniswap implementation", ZERO_EX.getFunctionImplementation(UniswapFeature.sellToUniswap.selector));
|
||||
// From now on it will be possible to call 0xEP.sellToUniswap which will be registered.
|
||||
|
||||
// Try deploying another Feature such as
|
||||
|
||||
// OtcOrdersFeature
|
||||
// ...
|
||||
emit log_named_address("fillOtcOrder implementation", ZERO_EX.getFunctionImplementation(OtcOrdersFeature.fillOtcOrder.selector));
|
||||
|
||||
// UniswapV3Feature
|
||||
// ...
|
||||
emit log_named_address("sellTokenForTokenToUniswapV3 implementation", ZERO_EX.getFunctionImplementation(UniswapV3Feature.sellTokenForTokenToUniswapV3.selector));
|
||||
|
||||
// Or for extra credit, the TransformERC20Feature
|
||||
emit log_named_address("transformERC20 implementation", ZERO_EX.getFunctionImplementation(TransformERC20Feature.transformERC20.selector));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
interface LanguageContract {
|
||||
function hello()
|
||||
external
|
||||
returns (address, string memory);
|
||||
}
|
||||
|
||||
contract ContractA is LanguageContract {
|
||||
function hello()
|
||||
public
|
||||
override
|
||||
returns (address, string memory)
|
||||
{
|
||||
return (address(this), "Why hello there!");
|
||||
}
|
||||
}
|
||||
|
||||
contract ContractB is LanguageContract {
|
||||
function hello()
|
||||
public
|
||||
override
|
||||
returns (address, string memory)
|
||||
{
|
||||
return (address(this), "Protocol Academy was here");
|
||||
}
|
||||
}
|
||||
|
||||
contract DelegateCaller {
|
||||
function delegateCall(address impl)
|
||||
public
|
||||
returns (address addr, string memory str)
|
||||
{
|
||||
(bool success, bytes memory resultData) = impl.delegatecall(abi.encodePacked(LanguageContract.hello.selector));
|
||||
(addr, str) = abi.decode(resultData, (address, string));
|
||||
}
|
||||
}
|
||||
|
||||
contract DelegateCall is Test {
|
||||
LanguageContract A;
|
||||
LanguageContract B;
|
||||
DelegateCaller caller;
|
||||
|
||||
function setUp()
|
||||
public
|
||||
{
|
||||
A = new ContractA();
|
||||
B = new ContractB();
|
||||
caller = new DelegateCaller();
|
||||
}
|
||||
|
||||
|
||||
function testDelegateCall()
|
||||
public
|
||||
returns (uint256 val)
|
||||
{
|
||||
// ← DelegateCaller: [0xefc56627233b02ea95bae7e19f648d7dcd5bb132], "Why hello there!"
|
||||
caller.delegateCall(address(A));
|
||||
|
||||
// ← DelegateCaller: [0xefc56627233b02ea95bae7e19f648d7dcd5bb132], "Protocol Academy was here"
|
||||
caller.delegateCall(address(B));
|
||||
|
||||
// Note how in the above results the context is always the DelegateCaller context
|
||||
// with address 0xefc56627233b02ea95bae7e19f648d7dcd5bb132
|
||||
|
||||
// ← ContractA: [0xce71065d4017f316ec606fe4422e11eb2c47c246], "Why hello there!"
|
||||
A.hello();
|
||||
|
||||
// ← ContractB: [0x185a4dc360ce69bdccee33b3784b0282f7961aea], "Protocol Academy was here"
|
||||
B.hello();
|
||||
|
||||
// Note how in the above results the context is each individual contract
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "src/IZeroEx.sol";
|
||||
import "src/ZeroEx.sol";
|
||||
import "src/migrations/InitialMigration.sol";
|
||||
import "src/features/OtcOrdersFeature.sol";
|
||||
import "src/features/OwnableFeature.sol";
|
||||
import "src/features/SimpleFunctionRegistryFeature.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/features/UniswapFeature.sol";
|
||||
import "src/features/UniswapV3Feature.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
|
||||
contract StorageContract {
|
||||
uint256 public storedValue = 0;
|
||||
|
||||
function increase()
|
||||
public
|
||||
returns (uint256)
|
||||
{
|
||||
storedValue++;
|
||||
return storedValue;
|
||||
}
|
||||
}
|
||||
|
||||
contract OpcodeCosts is Test {
|
||||
|
||||
uint256 public storedValue = 0;
|
||||
StorageContract store;
|
||||
|
||||
function setUp()
|
||||
public
|
||||
{
|
||||
store = new StorageContract();
|
||||
}
|
||||
|
||||
|
||||
function testLoadFromStorage()
|
||||
public
|
||||
returns (uint256 val)
|
||||
{
|
||||
// [2306] StorageContract::storedValue() [staticcall]
|
||||
val = store.storedValue();
|
||||
}
|
||||
|
||||
function testLoadAndStore()
|
||||
public
|
||||
returns (uint256 val)
|
||||
{
|
||||
// [22346] StorageContract::increase()
|
||||
val = store.increase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "src/IZeroEx.sol";
|
||||
import "src/ZeroEx.sol";
|
||||
import "src/features/interfaces/IFeature.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "../utils/TestUtils.sol";
|
||||
|
||||
|
||||
// TODO: Try running this test on various networks
|
||||
// forge test --match-contract DeployedFeatures --fork-url <rpc_url> -vvv
|
||||
contract DeployedFeatures is TestUtils {
|
||||
ZeroEx public ZERO_EX = ZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
|
||||
IZeroEx public IZERO_EX = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
|
||||
|
||||
function testLookupFunction()
|
||||
public
|
||||
{
|
||||
uint256 chainId;
|
||||
assembly { chainId := chainid() }
|
||||
// Skip if not in forking mode
|
||||
if (chainId == 31337) {
|
||||
emit log_string("Not in forking mode; skipping test");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: try swapping out this selector
|
||||
bytes4 selector = TransformERC20Feature.transformERC20.selector;
|
||||
address impl = ZERO_EX.getFunctionImplementation(selector);
|
||||
if (impl == address(0)) {
|
||||
emit log_named_address("v0.0.0", impl);
|
||||
} else {
|
||||
uint256 version = IFeature(impl).FEATURE_VERSION();
|
||||
// NOTE: we used to have a bug where the version was
|
||||
// being incorrectly encoded as 0 lol
|
||||
emit log_named_address(_versionString(version), impl);
|
||||
}
|
||||
|
||||
uint256 rollbackLength = IZERO_EX.getRollbackLength(selector);
|
||||
if (rollbackLength > 1) {
|
||||
for (uint256 i = rollbackLength - 1; i > 0; i--) {
|
||||
address prevImpl = IZERO_EX.getRollbackEntryAtIndex(selector, i);
|
||||
if (prevImpl == address(0)) {
|
||||
emit log_named_address("v0.0.0", prevImpl);
|
||||
} else {
|
||||
uint256 version = IFeature(prevImpl).FEATURE_VERSION();
|
||||
emit log_named_address(_versionString(version), prevImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _versionString(uint256 encodedVersion)
|
||||
private
|
||||
pure
|
||||
returns (string memory versionString)
|
||||
{
|
||||
uint32 major = uint32(encodedVersion >> 64);
|
||||
uint32 minor = uint32(encodedVersion >> 32);
|
||||
uint32 revision = uint32(encodedVersion);
|
||||
|
||||
return string(abi.encodePacked(
|
||||
"v",
|
||||
_toString(uint256(major)),
|
||||
".",
|
||||
_toString(uint256(minor)),
|
||||
".",
|
||||
_toString(uint256(revision))
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "src/fixins/FixinCommon.sol";
|
||||
import "src/migrations/LibMigrate.sol";
|
||||
import "../utils/DeployZeroEx.sol";
|
||||
|
||||
contract FibonacciFeature is FixinCommon {
|
||||
uint256 private prevFib;
|
||||
uint256 private fib;
|
||||
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.stepFibonacci.selector);
|
||||
_registerFeatureFunction(this.currentFibonacci.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
function stepFibonacci() external {
|
||||
if (prevFib == 0 && fib == 0) {
|
||||
fib = 1;
|
||||
return;
|
||||
} else {
|
||||
uint256 nextFib = prevFib + fib;
|
||||
prevFib = fib;
|
||||
fib = nextFib;
|
||||
}
|
||||
}
|
||||
|
||||
function currentFibonacci() external view returns (uint256) {
|
||||
return fib;
|
||||
}
|
||||
}
|
||||
|
||||
contract VoteFeature is FixinCommon {
|
||||
uint256 private good;
|
||||
uint256 private bad;
|
||||
mapping (address => bool) hasVoted;
|
||||
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.pineappleOnPizzaIsGood.selector);
|
||||
_registerFeatureFunction(this.pineappleOnPizzaIsBad.selector);
|
||||
_registerFeatureFunction(this.currentVotes.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
function pineappleOnPizzaIsGood() external {
|
||||
require(!hasVoted[msg.sender]);
|
||||
hasVoted[msg.sender] = true;
|
||||
good++;
|
||||
}
|
||||
|
||||
function pineappleOnPizzaIsBad() external {
|
||||
require(!hasVoted[msg.sender]);
|
||||
hasVoted[msg.sender] = true;
|
||||
bad++;
|
||||
}
|
||||
|
||||
function currentVotes() external view returns (uint256, uint256) {
|
||||
return (good, bad);
|
||||
}
|
||||
}
|
||||
|
||||
contract FeatureStorage is DeployZeroEx {
|
||||
|
||||
function setUp() public {
|
||||
deployZeroEx();
|
||||
FibonacciFeature fibonacciFeature = new FibonacciFeature();
|
||||
emit log_named_address("FibonacciFeature deployed at", address(fibonacciFeature));
|
||||
IZERO_EX.migrate(address(fibonacciFeature), abi.encodePacked(fibonacciFeature.migrate.selector), address(this));
|
||||
emit log_named_address("stepFibonacci implementation", ZERO_EX.getFunctionImplementation(FibonacciFeature.stepFibonacci.selector));
|
||||
|
||||
VoteFeature voteFeature = new VoteFeature();
|
||||
emit log_named_address("VoteFeature deployed at", address(voteFeature));
|
||||
IZERO_EX.migrate(address(voteFeature), abi.encodePacked(voteFeature.migrate.selector), address(this));
|
||||
}
|
||||
|
||||
function testFeatureStorage()
|
||||
public
|
||||
{
|
||||
for (uint256 i = 0; i < 10; i++) {
|
||||
emit log_named_uint("fib", FibonacciFeature(address(ZERO_EX)).currentFibonacci());
|
||||
FibonacciFeature(address(ZERO_EX)).stepFibonacci();
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < 10; i++) {
|
||||
address voter = _pseudoRandomAddress(i);
|
||||
bool likesPineappleOnPizza = (uint160(voter) % 2) == 1;
|
||||
hoax(voter);
|
||||
likesPineappleOnPizza
|
||||
? VoteFeature(address(ZERO_EX)).pineappleOnPizzaIsGood()
|
||||
: VoteFeature(address(ZERO_EX)).pineappleOnPizzaIsBad();
|
||||
}
|
||||
(uint256 good, uint256 bad) = VoteFeature(address(ZERO_EX)).currentVotes();
|
||||
|
||||
emit log_named_uint("good votes", good);
|
||||
emit log_named_uint("bad votes", bad);
|
||||
emit log_named_uint("fib", FibonacciFeature(address(ZERO_EX)).currentFibonacci());
|
||||
|
||||
// TODO: Fix the Fibonacci and Vote features so that their respective storage
|
||||
// variables do not collide. Create LibFibonacciStorage and LibVoteStorage
|
||||
// to do so.
|
||||
}
|
||||
|
||||
function _pseudoRandomAddress(uint256 seed) private pure returns (address) {
|
||||
return address(uint160(uint256(keccak256(abi.encodePacked(seed)))));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "src/fixins/FixinCommon.sol";
|
||||
import "src/migrations/LibMigrate.sol";
|
||||
import "../utils/DeployZeroEx.sol";
|
||||
|
||||
contract ZooFeature is FixinCommon {
|
||||
event Meow();
|
||||
event Woof();
|
||||
event SlothNoises();
|
||||
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.cat.selector);
|
||||
_registerFeatureFunction(this.dog.selector);
|
||||
_registerFeatureFunction(this.sloth.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
function cat() external {
|
||||
emit Meow();
|
||||
}
|
||||
function dog() external {
|
||||
emit Woof();
|
||||
}
|
||||
function sloth() external {
|
||||
emit SlothNoises();
|
||||
}
|
||||
}
|
||||
|
||||
contract ZooFeatureV2 is FixinCommon {
|
||||
event Meow();
|
||||
event Purr();
|
||||
event Woof();
|
||||
event CapybaraNoises();
|
||||
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.cat.selector);
|
||||
_registerFeatureFunction(this.dog.selector);
|
||||
_registerFeatureFunction(this.capybara.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
// NOTE: the function signature for `cat` has changed, so the selectors are different!
|
||||
function cat(bool happy) external {
|
||||
if (happy) {
|
||||
emit Purr();
|
||||
} else {
|
||||
emit Meow();
|
||||
}
|
||||
}
|
||||
// dog is the same
|
||||
function dog() external {
|
||||
emit Woof();
|
||||
}
|
||||
// new!
|
||||
function capybara() external {
|
||||
emit CapybaraNoises();
|
||||
}
|
||||
// sloth is not in V2
|
||||
}
|
||||
|
||||
contract Migration is DeployZeroEx {
|
||||
|
||||
function setUp() public {
|
||||
deployZeroEx();
|
||||
}
|
||||
|
||||
function testMigration()
|
||||
public
|
||||
{
|
||||
ZooFeature zooFeature = new ZooFeature();
|
||||
IZERO_EX.migrate(address(zooFeature), abi.encodePacked(zooFeature.migrate.selector), address(this));
|
||||
|
||||
// The functions in ZooFeature are now registered in the exchange proxy!
|
||||
// Try them out.
|
||||
ZooFeature(address(ZERO_EX)).dog();
|
||||
ZooFeature(address(ZERO_EX)).cat();
|
||||
ZooFeature(address(ZERO_EX)).sloth();
|
||||
}
|
||||
|
||||
function testUpgrade()
|
||||
public
|
||||
{
|
||||
// Original version
|
||||
ZooFeature zooFeature = new ZooFeature();
|
||||
IZERO_EX.migrate(address(zooFeature), abi.encodePacked(zooFeature.migrate.selector), address(this));
|
||||
// Upgrade
|
||||
ZooFeatureV2 zooFeatureV2 = new ZooFeatureV2();
|
||||
IZERO_EX.migrate(address(zooFeatureV2), abi.encodePacked(zooFeatureV2.migrate.selector), address(this));
|
||||
|
||||
// The functions in ZooFeatureV2 are now registered in the exchange proxy!
|
||||
// Try them out.
|
||||
ZooFeatureV2(address(ZERO_EX)).dog();
|
||||
ZooFeatureV2(address(ZERO_EX)).cat(true);
|
||||
ZooFeatureV2(address(ZERO_EX)).cat(false);
|
||||
ZooFeatureV2(address(ZERO_EX)).capybara();
|
||||
|
||||
// `sloth` is still registered
|
||||
ZooFeature(address(ZERO_EX)).sloth();
|
||||
// The old `cat` function is still registered, since it has a different selector
|
||||
emit log_named_bytes('cat()', abi.encodePacked(zooFeature.cat.selector));
|
||||
emit log_named_bytes('cat(bool)', abi.encodePacked(zooFeatureV2.cat.selector));
|
||||
ZooFeature(address(ZERO_EX)).cat();
|
||||
|
||||
// Let's deregister the sloth and old cat functions
|
||||
IZERO_EX.rollback(zooFeature.cat.selector, address(0));
|
||||
IZERO_EX.rollback(zooFeature.sloth.selector, address(0));
|
||||
|
||||
// Now trying to call the old cat or sloth reverts
|
||||
try ZooFeature(address(ZERO_EX)).cat() {}
|
||||
catch (bytes memory e) {
|
||||
emit log_string("cat() reverted");
|
||||
}
|
||||
try ZooFeature(address(ZERO_EX)).sloth() {}
|
||||
catch (bytes memory e) {
|
||||
emit log_string("sloth() reverted");
|
||||
}
|
||||
|
||||
// dog() was upgraded, so it's v1 address is in the `implHistory`
|
||||
emit log_named_uint(
|
||||
"dog() rollback length",
|
||||
IZERO_EX.getRollbackLength(zooFeature.dog.selector)
|
||||
);
|
||||
emit log_named_address(
|
||||
"dog() v1 is in the history",
|
||||
IZERO_EX.getRollbackEntryAtIndex(zooFeature.dog.selector, 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../utils/ForkUtils.sol";
|
||||
import "../utils/TestUtils.sol";
|
||||
import "src/IZeroEx.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/external/TransformerDeployer.sol";
|
||||
import "src/transformers/WethTransformer.sol";
|
||||
import "src/transformers/FillQuoteTransformer.sol";
|
||||
import "src/transformers/bridges/BridgeProtocols.sol";
|
||||
import "src/transformers/bridges/BridgeAdapter.sol";
|
||||
|
||||
/*
|
||||
This test must be run in forked mode
|
||||
e.g forge test -vvvv -m 'testBasicSwap' -f ETH_RPC_URL
|
||||
It is also helpful to have an Etherscan API key exported
|
||||
export ETHERSCAN_API_KEY=
|
||||
as Foundry will fetch source code and names
|
||||
*/
|
||||
|
||||
contract BasicSwapForkedTest is
|
||||
ForkUtils,
|
||||
TestUtils
|
||||
{
|
||||
IZeroEx public IZERO_EX = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
|
||||
IEtherTokenV06 WETH = IEtherTokenV06(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
||||
|
||||
// These addresses are taken from contract-addresses for Ethereum Mainnet
|
||||
TransformerDeployer transformerDeployer = TransformerDeployer(0x39dCe47a67aD34344EAB877eaE3Ef1FA2a1d50Bb);
|
||||
WethTransformer wethTransformer = WethTransformer(0xb2bc06a4EfB20FC6553a69Dbfa49B7bE938034A7);
|
||||
// Note this may be outdated as it is often updated
|
||||
FillQuoteTransformer fillQuoteTransformer = FillQuoteTransformer(0xADBE39F2988A8Be1C1120F05e28CC888b150c8a6);
|
||||
|
||||
function setUp()
|
||||
public
|
||||
onlyForked()
|
||||
{
|
||||
// HACK we deploy some fake transformers just so Foundry
|
||||
// can detect these contracts for decoding
|
||||
new WethTransformer(IEtherTokenV06(address(0)));
|
||||
vm.label(address(wethTransformer), "WethTransformer");
|
||||
new FillQuoteTransformer(IBridgeAdapter(address(0)), INativeOrdersFeature(address(0)));
|
||||
vm.label(address(fillQuoteTransformer), "FillQuoteTransformer");
|
||||
new BridgeAdapter(IEtherTokenV06(address(0)));
|
||||
vm.label(address(fillQuoteTransformer.bridgeAdapter()), "BridgeAdapter");
|
||||
vm.label(address(IZERO_EX.getTransformWallet()), "FlashWallet");
|
||||
}
|
||||
|
||||
function testBasicSwap()
|
||||
public
|
||||
onlyForked()
|
||||
{
|
||||
IERC20TokenV06 USDC = IERC20TokenV06(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
|
||||
// Create our list of transformations, let's do WethTransformer and FillQuoteTransformer
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](2);
|
||||
// Use our cheeky search helper to find the nonce rather than hardcode it
|
||||
// hint: it's 6 for WethTransformer and 22 for this FillQuoteTransformer
|
||||
transformations[0].deploymentNonce = _findTransformerNonce(address(wethTransformer), address(transformerDeployer));
|
||||
transformations[1].deploymentNonce = _findTransformerNonce(address(fillQuoteTransformer), address(transformerDeployer));
|
||||
|
||||
emit log_named_uint("WethTransformer nonce", transformations[0].deploymentNonce);
|
||||
emit log_named_uint("FillQuoteTransformer nonce", transformations[1].deploymentNonce);
|
||||
|
||||
// Set the first transformation to transform ETH into WETH
|
||||
transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18);
|
||||
|
||||
// Set up the FillQuoteTransformer data
|
||||
FillQuoteTransformer.TransformData memory fqtData;
|
||||
fqtData.side = FillQuoteTransformer.Side.Sell;
|
||||
// FQT deals with tokens, not ETH, so it needs a WETH transformer
|
||||
// to be applied beforehand
|
||||
fqtData.sellToken = IERC20TokenV06(address(WETH));
|
||||
fqtData.buyToken = IERC20TokenV06(address(USDC));
|
||||
// the FQT has a sequence, e.g first RFQ then Limit then Bridge
|
||||
// since solidity doesn't support arrays of different types, this is one simple solution
|
||||
// We use a Bridge order type here as we will fill on UniswapV2
|
||||
fqtData.fillSequence = new FillQuoteTransformer.OrderType[](1);
|
||||
fqtData.fillSequence[0] = FillQuoteTransformer.OrderType.Bridge;
|
||||
// The amount to fill
|
||||
fqtData.fillAmount = 1e18;
|
||||
// Now let's set up a UniswapV2 fill
|
||||
fqtData.bridgeOrders = new IBridgeAdapter.BridgeOrder[](1);
|
||||
IBridgeAdapter.BridgeOrder memory order;
|
||||
// The ID is shifted so we can concat <PROTOCOL><NAME>
|
||||
// e.g <UniswapV2Protocol><UniswapV2>
|
||||
// or <UniswapV2Protocol><SushiSwap> for forks
|
||||
order.source = bytes32(uint256(BridgeProtocols.UNISWAPV2) << 128);
|
||||
// How much we want to fill on this order, which can be different to the total
|
||||
// e.g 50/50 split this would be half
|
||||
order.takerTokenAmount = 1e18;
|
||||
// Set this low as the price of ETH/USDC can change
|
||||
order.makerTokenAmount = 1;
|
||||
// The data needed specifically for the source to fill,
|
||||
// e.g for UniswapV2 it is the router contract and a path. See MixinUniswapV2
|
||||
address[] memory uniPath = new address[](2);
|
||||
uniPath[0] = address(WETH);
|
||||
uniPath[1] = address(USDC);
|
||||
order.bridgeData = abi.encode(address(0xf164fC0Ec4E93095b804a4795bBe1e041497b92a), uniPath);
|
||||
fqtData.bridgeOrders[0] = order;
|
||||
// Now encode the FQT data into the transformation
|
||||
transformations[1].data = abi.encode(fqtData);
|
||||
|
||||
|
||||
// Now let's do it!
|
||||
// Give ourselves 1e18 ETH
|
||||
vm.deal(address(this), 1e18);
|
||||
IZERO_EX.transformERC20{value: 1e18}(
|
||||
// input token
|
||||
IERC20TokenV06(LibERC20Transformer.ETH_TOKEN_ADDRESS),
|
||||
// output token
|
||||
IERC20TokenV06(address(USDC)),
|
||||
// input token amount
|
||||
1e18,
|
||||
// min output token amount, set this low as ETH/USDC price will move
|
||||
1,
|
||||
// list of transform
|
||||
transformations
|
||||
);
|
||||
// Hoollly heck we bought some USDC
|
||||
assertGt(USDC.balanceOf(address(this)), 0);
|
||||
emit log_named_uint("USDC bought", USDC.balanceOf(address(this)));
|
||||
|
||||
/*
|
||||
Homework
|
||||
Try the following:
|
||||
* make this UniswapV2 trade go ETH->DAI->USDC
|
||||
* combine multiple trades, e.g Uniswap V2 ETH->USDC and ETH->DAI->USDC
|
||||
* create a UniswapV3 trade
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../utils/DeployZeroEx.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "src/features/TransformERC20Feature.sol";
|
||||
import "src/external/TransformerDeployer.sol";
|
||||
import "src/transformers/WethTransformer.sol";
|
||||
import "src/transformers/FillQuoteTransformer.sol";
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/WETH9.sol";
|
||||
|
||||
contract TransformERC20FeatureTest is DeployZeroEx {
|
||||
IEtherTokenV06 WETH;
|
||||
|
||||
TransformerDeployer transformerDeployer;
|
||||
WethTransformer wethTransformer;
|
||||
TransformERC20Feature transformERC20Feature;
|
||||
|
||||
function setUp() public {
|
||||
deployZeroEx();
|
||||
// Deploy from bytecode as this is Solidity v5
|
||||
WETH = IEtherTokenV06(deployCode("foundry-artifacts/WETH9.sol/WETH9.json"));
|
||||
emit log_named_address("WETH deployed at", address(WETH));
|
||||
}
|
||||
|
||||
function testMigration()
|
||||
public
|
||||
{
|
||||
// Note there is a lot here, so feel free to comment out further sections
|
||||
// so you can read the logs and traces
|
||||
|
||||
/*
|
||||
Deploy TransformERC20Feature
|
||||
*/
|
||||
|
||||
// Create a new TransformerDeployer, which will deploy all future transformers
|
||||
// Owners are typically EOA which are allowed to deploy
|
||||
// For this we will use this current contract address
|
||||
address[] memory owners = new address[](1);
|
||||
owners[0] = address(this);
|
||||
transformerDeployer = new TransformerDeployer(owners);
|
||||
emit log_named_address("TransformerDeployer deployed at", address(transformerDeployer));
|
||||
|
||||
|
||||
transformERC20Feature = new TransformERC20Feature();
|
||||
emit log_named_address("TransformERC20Feature deployed at", address(transformERC20Feature));
|
||||
|
||||
// Migrate 0x EP and initialize the TransformERC20Feature
|
||||
// Remember this performs a delegatecall and registers function selectors
|
||||
// function migrate(address target, bytes calldata data, address newOwner)
|
||||
IZERO_EX.migrate(
|
||||
address(transformERC20Feature),
|
||||
// Use encodeWithSelector to generate low level call data with the required arguments
|
||||
// <function selector><arg1>
|
||||
// The required migration function to call is the following:
|
||||
// function migrate(address transformerDeployer)
|
||||
// e.g ce5494bb00000000000000000000000042997ac9251e5bb0a61f4ff790e5b991ea07fd9b
|
||||
// try: cast calldata 'migrate(address)' '0x42997ac9251e5bb0a61f4ff790e5b991ea07fd9b'
|
||||
abi.encodeWithSelector(transformERC20Feature.migrate.selector, address(transformerDeployer)),
|
||||
address(this)
|
||||
);
|
||||
|
||||
// As part of the migration process a FlashWallet was generated inside of the 0x EP context
|
||||
emit log_named_address("FlashWallet deployed at", address(IZERO_EX.getTransformWallet()));
|
||||
// Homework: Recall why transformERC20Feature.getTransformWallet() returns address(0)
|
||||
|
||||
/*
|
||||
Deploy a WETH transformer
|
||||
*/
|
||||
|
||||
// Typically the code to deployed is passed in off-chain, so we will emulate something similar
|
||||
// by using vm.getCode cheatcode.
|
||||
wethTransformer = WethTransformer(transformerDeployer.deploy(
|
||||
abi.encodePacked(
|
||||
// Deploy the WethTransformer code
|
||||
vm.getCode("foundry-artifacts/WethTransformer.sol/WethTransformer.json"),
|
||||
// WethTransformer takes WETH address as a constructor argument
|
||||
abi.encode(address(WETH))
|
||||
)
|
||||
));
|
||||
assertEq(address(wethTransformer.weth()), address(WETH));
|
||||
emit log_named_address("WethTransformer deployed at", address(IZERO_EX.getTransformWallet()));
|
||||
|
||||
// Not much we can do with just a WethTransformer, I guess we could go ETH->WETH with it
|
||||
// Cheat code to give this contract 1e18 ETH
|
||||
vm.deal(address(this), 1e18);
|
||||
|
||||
// Deployment nonce is 1 as it is the first contract TransformerDeployer deployed
|
||||
// let's assert that is true
|
||||
assertEq(
|
||||
LibERC20Transformer.getDeployedAddress(address(transformerDeployer), 1),
|
||||
address(wethTransformer)
|
||||
);
|
||||
|
||||
/*
|
||||
Perform a ETH->WETH transformation
|
||||
*/
|
||||
|
||||
// Create our list of transformations, this will just have a WethTransformer
|
||||
ITransformERC20Feature.Transformation[] memory transformations = new ITransformERC20Feature.Transformation[](1);
|
||||
transformations[0].deploymentNonce = 1;
|
||||
transformations[0].data = abi.encode(LibERC20Transformer.ETH_TOKEN_ADDRESS, 1e18);
|
||||
|
||||
IZERO_EX.transformERC20{value: 1e18}(
|
||||
// input token
|
||||
IERC20TokenV06(LibERC20Transformer.ETH_TOKEN_ADDRESS),
|
||||
// output token
|
||||
IERC20TokenV06(address(WETH)),
|
||||
// input token amount
|
||||
1e18,
|
||||
// min output token amount
|
||||
1e18,
|
||||
// list of transform
|
||||
transformations
|
||||
);
|
||||
assertEq(WETH.balanceOf(address(this)), 1e18);
|
||||
|
||||
/*
|
||||
Perform a WETH->ETH transformation
|
||||
*/
|
||||
|
||||
// Let's go backwards, remember we need to set the approval of WETH to the 0x EP
|
||||
WETH.approve(address(IZERO_EX), uint256(-1));
|
||||
// Override the data in the previous transformation
|
||||
transformations[0].data = abi.encode(address(WETH), 1e18);
|
||||
// Note: No { value } here as it's a token
|
||||
IZERO_EX.transformERC20(
|
||||
// input token
|
||||
IERC20TokenV06(address(WETH)),
|
||||
// output token
|
||||
IERC20TokenV06(LibERC20Transformer.ETH_TOKEN_ADDRESS),
|
||||
// input token amount
|
||||
1e18,
|
||||
// min output token amount
|
||||
1e18,
|
||||
// list of transform
|
||||
transformations
|
||||
);
|
||||
|
||||
/*
|
||||
Deploy FillQuoteTransformer and BridgeAdapter
|
||||
*/
|
||||
|
||||
// At this time I would show how to do a ETH->USDC trade
|
||||
// but we need to set up more of the world, or to run in forked mode
|
||||
// with an AMM (find this in BasicSwapForked.t.sol)
|
||||
|
||||
// Instead let's just deploy the FQT and BridgeAdapter and as homework
|
||||
// you might explore running in forked mode and using transformations
|
||||
address bridgeAdapter = deployCode("foundry-artifacts/BridgeAdapter.sol/BridgeAdapter.json", abi.encode(address(WETH)));
|
||||
FillQuoteTransformer fillQuoteTransformer = FillQuoteTransformer(transformerDeployer.deploy(
|
||||
abi.encodePacked(
|
||||
// Deploy the FillQuoteTransformer code
|
||||
vm.getCode("foundry-artifacts/FillQuoteTransformer.sol/FillQuoteTransformer.json"),
|
||||
// FillQuoteTransformer takes the BridgeAdapter and ZeroEx address as arguments
|
||||
abi.encode(bridgeAdapter, address(ZERO_EX))
|
||||
)
|
||||
));
|
||||
assertEq(address(fillQuoteTransformer.bridgeAdapter()), bridgeAdapter);
|
||||
assertEq(
|
||||
LibERC20Transformer.getDeployedAddress(address(transformerDeployer), 2),
|
||||
address(fillQuoteTransformer)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// Note this is needed as the test is a contract, and to receive ETH
|
||||
// it needs to declare a receive function as per the Solidity requirements
|
||||
receive() external payable {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Receiver {
|
||||
function receivePure() public pure {
|
||||
1 + 1;
|
||||
}
|
||||
|
||||
function receiveImpure() public {
|
||||
1 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
contract Empty {
|
||||
|
||||
}
|
||||
|
||||
contract Call {
|
||||
address empty = address(new Empty());
|
||||
address receiver = address(new Receiver());
|
||||
|
||||
function callUnknown() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callUnknown()' --tc Call
|
||||
|
||||
// Why didn't this contract make a call out to 0x222... ?
|
||||
// Did solidity add a check for us?
|
||||
Receiver(0x2222222222222222222222222222222222222222).receiveImpure();
|
||||
}
|
||||
|
||||
function callEmpty() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callEmpty()' --tc Call
|
||||
|
||||
// Observe how the added Solidity checks "pass"
|
||||
// and ultimately how this results in a REVERT
|
||||
Receiver(empty).receiveImpure();
|
||||
}
|
||||
|
||||
function callEmptyRaw() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callEmptyRaw()' --tc Call
|
||||
|
||||
// Observe this low level call and how it differs from the above
|
||||
// example. Does it revert?
|
||||
empty.call(abi.encodeWithSelector(Receiver.receiveImpure.selector));
|
||||
}
|
||||
|
||||
function callEmptyEOA() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callEmptyEOA()' --tc Call
|
||||
|
||||
// Observe this low level call and how it differs from the above
|
||||
// example. Does it revert?
|
||||
address(msg.sender).call(abi.encodeWithSelector(Receiver.receiveImpure.selector));
|
||||
}
|
||||
|
||||
function callReceiverImpure() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callReceiverImpure()' --tc Call
|
||||
|
||||
// Observe how a CALL is set up and then ultimately made
|
||||
// and any checks prior to making the CALL
|
||||
Receiver(receiver).receiveImpure();
|
||||
}
|
||||
|
||||
function callReceiverPure() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Call.t.sol --sig 'callReceiverPure()' --tc Call
|
||||
|
||||
// Did you notice the different CALL type solidity chose here ?
|
||||
Receiver(receiver).receivePure();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Counter {
|
||||
uint256 private data = 1337;
|
||||
|
||||
function increment(uint256 val) public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Counter.t.sol --sig 'increment(uint256)' 10
|
||||
data += val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Hash {
|
||||
bytes data = hex"42";
|
||||
|
||||
function sha3() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Hash.sol --sig 'sha3()'
|
||||
|
||||
// Follow the opcodes:
|
||||
// how does the data get loaded
|
||||
// which one is keccak256
|
||||
keccak256(data);
|
||||
}
|
||||
|
||||
function sha2() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Hash.sol --sig 'sha2()'
|
||||
|
||||
// Follow the opcodes:
|
||||
// why is sha256 so different from keccak256
|
||||
sha256(data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Memory {
|
||||
|
||||
function expand() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Memory.t.sol --sig 'expand()'
|
||||
|
||||
// Observe the free memory pointer increase
|
||||
// when more memory is allocated
|
||||
bytes32[3] memory data;
|
||||
}
|
||||
|
||||
function mstore() public {
|
||||
// forge debug contracts/test/foundry/protocol-academy/8-evm/Memory.t.sol --sig 'mstore()'
|
||||
|
||||
// Observe the free memory pointer increase
|
||||
// when more memory is allocated
|
||||
bytes1[4] memory data;
|
||||
// Observe each value being set in the memory
|
||||
// slots
|
||||
data[1] = 0x42;
|
||||
data[0] = 0x0a;
|
||||
data[2] = 0xff;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "src/IZeroEx.sol";
|
||||
import "src/ZeroEx.sol";
|
||||
import "src/migrations/InitialMigration.sol";
|
||||
import "src/features/OwnableFeature.sol";
|
||||
import "src/features/SimpleFunctionRegistryFeature.sol";
|
||||
|
||||
|
||||
contract DeployZeroEx is Test {
|
||||
ZeroEx public ZERO_EX = ZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
|
||||
IZeroEx public IZERO_EX = IZeroEx(0xDef1C0ded9bec7F1a1670819833240f027b25EfF);
|
||||
|
||||
function deployZeroEx()
|
||||
internal
|
||||
{
|
||||
// HERE BE DRAGONS, feel free to ignore this for now
|
||||
// We want to deploy the ZeroEx contract, at 0xDef1C0ded9bec7F1a1670819833240f027b25EfF
|
||||
|
||||
// We use a special mechanism to deploy the ZeroEx contract.
|
||||
InitialMigration initialMigration = InitialMigration(deployCode(
|
||||
"foundry-artifacts/InitialMigration.sol/InitialMigration.json",
|
||||
abi.encode(address(this))
|
||||
));
|
||||
// Must occur from this address as the first transaction
|
||||
hoax(0xe750ad66DE350F8110E305fb78Ec6A9f594445E3);
|
||||
// Special Deployer code
|
||||
bytes memory deployerBytecode = hex"608060405234801561001057600080fd5b506040516103da3803806103da83398101604081905261002f91610077565b8060405161003c9061006a565b610046919061011f565b604051809103906000f080158015610062573d6000803e3d6000fd5b505050610198565b6101f5806101e583390190565b600060208284031215610088578081fd5b81516001600160401b038082111561009e578283fd5b818401915084601f8301126100b1578283fd5b8151818111156100c3576100c3610182565b604051601f8201601f19908116603f011681019083821181831017156100eb576100eb610182565b81604052828152876020848701011115610103578586fd5b610114836020830160208801610152565b979650505050505050565b600060208252825180602084015261013e816040850160208701610152565b601f01601f19169190910160400192915050565b60005b8381101561016d578181015183820152602001610155565b8381111561017c576000848401525b50505050565b634e487b7160e01b600052604160045260246000fd5b603f806101a66000396000f3fe6080604052600080fdfea26469706673582212201bd8b1a777b100d67435ca4bb0b2fdccb13a2c2dde019b227bb553ff9a95bd4464736f6c63430008020033608060405234801561001057600080fd5b506040516101f53803806101f583398101604081905261002f916100c9565b60008151602083016000f090506001600160a01b0381166100865760405162461bcd60e51b815260206004820152600d60248201526c1111541313d657d19052531151609a1b604482015260640160405180910390fd5b6040516001600160a01b03821681527ff40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e9060200160405180910390a150506101a8565b600060208083850312156100db578182fd5b82516001600160401b03808211156100f1578384fd5b818501915085601f830112610104578384fd5b81518181111561011657610116610192565b604051601f8201601f19908116603f0116810190838211818310171561013e5761013e610192565b816040528281528886848701011115610155578687fd5b8693505b828410156101765784840186015181850187015292850192610159565b8284111561018657868684830101525b98975050505050505050565b634e487b7160e01b600052604160045260246000fd5b603f806101b66000396000f3fe6080604052600080fdfea2646970667358221220fbca036a163ed7f008cefa7c834d98d25109a456a051d41d9c89d55d7185d12b64736f6c63430008020033";
|
||||
// Grab the bytecode of the ZeroEx artifact
|
||||
bytes memory zeroExBytecode = vm.getCode("foundry-artifacts/ZeroEx.sol/ZeroEx.json");
|
||||
// Append the required ZeroEx constructor arguments (address bootstrapper)
|
||||
bytes memory zeroExDeploycode = abi.encodePacked(zeroExBytecode, abi.encode(initialMigration));
|
||||
// Append the required deployer code constructor arguments (bytes initCode)
|
||||
bytes memory deployerDeploycode = abi.encodePacked(deployerBytecode, abi.encode(zeroExDeploycode));
|
||||
// The address is technically emitted in an event, but we know we did it correctly
|
||||
//│ │ ├─ emit topic 0: 0xf40fcec21964ffb566044d083b4073f29f7f7929110ea19e1b3ebe375d89055e
|
||||
//│ │ │ data: 0x000000000000000000000000def1c0ded9bec7f1a1670819833240f027b25eff
|
||||
assembly {
|
||||
pop(create(0, add(deployerDeploycode, 0x20), mload(deployerDeploycode)))
|
||||
}
|
||||
|
||||
initialMigration.initializeZeroEx(
|
||||
payable(address(this)),
|
||||
ZERO_EX,
|
||||
InitialMigration.BootstrapFeatures({ registry: new SimpleFunctionRegistryFeature(), ownable: new OwnableFeature()})
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
|
||||
contract ForkUtils is Test {
|
||||
/// Only run this function if the block number
|
||||
// is greater than some constant for Ethereum Mainnet
|
||||
modifier onlyForked {
|
||||
if (block.number >= 14206900) {
|
||||
_;
|
||||
} else {
|
||||
emit log_string("Requires fork mode, skipping");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2022 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "forge-std/Test.sol";
|
||||
|
||||
import "src/transformers/LibERC20Transformer.sol";
|
||||
|
||||
contract TestUtils is Test {
|
||||
uint256 private constant MAX_UINT256_STRING_LENGTH = 78;
|
||||
uint8 private constant ASCII_DIGIT_OFFSET = 48;
|
||||
|
||||
function _toString(uint256 n)
|
||||
internal
|
||||
pure
|
||||
returns (string memory nstr)
|
||||
{
|
||||
if (n == 0) {
|
||||
return "0";
|
||||
}
|
||||
// Overallocate memory
|
||||
nstr = new string(MAX_UINT256_STRING_LENGTH);
|
||||
uint256 k = MAX_UINT256_STRING_LENGTH;
|
||||
// Populate string from right to left (lsb to msb).
|
||||
while (n != 0) {
|
||||
assembly {
|
||||
let char := add(
|
||||
ASCII_DIGIT_OFFSET,
|
||||
mod(n, 10)
|
||||
)
|
||||
mstore(add(nstr, k), char)
|
||||
k := sub(k, 1)
|
||||
n := div(n, 10)
|
||||
}
|
||||
}
|
||||
assembly {
|
||||
// Shift pointer over to actual start of string.
|
||||
nstr := add(nstr, k)
|
||||
// Store actual string length.
|
||||
mstore(nstr, sub(MAX_UINT256_STRING_LENGTH, k))
|
||||
}
|
||||
return nstr;
|
||||
}
|
||||
|
||||
function _findTransformerNonce(
|
||||
address transformer,
|
||||
address deployer
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint32)
|
||||
{
|
||||
address current;
|
||||
for (uint32 i = 0; i < 1024; i++) {
|
||||
current = LibERC20Transformer.getDeployedAddress(deployer, i);
|
||||
if (current == transformer) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
8
contracts/zero-ex/foundry.toml
Normal file
8
contracts/zero-ex/foundry.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[default]
|
||||
src = 'contracts/src'
|
||||
out = 'foundry-artifacts'
|
||||
test = 'contracts/test/foundry'
|
||||
libs = ["../utils/contracts/src/", "contracts/deps/"]
|
||||
remappings = ['@0x/contracts-utils/=../utils/', '@0x/contracts-erc20/=../erc20/', 'src/=./contracts/src']
|
||||
cache_path = 'foundry-cache'
|
||||
optimizer_runs = 1000000
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.31.0",
|
||||
"version": "0.32.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -38,12 +38,13 @@
|
||||
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
|
||||
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
|
||||
"publish:private": "yarn build && gitpkg publish",
|
||||
"rollback": "node ./lib/scripts/rollback.js"
|
||||
"rollback": "node ./lib/scripts/rollback.js",
|
||||
"typechain": "typechain --target=ethers-v5 --out-dir='typechain-wrappers' './foundry-artifacts/**/*.json'"
|
||||
},
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinERC1155Spender|FixinERC721Spender|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC1155OrdersFeature|IERC1155Token|IERC165Feature|IERC20Bridge|IERC20Transformer|IERC721OrdersFeature|IERC721Token|IFeature|IFeeRecipient|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|IPropertyValidator|ISimpleFunctionRegistryFeature|IStaking|ITakerCallback|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC1155OrdersStorage|LibERC20Transformer|LibERC721OrdersStorage|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNFTOrder|LibNFTOrdersRichErrors|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBalancerV2Batch|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFeeRecipient|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC1155Token|TestMintableERC20Token|TestMintableERC721Token|TestMooniswap|TestNFTOrderPresigner|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestPropertyValidator|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -55,16 +56,17 @@
|
||||
},
|
||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.7.2",
|
||||
"@0x/contract-addresses": "^6.11.0",
|
||||
"@0x/contracts-erc20": "^3.3.26",
|
||||
"@0x/contracts-gen": "^2.0.43",
|
||||
"@0x/contracts-test-utils": "^5.4.17",
|
||||
"@0x/dev-utils": "^4.2.11",
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contract-addresses": "^6.13.0",
|
||||
"@0x/contracts-erc20": "^3.3.29",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.20",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@types/isomorphic-fetch": "^0.0.35",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
@@ -78,18 +80,19 @@
|
||||
"solhint": "^1.4.1",
|
||||
"truffle": "^5.0.32",
|
||||
"tslint": "5.11.0",
|
||||
"typechain": "^8.0.0",
|
||||
"typedoc": "~0.16.11",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/protocol-utils": "^1.11.0",
|
||||
"@0x/subproviders": "^6.6.2",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"ethereum-types": "^3.6.0",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/protocol-utils": "^11.12.0",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"ethereum-types": "^3.7.0",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
|
||||
@@ -101,6 +101,7 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
|
||||
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
|
||||
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
|
||||
import * as MixinBalancerV2Batch from '../test/generated-artifacts/MixinBalancerV2Batch.json';
|
||||
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
|
||||
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
|
||||
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
|
||||
@@ -313,6 +314,7 @@ export const artifacts = {
|
||||
MixinAaveV2: MixinAaveV2 as ContractArtifact,
|
||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
||||
MixinBalancerV2Batch: MixinBalancerV2Batch as ContractArtifact,
|
||||
MixinBancor: MixinBancor as ContractArtifact,
|
||||
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
||||
MixinCompound: MixinCompound as ContractArtifact,
|
||||
|
||||
@@ -99,6 +99,7 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
|
||||
export * from '../test/generated-wrappers/mixin_aave_v2';
|
||||
export * from '../test/generated-wrappers/mixin_balancer';
|
||||
export * from '../test/generated-wrappers/mixin_balancer_v2';
|
||||
export * from '../test/generated-wrappers/mixin_balancer_v2_batch';
|
||||
export * from '../test/generated-wrappers/mixin_bancor';
|
||||
export * from '../test/generated-wrappers/mixin_co_fi_x';
|
||||
export * from '../test/generated-wrappers/mixin_compound';
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
"test/generated-artifacts/MixinAaveV2.json",
|
||||
"test/generated-artifacts/MixinBalancer.json",
|
||||
"test/generated-artifacts/MixinBalancerV2.json",
|
||||
"test/generated-artifacts/MixinBalancerV2Batch.json",
|
||||
"test/generated-artifacts/MixinBancor.json",
|
||||
"test/generated-artifacts/MixinCoFiX.json",
|
||||
"test/generated-artifacts/MixinCompound.json",
|
||||
|
||||
@@ -4,23 +4,28 @@ Audits
|
||||
|
||||
Below are links to our third-party audit reports.
|
||||
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| **Release** | **Reports** |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V4 | * `Consensys Diligence (December 2020) <https://consensys.net/diligence/audits/2020/12/0x-exchange-v4/>`__ |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V3 | * `Trail of Bits <http://zeips.0x.org.s3-website.us-east-2.amazonaws.com/audits/56/trail-of-bits/audit.pdf>`_ |
|
||||
| | * `Consensys Diligence (Exchange) <https://diligence.consensys.net/audits/2019/09/0x-v3-exchange/>`__ |
|
||||
| | * `Consensys Diligence (Staking) <https://diligence.consensys.net/audits/2019/10/0x-v3-staking/>`__ |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V2.1 | * `First <https://docs.google.com/document/d/1jYv6V21MfCSwCS5fxD6ZyaLWGzkpRSUO0lZpST94XsA/edit>`_ |
|
||||
| | * `Consensys Diligence <https://github.com/ConsenSys/0x_audit_report_2018-07-23>`_ |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| MultiAssetProxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2018-12>`__ |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| ERC1155Proxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2019-05>`__ |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| StaticCallProxy | * No third-party audit. |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
| ERC20BridgeProxy | * No third-party audit. |
|
||||
+------------------+---------------------------------------------------------------------------------------------------------------+
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| **Release** | **Reports** |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| ERC721OrdersFeature | * `ABDK Consulting <https://s3.us-east-2.amazonaws.com/zeips.0x.org/audits/abdk-consulting/ABDK_0x_Solidity_v_1_0.pdf>`__ |
|
||||
| | |
|
||||
| | |
|
||||
| ERC1155OrdersFeature | |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V4 | * `Consensys Diligence (December 2020) <https://consensys.net/diligence/audits/2020/12/0x-exchange-v4/>`__ |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V3 | * `Trail of Bits <http://zeips.0x.org.s3-website.us-east-2.amazonaws.com/audits/56/trail-of-bits/audit.pdf>`__ |
|
||||
| | * `Consensys Diligence (Exchange) <https://diligence.consensys.net/audits/2019/09/0x-v3-exchange/>`__ |
|
||||
| | * `Consensys Diligence (Staking) <https://diligence.consensys.net/audits/2019/10/0x-v3-staking/>`__ |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| Exchange V2.1 | * `First <https://docs.google.com/document/d/1jYv6V21MfCSwCS5fxD6ZyaLWGzkpRSUO0lZpST94XsA/edit>`_ |
|
||||
| | * `Consensys Diligence <https://github.com/ConsenSys/0x_audit_report_2018-07-23>`_ |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| MultiAssetProxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2018-12>`__ |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| ERC1155Proxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2019-05>`__ |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| StaticCallProxy | * No third-party audit. |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
| ERC20BridgeProxy | * No third-party audit. |
|
||||
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
|
||||
|
||||
@@ -60,21 +60,22 @@
|
||||
"ignoreDependencyVersionsForPackage": "contract-wrappers"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/monorepo-scripts": "^3.2.1",
|
||||
"@0x-lerna-fork/lerna": "3.16.10",
|
||||
"@0x/monorepo-scripts": "^3.2.4",
|
||||
"@0xproject/npm-cli-login": "^0.0.11",
|
||||
"async-child-process": "^1.1.1",
|
||||
"coveralls": "^3.0.0",
|
||||
"ganache-cli": "6.8.0-istanbul.0",
|
||||
"ganache-cli": "6.12.2",
|
||||
"lcov-result-merger": "^3.0.0",
|
||||
"lerna": "^3.0.0-beta.25",
|
||||
"npm-run-all": "^4.1.2",
|
||||
"prettier": "1.19.1",
|
||||
"source-map-support": "^0.5.6",
|
||||
"typescript": "4.2.2",
|
||||
"typescript": "4.6.3",
|
||||
"wsrun": "^5.2.4"
|
||||
},
|
||||
"resolutions": {
|
||||
"merkle-patricia-tree": "^2.3.2"
|
||||
"merkle-patricia-tree": "3.0.0",
|
||||
"**/bignumber.js": "^9.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,216 @@
|
||||
[
|
||||
{
|
||||
"version": "16.60.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add BiSwap on BSC",
|
||||
"pr": 467
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "16.59.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Remove SnowSwap on mainnet",
|
||||
"pr": 468
|
||||
},
|
||||
{
|
||||
"note": "Offboard Swerve Finance and LinkSwap",
|
||||
"pr": 469
|
||||
},
|
||||
{
|
||||
"note": "Offboard Eth2Dai",
|
||||
"pr": 470
|
||||
},
|
||||
{
|
||||
"note": "Add an optional IRfqClient for SwapQuoter#getSwapQuoteAsync",
|
||||
"pr": 467
|
||||
}
|
||||
],
|
||||
"timestamp": 1652400434
|
||||
},
|
||||
{
|
||||
"version": "16.58.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update Saddle pools on Mainnet",
|
||||
"pr": 450
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "16.57.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix a runtime error related to BalancerV2SwapInfoCache",
|
||||
"pr": 472
|
||||
}
|
||||
],
|
||||
"timestamp": 1652146864
|
||||
},
|
||||
{
|
||||
"version": "16.57.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix missing AMM quotes on indicative Quote Reports",
|
||||
"pr": 466
|
||||
}
|
||||
],
|
||||
"timestamp": 1651526551
|
||||
},
|
||||
{
|
||||
"version": "16.57.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added QUICK/ANY pair on Polygon",
|
||||
"pr": 464
|
||||
},
|
||||
{
|
||||
"note": "Added cvxFXS/FXS curve pool on mainnet",
|
||||
"pr": 465
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "16.57.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add BalancerV2 batch swap support",
|
||||
"pr": 462
|
||||
}
|
||||
],
|
||||
"timestamp": 1650611093
|
||||
},
|
||||
{
|
||||
"version": "16.56.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add estimatedGas to ExtendedQuoteReport",
|
||||
"pr": 463
|
||||
}
|
||||
],
|
||||
"timestamp": 1650575781
|
||||
},
|
||||
{
|
||||
"version": "16.55.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix fillRfqOrder VIP being used for swaps that need transformERC20",
|
||||
"pr": 461
|
||||
}
|
||||
],
|
||||
"timestamp": 1649347667
|
||||
},
|
||||
{
|
||||
"version": "16.54.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add true VIP support for eligible RFQt swaps",
|
||||
"pr": 458
|
||||
}
|
||||
],
|
||||
"timestamp": 1649215576
|
||||
},
|
||||
{
|
||||
"version": "16.53.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Adds support for STG/USDC pool on Curve Mainnet",
|
||||
"pr": 451
|
||||
},
|
||||
{
|
||||
"note": "Use neon-router in asset-swapper tests",
|
||||
"pr": 453
|
||||
},
|
||||
{
|
||||
"note": "Add sampler blocknumber to quote report data",
|
||||
"pr": 448
|
||||
}
|
||||
],
|
||||
"timestamp": 1648739346
|
||||
},
|
||||
{
|
||||
"version": "16.52.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Adds support for mobius money on celo",
|
||||
"pr": 423
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "16.51.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added `Curve` `YFI-ETH` pool",
|
||||
"pr": 444
|
||||
}
|
||||
],
|
||||
"timestamp": 1646888282
|
||||
},
|
||||
{
|
||||
"version": "16.50.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Routing glue optimization",
|
||||
"pr": 439
|
||||
},
|
||||
{
|
||||
"note": "Move VIP source routing into neon-router & disable fallback orders for native/plp",
|
||||
"pr": 440
|
||||
}
|
||||
],
|
||||
"timestamp": 1646837959
|
||||
},
|
||||
{
|
||||
"version": "16.50.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update `Uniswap_V3` address on `Ropsten`",
|
||||
"pr": 441
|
||||
}
|
||||
],
|
||||
"timestamp": 1646617024
|
||||
},
|
||||
{
|
||||
"version": "16.50.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add BTRFLY/WETH Curve pool on mainnet",
|
||||
"pr": 437
|
||||
},
|
||||
{
|
||||
"note": "Lower Uniswap V3 Sampler gas allowance",
|
||||
"pr": 438
|
||||
}
|
||||
],
|
||||
"timestamp": 1646312638
|
||||
},
|
||||
{
|
||||
"version": "16.50.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Adding support for Geist on `Fantom`",
|
||||
"pr": 398
|
||||
},
|
||||
{
|
||||
"note": "Improve Uniswap V3 gas schedule",
|
||||
"pr": 424
|
||||
}
|
||||
],
|
||||
"timestamp": 1646225739
|
||||
},
|
||||
{
|
||||
"version": "16.49.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Fix native order scaling & filter out 1 wei quotes",
|
||||
"pr": "430"
|
||||
}
|
||||
],
|
||||
"timestamp": 1645696356
|
||||
},
|
||||
{
|
||||
"timestamp": 1645569128,
|
||||
"version": "16.49.8",
|
||||
|
||||
@@ -5,6 +5,83 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v16.59.0 - _May 13, 2022_
|
||||
|
||||
* Remove SnowSwap on mainnet (#468)
|
||||
* Offboard Swerve Finance and LinkSwap (#469)
|
||||
* Offboard Eth2Dai (#470)
|
||||
* Add an optional IRfqClient for SwapQuoter#getSwapQuoteAsync (#467)
|
||||
|
||||
## v16.58.0 - _Invalid date_
|
||||
|
||||
* Update Saddle pools on Mainnet (#450)
|
||||
|
||||
## v16.57.3 - _May 10, 2022_
|
||||
|
||||
* Fix a runtime error related to BalancerV2SwapInfoCache (#472)
|
||||
|
||||
## v16.57.2 - _May 2, 2022_
|
||||
|
||||
* Fix missing AMM quotes on indicative Quote Reports (#466)
|
||||
|
||||
## v16.57.1 - _Invalid date_
|
||||
|
||||
* Added QUICK/ANY pair on Polygon (#464)
|
||||
* Added cvxFXS/FXS curve pool on mainnet (#465)
|
||||
|
||||
## v16.57.0 - _April 22, 2022_
|
||||
|
||||
* Add BalancerV2 batch swap support (#462)
|
||||
|
||||
## v16.56.0 - _April 21, 2022_
|
||||
|
||||
* Add estimatedGas to ExtendedQuoteReport (#463)
|
||||
|
||||
## v16.55.0 - _April 7, 2022_
|
||||
|
||||
* Fix fillRfqOrder VIP being used for swaps that need transformERC20 (#461)
|
||||
|
||||
## v16.54.0 - _April 6, 2022_
|
||||
|
||||
* Add true VIP support for eligible RFQt swaps (#458)
|
||||
|
||||
## v16.53.0 - _March 31, 2022_
|
||||
|
||||
* Adds support for STG/USDC pool on Curve Mainnet (#451)
|
||||
* Use neon-router in asset-swapper tests (#453)
|
||||
* Add sampler blocknumber to quote report data (#448)
|
||||
|
||||
## v16.52.0 - _Invalid date_
|
||||
|
||||
* Adds support for mobius money on celo (#423)
|
||||
|
||||
## v16.51.0 - _March 10, 2022_
|
||||
|
||||
* Added `Curve` `YFI-ETH` pool (#444)
|
||||
|
||||
## v16.50.3 - _March 9, 2022_
|
||||
|
||||
* Routing glue optimization (#439)
|
||||
* Move VIP source routing into neon-router & disable fallback orders for native/plp (#440)
|
||||
|
||||
## v16.50.2 - _March 7, 2022_
|
||||
|
||||
* Update `Uniswap_V3` address on `Ropsten` (#441)
|
||||
|
||||
## v16.50.1 - _March 3, 2022_
|
||||
|
||||
* Add BTRFLY/WETH Curve pool on mainnet (#437)
|
||||
* Lower Uniswap V3 Sampler gas allowance (#438)
|
||||
|
||||
## v16.50.0 - _March 2, 2022_
|
||||
|
||||
* Adding support for Geist on `Fantom` (#398)
|
||||
* Improve Uniswap V3 gas schedule (#424)
|
||||
|
||||
## v16.49.9 - _February 24, 2022_
|
||||
|
||||
* Fix native order scaling & filter out 1 wei quotes (#430)
|
||||
|
||||
## v16.49.8 - _February 22, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"shouldSaveStandardInput": true,
|
||||
"compilerSettings": {
|
||||
"evmVersion": "istanbul",
|
||||
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": true, "deduplicate": true } },
|
||||
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": false, "deduplicate": true } },
|
||||
"outputSelection": {
|
||||
"*": {
|
||||
"*": [
|
||||
|
||||
105
packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol
Normal file
105
packages/asset-swapper/contracts/src/BalancerV2BatchSampler.sol
Normal file
@@ -0,0 +1,105 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBalancerV2Vault.sol";
|
||||
import "./BalancerV2Common.sol";
|
||||
|
||||
contract BalancerV2BatchSampler is BalancerV2Common {
|
||||
|
||||
// Replaces amount for first step with each takerTokenAmount and calls queryBatchSwap using supplied steps
|
||||
/// @dev Sample sell quotes from Balancer V2 supporting multihops.
|
||||
/// @param swapSteps Array of swap steps (can be >= 1).
|
||||
/// @param swapAssets Array of token address for swaps.
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
function sampleMultihopSellsFromBalancerV2(
|
||||
IBalancerV2Vault vault,
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps,
|
||||
address[] memory swapAssets,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
IBalancerV2Vault.FundManagement memory swapFunds =
|
||||
_createSwapFunds();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
swapSteps[0].amount = takerTokenAmounts[i];
|
||||
try
|
||||
// For sells we specify the takerToken which is what the vault will receive from the trade
|
||||
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds)
|
||||
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
||||
returns (int256[] memory amounts) {
|
||||
// Outgoing balance is negative so we need to flip the sign
|
||||
// Note - queryBatchSwap will return a delta for each token in the assets array and last asset should be tokenOut
|
||||
int256 amountOutFromPool = amounts[amounts.length - 1] * -1;
|
||||
if (amountOutFromPool <= 0) {
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = uint256(amountOutFromPool);
|
||||
} catch {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces amount for first step with each makerTokenAmount and calls queryBatchSwap using supplied steps
|
||||
/// @dev Sample buy quotes from Balancer V2 supporting multihops.
|
||||
/// @param swapSteps Array of swap steps (can be >= 1).
|
||||
/// @param swapAssets Array of token address for swaps.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
function sampleMultihopBuysFromBalancerV2(
|
||||
IBalancerV2Vault vault,
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps,
|
||||
address[] memory swapAssets,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (uint256[] memory takerTokenAmounts)
|
||||
{
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
IBalancerV2Vault.FundManagement memory swapFunds =
|
||||
_createSwapFunds();
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
swapSteps[0].amount = makerTokenAmounts[i];
|
||||
try
|
||||
// Uses GIVEN_OUT type for Buy
|
||||
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds)
|
||||
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
||||
returns (int256[] memory amounts) {
|
||||
int256 amountIntoPool = amounts[0];
|
||||
if (amountIntoPool <= 0) {
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = uint256(amountIntoPool);
|
||||
} catch {
|
||||
// Swallow failures, leaving all results as zero.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
packages/asset-swapper/contracts/src/BalancerV2Common.sol
Normal file
41
packages/asset-swapper/contracts/src/BalancerV2Common.sol
Normal file
@@ -0,0 +1,41 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBalancerV2Vault.sol";
|
||||
|
||||
|
||||
contract BalancerV2Common {
|
||||
|
||||
function _createSwapFunds()
|
||||
internal
|
||||
view
|
||||
returns (IBalancerV2Vault.FundManagement memory)
|
||||
{
|
||||
return
|
||||
IBalancerV2Vault.FundManagement({
|
||||
sender: address(this),
|
||||
fromInternalBalance: false,
|
||||
recipient: payable(address(this)),
|
||||
toInternalBalance: false
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -21,44 +21,11 @@ pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./SamplerUtils.sol";
|
||||
import "./interfaces/IBalancerV2Vault.sol";
|
||||
import "./BalancerV2Common.sol";
|
||||
|
||||
/// @dev Minimal Balancer V2 Vault interface
|
||||
/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
|
||||
interface IBalancerV2Vault {
|
||||
enum SwapKind { GIVEN_IN, GIVEN_OUT }
|
||||
|
||||
struct BatchSwapStep {
|
||||
bytes32 poolId;
|
||||
uint256 assetInIndex;
|
||||
uint256 assetOutIndex;
|
||||
uint256 amount;
|
||||
bytes userData;
|
||||
}
|
||||
|
||||
struct FundManagement {
|
||||
address sender;
|
||||
bool fromInternalBalance;
|
||||
address payable recipient;
|
||||
bool toInternalBalance;
|
||||
}
|
||||
|
||||
function queryBatchSwap(
|
||||
SwapKind kind,
|
||||
BatchSwapStep[] calldata swaps,
|
||||
IAsset[] calldata assets,
|
||||
FundManagement calldata funds
|
||||
) external returns (int256[] memory assetDeltas);
|
||||
}
|
||||
interface IAsset {
|
||||
// solhint-disable-previous-line no-empty-blocks
|
||||
}
|
||||
|
||||
contract BalancerV2Sampler is SamplerUtils {
|
||||
|
||||
struct BalancerV2PoolInfo {
|
||||
bytes32 poolId;
|
||||
address vault;
|
||||
}
|
||||
contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
|
||||
|
||||
/// @dev Sample sell quotes from Balancer V2.
|
||||
/// @param poolInfo Struct with pool related data
|
||||
@@ -68,7 +35,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromBalancerV2(
|
||||
BalancerV2PoolInfo memory poolInfo,
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
@@ -78,9 +45,9 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||
IAsset[] memory swapAssets = new IAsset[](2);
|
||||
swapAssets[0] = IAsset(takerToken);
|
||||
swapAssets[1] = IAsset(makerToken);
|
||||
address[] memory swapAssets = new address[](2);
|
||||
swapAssets[0] = takerToken;
|
||||
swapAssets[1] = makerToken;
|
||||
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
@@ -97,7 +64,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
||||
returns (int256[] memory amounts) {
|
||||
// Outgoing balance is negative so we need to flip the sign
|
||||
int256 amountOutFromPool = amounts[1] * -1;
|
||||
int256 amountOutFromPool = amounts[amounts.length - 1] * -1;
|
||||
if (amountOutFromPool <= 0) {
|
||||
break;
|
||||
}
|
||||
@@ -117,7 +84,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromBalancerV2(
|
||||
BalancerV2PoolInfo memory poolInfo,
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
address takerToken,
|
||||
address makerToken,
|
||||
uint256[] memory makerTokenAmounts
|
||||
@@ -127,9 +94,9 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
{
|
||||
_assertValidPair(makerToken, takerToken);
|
||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||
IAsset[] memory swapAssets = new IAsset[](2);
|
||||
swapAssets[0] = IAsset(takerToken);
|
||||
swapAssets[1] = IAsset(makerToken);
|
||||
address[] memory swapAssets = new address[](2);
|
||||
swapAssets[0] = takerToken;
|
||||
swapAssets[1] = makerToken;
|
||||
|
||||
uint256 numSamples = makerTokenAmounts.length;
|
||||
takerTokenAmounts = new uint256[](numSamples);
|
||||
@@ -157,7 +124,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
}
|
||||
|
||||
function _createSwapSteps(
|
||||
BalancerV2PoolInfo memory poolInfo,
|
||||
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||
uint256 amount
|
||||
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
|
||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
||||
@@ -172,18 +139,4 @@ contract BalancerV2Sampler is SamplerUtils {
|
||||
|
||||
return swapSteps;
|
||||
}
|
||||
|
||||
function _createSwapFunds()
|
||||
private
|
||||
view
|
||||
returns (IBalancerV2Vault.FundManagement memory)
|
||||
{
|
||||
return
|
||||
IBalancerV2Vault.FundManagement({
|
||||
sender: address(this),
|
||||
fromInternalBalance: false,
|
||||
recipient: payable(address(this)),
|
||||
toInternalBalance: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,9 +22,8 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IBancor.sol";
|
||||
|
||||
contract CompilerHack {}
|
||||
|
||||
contract BancorSampler is CompilerHack {
|
||||
contract BancorSampler {
|
||||
|
||||
/// @dev Base gas limit for Bancor calls.
|
||||
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
|
||||
|
||||
@@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./BalancerSampler.sol";
|
||||
import "./BalancerV2Sampler.sol";
|
||||
import "./BalancerV2BatchSampler.sol";
|
||||
import "./BancorSampler.sol";
|
||||
import "./CompoundSampler.sol";
|
||||
import "./CurveSampler.sol";
|
||||
@@ -32,7 +33,6 @@ import "./KyberDmmSampler.sol";
|
||||
import "./LidoSampler.sol";
|
||||
import "./LiquidityProviderSampler.sol";
|
||||
import "./MakerPSMSampler.sol";
|
||||
import "./MultiBridgeSampler.sol";
|
||||
import "./MStableSampler.sol";
|
||||
import "./MooniswapSampler.sol";
|
||||
import "./NativeOrderSampler.sol";
|
||||
@@ -48,6 +48,7 @@ import "./UtilitySampler.sol";
|
||||
contract ERC20BridgeSampler is
|
||||
BalancerSampler,
|
||||
BalancerV2Sampler,
|
||||
BalancerV2BatchSampler,
|
||||
BancorSampler,
|
||||
CompoundSampler,
|
||||
CurveSampler,
|
||||
@@ -60,7 +61,6 @@ contract ERC20BridgeSampler is
|
||||
MakerPSMSampler,
|
||||
MStableSampler,
|
||||
MooniswapSampler,
|
||||
MultiBridgeSampler,
|
||||
NativeOrderSampler,
|
||||
ShellSampler,
|
||||
SmoothySampler,
|
||||
@@ -92,4 +92,6 @@ contract ERC20BridgeSampler is
|
||||
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
|
||||
}
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
}
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./interfaces/IMultiBridge.sol";
|
||||
|
||||
|
||||
contract MultiBridgeSampler {
|
||||
|
||||
/// @dev Default gas limit for multibridge calls.
|
||||
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
|
||||
|
||||
/// @dev Sample sell quotes from MultiBridge.
|
||||
/// @param multibridge Address of the MultiBridge contract.
|
||||
/// @param takerToken Address of the taker token (what to sell).
|
||||
/// @param intermediateToken The address of the intermediate token to
|
||||
/// use in an indirect route.
|
||||
/// @param makerToken Address of the maker token (what to buy).
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromMultiBridge(
|
||||
address multibridge,
|
||||
address takerToken,
|
||||
address intermediateToken,
|
||||
address makerToken,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory makerTokenAmounts)
|
||||
{
|
||||
// Initialize array of maker token amounts.
|
||||
uint256 numSamples = takerTokenAmounts.length;
|
||||
makerTokenAmounts = new uint256[](numSamples);
|
||||
|
||||
// If no address provided, return all zeros.
|
||||
if (multibridge == address(0)) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < numSamples; i++) {
|
||||
(bool didSucceed, bytes memory resultData) =
|
||||
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
|
||||
abi.encodeWithSelector(
|
||||
IMultiBridge(0).getSellQuote.selector,
|
||||
takerToken,
|
||||
intermediateToken,
|
||||
makerToken,
|
||||
takerTokenAmounts[i]
|
||||
));
|
||||
uint256 buyAmount = 0;
|
||||
if (didSucceed) {
|
||||
buyAmount = abi.decode(resultData, (uint256));
|
||||
}
|
||||
// Exit early if the amount is too high for the source to serve
|
||||
if (buyAmount == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
makerTokenAmounts[i] = buyAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,17 +22,43 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
interface IUniswapV3Quoter {
|
||||
interface IUniswapV3QuoterV2 {
|
||||
function factory()
|
||||
external
|
||||
view
|
||||
returns (IUniswapV3Factory factory);
|
||||
|
||||
// @notice Returns the amount out received for a given exact input swap without executing the swap
|
||||
// @param path The path of the swap, i.e. each token pair and the pool fee
|
||||
// @param amountIn The amount of the first token to swap
|
||||
// @return amountOut The amount of the last token that would be received
|
||||
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
|
||||
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
|
||||
// @return gasEstimate The estimate of the gas that the swap consumes
|
||||
function quoteExactInput(bytes memory path, uint256 amountIn)
|
||||
external
|
||||
returns (uint256 amountOut);
|
||||
returns (
|
||||
uint256 amountOut,
|
||||
uint160[] memory sqrtPriceX96AfterList,
|
||||
uint32[] memory initializedTicksCrossedList,
|
||||
uint256 gasEstimate
|
||||
);
|
||||
|
||||
// @notice Returns the amount in required for a given exact output swap without executing the swap
|
||||
// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
|
||||
// @param amountOut The amount of the last token to receive
|
||||
// @return amountIn The amount of first token required to be paid
|
||||
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
|
||||
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
|
||||
// @return gasEstimate The estimate of the gas that the swap consumes
|
||||
function quoteExactOutput(bytes memory path, uint256 amountOut)
|
||||
external
|
||||
returns (uint256 amountIn);
|
||||
returns (
|
||||
uint256 amountIn,
|
||||
uint160[] memory sqrtPriceX96AfterList,
|
||||
uint32[] memory initializedTicksCrossedList,
|
||||
uint256 gasEstimate
|
||||
);
|
||||
}
|
||||
|
||||
interface IUniswapV3Factory {
|
||||
@@ -51,23 +77,25 @@ interface IUniswapV3Pool {
|
||||
contract UniswapV3Sampler
|
||||
{
|
||||
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
|
||||
uint256 constant private QUOTE_GAS = 600e3;
|
||||
uint256 constant private QUOTE_GAS = 700e3;
|
||||
|
||||
/// @dev Sample sell quotes from UniswapV3.
|
||||
/// @param quoter UniswapV3 Quoter contract.
|
||||
/// @param path Token route. Should be takerToken -> makerToken
|
||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||
/// @return uniswapPaths The encoded uniswap path for each sample.
|
||||
/// @return uniswapGasUsed Estimated amount of gas used
|
||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||
/// amount.
|
||||
function sampleSellsFromUniswapV3(
|
||||
IUniswapV3Quoter quoter,
|
||||
IUniswapV3QuoterV2 quoter,
|
||||
IERC20TokenV06[] memory path,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (
|
||||
bytes[] memory uniswapPaths,
|
||||
uint256[] memory uniswapGasUsed,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
{
|
||||
@@ -76,31 +104,39 @@ contract UniswapV3Sampler
|
||||
|
||||
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
|
||||
uniswapPaths = new bytes[](takerTokenAmounts.length);
|
||||
uniswapGasUsed = new uint256[](takerTokenAmounts.length);
|
||||
|
||||
for (uint256 i = 0; i < takerTokenAmounts.length; ++i) {
|
||||
// Pick the best result from all the paths.
|
||||
bytes memory topUniswapPath;
|
||||
uint256 topBuyAmount = 0;
|
||||
for (uint256 j = 0; j < poolPaths.length; ++j) {
|
||||
bytes memory uniswapPath = _toUniswapPath(path, poolPaths[j]);
|
||||
try
|
||||
quoter.quoteExactInput
|
||||
{ gas: QUOTE_GAS }
|
||||
(uniswapPath, takerTokenAmounts[i])
|
||||
returns (uint256 buyAmount)
|
||||
try quoter.quoteExactInput
|
||||
{ gas: QUOTE_GAS }
|
||||
(uniswapPath, takerTokenAmounts[i])
|
||||
returns (
|
||||
uint256 buyAmount,
|
||||
uint160[] memory, /* sqrtPriceX96AfterList */
|
||||
uint32[] memory, /* initializedTicksCrossedList */
|
||||
uint256 gasUsed
|
||||
)
|
||||
{
|
||||
if (topBuyAmount <= buyAmount) {
|
||||
topBuyAmount = buyAmount;
|
||||
topUniswapPath = uniswapPath;
|
||||
uniswapPaths[i] = uniswapPath;
|
||||
uniswapGasUsed[i] = gasUsed;
|
||||
}
|
||||
} catch { }
|
||||
} catch {}
|
||||
}
|
||||
// Break early if we can't complete the buys.
|
||||
// Break early if we can't complete the sells.
|
||||
if (topBuyAmount == 0) {
|
||||
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
|
||||
// then reset if no valid valid quote was found
|
||||
uniswapPaths[i] = "";
|
||||
uniswapGasUsed[i] = 0;
|
||||
break;
|
||||
}
|
||||
makerTokenAmounts[i] = topBuyAmount;
|
||||
uniswapPaths[i] = topUniswapPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,16 +145,18 @@ contract UniswapV3Sampler
|
||||
/// @param path Token route. Should be takerToken -> makerToken.
|
||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||
/// @return uniswapPaths The encoded uniswap path for each sample.
|
||||
/// @return uniswapGasUsed Estimated amount of gas used
|
||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||
/// amount.
|
||||
function sampleBuysFromUniswapV3(
|
||||
IUniswapV3Quoter quoter,
|
||||
IUniswapV3QuoterV2 quoter,
|
||||
IERC20TokenV06[] memory path,
|
||||
uint256[] memory makerTokenAmounts
|
||||
)
|
||||
public
|
||||
returns (
|
||||
bytes[] memory uniswapPaths,
|
||||
uint256[] memory uniswapGasUsed,
|
||||
uint256[] memory takerTokenAmounts
|
||||
)
|
||||
{
|
||||
@@ -128,10 +166,10 @@ contract UniswapV3Sampler
|
||||
|
||||
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
|
||||
uniswapPaths = new bytes[](makerTokenAmounts.length);
|
||||
uniswapGasUsed = new uint256[](makerTokenAmounts.length);
|
||||
|
||||
for (uint256 i = 0; i < makerTokenAmounts.length; ++i) {
|
||||
// Pick the best result from all the paths.
|
||||
bytes memory topUniswapPath;
|
||||
uint256 topSellAmount = 0;
|
||||
for (uint256 j = 0; j < poolPaths.length; ++j) {
|
||||
// quoter requires path to be reversed for buys.
|
||||
@@ -143,21 +181,30 @@ contract UniswapV3Sampler
|
||||
quoter.quoteExactOutput
|
||||
{ gas: QUOTE_GAS }
|
||||
(uniswapPath, makerTokenAmounts[i])
|
||||
returns (uint256 sellAmount)
|
||||
returns (
|
||||
uint256 sellAmount,
|
||||
uint160[] memory, /* sqrtPriceX96AfterList */
|
||||
uint32[] memory, /* initializedTicksCrossedList */
|
||||
uint256 gasUsed
|
||||
)
|
||||
{
|
||||
if (topSellAmount == 0 || topSellAmount >= sellAmount) {
|
||||
topSellAmount = sellAmount;
|
||||
// But the output path should still be encoded for sells.
|
||||
topUniswapPath = _toUniswapPath(path, poolPaths[j]);
|
||||
uniswapPaths[i] = _toUniswapPath(path, poolPaths[j]);
|
||||
uniswapGasUsed[i] = gasUsed;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// Break early if we can't complete the buys.
|
||||
if (topSellAmount == 0) {
|
||||
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
|
||||
// then reset if no valid valid quote was found
|
||||
uniswapPaths[i] = "";
|
||||
uniswapGasUsed[i] = 0;
|
||||
break;
|
||||
}
|
||||
takerTokenAmounts[i] = topSellAmount;
|
||||
uniswapPaths[i] = topUniswapPath;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +283,7 @@ contract UniswapV3Sampler
|
||||
|
||||
function _reverseTokenPath(IERC20TokenV06[] memory tokenPath)
|
||||
private
|
||||
pure
|
||||
returns (IERC20TokenV06[] memory reversed)
|
||||
{
|
||||
reversed = new IERC20TokenV06[](tokenPath.length);
|
||||
@@ -246,6 +294,7 @@ contract UniswapV3Sampler
|
||||
|
||||
function _reversePoolPath(IUniswapV3Pool[] memory poolPath)
|
||||
private
|
||||
pure
|
||||
returns (IUniswapV3Pool[] memory reversed)
|
||||
{
|
||||
reversed = new IUniswapV3Pool[](poolPath.length);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
/// @dev Minimal Balancer V2 Vault interface
|
||||
/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
|
||||
interface IBalancerV2Vault {
|
||||
enum SwapKind { GIVEN_IN, GIVEN_OUT }
|
||||
|
||||
struct BatchSwapStep {
|
||||
bytes32 poolId;
|
||||
uint256 assetInIndex;
|
||||
uint256 assetOutIndex;
|
||||
uint256 amount;
|
||||
bytes userData;
|
||||
}
|
||||
|
||||
struct FundManagement {
|
||||
address sender;
|
||||
bool fromInternalBalance;
|
||||
address payable recipient;
|
||||
bool toInternalBalance;
|
||||
}
|
||||
|
||||
struct BalancerV2PoolInfo {
|
||||
bytes32 poolId;
|
||||
address vault;
|
||||
}
|
||||
|
||||
function queryBatchSwap(
|
||||
SwapKind kind,
|
||||
BatchSwapStep[] calldata swaps,
|
||||
address[] calldata assets,
|
||||
FundManagement calldata funds
|
||||
) external returns (int256[] memory assetDeltas);
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract DummyLiquidityProvider
|
||||
{
|
||||
/// @dev Quotes the amount of `makerToken` that would be obtained by
|
||||
/// selling `sellAmount` of `takerToken`.
|
||||
/// @param sellAmount Amount of `takerToken` to sell.
|
||||
/// @return makerTokenAmount Amount of `makerToken` that would be obtained.
|
||||
function getSellQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 makerTokenAmount)
|
||||
{
|
||||
makerTokenAmount = sellAmount - 1;
|
||||
}
|
||||
|
||||
/// @dev Quotes the amount of `takerToken` that would need to be sold in
|
||||
/// order to obtain `buyAmount` of `makerToken`.
|
||||
/// @param buyAmount Amount of `makerToken` to buy.
|
||||
/// @return takerTokenAmount Amount of `takerToken` that would need to be sold.
|
||||
function getBuyQuote(
|
||||
address, /* takerToken */
|
||||
address, /* makerToken */
|
||||
uint256 buyAmount
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 takerTokenAmount)
|
||||
{
|
||||
takerTokenAmount = buyAmount + 1;
|
||||
}
|
||||
}
|
||||
@@ -1,455 +0,0 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/ERC20BridgeSampler.sol";
|
||||
import "../src/interfaces/IKyberNetwork.sol";
|
||||
import "../src/interfaces/IUniswapV2Router01.sol";
|
||||
|
||||
|
||||
library LibDeterministicQuotes {
|
||||
|
||||
address private constant WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
uint256 private constant RATE_DENOMINATOR = 1 ether;
|
||||
uint256 private constant MIN_RATE = RATE_DENOMINATOR / 100;
|
||||
uint256 private constant MAX_RATE = 100 * RATE_DENOMINATOR;
|
||||
uint8 private constant MIN_DECIMALS = 4;
|
||||
uint8 private constant MAX_DECIMALS = 20;
|
||||
|
||||
function getDeterministicSellQuote(
|
||||
bytes32 salt,
|
||||
address sellToken,
|
||||
address buyToken,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
|
||||
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
|
||||
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||
return sellAmount * rate * buyBase / sellBase / RATE_DENOMINATOR;
|
||||
}
|
||||
|
||||
function getDeterministicBuyQuote(
|
||||
bytes32 salt,
|
||||
address sellToken,
|
||||
address buyToken,
|
||||
uint256 buyAmount
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 sellAmount)
|
||||
{
|
||||
uint256 sellBase = uint256(10) ** getDeterministicTokenDecimals(sellToken);
|
||||
uint256 buyBase = uint256(10) ** getDeterministicTokenDecimals(buyToken);
|
||||
uint256 rate = getDeterministicRate(salt, sellToken, buyToken);
|
||||
return buyAmount * RATE_DENOMINATOR * sellBase / rate / buyBase;
|
||||
}
|
||||
|
||||
function getDeterministicTokenDecimals(address token)
|
||||
internal
|
||||
pure
|
||||
returns (uint8 decimals)
|
||||
{
|
||||
if (token == WETH_ADDRESS) {
|
||||
return 18;
|
||||
}
|
||||
bytes32 seed = keccak256(abi.encodePacked(token));
|
||||
return uint8(uint256(seed) % (MAX_DECIMALS - MIN_DECIMALS)) + MIN_DECIMALS;
|
||||
}
|
||||
|
||||
function getDeterministicRate(bytes32 salt, address sellToken, address buyToken)
|
||||
internal
|
||||
pure
|
||||
returns (uint256 rate)
|
||||
{
|
||||
bytes32 seed = keccak256(abi.encodePacked(salt, sellToken, buyToken));
|
||||
return uint256(seed) % (MAX_RATE - MIN_RATE) + MIN_RATE;
|
||||
}
|
||||
}
|
||||
|
||||
contract TestDeploymentConstants {
|
||||
|
||||
// solhint-disable separate-by-one-line-in-contract
|
||||
|
||||
// Mainnet addresses ///////////////////////////////////////////////////////
|
||||
/// @dev Mainnet address of the WETH contract.
|
||||
address constant private WETH_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
|
||||
|
||||
/// @dev Overridable way to get the WETH address.
|
||||
/// @return wethAddress The WETH address.
|
||||
function _getWethAddress()
|
||||
internal
|
||||
view
|
||||
returns (address wethAddress)
|
||||
{
|
||||
return WETH_ADDRESS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
contract FailTrigger {
|
||||
|
||||
// Give this address a balance to force operations to fail.
|
||||
address payable constant public FAILURE_ADDRESS = 0xe9dB8717BC5DFB20aaf538b4a5a02B7791FF430C;
|
||||
|
||||
// Funds `FAILURE_ADDRESS`.
|
||||
function enableFailTrigger() external payable {
|
||||
FAILURE_ADDRESS.transfer(msg.value);
|
||||
}
|
||||
|
||||
function _revertIfShouldFail() internal view {
|
||||
if (FAILURE_ADDRESS.balance != 0) {
|
||||
revert("FAIL_TRIGGERED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSamplerUniswapExchange is
|
||||
IUniswapExchangeQuotes,
|
||||
TestDeploymentConstants,
|
||||
FailTrigger
|
||||
{
|
||||
bytes32 constant private BASE_SALT = 0x1d6a6a0506b0b4a554b907a4c29d9f4674e461989d9c1921feb17b26716385ab;
|
||||
|
||||
address public tokenAddress;
|
||||
bytes32 public salt;
|
||||
|
||||
constructor(address _tokenAddress) public {
|
||||
tokenAddress = _tokenAddress;
|
||||
salt = keccak256(abi.encodePacked(BASE_SALT, _tokenAddress));
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapExchangeQuotes.getEthToTokenInputPrice()`.
|
||||
function getEthToTokenInputPrice(
|
||||
uint256 ethSold
|
||||
)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256 tokensBought)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||
salt,
|
||||
tokenAddress,
|
||||
_getWethAddress(),
|
||||
ethSold
|
||||
);
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapExchangeQuotes.getEthToTokenOutputPrice()`.
|
||||
function getEthToTokenOutputPrice(
|
||||
uint256 tokensBought
|
||||
)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256 ethSold)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||
salt,
|
||||
_getWethAddress(),
|
||||
tokenAddress,
|
||||
tokensBought
|
||||
);
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapExchangeQuotes.getTokenToEthInputPrice()`.
|
||||
function getTokenToEthInputPrice(
|
||||
uint256 tokensSold
|
||||
)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256 ethBought)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
return LibDeterministicQuotes.getDeterministicSellQuote(
|
||||
salt,
|
||||
tokenAddress,
|
||||
_getWethAddress(),
|
||||
tokensSold
|
||||
);
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapExchangeQuotes.getTokenToEthOutputPrice()`.
|
||||
function getTokenToEthOutputPrice(
|
||||
uint256 ethBought
|
||||
)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256 tokensSold)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
return LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||
salt,
|
||||
_getWethAddress(),
|
||||
tokenAddress,
|
||||
ethBought
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSamplerUniswapV2Router01 is
|
||||
IUniswapV2Router01,
|
||||
TestDeploymentConstants,
|
||||
FailTrigger
|
||||
{
|
||||
bytes32 constant private SALT = 0xadc7fcb33c735913b8635927e66896b356a53a912ab2ceff929e60a04b53b3c1;
|
||||
|
||||
// Deterministic `IUniswapV2Router01.getAmountsOut()`.
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
require(path.length >= 2, "PATH_TOO_SHORT");
|
||||
_revertIfShouldFail();
|
||||
amounts = new uint256[](path.length);
|
||||
amounts[0] = amountIn;
|
||||
for (uint256 i = 0; i < path.length - 1; ++i) {
|
||||
amounts[i + 1] = LibDeterministicQuotes.getDeterministicSellQuote(
|
||||
SALT,
|
||||
path[i],
|
||||
path[i + 1],
|
||||
amounts[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Deterministic `IUniswapV2Router01.getAmountsInt()`.
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (uint256[] memory amounts)
|
||||
{
|
||||
require(path.length >= 2, "PATH_TOO_SHORT");
|
||||
_revertIfShouldFail();
|
||||
amounts = new uint256[](path.length);
|
||||
amounts[path.length - 1] = amountOut;
|
||||
for (uint256 i = path.length - 1; i > 0; --i) {
|
||||
amounts[i - 1] = LibDeterministicQuotes.getDeterministicBuyQuote(
|
||||
SALT,
|
||||
path[i - 1],
|
||||
path[i],
|
||||
amounts[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// solhint-disable space-after-comma
|
||||
contract TestERC20BridgeSamplerKyberNetwork is
|
||||
TestDeploymentConstants,
|
||||
FailTrigger
|
||||
{
|
||||
bytes32 constant private SALT = 0x0ff3ca9d46195c39f9a12afb74207b4970349fb3cfb1e459bbf170298d326bc7;
|
||||
address constant public ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
|
||||
enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
|
||||
enum ProcessWithRate {NotRequired, Required}
|
||||
|
||||
// IKyberHintHandler
|
||||
function buildTokenToEthHint(
|
||||
address tokenSrc,
|
||||
TradeType /* tokenToEthType */,
|
||||
bytes32[] calldata /* tokenToEthReserveIds */,
|
||||
uint256[] calldata /* tokenToEthSplits */
|
||||
) external view returns (bytes memory hint)
|
||||
{
|
||||
return abi.encode(tokenSrc);
|
||||
}
|
||||
|
||||
function buildEthToTokenHint(
|
||||
address tokenDest,
|
||||
TradeType /* ethToTokenType */,
|
||||
bytes32[] calldata /* ethToTokenReserveIds */,
|
||||
uint256[] calldata /* ethToTokenSplits */
|
||||
) external view returns (bytes memory hint)
|
||||
{
|
||||
return abi.encode(tokenDest);
|
||||
}
|
||||
|
||||
// IKyberHintHandler
|
||||
function buildTokenToTokenHint(
|
||||
address tokenSrc,
|
||||
TradeType /* tokenToEthType */,
|
||||
bytes32[] calldata /* tokenToEthReserveIds */,
|
||||
uint256[] calldata /* tokenToEthSplits */,
|
||||
address /* tokenDest */,
|
||||
TradeType /* EthToTokenType */,
|
||||
bytes32[] calldata /* EthToTokenReserveIds */,
|
||||
uint256[] calldata /* EthToTokenSplits */
|
||||
) external view returns (bytes memory hint)
|
||||
{
|
||||
return abi.encode(tokenSrc);
|
||||
}
|
||||
|
||||
// IKyberHintHandler
|
||||
function getTradingReserves(
|
||||
address tokenSrc,
|
||||
address tokenDest,
|
||||
bool isTokenToToken,
|
||||
bytes calldata hint
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (
|
||||
bytes32[] memory reserveIds,
|
||||
uint256[] memory splitValuesBps,
|
||||
ProcessWithRate processWithRate
|
||||
)
|
||||
{
|
||||
reserveIds = new bytes32[](1);
|
||||
reserveIds[0] = bytes32(uint256(1));
|
||||
splitValuesBps = new uint256[](0);
|
||||
processWithRate = ProcessWithRate.NotRequired;
|
||||
}
|
||||
|
||||
// Deterministic `IKyberNetworkProxy.getExpectedRateAfterFee()`.
|
||||
function getExpectedRateAfterFee(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256 /* srcQty */,
|
||||
uint256 /* fee */,
|
||||
bytes calldata /* hint */
|
||||
)
|
||||
external
|
||||
view
|
||||
returns
|
||||
(uint256 expectedRate)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
|
||||
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
|
||||
expectedRate = LibDeterministicQuotes.getDeterministicRate(
|
||||
SALT,
|
||||
fromToken,
|
||||
toToken
|
||||
);
|
||||
}
|
||||
|
||||
// Deterministic `IKyberNetworkProxy.getExpectedRate()`.
|
||||
function getExpectedRate(
|
||||
address fromToken,
|
||||
address toToken,
|
||||
uint256
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (uint256 expectedRate, uint256)
|
||||
{
|
||||
_revertIfShouldFail();
|
||||
fromToken = fromToken == ETH_ADDRESS ? _getWethAddress() : fromToken;
|
||||
toToken = toToken == ETH_ADDRESS ? _getWethAddress() : toToken;
|
||||
expectedRate = LibDeterministicQuotes.getDeterministicRate(
|
||||
SALT,
|
||||
fromToken,
|
||||
toToken
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSamplerUniswapExchangeFactory is
|
||||
IUniswapExchangeFactory
|
||||
{
|
||||
mapping (address => IUniswapExchangeQuotes) private _exchangesByToken;
|
||||
|
||||
// Creates Uniswap exchange contracts for tokens.
|
||||
function createTokenExchanges(address[] calldata tokenAddresses)
|
||||
external
|
||||
{
|
||||
for (uint256 i = 0; i < tokenAddresses.length; i++) {
|
||||
address tokenAddress = tokenAddresses[i];
|
||||
_exchangesByToken[tokenAddress] =
|
||||
new TestERC20BridgeSamplerUniswapExchange(tokenAddress);
|
||||
}
|
||||
}
|
||||
|
||||
// `IUniswapExchangeFactory.getExchange()`.
|
||||
function getExchange(address tokenAddress)
|
||||
override
|
||||
external
|
||||
view
|
||||
returns (address)
|
||||
{
|
||||
return address(_exchangesByToken[tokenAddress]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contract TestERC20BridgeSampler is
|
||||
ERC20BridgeSampler,
|
||||
FailTrigger
|
||||
{
|
||||
TestERC20BridgeSamplerUniswapExchangeFactory public uniswap;
|
||||
TestERC20BridgeSamplerUniswapV2Router01 public uniswapV2Router;
|
||||
TestERC20BridgeSamplerKyberNetwork public kyber;
|
||||
|
||||
uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1;
|
||||
|
||||
constructor() public ERC20BridgeSampler() {
|
||||
uniswap = new TestERC20BridgeSamplerUniswapExchangeFactory();
|
||||
uniswapV2Router = new TestERC20BridgeSamplerUniswapV2Router01();
|
||||
kyber = new TestERC20BridgeSamplerKyberNetwork();
|
||||
}
|
||||
|
||||
// Creates Uniswap exchange contracts for tokens.
|
||||
function createTokenExchanges(address[] calldata tokenAddresses)
|
||||
external
|
||||
{
|
||||
uniswap.createTokenExchanges(tokenAddresses);
|
||||
}
|
||||
|
||||
// Overridden to return deterministic states.
|
||||
function getLimitOrderFillableTakerAmount(
|
||||
IExchange.LimitOrder memory order,
|
||||
IExchange.Signature memory,
|
||||
IExchange
|
||||
)
|
||||
override
|
||||
public
|
||||
view
|
||||
returns (uint256 fillableTakerAmount)
|
||||
{
|
||||
return uint256(keccak256(abi.encode(order.salt))) % order.takerAmount;
|
||||
}
|
||||
|
||||
// Overriden to return deterministic decimals.
|
||||
function _getTokenDecimals(address tokenAddress)
|
||||
override
|
||||
internal
|
||||
view
|
||||
returns (uint8 decimals)
|
||||
{
|
||||
return LibDeterministicQuotes.getDeterministicTokenDecimals(tokenAddress);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/asset-swapper",
|
||||
"version": "16.49.8",
|
||||
"version": "16.59.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -39,7 +39,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
|
||||
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2BatchSampler|BalancerV2Common|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|IBalancer|IBalancerV2Vault|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
|
||||
"postpublish": {
|
||||
"assets": []
|
||||
}
|
||||
@@ -58,21 +58,22 @@
|
||||
"registry": "git@github.com:0xProject/gitpkg-registry.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.31",
|
||||
"@0x/base-contract": "^6.4.5",
|
||||
"@0x/contract-addresses": "^6.11.0",
|
||||
"@0x/contract-wrappers": "^13.19.0",
|
||||
"@0x/contracts-erc20": "^3.3.26",
|
||||
"@0x/contracts-zero-ex": "^0.31.0",
|
||||
"@0x/dev-utils": "^4.2.11",
|
||||
"@0x/json-schemas": "^6.4.1",
|
||||
"@0x/neon-router": "^0.3.2",
|
||||
"@0x/protocol-utils": "^1.11.0",
|
||||
"@0x/assert": "^3.0.34",
|
||||
"@0x/base-contract": "^6.5.0",
|
||||
"@0x/contract-addresses": "^6.13.0",
|
||||
"@0x/contract-wrappers": "^13.20.1",
|
||||
"@0x/contracts-erc20": "^3.3.29",
|
||||
"@0x/contracts-zero-ex": "^0.32.0",
|
||||
"@0x/dev-utils": "^4.2.14",
|
||||
"@0x/json-schemas": "^6.4.4",
|
||||
"@0x/neon-router": "^0.3.5",
|
||||
"@0x/protocol-utils": "^11.12.0",
|
||||
"@0x/quote-server": "^6.0.6",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/typescript-typings": "^5.2.1",
|
||||
"@0x/utils": "^6.5.0",
|
||||
"@0x/web3-wrapper": "^7.6.2",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@0x/typescript-typings": "^5.3.1",
|
||||
"@0x/utils": "^6.5.3",
|
||||
"@0x/web3-wrapper": "^7.6.5",
|
||||
"@balancer-labs/sdk": "^0.1.6",
|
||||
"@balancer-labs/sor": "0.3.2",
|
||||
"@bancor/sdk": "0.2.9",
|
||||
"@ethersproject/abi": "^5.0.1",
|
||||
@@ -84,29 +85,30 @@
|
||||
"axios-mock-adapter": "^1.19.0",
|
||||
"cream-sor": "^0.3.3",
|
||||
"decimal.js": "^10.2.0",
|
||||
"ethereum-types": "^3.6.0",
|
||||
"ethereum-types": "^3.7.0",
|
||||
"ethereumjs-util": "^7.0.10",
|
||||
"fast-abi": "^0.0.4",
|
||||
"graphql": "^15.4.0",
|
||||
"graphql-request": "^3.4.0",
|
||||
"heartbeats": "^5.0.1",
|
||||
"lodash": "^4.17.11"
|
||||
"lodash": "^4.17.11",
|
||||
"sorV2": "npm:@balancer-labs/sor"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@0x/abi-gen": "^5.7.2",
|
||||
"@0x/abi-gen": "^5.8.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.43",
|
||||
"@0x/contracts-test-utils": "^5.4.17",
|
||||
"@0x/contracts-utils": "^4.8.7",
|
||||
"@0x/contracts-gen": "^2.0.46",
|
||||
"@0x/contracts-test-utils": "^5.4.20",
|
||||
"@0x/contracts-utils": "^4.8.10",
|
||||
"@0x/mesh-rpc-client": "^9.4.2",
|
||||
"@0x/migrations": "^8.1.15",
|
||||
"@0x/sol-compiler": "^4.7.8",
|
||||
"@0x/subproviders": "^6.6.2",
|
||||
"@0x/migrations": "^8.1.18",
|
||||
"@0x/sol-compiler": "^4.8.1",
|
||||
"@0x/subproviders": "^6.6.5",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@0x/types": "^3.3.4",
|
||||
"@0x/types": "^3.3.6",
|
||||
"@types/lodash": "4.14.104",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "12.12.54",
|
||||
@@ -123,7 +125,7 @@
|
||||
"tslint": "5.11.0",
|
||||
"typedoc": "~0.16.11",
|
||||
"typemoq": "^2.1.0",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -116,6 +116,15 @@ export {
|
||||
SamplerMetrics,
|
||||
} from './types';
|
||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||
export {
|
||||
IRfqClient,
|
||||
RfqClientV1Price,
|
||||
RfqClientV1PriceRequest,
|
||||
RfqClientV1PriceResponse,
|
||||
RfqClientV1Quote,
|
||||
RfqClientV1QuoteRequest,
|
||||
RfqClientV1QuoteResponse,
|
||||
} from './utils/irfq_client';
|
||||
export {
|
||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||
DEFAULT_GAS_SCHEDULE,
|
||||
|
||||
57
packages/asset-swapper/src/noop_samplers/GeistSampler.ts
Normal file
57
packages/asset-swapper/src/noop_samplers/GeistSampler.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { ZERO_AMOUNT } from '../constants';
|
||||
export interface GeistInfo {
|
||||
lendingPool: string;
|
||||
gToken: string;
|
||||
underlyingToken: string;
|
||||
}
|
||||
// tslint:disable-next-line:no-unnecessary-class
|
||||
export class GeistSampler {
|
||||
public static sampleSellsFromGeist(
|
||||
geistInfo: GeistInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
takerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> gToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
|
||||
) {
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = takerTokenAmounts.length;
|
||||
|
||||
const makerTokenAmounts = new Array(numSamples);
|
||||
makerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
public static sampleBuysFromGeist(
|
||||
geistInfo: GeistInfo,
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
makerTokenAmounts: BigNumber[],
|
||||
): BigNumber[] {
|
||||
// Deposit/Withdrawal underlying <-> gToken is always 1:1
|
||||
if (
|
||||
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
|
||||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
|
||||
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
|
||||
) {
|
||||
return makerTokenAmounts;
|
||||
}
|
||||
|
||||
// Not matching the reserve return 0 results
|
||||
const numSamples = makerTokenAmounts.length;
|
||||
const takerTokenAmounts = new Array(numSamples);
|
||||
takerTokenAmounts.fill(ZERO_AMOUNT);
|
||||
return takerTokenAmounts;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
FinalUniswapV3FillData,
|
||||
LiquidityProviderFillData,
|
||||
MooniswapFillData,
|
||||
NativeRfqOrderFillData,
|
||||
OptimizedMarketBridgeOrder,
|
||||
OptimizedMarketOrder,
|
||||
UniswapV2FillData,
|
||||
@@ -60,6 +61,7 @@ import {
|
||||
isDirectSwapCompatible,
|
||||
isMultiplexBatchFillCompatible,
|
||||
isMultiplexMultiHopFillCompatible,
|
||||
requiresTransformERC20,
|
||||
} from './quote_consumer_utils';
|
||||
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
@@ -278,7 +280,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
|
||||
if (
|
||||
this.chainId === ChainId.Mainnet &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
|
||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve]) &&
|
||||
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
|
||||
// into WETH prior/post the trade.
|
||||
// ETH buy/sell is supported
|
||||
@@ -333,6 +335,49 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
||||
};
|
||||
}
|
||||
|
||||
// RFQT VIP
|
||||
if (
|
||||
[ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&
|
||||
!isToETH &&
|
||||
!isFromETH &&
|
||||
quote.orders.every(o => o.type === FillQuoteTransformerOrderType.Rfq) &&
|
||||
!requiresTransformERC20(optsWithDefaults)
|
||||
) {
|
||||
const rfqOrdersData = quote.orders.map(o => o.fillData as NativeRfqOrderFillData);
|
||||
const fillAmountPerOrder = (() => {
|
||||
// Don't think order taker amounts are clipped to actual sell amount
|
||||
// (the last one might be too large) so figure them out manually.
|
||||
let remaining = sellAmount;
|
||||
const fillAmounts = [];
|
||||
for (const o of quote.orders) {
|
||||
const fillAmount = BigNumber.min(o.takerAmount, remaining);
|
||||
fillAmounts.push(fillAmount);
|
||||
remaining = remaining.minus(fillAmount);
|
||||
}
|
||||
return fillAmounts;
|
||||
})();
|
||||
const callData =
|
||||
quote.orders.length === 1
|
||||
? this._exchangeProxy
|
||||
.fillRfqOrder(rfqOrdersData[0].order, rfqOrdersData[0].signature, fillAmountPerOrder[0])
|
||||
.getABIEncodedTransactionData()
|
||||
: this._exchangeProxy
|
||||
.batchFillRfqOrders(
|
||||
rfqOrdersData.map(d => d.order),
|
||||
rfqOrdersData.map(d => d.signature),
|
||||
fillAmountPerOrder,
|
||||
true,
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
return {
|
||||
calldataHexString: callData,
|
||||
ethAmount: ZERO_AMOUNT,
|
||||
toAddress: this._exchangeProxy.address,
|
||||
allowanceTarget: this._exchangeProxy.address,
|
||||
gasOverhead: ZERO_AMOUNT,
|
||||
};
|
||||
}
|
||||
|
||||
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
|
||||
return {
|
||||
calldataHexString: this._encodeMultiplexBatchFillCalldata(
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
SwapQuoterRfqOpts,
|
||||
} from './types';
|
||||
import { assert } from './utils/assert';
|
||||
import { IRfqClient } from './utils/irfq_client';
|
||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
||||
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
||||
@@ -327,6 +328,7 @@ export class SwapQuoter {
|
||||
assetFillAmount: BigNumber,
|
||||
marketOperation: MarketOperation,
|
||||
options: Partial<SwapQuoteRequestOpts>,
|
||||
rfqClient?: IRfqClient | undefined,
|
||||
): Promise<SwapQuote> {
|
||||
assert.isETHAddressHex('makerToken', makerToken);
|
||||
assert.isETHAddressHex('takerToken', takerToken);
|
||||
@@ -381,6 +383,7 @@ export class SwapQuoter {
|
||||
this.expiryBufferMs,
|
||||
rfqtOptions?.metricsProxy,
|
||||
);
|
||||
calcOpts.rfqt.rfqClient = rfqClient;
|
||||
}
|
||||
|
||||
const result: OptimizerResultWithReport = await this._marketOperationUtils.getOptimizerResultAsync(
|
||||
@@ -519,7 +522,7 @@ function createSwapQuote(
|
||||
: calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, gasSchedule, slippage);
|
||||
|
||||
// Put together the swap quote
|
||||
const { makerTokenDecimals, takerTokenDecimals } = optimizerResult.marketSideLiquidity;
|
||||
const { makerTokenDecimals, takerTokenDecimals, blockNumber } = optimizerResult.marketSideLiquidity;
|
||||
const swapQuote = {
|
||||
makerToken,
|
||||
takerToken,
|
||||
@@ -536,6 +539,7 @@ function createSwapQuote(
|
||||
extendedQuoteReportSources,
|
||||
isTwoHop,
|
||||
priceComparisonsReport,
|
||||
blockNumber,
|
||||
};
|
||||
|
||||
if (operation === MarketOperation.Buy) {
|
||||
|
||||
@@ -179,6 +179,7 @@ export interface SwapQuoteBase {
|
||||
takerTokenDecimals: number;
|
||||
takerAmountPerEth: BigNumber;
|
||||
makerAmountPerEth: BigNumber;
|
||||
blockNumber: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,7 +17,10 @@ import {
|
||||
|
||||
const SUCCESS_CODE = 201;
|
||||
|
||||
function getAltMarketInfo(
|
||||
/**
|
||||
* Returns the AltOffering if it exists for a given pair
|
||||
*/
|
||||
export function getAltMarketInfo(
|
||||
offerings: AltOffering[],
|
||||
buyTokenAddress: string,
|
||||
sellTokenAddress: string,
|
||||
|
||||
59
packages/asset-swapper/src/utils/irfq_client.ts
Normal file
59
packages/asset-swapper/src/utils/irfq_client.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { RfqOrder, Signature } from '@0x/protocol-utils';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { AltRfqMakerAssetOfferings } from '../types';
|
||||
|
||||
export interface RfqClientV1PriceRequest {
|
||||
altRfqAssetOfferings: AltRfqMakerAssetOfferings | undefined;
|
||||
assetFillAmount: BigNumber;
|
||||
chainId: number;
|
||||
comparisonPrice: BigNumber | undefined;
|
||||
integratorId: string;
|
||||
intentOnFilling: boolean;
|
||||
makerToken: string;
|
||||
marketOperation: 'Sell' | 'Buy';
|
||||
takerAddress: string;
|
||||
takerToken: string;
|
||||
txOrigin: string;
|
||||
}
|
||||
|
||||
export interface RfqClientV1QuoteRequest extends RfqClientV1PriceRequest {}
|
||||
|
||||
export interface RfqClientV1Price {
|
||||
expiry: BigNumber;
|
||||
kind: 'rfq' | 'otc';
|
||||
makerAmount: BigNumber;
|
||||
makerToken: string;
|
||||
makerUri: string;
|
||||
takerAmount: BigNumber;
|
||||
takerToken: string;
|
||||
}
|
||||
|
||||
export interface RfqClientV1PriceResponse {
|
||||
prices: RfqClientV1Price[];
|
||||
}
|
||||
|
||||
export interface RfqClientV1Quote {
|
||||
makerUri: string;
|
||||
order: RfqOrder;
|
||||
signature: Signature;
|
||||
}
|
||||
|
||||
export interface RfqClientV1QuoteResponse {
|
||||
quotes: RfqClientV1Quote[];
|
||||
}
|
||||
|
||||
/**
|
||||
* IRfqClient is an interface that defines how to connect with an Rfq system.
|
||||
*/
|
||||
export interface IRfqClient {
|
||||
/**
|
||||
* Fetches a list of "indicative quotes" or prices from a remote Rfq server
|
||||
*/
|
||||
getV1PricesAsync(request: RfqClientV1PriceRequest): Promise<RfqClientV1PriceResponse>;
|
||||
|
||||
/**
|
||||
* Fetches a list of "firm quotes" or signed quotes from a remote Rfq server.
|
||||
*/
|
||||
getV1QuotesAsync(request: RfqClientV1QuoteRequest): Promise<RfqClientV1QuoteResponse>;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
APESWAP_ROUTER_BY_CHAIN_ID,
|
||||
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
|
||||
BELT_BSC_INFOS,
|
||||
BISWAP_ROUTER_BY_CHAIN_ID,
|
||||
CAFESWAP_ROUTER_BY_CHAIN_ID,
|
||||
CHEESESWAP_ROUTER_BY_CHAIN_ID,
|
||||
COMETHSWAP_ROUTER_BY_CHAIN_ID,
|
||||
@@ -31,6 +32,7 @@ import {
|
||||
KYBER_BRIDGED_LIQUIDITY_PREFIX,
|
||||
MAX_DODOV2_POOLS_QUERIED,
|
||||
MAX_KYBER_RESERVES_QUERIED,
|
||||
MOBIUSMONEY_CELO_INFOS,
|
||||
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
|
||||
MSTABLE_POOLS_BY_CHAIN_ID,
|
||||
NERVE_BSC_INFOS,
|
||||
@@ -45,11 +47,9 @@ import {
|
||||
SHIBASWAP_ROUTER_BY_CHAIN_ID,
|
||||
SMOOTHY_BSC_INFOS,
|
||||
SMOOTHY_MAINNET_INFOS,
|
||||
SNOWSWAP_MAINNET_INFOS,
|
||||
SPIRITSWAP_ROUTER_BY_CHAIN_ID,
|
||||
SPOOKYSWAP_ROUTER_BY_CHAIN_ID,
|
||||
SUSHISWAP_ROUTER_BY_CHAIN_ID,
|
||||
SWERVE_MAINNET_INFOS,
|
||||
SYNAPSE_AVALANCHE_INFOS,
|
||||
SYNAPSE_BSC_INFOS,
|
||||
SYNAPSE_FANTOM_INFOS,
|
||||
@@ -224,32 +224,6 @@ export function getCurveV2InfosForPair(chainId: ChainId, takerToken: string, mak
|
||||
}
|
||||
}
|
||||
|
||||
export function getSwerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||
if (chainId !== ChainId.Mainnet) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(SWERVE_MAINNET_INFOS).filter(c =>
|
||||
[makerToken, takerToken].every(
|
||||
t =>
|
||||
(c.tokens.includes(t) && c.metaTokens === undefined) ||
|
||||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getSnowSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||
if (chainId !== ChainId.Mainnet) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(SNOWSWAP_MAINNET_INFOS).filter(c =>
|
||||
[makerToken, takerToken].every(
|
||||
t =>
|
||||
(c.tokens.includes(t) && c.metaTokens === undefined) ||
|
||||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||
if (chainId !== ChainId.BSC) {
|
||||
return [];
|
||||
@@ -449,6 +423,18 @@ export function getAcryptosInfosForPair(chainId: ChainId, takerToken: string, ma
|
||||
),
|
||||
);
|
||||
}
|
||||
export function getMobiusMoneyInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||
if (chainId !== ChainId.Celo) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(MOBIUSMONEY_CELO_INFOS).filter(c =>
|
||||
[makerToken, takerToken].every(
|
||||
t =>
|
||||
(c.tokens.includes(t) && c.metaTokens === undefined) ||
|
||||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
export function getShellLikeInfosForPair(
|
||||
chainId: ChainId,
|
||||
@@ -480,8 +466,6 @@ export function getCurveLikeInfosForPair(
|
||||
source:
|
||||
| ERC20BridgeSource.Curve
|
||||
| ERC20BridgeSource.CurveV2
|
||||
| ERC20BridgeSource.Swerve
|
||||
| ERC20BridgeSource.SnowSwap
|
||||
| ERC20BridgeSource.Nerve
|
||||
| ERC20BridgeSource.Synapse
|
||||
| ERC20BridgeSource.Belt
|
||||
@@ -491,7 +475,8 @@ export function getCurveLikeInfosForPair(
|
||||
| ERC20BridgeSource.IronSwap
|
||||
| ERC20BridgeSource.XSigma
|
||||
| ERC20BridgeSource.FirebirdOneSwap
|
||||
| ERC20BridgeSource.ACryptos,
|
||||
| ERC20BridgeSource.ACryptos
|
||||
| ERC20BridgeSource.MobiusMoney,
|
||||
): CurveDetailedInfo[] {
|
||||
let pools: CurveInfo[] = [];
|
||||
switch (source) {
|
||||
@@ -501,12 +486,6 @@ export function getCurveLikeInfosForPair(
|
||||
case ERC20BridgeSource.CurveV2:
|
||||
pools = getCurveV2InfosForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
case ERC20BridgeSource.Swerve:
|
||||
pools = getSwerveInfosForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
case ERC20BridgeSource.SnowSwap:
|
||||
pools = getSnowSwapInfosForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
case ERC20BridgeSource.Nerve:
|
||||
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
@@ -537,6 +516,9 @@ export function getCurveLikeInfosForPair(
|
||||
case ERC20BridgeSource.ACryptos:
|
||||
pools = getAcryptosInfosForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
case ERC20BridgeSource.MobiusMoney:
|
||||
pools = getMobiusMoneyInfoForPair(chainId, takerToken, makerToken);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown Curve like source ${source}`);
|
||||
}
|
||||
@@ -572,7 +554,8 @@ export function uniswapV2LikeRouterAddress(
|
||||
| ERC20BridgeSource.UbeSwap
|
||||
| ERC20BridgeSource.MorpheusSwap
|
||||
| ERC20BridgeSource.SpookySwap
|
||||
| ERC20BridgeSource.SpiritSwap,
|
||||
| ERC20BridgeSource.SpiritSwap
|
||||
| ERC20BridgeSource.BiSwap,
|
||||
): string {
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
@@ -621,6 +604,8 @@ export function uniswapV2LikeRouterAddress(
|
||||
return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.SpiritSwap:
|
||||
return SPIRITSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
case ERC20BridgeSource.BiSwap:
|
||||
return BISWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||
default:
|
||||
throw new Error(`Unknown UniswapV2 like source ${source}`);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
|
||||
import { SourceFilters } from './source_filters';
|
||||
import {
|
||||
AaveV2FillData,
|
||||
BalancerV2BatchSwapFillData,
|
||||
BancorFillData,
|
||||
CompoundFillData,
|
||||
CurveFillData,
|
||||
@@ -17,7 +18,10 @@ import {
|
||||
ERC20BridgeSource,
|
||||
FeeSchedule,
|
||||
FillData,
|
||||
FinalUniswapV3FillData,
|
||||
GeistFillData,
|
||||
GetMarketOrdersOpts,
|
||||
isFinalUniswapV3FillData,
|
||||
KyberSamplerOpts,
|
||||
LidoInfo,
|
||||
LiquidityProviderFillData,
|
||||
@@ -75,7 +79,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
@@ -83,8 +86,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Bancor,
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Swerve,
|
||||
ERC20BridgeSource.SnowSwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Shell,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
@@ -93,7 +94,6 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Cream,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.CryptoCom,
|
||||
ERC20BridgeSource.Linkswap,
|
||||
ERC20BridgeSource.Lido,
|
||||
ERC20BridgeSource.MakerPsm,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
@@ -146,6 +146,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.ACryptos,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.BiSwap,
|
||||
]),
|
||||
[ChainId.Polygon]: new SourceFilters([
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -187,6 +188,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Beethovenx,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.Geist,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.MorpheusSwap,
|
||||
ERC20BridgeSource.SpiritSwap,
|
||||
@@ -198,6 +200,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.UbeSwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.MobiusMoney,
|
||||
]),
|
||||
[ChainId.Optimism]: new SourceFilters([
|
||||
ERC20BridgeSource.UniswapV3,
|
||||
@@ -219,7 +222,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.UniswapV2,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.Balancer,
|
||||
@@ -228,8 +230,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.MStable,
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.Shell,
|
||||
ERC20BridgeSource.Swerve,
|
||||
ERC20BridgeSource.SnowSwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.Dodo,
|
||||
@@ -238,7 +238,6 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Lido,
|
||||
ERC20BridgeSource.LiquidityProvider,
|
||||
ERC20BridgeSource.CryptoCom,
|
||||
ERC20BridgeSource.Linkswap,
|
||||
ERC20BridgeSource.MakerPsm,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.Smoothy,
|
||||
@@ -275,6 +274,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Mooniswap,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.Nerve,
|
||||
ERC20BridgeSource.Synapse,
|
||||
ERC20BridgeSource.PancakeSwap,
|
||||
ERC20BridgeSource.PancakeSwapV2,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -290,6 +290,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.ACryptos,
|
||||
ERC20BridgeSource.KyberDmm,
|
||||
ERC20BridgeSource.Synapse,
|
||||
ERC20BridgeSource.BiSwap,
|
||||
]),
|
||||
[ChainId.Polygon]: new SourceFilters([
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
@@ -331,6 +332,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.Beethovenx,
|
||||
ERC20BridgeSource.Curve,
|
||||
ERC20BridgeSource.CurveV2,
|
||||
ERC20BridgeSource.Geist,
|
||||
ERC20BridgeSource.JetSwap,
|
||||
ERC20BridgeSource.MorpheusSwap,
|
||||
ERC20BridgeSource.SpiritSwap,
|
||||
@@ -342,6 +344,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
|
||||
ERC20BridgeSource.UbeSwap,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.MultiHop,
|
||||
ERC20BridgeSource.MobiusMoney,
|
||||
]),
|
||||
[ChainId.Optimism]: new SourceFilters([
|
||||
ERC20BridgeSource.UniswapV3,
|
||||
@@ -428,6 +431,7 @@ export const MAINNET_TOKENS = {
|
||||
RenBTC: '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d',
|
||||
sBTC: '0xfe18be6b3bd88a2d2a7f928d00292e7a9963cfc6',
|
||||
tBTC: '0x8daebade922df735c38c80c7ebd708af50815faa',
|
||||
tBTCv2: '0x18084fbA666a33d37592fA2633fD49a74DD93a88',
|
||||
hBTC: '0x0316eb71485b0ab14103307bf65a021042c6d380',
|
||||
pBTC: '0x5228a22e72ccc52d415ecfd199f99d0665e7733b',
|
||||
bBTC: '0x9be89d2a4cd102d8fecc6bf9da793be995c22541',
|
||||
@@ -470,10 +474,13 @@ export const MAINNET_TOKENS = {
|
||||
alUSD: '0xbc6da0fe9ad5f3b0d58160288917aa56653660e9',
|
||||
// Frax ecosystem
|
||||
FRAX: '0x853d955acef822db058eb8505911ed77f175b99e',
|
||||
cvxFXS: '0xfeef77d3f69374f66429c91d732a244f074bdf74',
|
||||
FXS: '0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0',
|
||||
OHM: '0x383518188c0c6d7730d91b2c03a03c837814a899',
|
||||
OHMV2: '0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5',
|
||||
BTRFLY: '0xc0d4ceb216b3ba9c3701b291766fdcba977cec3a',
|
||||
// Stargate
|
||||
STG: '0xaf5191b0de278c7286d6c7cc6ab6bb8a73ba2cd6',
|
||||
//
|
||||
LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0',
|
||||
// Fei Ecosystem
|
||||
@@ -495,6 +502,7 @@ export const MAINNET_TOKENS = {
|
||||
OUSD: '0x2a8e1e676ec238d8a992307b495b45b3feaa5e86',
|
||||
agEUR: '0x1a7e4e63778b4f12a199c062f3efdd288afcbce8',
|
||||
ibEUR: '0x96e61422b6a9ba0e068b6c5add4ffabc6a4aae27',
|
||||
YFI: '0x0bc529c00c6401aef6d220be8c6ea1667f6ad93e',
|
||||
};
|
||||
|
||||
export const BSC_TOKENS = {
|
||||
@@ -512,6 +520,7 @@ export const BSC_TOKENS = {
|
||||
renBTC: '0xfce146bf3146100cfe5db4129cf6c82b0ef4ad8c',
|
||||
pBTC: '0xed28a457a5a76596ac48d87c0f577020f6ea1c4c',
|
||||
nUSD: '0x23b891e5c62e0955ae2bd185990103928ab817b3',
|
||||
BSW: '0x965F527D9159dCe6288a2219DB51fc6Eef120dD1',
|
||||
};
|
||||
|
||||
export const POLYGON_TOKENS = {
|
||||
@@ -530,6 +539,7 @@ export const POLYGON_TOKENS = {
|
||||
BANANA: '0x5d47baba0d66083c52009271faf3f50dcc01023c',
|
||||
WEXPOLY: '0x4c4bf319237d98a30a929a96112effa8da3510eb',
|
||||
nUSD: '0xb6c473756050de474286bed418b77aeac39b02af',
|
||||
ANY: '0x6aB6d61428fde76768D7b45D8BFeec19c6eF91A8',
|
||||
};
|
||||
|
||||
export const AVALANCHE_TOKENS = {
|
||||
@@ -549,6 +559,7 @@ export const AVALANCHE_TOKENS = {
|
||||
nUSD: '0xcfc37a6ab183dd4aed08c204d1c2773c0b1bdf46',
|
||||
aWETH: '0x53f7c5869a859f0aec3d334ee8b4cf01e3492f21',
|
||||
MIM: '0x130966628846bfd36ff31a822705796e8cb8c18d',
|
||||
MAG: '0x1d60109178C48E4A937D8AB71699D8eBb6F7c5dE',
|
||||
};
|
||||
|
||||
export const CELO_TOKENS = {
|
||||
@@ -556,11 +567,11 @@ export const CELO_TOKENS = {
|
||||
// Some of these tokens are Optics bridge? tokens which
|
||||
// had an issue and migrated from v1 to v2
|
||||
WETHv1: '0xe919f65739c26a42616b7b8eedc6b5524d1e3ac4',
|
||||
WETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455',
|
||||
oWETH: '0x122013fd7df1c6f636a5bb8f03108e876548b455',
|
||||
WBTC: '0xbaab46e28388d2779e6e31fd00cf0e5ad95e327b',
|
||||
cUSD: '0x765de816845861e75a25fca122bb6898b8b1282a',
|
||||
// ??
|
||||
WBTCv1: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b',
|
||||
cBTC: '0xd629eb00deced2a080b7ec630ef6ac117e614f1b',
|
||||
cETH: '0x2def4285787d58a2f811af24755a8150622f4361',
|
||||
UBE: '0x00be915b9dcf56a3cbe739d9b9c202ca692409ec',
|
||||
// Moolah
|
||||
@@ -569,6 +580,22 @@ export const CELO_TOKENS = {
|
||||
mCEUR: '0xe273ad7ee11dcfaa87383ad5977ee1504ac07568',
|
||||
amCUSD: '0x64defa3544c695db8c535d289d843a189aa26b98',
|
||||
MOO: '0x17700282592d6917f6a73d0bf8accf4d578c131e',
|
||||
|
||||
//
|
||||
wBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
|
||||
wETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
||||
wBTCO: '0xbe50a3013a1c94768a1abb78c3cb79ab28fc1ace',
|
||||
pUSDC: '0xcc82628f6a8defa1e2b0ad7ed448bef3647f7941',
|
||||
cUSDC: '0x2a3684e9dc20b857375ea04235f2f7edbe818fa7',
|
||||
cUSDC_V2: '0xef4229c8c3250c675f21bcefa42f58efbff6002a',
|
||||
pUSDC_V2: '0x1bfc26ce035c368503fae319cc2596716428ca44',
|
||||
pUSD: '0xeadf4a7168a82d30ba0619e64d5bcf5b30b45226',
|
||||
pCELO: '0x301a61d01a63c8d670c2b8a43f37d12ef181f997',
|
||||
aaUSDC: '0xb70e0a782b058bfdb0d109a3599bec1f19328e36',
|
||||
asUSDC: '0xcd7d7ff64746c1909e44db8e95331f9316478817',
|
||||
mcUSDT: '0xcfffe0c89a779c09df3df5624f54cdf7ef5fdd5d',
|
||||
mcUSDC: '0x93db49be12b864019da9cb147ba75cdc0506190e',
|
||||
DAI: '0x90ca507a5d4458a4c6c6249d186b6dcb02a5bccd',
|
||||
};
|
||||
|
||||
export const FANTOM_TOKENS = {
|
||||
@@ -578,6 +605,7 @@ export const FANTOM_TOKENS = {
|
||||
DAI: '0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e',
|
||||
fUSDT: '0x049d68029688eabf473097a2fc38ef61633a3c7a',
|
||||
WBTC: '0x321162cd933e2be498cd2267a90534a804051b11',
|
||||
WCRV: '0x1e4f97b9f9f913c46f1632781732927b9019c68b',
|
||||
renBTC: '0xdbf31df14b66535af65aac99c32e9ea844e14501',
|
||||
MIM: '0x82f0b8b456c1a451378467398982d4834b6829c1',
|
||||
nUSD: '0xed2a7edd7413021d440b09d654f3b87712abab66',
|
||||
@@ -586,6 +614,15 @@ export const FANTOM_TOKENS = {
|
||||
gUSDC: '0xe578c856933d8e1082740bf7661e379aa2a30b26',
|
||||
gDAI: '0x07e6332dd090d287d3489245038daf987955dcfb',
|
||||
FRAX: '0xdc301622e621166bd8e82f2ca0a26c13ad0be355',
|
||||
gFTM: '0x39b3bd37208cbade74d0fcbdbb12d606295b430a',
|
||||
gETH: '0x25c130b2624cf12a4ea30143ef50c5d68cefa22f',
|
||||
gWBTC: '0x38aca5484b8603373acc6961ecd57a6a594510a3',
|
||||
gCRV: '0x690754a168b022331caa2467207c61919b3f8a98',
|
||||
gMIM: '0xc664fc7b8487a3e10824cda768c1d239f2403bbe',
|
||||
};
|
||||
|
||||
export const GEIST_FANTOM_POOLS = {
|
||||
lendingPool: '0x9fad24f572045c7869117160a571b2e50b10d068',
|
||||
};
|
||||
|
||||
export const OPTIMISM_TOKENS = {
|
||||
@@ -631,6 +668,8 @@ export const CURVE_POOLS = {
|
||||
USDP: '0x42d7025938bec20b69cbae5a77421082407f053a', // usdp
|
||||
ib: '0x2dded6da1bf5dbdf597c45fcfaa3194e53ecfeaf', // iron bank
|
||||
link: '0xf178c0b5bb7e7abf4e12a4838c7b7c5ba2c623c0', // link
|
||||
btrflyweth: '0xf43b15ab692fde1f9c24a9fce700adcc809d5391', // redacted cartel
|
||||
stgusdc: '0x3211c6cbef1429da3d0d58494938299c92ad5860', // stargate
|
||||
// StableSwap "open pools" (crv.finance)
|
||||
TUSD: '0xecd5e75afb02efa118af914515d6521aabd189f1',
|
||||
STABLEx: '0x3252efd4ea2d6c78091a1f43982ee2c3659cc3d1',
|
||||
@@ -640,6 +679,7 @@ export const CURVE_POOLS = {
|
||||
BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a',
|
||||
DSU3CRV: '0x6ec80df362d7042c50d4469bcfbc174c9dd9109a',
|
||||
cvxcrv: '0x9d0464996170c6b9e75eed71c68b99ddedf279e8',
|
||||
cvxfxs: '0xd658a338613198204dca1143ac3f01a722b5d94a',
|
||||
mim: '0x5a6a4d54456819380173272a5e8e9b9904bdf41b',
|
||||
eurt: '0xfd5db7463a3ab53fd211b4af195c5bccc1a03890',
|
||||
ethcrv: '0x8301ae4fc9c624d1d396cbdaa1ed877821d7c511',
|
||||
@@ -653,6 +693,7 @@ export const CURVE_POOLS = {
|
||||
d3pool: '0xbaaa1f5dba42c3389bdbc2c9d2de134f5cd0dc89',
|
||||
triEURpool: '0xb9446c4ef5ebe66268da6700d26f96273de3d571',
|
||||
ibEURsEUR: '0x19b080fe1ffa0553469d20ca36219f17fcf03859',
|
||||
wethyfi: '0xc26b89a667578ec7b3f11b2f98d6fd15c07c54ba',
|
||||
};
|
||||
|
||||
export const CURVE_V2_POOLS = {
|
||||
@@ -696,28 +737,13 @@ export const CURVE_OPTIMISM_POOLS = {
|
||||
tri: '0x1337bedc9d22ecbe766df105c9623922a27963ec',
|
||||
};
|
||||
|
||||
export const SWERVE_POOLS = {
|
||||
y: '0x329239599afb305da0a2ec69c58f8a6697f9f88d',
|
||||
};
|
||||
|
||||
export const SNOWSWAP_POOLS = {
|
||||
yUSD: '0xbf7ccd6c446acfcc5df023043f2167b62e81899b',
|
||||
yVault: '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b',
|
||||
// POOL Disabled as it uses WETH over ETH
|
||||
// There is a conflict with Curve and SnowSwap
|
||||
// where Curve uses ETH and SnowSwap uses WETH
|
||||
// To re-enable this we need to flag an WETH
|
||||
// unwrap or not
|
||||
// eth: '0x16bea2e63adade5984298d53a4d4d9c09e278192',
|
||||
};
|
||||
|
||||
export const SMOOTHY_POOLS = {
|
||||
syUSD: '0xe5859f4efc09027a9b718781dcb2c6910cac6e91',
|
||||
};
|
||||
|
||||
export const SADDLE_POOLS = {
|
||||
stables: '0x3911f80530595fbd01ab1516ab61255d75aeb066',
|
||||
bitcoins: '0x4f6a43ad7cba042606decaca730d4ce0a57ac62e',
|
||||
stablesV2: '0xaCb83E0633d6605c5001e2Ab59EF3C745547C8C7',
|
||||
bitcoinsV2: '0xdf3309771d2BF82cb2B6C56F9f5365C8bD97c4f2',
|
||||
alETH: '0xa6018520eaacc06c30ff2e1b3ee2c7c22e64196a',
|
||||
d4: '0xc69ddcd4dfef25d8a793241834d4cc4b3668ead6',
|
||||
};
|
||||
@@ -780,6 +806,14 @@ export const FIREBIRDONESWAP_BSC_POOLS = {
|
||||
export const FIREBIRDONESWAP_POLYGON_POOLS = {
|
||||
oneswap: '0x01c9475dbd36e46d1961572c8de24b74616bae9e',
|
||||
};
|
||||
export const MOBIUSMONEY_CELO_POOLS = {
|
||||
usdc_optics_v2: '0x9906589ea8fd27504974b7e8201df5bbde986b03',
|
||||
dai_optics_v2: '0xf3f65dfe0c8c8f2986da0fec159abe6fd4e700b4',
|
||||
weth_optics_v2: '0x74ef28d635c6c5800dd3cd62d4c4f8752daacb09',
|
||||
pusdc_optics_v2: '0xcce0d62ce14fb3e4363eb92db37ff3630836c252',
|
||||
usdc_allbridge_solana: '0x63c1914bf00a9b395a2bf89aada55a5615a3656e',
|
||||
usdc_poly_optics: '0xa2f0e57d4ceacf025e81c76f28b9ad6e9fbe8735',
|
||||
};
|
||||
|
||||
export const ACRYPTOS_POOLS = {
|
||||
acs4usd: '0xb3f0c9ea1f05e312093fdb031e789a756659b0ac',
|
||||
@@ -829,6 +863,7 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
|
||||
AVALANCHE_TOKENS.nUSD,
|
||||
AVALANCHE_TOKENS.nETH,
|
||||
AVALANCHE_TOKENS.aWETH,
|
||||
AVALANCHE_TOKENS.MIM,
|
||||
],
|
||||
[ChainId.Fantom]: [
|
||||
FANTOM_TOKENS.WFTM,
|
||||
@@ -839,7 +874,13 @@ export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
|
||||
FANTOM_TOKENS.nETH,
|
||||
FANTOM_TOKENS.MIM,
|
||||
],
|
||||
[ChainId.Celo]: [CELO_TOKENS.WCELO, CELO_TOKENS.mCUSD, CELO_TOKENS.WETH, CELO_TOKENS.amCUSD, CELO_TOKENS.WBTC],
|
||||
[ChainId.Celo]: [
|
||||
CELO_TOKENS.WCELO,
|
||||
CELO_TOKENS.mCUSD,
|
||||
CELO_TOKENS.WETHv1,
|
||||
CELO_TOKENS.amCUSD,
|
||||
CELO_TOKENS.WBTC,
|
||||
],
|
||||
[ChainId.Optimism]: [
|
||||
OPTIMISM_TOKENS.WETH,
|
||||
OPTIMISM_TOKENS.DAI,
|
||||
@@ -864,6 +905,8 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
|
||||
builder.add(MAINNET_TOKENS.MIR, MAINNET_TOKENS.UST);
|
||||
// Convex and Curve
|
||||
builder.add(MAINNET_TOKENS.cvxCRV, MAINNET_TOKENS.CRV).add(MAINNET_TOKENS.CRV, MAINNET_TOKENS.cvxCRV);
|
||||
// Convex and FXS
|
||||
builder.add(MAINNET_TOKENS.cvxFXS, MAINNET_TOKENS.FXS).add(MAINNET_TOKENS.FXS, MAINNET_TOKENS.cvxFXS);
|
||||
// FEI TRIBE liquid in UniV2
|
||||
builder.add(MAINNET_TOKENS.FEI, MAINNET_TOKENS.TRIBE).add(MAINNET_TOKENS.TRIBE, MAINNET_TOKENS.FEI);
|
||||
// FRAX ecosystem
|
||||
@@ -881,15 +924,21 @@ export const DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID = valueByChainId<TokenAdj
|
||||
}).build(),
|
||||
[ChainId.Polygon]: new TokenAdjacencyGraphBuilder({
|
||||
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Polygon],
|
||||
}).build(),
|
||||
})
|
||||
.tap(builder => {
|
||||
builder.add(POLYGON_TOKENS.QUICK, POLYGON_TOKENS.ANY).add(POLYGON_TOKENS.ANY, POLYGON_TOKENS.QUICK);
|
||||
})
|
||||
.build(),
|
||||
[ChainId.Avalanche]: new TokenAdjacencyGraphBuilder({
|
||||
default: DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID[ChainId.Avalanche],
|
||||
})
|
||||
.tap(builder => {
|
||||
// Synape nETH/aWETH pool
|
||||
// Synapse nETH/aWETH pool
|
||||
builder
|
||||
.add(AVALANCHE_TOKENS.aWETH, AVALANCHE_TOKENS.nETH)
|
||||
.add(AVALANCHE_TOKENS.nETH, AVALANCHE_TOKENS.aWETH);
|
||||
// Trader Joe MAG/MIM pool
|
||||
builder.add(AVALANCHE_TOKENS.MIM, AVALANCHE_TOKENS.MAG).add(AVALANCHE_TOKENS.MAG, AVALANCHE_TOKENS.MIM);
|
||||
})
|
||||
.build(),
|
||||
[ChainId.Fantom]: new TokenAdjacencyGraphBuilder({
|
||||
@@ -1004,6 +1053,25 @@ const createCurveV2MetaTriPool = (info: { tokens: string[]; pool: string; gasSch
|
||||
gasSchedule: info.gasSchedule,
|
||||
});
|
||||
|
||||
const createCurveFactoryCryptoExchangePool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
|
||||
exchangeFunctionSelector: CurveFunctionSelectors.exchange_underlying_uint256,
|
||||
sellQuoteFunctionSelector: CurveFunctionSelectors.get_dy_uint256,
|
||||
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
|
||||
tokens: info.tokens,
|
||||
metaTokens: undefined,
|
||||
poolAddress: info.pool,
|
||||
gasSchedule: info.gasSchedule,
|
||||
});
|
||||
const MOBIUSMONEY_CELO_SHARED: CurveInfo = {
|
||||
exchangeFunctionSelector: CurveFunctionSelectors.swap,
|
||||
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
|
||||
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
|
||||
metaTokens: undefined,
|
||||
gasSchedule: 150e3,
|
||||
poolAddress: NULL_ADDRESS,
|
||||
tokens: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Mainnet Curve configuration
|
||||
* The tokens are in order of their index, which each curve defines
|
||||
@@ -1270,6 +1338,26 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
pool: CURVE_POOLS.ibEURsEUR,
|
||||
gasSchedule: 176e3,
|
||||
}),
|
||||
[CURVE_POOLS.btrflyweth]: createCurveFactoryCryptoExchangePool({
|
||||
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.BTRFLY],
|
||||
pool: CURVE_POOLS.btrflyweth,
|
||||
gasSchedule: 250e3,
|
||||
}),
|
||||
[CURVE_POOLS.wethyfi]: createCurveFactoryCryptoExchangePool({
|
||||
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.YFI],
|
||||
pool: CURVE_POOLS.wethyfi,
|
||||
gasSchedule: 250e3,
|
||||
}),
|
||||
[CURVE_POOLS.stgusdc]: createCurveFactoryCryptoExchangePool({
|
||||
tokens: [MAINNET_TOKENS.STG, MAINNET_TOKENS.USDC],
|
||||
pool: CURVE_POOLS.stgusdc,
|
||||
gasSchedule: 250e3,
|
||||
}),
|
||||
[CURVE_POOLS.cvxfxs]: createCurveFactoryCryptoExchangePool({
|
||||
tokens: [MAINNET_TOKENS.FXS, MAINNET_TOKENS.cvxFXS],
|
||||
pool: CURVE_POOLS.cvxfxs,
|
||||
gasSchedule: 390e3,
|
||||
}),
|
||||
};
|
||||
|
||||
export const CURVE_V2_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
@@ -1407,38 +1495,6 @@ export const CURVE_OPTIMISM_INFOS: { [name: string]: CurveInfo } = {
|
||||
}),
|
||||
};
|
||||
|
||||
export const SWERVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
[SWERVE_POOLS.y]: createCurveExchangePool({
|
||||
tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT, MAINNET_TOKENS.TUSD],
|
||||
pool: SWERVE_POOLS.y,
|
||||
gasSchedule: 140e3,
|
||||
}),
|
||||
};
|
||||
|
||||
export const SNOWSWAP_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
[SNOWSWAP_POOLS.yUSD]: createCurveExchangePool({
|
||||
tokens: [MAINNET_TOKENS.yUSD, MAINNET_TOKENS.ybCRV],
|
||||
pool: SNOWSWAP_POOLS.yUSD,
|
||||
gasSchedule: 990e3,
|
||||
}),
|
||||
[SNOWSWAP_POOLS.yUSD]: createCurveExchangeUnderlyingPool({
|
||||
tokens: [MAINNET_TOKENS.yCRV, MAINNET_TOKENS.bCRV],
|
||||
pool: SNOWSWAP_POOLS.yUSD,
|
||||
gasSchedule: 990e3,
|
||||
}),
|
||||
[SNOWSWAP_POOLS.yVault]: createCurveExchangePool({
|
||||
tokens: [MAINNET_TOKENS.yDAI, MAINNET_TOKENS.yUSDC, MAINNET_TOKENS.yUSDT, MAINNET_TOKENS.yTUSD],
|
||||
pool: SNOWSWAP_POOLS.yVault,
|
||||
gasSchedule: 1490e3,
|
||||
}),
|
||||
// Unsupported due to collision with WETH and ETH with execution using MixinCurve
|
||||
// [SNOWSWAP_POOLS.eth]: createCurveExchangePool({
|
||||
// tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.vETH, MAINNET_TOKENS.ankrETH, MAINNET_TOKENS.crETH],
|
||||
// pool: SNOWSWAP_POOLS.eth,
|
||||
// gasSchedule: 990e3,
|
||||
// }),
|
||||
};
|
||||
|
||||
export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = {
|
||||
[BELT_POOLS.vPool]: createCurveExchangeUnderlyingPool({
|
||||
tokens: [BSC_TOKENS.DAI, BSC_TOKENS.USDC, BSC_TOKENS.USDT, BSC_TOKENS.BUSD],
|
||||
@@ -1465,21 +1521,21 @@ export const XSIGMA_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
|
||||
// Curve-like sources using custom selectors
|
||||
export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
|
||||
[SADDLE_POOLS.stables]: {
|
||||
[SADDLE_POOLS.stablesV2]: {
|
||||
exchangeFunctionSelector: CurveFunctionSelectors.swap,
|
||||
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
|
||||
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
|
||||
poolAddress: SADDLE_POOLS.stables,
|
||||
poolAddress: SADDLE_POOLS.stablesV2,
|
||||
tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT],
|
||||
metaTokens: undefined,
|
||||
gasSchedule: 150e3,
|
||||
},
|
||||
[SADDLE_POOLS.bitcoins]: {
|
||||
[SADDLE_POOLS.bitcoinsV2]: {
|
||||
exchangeFunctionSelector: CurveFunctionSelectors.swap,
|
||||
sellQuoteFunctionSelector: CurveFunctionSelectors.calculateSwap,
|
||||
buyQuoteFunctionSelector: CurveFunctionSelectors.None,
|
||||
poolAddress: SADDLE_POOLS.bitcoins,
|
||||
tokens: [MAINNET_TOKENS.tBTC, MAINNET_TOKENS.WBTC, MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.sBTC],
|
||||
poolAddress: SADDLE_POOLS.bitcoinsV2,
|
||||
tokens: [MAINNET_TOKENS.WBTC, MAINNET_TOKENS.RenBTC, MAINNET_TOKENS.sBTC],
|
||||
metaTokens: undefined,
|
||||
gasSchedule: 150e3,
|
||||
},
|
||||
@@ -1665,6 +1721,39 @@ export const FIREBIRDONESWAP_POLYGON_INFOS: { [name: string]: CurveInfo } = {
|
||||
},
|
||||
};
|
||||
|
||||
export const MOBIUSMONEY_CELO_INFOS: { [name: string]: CurveInfo } = {
|
||||
[MOBIUSMONEY_CELO_POOLS.usdc_optics_v2]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_optics_v2,
|
||||
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.cUSDC_V2],
|
||||
},
|
||||
[MOBIUSMONEY_CELO_POOLS.weth_optics_v2]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.weth_optics_v2,
|
||||
tokens: [CELO_TOKENS.cETH, CELO_TOKENS.oWETH],
|
||||
},
|
||||
[MOBIUSMONEY_CELO_POOLS.pusdc_optics_v2]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.pusdc_optics_v2,
|
||||
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSDC_V2],
|
||||
},
|
||||
[MOBIUSMONEY_CELO_POOLS.usdc_allbridge_solana]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_allbridge_solana,
|
||||
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.asUSDC],
|
||||
},
|
||||
[MOBIUSMONEY_CELO_POOLS.usdc_poly_optics]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.usdc_poly_optics,
|
||||
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.pUSD],
|
||||
},
|
||||
[MOBIUSMONEY_CELO_POOLS.dai_optics_v2]: {
|
||||
...MOBIUSMONEY_CELO_SHARED,
|
||||
poolAddress: MOBIUSMONEY_CELO_POOLS.dai_optics_v2,
|
||||
tokens: [CELO_TOKENS.cUSD, CELO_TOKENS.DAI],
|
||||
},
|
||||
};
|
||||
|
||||
const ACRYPTOS_ACS4USD_POOL_BSC_TOKENS = [BSC_TOKENS.BUSD, BSC_TOKENS.USDT, BSC_TOKENS.DAI, BSC_TOKENS.USDC];
|
||||
|
||||
const createAcryptosMetaUsdPool = (info: { tokens: string[]; pool: string; gasSchedule: number }) => ({
|
||||
@@ -1794,11 +1883,6 @@ export const CRYPTO_COM_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const LINKSWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{ [ChainId.Mainnet]: '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1' },
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const SHIBASWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0x03f7724180aa6b939894b5ca4314783b0b36b329',
|
||||
@@ -1859,6 +1943,13 @@ export const KYBER_DMM_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const BISWAP_ROUTER_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.BSC]: '0x3a6d8ca21d1cf76f653a67577fa0d27453350dd8',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const MOONISWAP_REGISTRIES_BY_CHAIN_ID = valueByChainId(
|
||||
{
|
||||
[ChainId.Mainnet]: ['0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643'],
|
||||
@@ -1996,6 +2087,13 @@ export const COMPONENT_POOLS_BY_CHAIN_ID = valueByChainId(
|
||||
},
|
||||
);
|
||||
|
||||
export const GEIST_INFO_ADDRESS_BY_CHAIN_ID = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Fantom]: '0xd8321aa83fb0a4ecd6348d4577431310a6e0814d',
|
||||
},
|
||||
NULL_ADDRESS,
|
||||
);
|
||||
|
||||
export const BALANCER_V2_VAULT_ADDRESS_BY_CHAIN = valueByChainId<string>(
|
||||
{
|
||||
[ChainId.Mainnet]: '0xba12222222228d8ba445958a75a0704d566bf2c8',
|
||||
@@ -2028,11 +2126,12 @@ export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/ba
|
||||
export const BALANCER_TOP_POOLS_FETCHED = 250;
|
||||
export const BALANCER_MAX_POOLS_FETCHED = 3;
|
||||
|
||||
export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
|
||||
export const BALANCER_V2_SUBGRAPH_URL_BY_CHAIN = valueByChainId(
|
||||
{
|
||||
[ChainId.Mainnet]: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2',
|
||||
[ChainId.Polygon]: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-polygon-v2',
|
||||
},
|
||||
'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2',
|
||||
null,
|
||||
);
|
||||
|
||||
export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
|
||||
@@ -2045,19 +2144,19 @@ export const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = valueByChainId<string>(
|
||||
export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
|
||||
{
|
||||
[ChainId.Mainnet]: {
|
||||
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
|
||||
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
|
||||
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
},
|
||||
[ChainId.Ropsten]: {
|
||||
quoter: '0x2f9e608fd881861b8916257b76613cb22ee0652c',
|
||||
router: '0x03782388516e94fcd4c18666303601a12aa729ea',
|
||||
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
|
||||
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
},
|
||||
[ChainId.Polygon]: {
|
||||
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
|
||||
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
|
||||
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
},
|
||||
[ChainId.Optimism]: {
|
||||
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
|
||||
quoter: '0x61ffe014ba17989e743c5f6cb21bf9697530b21e',
|
||||
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
|
||||
},
|
||||
},
|
||||
@@ -2282,12 +2381,9 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.LiquidityProvider]: fillData => {
|
||||
return (fillData as LiquidityProviderFillData).gasCost || 100e3;
|
||||
},
|
||||
[ERC20BridgeSource.Eth2Dai]: () => 400e3,
|
||||
[ERC20BridgeSource.Kyber]: () => 450e3,
|
||||
[ERC20BridgeSource.Curve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.CurveV2]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.Swerve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.SnowSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.Nerve]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.Synapse]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.Belt]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
@@ -2297,14 +2393,17 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
[ERC20BridgeSource.IronSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.XSigma]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.FirebirdOneSwap]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.MobiusMoney]: fillData => (fillData as CurveFillData).pool.gasSchedule,
|
||||
[ERC20BridgeSource.MultiBridge]: () => 350e3,
|
||||
[ERC20BridgeSource.UniswapV2]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.SushiSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.CryptoCom]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Linkswap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.ShibaSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.BiSwap]: uniswapV2CloneGasSchedule,
|
||||
[ERC20BridgeSource.Balancer]: () => 120e3,
|
||||
[ERC20BridgeSource.BalancerV2]: () => 100e3,
|
||||
[ERC20BridgeSource.BalancerV2]: (fillData?: FillData) => {
|
||||
return 100e3 + ((fillData as BalancerV2BatchSwapFillData).swapSteps.length - 1) * 50e3;
|
||||
},
|
||||
[ERC20BridgeSource.Cream]: () => 120e3,
|
||||
[ERC20BridgeSource.MStable]: () => 200e3,
|
||||
[ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {
|
||||
@@ -2349,11 +2448,34 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
return gas;
|
||||
},
|
||||
[ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => {
|
||||
let gas = 100e3;
|
||||
const path = (fillData as UniswapV3FillData).tokenAddressPath;
|
||||
if (path.length > 2) {
|
||||
gas += (path.length - 2) * 32e3; // +32k for each hop.
|
||||
const uniFillData = fillData as UniswapV3FillData | FinalUniswapV3FillData;
|
||||
// NOTE: This base value was heuristically chosen by looking at how much it generally
|
||||
// underestimated gas usage
|
||||
const base = 34e3; // 34k base
|
||||
let gas = base;
|
||||
if (isFinalUniswapV3FillData(uniFillData)) {
|
||||
gas += uniFillData.gasUsed;
|
||||
} else {
|
||||
// NOTE: We don't actually know which of the paths would be used in the router
|
||||
// therefore we estimate using the median of gas usage returned from UniswapV3
|
||||
// For the best case scenario (least amount of hops & ticks) this will
|
||||
// overestimate the gas usage
|
||||
const pathAmountsWithGasUsed = uniFillData.pathAmounts.filter(p => p.gasUsed > 0);
|
||||
const medianGasUsedForPath =
|
||||
pathAmountsWithGasUsed[Math.floor(pathAmountsWithGasUsed.length / 2)]?.gasUsed ?? 0;
|
||||
gas += medianGasUsedForPath;
|
||||
}
|
||||
|
||||
// If we for some reason could not read `gasUsed` when sampling
|
||||
// fall back to legacy gas estimation
|
||||
if (gas === base) {
|
||||
gas = 100e3;
|
||||
const path = uniFillData.tokenAddressPath;
|
||||
if (path.length > 2) {
|
||||
gas += (path.length - 2) * 32e3; // +32k for each hop.
|
||||
}
|
||||
}
|
||||
|
||||
return gas;
|
||||
},
|
||||
[ERC20BridgeSource.Lido]: () => 226e3,
|
||||
@@ -2362,6 +2484,10 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
|
||||
// NOTE: The Aave deposit method is more expensive than the withdraw
|
||||
return aaveFillData.takerToken === aaveFillData.underlyingToken ? 400e3 : 300e3;
|
||||
},
|
||||
[ERC20BridgeSource.Geist]: (fillData?: FillData) => {
|
||||
const geistFillData = fillData as GeistFillData;
|
||||
return geistFillData.takerToken === geistFillData.underlyingToken ? 400e3 : 300e3;
|
||||
},
|
||||
[ERC20BridgeSource.Compound]: (fillData?: FillData) => {
|
||||
// NOTE: cETH is handled differently than other cTokens
|
||||
const wethAddress = NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet];
|
||||
|
||||
@@ -175,9 +175,9 @@ export function dexSamplesToFills(
|
||||
const { source, fillData } = sample;
|
||||
const input = sample.input.minus(prevSample ? prevSample.input : 0);
|
||||
const output = sample.output.minus(prevSample ? prevSample.output : 0);
|
||||
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
|
||||
let penalty = ZERO_AMOUNT;
|
||||
if (i === 0) {
|
||||
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
|
||||
// Only the first fill in a DEX path incurs a penalty.
|
||||
penalty = ethToOutputAmount({
|
||||
input,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { FANTOM_TOKENS, GEIST_FANTOM_POOLS } from './constants';
|
||||
import { GeistInfo } from './types';
|
||||
|
||||
const gTokenToUnderlyingToken = new Map<string, string>([
|
||||
[FANTOM_TOKENS.gFTM, FANTOM_TOKENS.WFTM],
|
||||
[FANTOM_TOKENS.gfUSDT, FANTOM_TOKENS.fUSDT],
|
||||
[FANTOM_TOKENS.gDAI, FANTOM_TOKENS.DAI],
|
||||
[FANTOM_TOKENS.gUSDC, FANTOM_TOKENS.USDC],
|
||||
[FANTOM_TOKENS.gETH, FANTOM_TOKENS.WETH],
|
||||
[FANTOM_TOKENS.gWBTC, FANTOM_TOKENS.WBTC],
|
||||
[FANTOM_TOKENS.gCRV, FANTOM_TOKENS.WCRV],
|
||||
[FANTOM_TOKENS.gMIM, FANTOM_TOKENS.MIM],
|
||||
]);
|
||||
|
||||
/**
|
||||
* Returns GeistInfo for a certain pair if that pair exists on Geist
|
||||
*/
|
||||
export function getGeistInfoForPair(takerToken: string, makerToken: string): GeistInfo | undefined {
|
||||
let gToken;
|
||||
let underlyingToken;
|
||||
if (gTokenToUnderlyingToken.get(takerToken) === makerToken) {
|
||||
gToken = takerToken;
|
||||
underlyingToken = makerToken;
|
||||
} else if (gTokenToUnderlyingToken.get(makerToken) === takerToken) {
|
||||
gToken = makerToken;
|
||||
underlyingToken = takerToken;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
lendingPool: GEIST_FANTOM_POOLS.lendingPool,
|
||||
gToken,
|
||||
underlyingToken,
|
||||
};
|
||||
}
|
||||
@@ -4,12 +4,15 @@ import * as _ from 'lodash';
|
||||
|
||||
import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants';
|
||||
import {
|
||||
AltRfqMakerAssetOfferings,
|
||||
AssetSwapperContractAddresses,
|
||||
MarketOperation,
|
||||
NativeOrderWithFillableAmounts,
|
||||
SignedNativeOrder,
|
||||
} from '../../types';
|
||||
import { QuoteRequestor } from '../quote_requestor';
|
||||
import { getAltMarketInfo } from '../alt_mm_implementation_utils';
|
||||
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../quote_requestor';
|
||||
import { toSignedNativeOrder } from '../rfq_client_mappers';
|
||||
import {
|
||||
getNativeAdjustedFillableAmountsFromMakerAmount,
|
||||
getNativeAdjustedFillableAmountsFromTakerAmount,
|
||||
@@ -42,7 +45,7 @@ import { createFills } from './fills';
|
||||
import { getBestTwoHopQuote } from './multihop_utils';
|
||||
import { createOrdersFromTwoHopSample } from './orders';
|
||||
import { Path, PathPenaltyOpts } from './path';
|
||||
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
|
||||
import { findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
|
||||
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
||||
import { SourceFilters } from './source_filters';
|
||||
import {
|
||||
@@ -241,6 +244,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes,
|
||||
},
|
||||
isRfqSupported,
|
||||
blockNumber: blockNumber.toNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -269,6 +273,7 @@ export class MarketOperationUtils {
|
||||
|
||||
// Call the sampler contract.
|
||||
const samplerPromise = this._sampler.executeAsync(
|
||||
this._sampler.getBlockNumber(),
|
||||
this._sampler.getTokenDecimals([makerToken, takerToken]),
|
||||
// Get native order fillable amounts.
|
||||
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
||||
@@ -302,6 +307,7 @@ export class MarketOperationUtils {
|
||||
|
||||
const [
|
||||
[
|
||||
blockNumber,
|
||||
tokenDecimals,
|
||||
orderFillableMakerAmounts,
|
||||
ethToMakerAssetRate,
|
||||
@@ -342,6 +348,7 @@ export class MarketOperationUtils {
|
||||
dexQuotes,
|
||||
},
|
||||
isRfqSupported,
|
||||
blockNumber: blockNumber.toNumber(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -372,6 +379,7 @@ export class MarketOperationUtils {
|
||||
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
|
||||
|
||||
const ops = [
|
||||
this._sampler.getBlockNumber(),
|
||||
...batchNativeOrders.map(orders =>
|
||||
this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
|
||||
),
|
||||
@@ -396,13 +404,15 @@ export class MarketOperationUtils {
|
||||
),
|
||||
];
|
||||
|
||||
const executeResults = await this._sampler.executeBatchAsync(ops);
|
||||
const [blockNumberRaw, ...executeResults] = await this._sampler.executeBatchAsync(ops);
|
||||
const batchOrderFillableMakerAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
|
||||
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
|
||||
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
|
||||
const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
|
||||
const inputAmountPerEth = ZERO_AMOUNT;
|
||||
|
||||
const blockNumber: number = (blockNumberRaw as BigNumber).toNumber();
|
||||
|
||||
return Promise.all(
|
||||
batchNativeOrders.map(async (nativeOrders, i) => {
|
||||
if (nativeOrders.length === 0) {
|
||||
@@ -435,6 +445,7 @@ export class MarketOperationUtils {
|
||||
twoHopQuotes: [],
|
||||
},
|
||||
isRfqSupported: false,
|
||||
blockNumber,
|
||||
},
|
||||
{
|
||||
bridgeSlippage: _opts.bridgeSlippage,
|
||||
@@ -493,18 +504,6 @@ export class MarketOperationUtils {
|
||||
} as NativeOrderWithFillableAmounts),
|
||||
);
|
||||
|
||||
// Convert native orders and dex quotes into `Fill` objects.
|
||||
const fills = createFills({
|
||||
side,
|
||||
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
|
||||
dexQuotes,
|
||||
targetInput: inputAmount,
|
||||
outputAmountPerEth,
|
||||
inputAmountPerEth,
|
||||
excludedSources: opts.excludedSources,
|
||||
feeSchedule: opts.feeSchedule,
|
||||
});
|
||||
|
||||
// Find the optimal path.
|
||||
const penaltyOpts: PathPenaltyOpts = {
|
||||
outputAmountPerEth,
|
||||
@@ -517,13 +516,11 @@ export class MarketOperationUtils {
|
||||
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
|
||||
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
|
||||
|
||||
// Find the unoptimized best rate to calculate savings from optimizer
|
||||
const _unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, penaltyOpts)[0];
|
||||
const unoptimizedPath = _unoptimizedPath ? _unoptimizedPath.collapse(orderOpts) : undefined;
|
||||
|
||||
let fills: Fill[][];
|
||||
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
|
||||
let optimalPath: Path | undefined;
|
||||
if (SHOULD_USE_RUST_ROUTER) {
|
||||
fills = [[]];
|
||||
optimalPath = findOptimalRustPathFromSamples(
|
||||
side,
|
||||
dexQuotes,
|
||||
@@ -536,6 +533,18 @@ export class MarketOperationUtils {
|
||||
opts.samplerMetrics,
|
||||
);
|
||||
} else {
|
||||
// Convert native orders and dex quotes into `Fill` objects.
|
||||
fills = createFills({
|
||||
side,
|
||||
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
|
||||
dexQuotes,
|
||||
targetInput: inputAmount,
|
||||
outputAmountPerEth,
|
||||
inputAmountPerEth,
|
||||
excludedSources: opts.excludedSources,
|
||||
feeSchedule: opts.feeSchedule,
|
||||
});
|
||||
|
||||
optimalPath = await findOptimalPathJSAsync(
|
||||
side,
|
||||
fills,
|
||||
@@ -561,7 +570,6 @@ export class MarketOperationUtils {
|
||||
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
||||
marketSideLiquidity,
|
||||
adjustedRate: bestTwoHopRate,
|
||||
unoptimizedPath,
|
||||
takerAmountPerEth,
|
||||
makerAmountPerEth,
|
||||
};
|
||||
@@ -573,7 +581,10 @@ export class MarketOperationUtils {
|
||||
}
|
||||
|
||||
// Generate a fallback path if required
|
||||
await this._addOptionalFallbackAsync(side, inputAmount, optimalPath, dexQuotes, fills, opts, penaltyOpts);
|
||||
// TODO(kimpers): Will experiment with disabling this and see how it affects revert rate
|
||||
// to avoid yet another router roundtrip
|
||||
// TODO: clean this up if we don't need it
|
||||
// await this._addOptionalFallbackAsync(side, inputAmount, optimalPath, dexQuotes, fills, opts, penaltyOpts);
|
||||
const collapsedPath = optimalPath.collapse(orderOpts);
|
||||
|
||||
return {
|
||||
@@ -582,7 +593,6 @@ export class MarketOperationUtils {
|
||||
sourceFlags: collapsedPath.sourceFlags,
|
||||
marketSideLiquidity,
|
||||
adjustedRate: optimalPathRate,
|
||||
unoptimizedPath,
|
||||
takerAmountPerEth,
|
||||
makerAmountPerEth,
|
||||
};
|
||||
@@ -656,17 +666,49 @@ export class MarketOperationUtils {
|
||||
// Timing of RFQT lifecycle
|
||||
const timeStart = new Date().getTime();
|
||||
const { makerToken, takerToken } = nativeOrders[0].order;
|
||||
|
||||
// Filter Alt Rfq Maker Asset Offerings to the current pair
|
||||
const filteredOfferings: AltRfqMakerAssetOfferings = {};
|
||||
if (rfqt.altRfqAssetOfferings) {
|
||||
const endpoints = Object.keys(rfqt.altRfqAssetOfferings);
|
||||
for (const endpoint of endpoints) {
|
||||
// Get the current pair if being offered
|
||||
const offering = getAltMarketInfo(rfqt.altRfqAssetOfferings[endpoint], makerToken, takerToken);
|
||||
if (offering) {
|
||||
filteredOfferings[endpoint] = [offering];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rfqt.isIndicative) {
|
||||
// An indicative quote is being requested, and indicative quotes price-aware enabled
|
||||
// Make the RFQT request and then re-run the sampler if new orders come back.
|
||||
const indicativeQuotes = await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
|
||||
makerToken,
|
||||
takerToken,
|
||||
amount,
|
||||
side,
|
||||
wholeOrderPrice,
|
||||
rfqt,
|
||||
);
|
||||
|
||||
const indicativeQuotes =
|
||||
rfqt.rfqClient !== undefined
|
||||
? ((
|
||||
await rfqt.rfqClient.getV1PricesAsync({
|
||||
altRfqAssetOfferings: filteredOfferings,
|
||||
assetFillAmount: amount,
|
||||
chainId: this._sampler.chainId,
|
||||
comparisonPrice: wholeOrderPrice,
|
||||
integratorId: rfqt.integrator.integratorId,
|
||||
intentOnFilling: rfqt.intentOnFilling,
|
||||
makerToken,
|
||||
marketOperation: side,
|
||||
takerAddress: rfqt.takerAddress,
|
||||
takerToken,
|
||||
txOrigin: rfqt.txOrigin,
|
||||
})
|
||||
).prices as V4RFQIndicativeQuoteMM[])
|
||||
: await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
|
||||
makerToken,
|
||||
takerToken,
|
||||
amount,
|
||||
side,
|
||||
wholeOrderPrice,
|
||||
rfqt,
|
||||
);
|
||||
const deltaTime = new Date().getTime() - timeStart;
|
||||
DEFAULT_INFO_LOGGER({
|
||||
rfqQuoteType: 'indicative',
|
||||
@@ -680,14 +722,31 @@ export class MarketOperationUtils {
|
||||
} else {
|
||||
// A firm quote is being requested, and firm quotes price-aware enabled.
|
||||
// Ensure that `intentOnFilling` is enabled and make the request.
|
||||
const firmQuotes = await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
|
||||
makerToken,
|
||||
takerToken,
|
||||
amount,
|
||||
side,
|
||||
wholeOrderPrice,
|
||||
rfqt,
|
||||
);
|
||||
const firmQuotes =
|
||||
rfqt.rfqClient !== undefined
|
||||
? (
|
||||
await rfqt.rfqClient.getV1QuotesAsync({
|
||||
altRfqAssetOfferings: filteredOfferings,
|
||||
assetFillAmount: amount,
|
||||
chainId: this._sampler.chainId,
|
||||
comparisonPrice: wholeOrderPrice,
|
||||
integratorId: rfqt.integrator.integratorId,
|
||||
intentOnFilling: rfqt.intentOnFilling,
|
||||
makerToken,
|
||||
marketOperation: side,
|
||||
takerAddress: rfqt.takerAddress,
|
||||
takerToken,
|
||||
txOrigin: rfqt.txOrigin,
|
||||
})
|
||||
).quotes.map(toSignedNativeOrder)
|
||||
: await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
|
||||
makerToken,
|
||||
takerToken,
|
||||
amount,
|
||||
side,
|
||||
wholeOrderPrice,
|
||||
rfqt,
|
||||
);
|
||||
const deltaTime = new Date().getTime() - timeStart;
|
||||
DEFAULT_INFO_LOGGER({
|
||||
rfqQuoteType: 'firm',
|
||||
@@ -770,7 +829,7 @@ export class MarketOperationUtils {
|
||||
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
|
||||
void Promise.all(
|
||||
Object.values(this._sampler.poolsCaches).map(async cache => {
|
||||
if (cache.isFresh(takerToken, makerToken)) {
|
||||
if (!cache || cache.isFresh(takerToken, makerToken)) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
|
||||
@@ -778,6 +837,8 @@ export class MarketOperationUtils {
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO(kimpers): Remove this when we know that it's safe to drop the fallbacks on native orders
|
||||
// tslint:disable-next-line: prefer-function-over-method
|
||||
private async _addOptionalFallbackAsync(
|
||||
side: MarketOperation,
|
||||
@@ -843,6 +904,7 @@ export class MarketOperationUtils {
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// tslint:disable: max-file-line-count
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
AaveV2FillData,
|
||||
AggregationError,
|
||||
BalancerFillData,
|
||||
BalancerV2BatchSwapFillData,
|
||||
BalancerV2FillData,
|
||||
BancorFillData,
|
||||
CollapsedFill,
|
||||
@@ -18,6 +19,7 @@ import {
|
||||
ERC20BridgeSource,
|
||||
FillData,
|
||||
FinalUniswapV3FillData,
|
||||
GeistFillData,
|
||||
GenericRouterFillData,
|
||||
KyberDmmFillData,
|
||||
KyberFillData,
|
||||
@@ -36,6 +38,7 @@ import {
|
||||
ShellFillData,
|
||||
UniswapV2FillData,
|
||||
UniswapV3FillData,
|
||||
UniswapV3PathAmount,
|
||||
} from './types';
|
||||
|
||||
// tslint:disable completed-docs
|
||||
@@ -84,7 +87,7 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
||||
case ERC20BridgeSource.Balancer:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
|
||||
case ERC20BridgeSource.BalancerV2:
|
||||
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'BalancerV2');
|
||||
return encodeBridgeSourceId(BridgeProtocol.BalancerV2Batch, 'BalancerV2');
|
||||
case ERC20BridgeSource.Bancor:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
|
||||
// case ERC20BridgeSource.CoFiX:
|
||||
@@ -108,24 +111,16 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
||||
return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap');
|
||||
case ERC20BridgeSource.MStable:
|
||||
return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable');
|
||||
case ERC20BridgeSource.Eth2Dai:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Oasis, 'Eth2Dai');
|
||||
case ERC20BridgeSource.Shell:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell');
|
||||
case ERC20BridgeSource.SnowSwap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'SnowSwap');
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap');
|
||||
case ERC20BridgeSource.Swerve:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Swerve');
|
||||
case ERC20BridgeSource.Uniswap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap');
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2');
|
||||
case ERC20BridgeSource.DodoV2:
|
||||
return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2');
|
||||
case ERC20BridgeSource.Linkswap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Linkswap');
|
||||
case ERC20BridgeSource.PancakeSwap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap');
|
||||
case ERC20BridgeSource.PancakeSwapV2:
|
||||
@@ -202,6 +197,12 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
||||
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
|
||||
case ERC20BridgeSource.Compound:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound');
|
||||
case ERC20BridgeSource.Geist:
|
||||
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'Geist');
|
||||
case ERC20BridgeSource.MobiusMoney:
|
||||
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
|
||||
case ERC20BridgeSource.BiSwap:
|
||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BiSwap');
|
||||
default:
|
||||
throw new Error(AggregationError.NoBridgeForSource);
|
||||
}
|
||||
@@ -225,8 +226,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
switch (order.source) {
|
||||
case ERC20BridgeSource.Curve:
|
||||
case ERC20BridgeSource.CurveV2:
|
||||
case ERC20BridgeSource.Swerve:
|
||||
case ERC20BridgeSource.SnowSwap:
|
||||
case ERC20BridgeSource.Nerve:
|
||||
case ERC20BridgeSource.Synapse:
|
||||
case ERC20BridgeSource.Belt:
|
||||
@@ -237,6 +236,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
case ERC20BridgeSource.FirebirdOneSwap:
|
||||
case ERC20BridgeSource.IronSwap:
|
||||
case ERC20BridgeSource.ACryptos:
|
||||
case ERC20BridgeSource.MobiusMoney:
|
||||
const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
|
||||
bridgeData = encoder.encode([
|
||||
curveFillData.pool.poolAddress,
|
||||
@@ -251,9 +251,18 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
bridgeData = encoder.encode([balancerFillData.poolAddress]);
|
||||
break;
|
||||
case ERC20BridgeSource.BalancerV2:
|
||||
{
|
||||
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2BatchSwapFillData>).fillData;
|
||||
bridgeData = encoder.encode([
|
||||
balancerV2FillData.vault,
|
||||
balancerV2FillData.swapSteps,
|
||||
balancerV2FillData.assets,
|
||||
]);
|
||||
}
|
||||
break;
|
||||
case ERC20BridgeSource.Beethovenx:
|
||||
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
|
||||
const { vault, poolId } = balancerV2FillData;
|
||||
const beethovenFillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
|
||||
const { vault, poolId } = beethovenFillData;
|
||||
bridgeData = encoder.encode([vault, poolId]);
|
||||
break;
|
||||
case ERC20BridgeSource.Bancor:
|
||||
@@ -263,7 +272,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
case ERC20BridgeSource.UniswapV2:
|
||||
case ERC20BridgeSource.SushiSwap:
|
||||
case ERC20BridgeSource.CryptoCom:
|
||||
case ERC20BridgeSource.Linkswap:
|
||||
case ERC20BridgeSource.PancakeSwap:
|
||||
case ERC20BridgeSource.PancakeSwapV2:
|
||||
case ERC20BridgeSource.BakerySwap:
|
||||
@@ -284,6 +292,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
case ERC20BridgeSource.SpiritSwap:
|
||||
case ERC20BridgeSource.SpookySwap:
|
||||
case ERC20BridgeSource.MorpheusSwap:
|
||||
case ERC20BridgeSource.BiSwap:
|
||||
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
|
||||
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
|
||||
break;
|
||||
@@ -320,10 +329,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
const uniFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
||||
bridgeData = encoder.encode([uniFillData.router]);
|
||||
break;
|
||||
case ERC20BridgeSource.Eth2Dai:
|
||||
const oasisFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
||||
bridgeData = encoder.encode([oasisFillData.router]);
|
||||
break;
|
||||
case ERC20BridgeSource.MStable:
|
||||
const mStableFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
||||
bridgeData = encoder.encode([mStableFillData.router]);
|
||||
@@ -356,6 +361,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
||||
const compoundFillData = (order as OptimizedMarketBridgeOrder<CompoundFillData>).fillData;
|
||||
bridgeData = encoder.encode([compoundFillData.cToken]);
|
||||
break;
|
||||
case ERC20BridgeSource.Geist:
|
||||
const geistFillData = (order as OptimizedMarketBridgeOrder<GeistFillData>).fillData;
|
||||
bridgeData = encoder.encode([geistFillData.lendingPool, geistFillData.gToken]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(AggregationError.NoBridgeForSource);
|
||||
@@ -387,11 +396,14 @@ function createFinalBridgeOrderFillDataFromCollapsedFill(fill: CollapsedFill): F
|
||||
switch (fill.source) {
|
||||
case ERC20BridgeSource.UniswapV3: {
|
||||
const fd = fill.fillData as UniswapV3FillData;
|
||||
return {
|
||||
const { uniswapPath, gasUsed } = getBestUniswapV3PathAmountForInputAmount(fd, fill.input);
|
||||
const finalFillData: FinalUniswapV3FillData = {
|
||||
router: fd.router,
|
||||
tokenAddressPath: fd.tokenAddressPath,
|
||||
uniswapPath: getBestUniswapV3PathForInputAmount(fd, fill.input),
|
||||
uniswapPath,
|
||||
gasUsed,
|
||||
};
|
||||
return finalFillData;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
@@ -399,18 +411,21 @@ function createFinalBridgeOrderFillDataFromCollapsedFill(fill: CollapsedFill): F
|
||||
return fill.fillData;
|
||||
}
|
||||
|
||||
function getBestUniswapV3PathForInputAmount(fillData: UniswapV3FillData, inputAmount: BigNumber): string {
|
||||
function getBestUniswapV3PathAmountForInputAmount(
|
||||
fillData: UniswapV3FillData,
|
||||
inputAmount: BigNumber,
|
||||
): UniswapV3PathAmount {
|
||||
if (fillData.pathAmounts.length === 0) {
|
||||
throw new Error(`No Uniswap V3 paths`);
|
||||
}
|
||||
// Find the best path that can satisfy `inputAmount`.
|
||||
// Assumes `fillData.pathAmounts` is sorted ascending.
|
||||
for (const { inputAmount: pathInputAmount, uniswapPath } of fillData.pathAmounts) {
|
||||
if (pathInputAmount.gte(inputAmount)) {
|
||||
return uniswapPath;
|
||||
for (const pathAmount of fillData.pathAmounts) {
|
||||
if (pathAmount.inputAmount.gte(inputAmount)) {
|
||||
return pathAmount;
|
||||
}
|
||||
}
|
||||
return fillData.pathAmounts[fillData.pathAmounts.length - 1].uniswapPath;
|
||||
return fillData.pathAmounts[fillData.pathAmounts.length - 1];
|
||||
}
|
||||
|
||||
export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] {
|
||||
@@ -463,8 +478,6 @@ export const BRIDGE_ENCODERS: {
|
||||
// Curve like
|
||||
[ERC20BridgeSource.Curve]: curveEncoder,
|
||||
[ERC20BridgeSource.CurveV2]: curveEncoder,
|
||||
[ERC20BridgeSource.Swerve]: curveEncoder,
|
||||
[ERC20BridgeSource.SnowSwap]: curveEncoder,
|
||||
[ERC20BridgeSource.Nerve]: curveEncoder,
|
||||
[ERC20BridgeSource.Synapse]: curveEncoder,
|
||||
[ERC20BridgeSource.Belt]: curveEncoder,
|
||||
@@ -475,18 +488,19 @@ export const BRIDGE_ENCODERS: {
|
||||
[ERC20BridgeSource.FirebirdOneSwap]: curveEncoder,
|
||||
[ERC20BridgeSource.IronSwap]: curveEncoder,
|
||||
[ERC20BridgeSource.ACryptos]: curveEncoder,
|
||||
[ERC20BridgeSource.MobiusMoney]: curveEncoder,
|
||||
// UniswapV2 like, (router, address[])
|
||||
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.Linkswap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.ShibaSwap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.Pangolin]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.SpiritSwap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.SpookySwap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder,
|
||||
[ERC20BridgeSource.BiSwap]: routerAddressPathEncoder,
|
||||
// Celo
|
||||
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
|
||||
// BSC
|
||||
@@ -508,14 +522,27 @@ export const BRIDGE_ENCODERS: {
|
||||
[ERC20BridgeSource.Shell]: poolEncoder,
|
||||
[ERC20BridgeSource.Component]: poolEncoder,
|
||||
[ERC20BridgeSource.Mooniswap]: poolEncoder,
|
||||
[ERC20BridgeSource.Eth2Dai]: poolEncoder,
|
||||
[ERC20BridgeSource.MStable]: poolEncoder,
|
||||
[ERC20BridgeSource.Balancer]: poolEncoder,
|
||||
[ERC20BridgeSource.Cream]: poolEncoder,
|
||||
[ERC20BridgeSource.Uniswap]: poolEncoder,
|
||||
// Custom integrations
|
||||
[ERC20BridgeSource.MakerPsm]: makerPsmEncoder,
|
||||
[ERC20BridgeSource.BalancerV2]: balancerV2Encoder,
|
||||
[ERC20BridgeSource.BalancerV2]: AbiEncoder.create([
|
||||
{ name: 'vault', type: 'address' },
|
||||
{
|
||||
name: 'swapSteps',
|
||||
type: 'tuple[]',
|
||||
components: [
|
||||
{ name: 'poolId', type: 'bytes32' },
|
||||
{ name: 'assetInIndex', type: 'uint256' },
|
||||
{ name: 'assetOutIndex', type: 'uint256' },
|
||||
{ name: 'amount', type: 'uint256' },
|
||||
{ name: 'userData', type: 'bytes' },
|
||||
],
|
||||
},
|
||||
{ name: 'assets', type: 'address[]' },
|
||||
]),
|
||||
[ERC20BridgeSource.Beethovenx]: balancerV2Encoder,
|
||||
[ERC20BridgeSource.UniswapV3]: AbiEncoder.create([
|
||||
{ name: 'router', type: 'address' },
|
||||
@@ -525,6 +552,7 @@ export const BRIDGE_ENCODERS: {
|
||||
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
|
||||
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
|
||||
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
|
||||
[ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'),
|
||||
};
|
||||
|
||||
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {
|
||||
|
||||
@@ -122,17 +122,8 @@ export class Path {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
// If there are contiguous bridge orders, we can batch them together.
|
||||
// TODO jacob pretty sure this is from DFB and we can remove
|
||||
const contiguousBridgeFills = [collapsedFills[i]];
|
||||
for (let j = i + 1; j < collapsedFills.length; ++j) {
|
||||
if (collapsedFills[j].source === ERC20BridgeSource.Native) {
|
||||
break;
|
||||
}
|
||||
contiguousBridgeFills.push(collapsedFills[j]);
|
||||
}
|
||||
|
||||
this.orders.push(createBridgeOrder(contiguousBridgeFills[0], makerToken, takerToken, opts.side));
|
||||
this.orders.push(createBridgeOrder(collapsedFills[i], makerToken, takerToken, opts.side));
|
||||
i += 1;
|
||||
}
|
||||
return this as CollapsedPath;
|
||||
|
||||
@@ -21,6 +21,8 @@ const MIN_NUM_SAMPLE_INPUTS = 3;
|
||||
|
||||
const isDexSample = (obj: DexSample | NativeOrderWithFillableAmounts): obj is DexSample => !!(obj as DexSample).source;
|
||||
|
||||
const ONE_BASE_UNIT = new BigNumber(1);
|
||||
|
||||
function nativeOrderToNormalizedAmounts(
|
||||
side: MarketOperation,
|
||||
nativeOrder: NativeOrderWithFillableAmounts,
|
||||
@@ -74,7 +76,15 @@ function findRoutesAndCreateOptimalPath(
|
||||
opts: PathPenaltyOpts,
|
||||
fees: FeeSchedule,
|
||||
neonRouterNumSamples: number,
|
||||
): Path | undefined {
|
||||
vipSourcesSet: Set<ERC20BridgeSource>,
|
||||
): { allSourcesPath: Path | undefined; vipSourcesPath: Path | undefined } | undefined {
|
||||
// Currently the rust router is unable to handle 1 base unit sized quotes and will error out
|
||||
// To avoid flooding the logs with these errors we just return an insufficient liquidity error
|
||||
// which is how the JS router handles these quotes today
|
||||
if (input.isLessThanOrEqualTo(ONE_BASE_UNIT)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const createFill = (sample: DexSample): Fill | undefined => {
|
||||
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
|
||||
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
|
||||
@@ -85,6 +95,127 @@ function findRoutesAndCreateOptimalPath(
|
||||
return fills[0];
|
||||
};
|
||||
|
||||
const createPathFromStrategy = (sourcesRustRoute: Float64Array, sourcesOutputAmounts: Float64Array) => {
|
||||
const routesAndSamplesAndOutputs = _.zip(
|
||||
sourcesRustRoute,
|
||||
samplesAndNativeOrdersWithResults,
|
||||
sourcesOutputAmounts,
|
||||
sampleSourcePathIds,
|
||||
);
|
||||
const adjustedFills: Fill[] = [];
|
||||
const totalRoutedAmount = BigNumber.sum(...sourcesRustRoute);
|
||||
|
||||
const scale = input.dividedBy(totalRoutedAmount);
|
||||
for (const [
|
||||
routeInput,
|
||||
routeSamplesAndNativeOrders,
|
||||
outputAmount,
|
||||
sourcePathId,
|
||||
] of routesAndSamplesAndOutputs) {
|
||||
if (!Number.isFinite(outputAmount)) {
|
||||
DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`);
|
||||
return undefined;
|
||||
}
|
||||
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
|
||||
continue;
|
||||
}
|
||||
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
|
||||
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
|
||||
const rustInputAdjusted = BigNumber.min(
|
||||
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
|
||||
input,
|
||||
);
|
||||
|
||||
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
|
||||
if (!isDexSample(current)) {
|
||||
const nativeFill = nativeOrdersToFills(
|
||||
side,
|
||||
[current],
|
||||
rustInputAdjusted,
|
||||
opts.outputAmountPerEth,
|
||||
opts.inputAmountPerEth,
|
||||
fees,
|
||||
false,
|
||||
)[0] as Fill | undefined;
|
||||
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
|
||||
// and nativeFill will be `undefined`
|
||||
if (nativeFill) {
|
||||
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
||||
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: For DexSamples only
|
||||
let fill = createFill(current);
|
||||
if (!fill) {
|
||||
continue;
|
||||
}
|
||||
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
||||
// Descend to approach a closer fill for fillData which may not be consistent
|
||||
// throughout the path (UniswapV3) and for a closer guesstimate at
|
||||
// gas used
|
||||
|
||||
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
||||
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
||||
if (k === 0) {
|
||||
fill = createFill(routeSamples[0]) ?? fill;
|
||||
}
|
||||
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
|
||||
const left = routeSamples[k];
|
||||
const right = routeSamples[k + 1];
|
||||
if (left && right) {
|
||||
fill =
|
||||
createFill({
|
||||
...right, // default to the greater (for gas used)
|
||||
input: rustInputAdjusted,
|
||||
output: new BigNumber(outputAmount),
|
||||
}) ?? fill;
|
||||
} else {
|
||||
assert.assert(Boolean(left || right), 'No valid sample to use');
|
||||
fill = createFill(left || right) ?? fill;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
|
||||
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output));
|
||||
// Scale output by scale factor but never go above the largest sample in sell quotes (unknown liquidity) or below 1 base unit (unfillable)
|
||||
const scaleOutput = (output: BigNumber) => {
|
||||
// Don't try to scale 0 output as it will be clamped to 1
|
||||
if (output.eq(ZERO_AMOUNT)) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const scaled = output
|
||||
.times(scale)
|
||||
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
|
||||
const capped = MarketOperation.Sell ? BigNumber.min(scaled, maxSampledOutput) : scaled;
|
||||
|
||||
return BigNumber.max(capped, 1);
|
||||
};
|
||||
|
||||
adjustedFills.push({
|
||||
...fill,
|
||||
input: rustInputAdjusted,
|
||||
output: scaleOutput(fill.output),
|
||||
adjustedOutput: scaleOutput(fill.adjustedOutput),
|
||||
index: 0,
|
||||
parent: undefined,
|
||||
sourcePathId: sourcePathId ?? hexUtils.random(),
|
||||
});
|
||||
}
|
||||
|
||||
if (adjustedFills.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const pathFromRustInputs = Path.create(side, adjustedFills, input, opts);
|
||||
|
||||
return pathFromRustInputs;
|
||||
};
|
||||
|
||||
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
|
||||
const serializedPaths: SerializedPath[] = [];
|
||||
const sampleSourcePathIds: string[] = [];
|
||||
@@ -96,8 +227,9 @@ function findRoutesAndCreateOptimalPath(
|
||||
const sourcePathId = hexUtils.random();
|
||||
const singleSourceSamplesWithOutput = [...singleSourceSamples];
|
||||
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
|
||||
if (singleSourceSamples[i].output.isZero()) {
|
||||
// Remove trailing 0 output samples
|
||||
const currentOutput = singleSourceSamples[i].output;
|
||||
if (currentOutput.isZero() || !currentOutput.isFinite()) {
|
||||
// Remove trailing 0/invalid output samples
|
||||
singleSourceSamplesWithOutput.pop();
|
||||
} else {
|
||||
break;
|
||||
@@ -127,6 +259,7 @@ function findRoutesAndCreateOptimalPath(
|
||||
inputs: [],
|
||||
outputs: [],
|
||||
outputFees: [],
|
||||
isVip: vipSourcesSet.has(singleSourceSamplesWithOutput[0]?.source),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -155,12 +288,24 @@ function findRoutesAndCreateOptimalPath(
|
||||
const inputs = [];
|
||||
const outputs = [];
|
||||
const outputFees = [];
|
||||
// NOTE: We start at 0 here because the native order might be much larger than the amount
|
||||
// By starting at 0 we make sure we can always use a portion of the native order to fill/partial fill
|
||||
for (let i = 0; i <= 12; i++) {
|
||||
const fraction = i / 12;
|
||||
const currentInput = BigNumber.min(normalizedOrderInput.times(fraction), normalizedOrderInput);
|
||||
const currentOutput = BigNumber.min(normalizedOrderOutput.times(fraction), normalizedOrderOutput);
|
||||
|
||||
// NOTE: Limit orders can be both larger or smaller than the input amount
|
||||
// If the order is larger than the input we can scale the order to the size of
|
||||
// the quote input (order pricing is constant) and then create 13 "samples" up to
|
||||
// and including the full quote input amount.
|
||||
// If the order is smaller we don't need to scale anything, we will just end up
|
||||
// with trailing duplicate samples for the order input as we cannot go higher
|
||||
const scaleToInput = BigNumber.min(input.dividedBy(normalizedOrderInput), 1);
|
||||
for (let i = 1; i <= 13; i++) {
|
||||
const fraction = i / 13;
|
||||
const currentInput = BigNumber.min(
|
||||
normalizedOrderInput.times(scaleToInput).times(fraction),
|
||||
normalizedOrderInput,
|
||||
);
|
||||
const currentOutput = BigNumber.min(
|
||||
normalizedOrderOutput.times(scaleToInput).times(fraction),
|
||||
normalizedOrderOutput,
|
||||
);
|
||||
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
|
||||
inputs.push(currentInput.integerValue().toNumber());
|
||||
outputs.push(currentOutput.integerValue().toNumber());
|
||||
@@ -173,6 +318,7 @@ function findRoutesAndCreateOptimalPath(
|
||||
inputs,
|
||||
outputs,
|
||||
outputFees,
|
||||
isVip: true,
|
||||
};
|
||||
|
||||
samplesAndNativeOrdersWithResults.push([nativeOrder]);
|
||||
@@ -191,129 +337,42 @@ function findRoutesAndCreateOptimalPath(
|
||||
};
|
||||
|
||||
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
|
||||
const strategySourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
|
||||
route(rustArgs, allSourcesRustRoute, strategySourcesOutputAmounts, neonRouterNumSamples);
|
||||
const allSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
|
||||
const vipSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
|
||||
const vipSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
|
||||
|
||||
route(
|
||||
rustArgs,
|
||||
allSourcesRustRoute,
|
||||
allSourcesOutputAmounts,
|
||||
vipSourcesRustRoute,
|
||||
vipSourcesOutputAmounts,
|
||||
neonRouterNumSamples,
|
||||
);
|
||||
assert.assert(
|
||||
rustArgs.pathsIn.length === allSourcesRustRoute.length,
|
||||
'different number of sources in the Router output than the input',
|
||||
);
|
||||
assert.assert(
|
||||
rustArgs.pathsIn.length === strategySourcesOutputAmounts.length,
|
||||
rustArgs.pathsIn.length === allSourcesOutputAmounts.length,
|
||||
'different number of sources in the Router output amounts results than the input',
|
||||
);
|
||||
assert.assert(
|
||||
rustArgs.pathsIn.length === vipSourcesRustRoute.length,
|
||||
'different number of sources in the Router output than the input',
|
||||
);
|
||||
assert.assert(
|
||||
rustArgs.pathsIn.length === vipSourcesOutputAmounts.length,
|
||||
'different number of sources in the Router output amounts results than the input',
|
||||
);
|
||||
|
||||
const routesAndSamplesAndOutputs = _.zip(
|
||||
allSourcesRustRoute,
|
||||
samplesAndNativeOrdersWithResults,
|
||||
strategySourcesOutputAmounts,
|
||||
sampleSourcePathIds,
|
||||
);
|
||||
const adjustedFills: Fill[] = [];
|
||||
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
|
||||
const allSourcesPath = createPathFromStrategy(allSourcesRustRoute, allSourcesOutputAmounts);
|
||||
const vipSourcesPath = createPathFromStrategy(vipSourcesRustRoute, vipSourcesOutputAmounts);
|
||||
|
||||
const scale = input.dividedBy(totalRoutedAmount);
|
||||
for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) {
|
||||
if (!Number.isFinite(outputAmount)) {
|
||||
DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`);
|
||||
return undefined;
|
||||
}
|
||||
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
|
||||
continue;
|
||||
}
|
||||
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
|
||||
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
|
||||
const rustInputAdjusted = BigNumber.min(
|
||||
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
|
||||
input,
|
||||
);
|
||||
|
||||
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
|
||||
if (!isDexSample(current)) {
|
||||
const nativeFill = nativeOrdersToFills(
|
||||
side,
|
||||
[current],
|
||||
rustInputAdjusted,
|
||||
opts.outputAmountPerEth,
|
||||
opts.inputAmountPerEth,
|
||||
fees,
|
||||
false,
|
||||
)[0] as Fill | undefined;
|
||||
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
|
||||
// and nativeFill will be `undefined`
|
||||
if (nativeFill) {
|
||||
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
||||
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE: For DexSamples only
|
||||
let fill = createFill(current);
|
||||
if (!fill) {
|
||||
continue;
|
||||
}
|
||||
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
||||
// Descend to approach a closer fill for fillData which may not be consistent
|
||||
// throughout the path (UniswapV3) and for a closer guesstimate at
|
||||
// gas used
|
||||
|
||||
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
||||
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
||||
if (k === 0) {
|
||||
fill = createFill(routeSamples[0]) ?? fill;
|
||||
}
|
||||
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
|
||||
const left = routeSamples[k];
|
||||
const right = routeSamples[k + 1];
|
||||
if (left && right) {
|
||||
fill =
|
||||
createFill({
|
||||
...right, // default to the greater (for gas used)
|
||||
input: rustInputAdjusted,
|
||||
output: new BigNumber(outputAmount),
|
||||
}) ?? fill;
|
||||
} else {
|
||||
assert.assert(Boolean(left || right), 'No valid sample to use');
|
||||
fill = createFill(left || right) ?? fill;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
|
||||
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output));
|
||||
// Scale output by scale factor but never go above the largest sample (unknown liquidity) or below 1 base unit (unfillable)
|
||||
const scaleOutput = (output: BigNumber) => {
|
||||
// Don't try to scale 0 output as it will be clamped to 1
|
||||
if (output.eq(ZERO_AMOUNT)) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const scaled = output
|
||||
.times(scale)
|
||||
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
|
||||
|
||||
return BigNumber.max(BigNumber.min(scaled, maxSampledOutput), 1);
|
||||
};
|
||||
|
||||
adjustedFills.push({
|
||||
...fill,
|
||||
input: rustInputAdjusted,
|
||||
output: scaleOutput(fill.output),
|
||||
adjustedOutput: scaleOutput(fill.adjustedOutput),
|
||||
index: 0,
|
||||
parent: undefined,
|
||||
sourcePathId: sourcePathId ?? hexUtils.random(),
|
||||
});
|
||||
}
|
||||
|
||||
if (adjustedFills.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const pathFromRustInputs = Path.create(side, adjustedFills, input, opts);
|
||||
|
||||
return pathFromRustInputs;
|
||||
return {
|
||||
allSourcesPath,
|
||||
vipSourcesPath,
|
||||
};
|
||||
}
|
||||
|
||||
export function findOptimalRustPathFromSamples(
|
||||
@@ -327,9 +386,18 @@ export function findOptimalRustPathFromSamples(
|
||||
neonRouterNumSamples: number,
|
||||
samplerMetrics?: SamplerMetrics,
|
||||
): Path | undefined {
|
||||
const beforeAllTimeMs = performance.now();
|
||||
let beforeTimeMs = performance.now();
|
||||
const allSourcesPath = findRoutesAndCreateOptimalPath(
|
||||
const beforeTimeMs = performance.now();
|
||||
const sendMetrics = () => {
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
samplerMetrics &&
|
||||
samplerMetrics.logRouterDetails({
|
||||
router: 'neon-router',
|
||||
type: 'total',
|
||||
timingMs: performance.now() - beforeTimeMs,
|
||||
});
|
||||
};
|
||||
const vipSourcesSet = new Set(VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId]);
|
||||
const paths = findRoutesAndCreateOptimalPath(
|
||||
side,
|
||||
samples,
|
||||
nativeOrders,
|
||||
@@ -337,58 +405,22 @@ export function findOptimalRustPathFromSamples(
|
||||
opts,
|
||||
fees,
|
||||
neonRouterNumSamples,
|
||||
vipSourcesSet,
|
||||
);
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
samplerMetrics &&
|
||||
samplerMetrics.logRouterDetails({
|
||||
router: 'neon-router',
|
||||
type: 'all',
|
||||
timingMs: performance.now() - beforeTimeMs,
|
||||
});
|
||||
if (!allSourcesPath) {
|
||||
|
||||
if (!paths) {
|
||||
sendMetrics();
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const vipSources = VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId];
|
||||
const { allSourcesPath, vipSourcesPath } = paths;
|
||||
|
||||
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
|
||||
// we need to try to route them in isolation and compare with the results all sources
|
||||
if (vipSources.length > 0) {
|
||||
beforeTimeMs = performance.now();
|
||||
const vipSourcesSet = new Set(vipSources);
|
||||
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
|
||||
|
||||
if (vipSourcesSamples.length > 0) {
|
||||
const vipSourcesPath = findRoutesAndCreateOptimalPath(
|
||||
side,
|
||||
vipSourcesSamples,
|
||||
[],
|
||||
input,
|
||||
opts,
|
||||
fees,
|
||||
neonRouterNumSamples,
|
||||
);
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
samplerMetrics &&
|
||||
samplerMetrics.logRouterDetails({
|
||||
router: 'neon-router',
|
||||
type: 'vip',
|
||||
timingMs: performance.now() - beforeTimeMs,
|
||||
});
|
||||
|
||||
if (vipSourcesPath?.isBetterThan(allSourcesPath)) {
|
||||
return vipSourcesPath;
|
||||
}
|
||||
}
|
||||
if (!allSourcesPath || vipSourcesPath?.isBetterThan(allSourcesPath)) {
|
||||
sendMetrics();
|
||||
return vipSourcesPath;
|
||||
}
|
||||
// tslint:disable-next-line: no-unused-expression
|
||||
samplerMetrics &&
|
||||
samplerMetrics.logRouterDetails({
|
||||
router: 'neon-router',
|
||||
type: 'total',
|
||||
timingMs: performance.now() - beforeAllTimeMs,
|
||||
});
|
||||
|
||||
sendMetrics();
|
||||
return allSourcesPath;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ export class BalancerV2PoolsCache extends PoolsCache {
|
||||
|
||||
constructor(
|
||||
chainId: ChainId,
|
||||
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId],
|
||||
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId]!,
|
||||
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
||||
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
|
||||
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
|
||||
|
||||
@@ -0,0 +1,190 @@
|
||||
import { ChainId } from '@0x/contract-addresses';
|
||||
import { BigNumber } from '@0x/utils';
|
||||
import {
|
||||
BalancerSDK,
|
||||
BalancerSdkConfig,
|
||||
formatSequence,
|
||||
getTokenAddressesForSwap,
|
||||
NewPath,
|
||||
parseToPoolsDict,
|
||||
PoolDictionary,
|
||||
RouteProposer,
|
||||
SwapTypes,
|
||||
} from '@balancer-labs/sdk';
|
||||
|
||||
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
|
||||
import { LogFunction } from '../../../types';
|
||||
import { BALANCER_V2_SUBGRAPH_URL_BY_CHAIN, ONE_SECOND_MS } from '../constants';
|
||||
import { BalancerSwapInfo, BalancerSwaps } from '../types';
|
||||
|
||||
import { CacheValue, EMPTY_BALANCER_SWAPS, SwapInfoCache } from './pair_swaps_cache';
|
||||
import { SubgraphPoolDataService } from './sgPoolDataService';
|
||||
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const ONE_DAY_MS = 24 * 60 * 60 * ONE_SECOND_MS;
|
||||
|
||||
export interface BalancerPoolResponse {
|
||||
poolType: string;
|
||||
id: string;
|
||||
tokens: Array<{ address: string }>;
|
||||
tokensList: string[];
|
||||
}
|
||||
|
||||
export class BalancerV2SwapInfoCache extends SwapInfoCache {
|
||||
private static readonly _MAX_POOLS_PER_PATH = 4;
|
||||
private static readonly _MAX_CANDIDATE_PATHS_PER_PAIR = 2;
|
||||
private readonly _routeProposer: RouteProposer;
|
||||
private readonly _poolDataService: SubgraphPoolDataService;
|
||||
|
||||
constructor(
|
||||
chainId: ChainId,
|
||||
subgraphUrl: string | null = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId],
|
||||
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
|
||||
cache: { [key: string]: CacheValue } = {},
|
||||
) {
|
||||
super(cache);
|
||||
const config: BalancerSdkConfig = {
|
||||
network: chainId as number, // wtf TS
|
||||
rpcUrl: '', // Not actually used by SDK for this.
|
||||
};
|
||||
const balancerSdk = new BalancerSDK(config);
|
||||
// The RouteProposer finds paths between a token pair using direct/multihop/linearPool routes
|
||||
this._routeProposer = balancerSdk.sor.routeProposer;
|
||||
// Uses Subgraph to retrieve up to date pool data required for routeProposer
|
||||
this._poolDataService = new SubgraphPoolDataService({
|
||||
chainId,
|
||||
subgraphUrl,
|
||||
});
|
||||
void this._loadTopPoolsAsync();
|
||||
// Reload the top pools every 12 hours
|
||||
setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2);
|
||||
}
|
||||
|
||||
protected async _loadTopPoolsAsync(): Promise<void> {
|
||||
const fromToSwapInfo: {
|
||||
[from: string]: { [to: string]: BalancerSwaps };
|
||||
} = {};
|
||||
|
||||
// Retrieve pool data from Subgraph
|
||||
const pools = await this._poolDataService.getPools();
|
||||
// timestamp is used for Element pools
|
||||
const timestamp = Math.floor(Date.now() / ONE_SECOND_MS);
|
||||
const poolsDict = parseToPoolsDict(pools, timestamp);
|
||||
|
||||
for (const pool of pools) {
|
||||
const { tokensList } = pool;
|
||||
// tslint:disable-next-line: await-promise
|
||||
await null; // This loop can be CPU heavy so yield to event loop.
|
||||
for (const from of tokensList) {
|
||||
for (const to of tokensList.filter(t => t.toLowerCase() !== from.toLowerCase())) {
|
||||
fromToSwapInfo[from] = fromToSwapInfo[from] || {};
|
||||
// If a record for pair already exists skip as all paths alreay found
|
||||
if (fromToSwapInfo[from][to]) {
|
||||
continue;
|
||||
} else {
|
||||
try {
|
||||
const expiresAt = Date.now() + this._cacheTimeMs;
|
||||
// Retrieve swap steps and assets for a token pair
|
||||
// This only needs to be called once per pair as all paths will be created from single call
|
||||
const pairSwapInfo = this._getPoolPairSwapInfo(poolsDict, from, to);
|
||||
fromToSwapInfo[from][to] = pairSwapInfo;
|
||||
this._cacheSwapInfoForPair(from, to, fromToSwapInfo[from][to], expiresAt);
|
||||
} catch (err) {
|
||||
this._warningLogger(err, `Failed to load Balancer V2 top pools`);
|
||||
// soldier on
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will retrieve fresh pair and path data from Subgraph and return and array of swap info for pair..
|
||||
* @param takerToken Address of takerToken.
|
||||
* @param makerToken Address of makerToken.
|
||||
* @returns Swap data for pair consisting of assets and swap steps for ExactIn and ExactOut swap types.
|
||||
*/
|
||||
protected async _fetchSwapInfoForPairAsync(takerToken: string, makerToken: string): Promise<BalancerSwaps> {
|
||||
try {
|
||||
// retrieve up to date pools from SG
|
||||
const pools = await this._poolDataService.getPools();
|
||||
|
||||
// timestamp is used for Element pools
|
||||
const timestamp = Math.floor(Date.now() / ONE_SECOND_MS);
|
||||
const poolDictionary = parseToPoolsDict(pools, timestamp);
|
||||
return this._getPoolPairSwapInfo(poolDictionary, takerToken, makerToken);
|
||||
} catch (e) {
|
||||
return EMPTY_BALANCER_SWAPS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses pool data from provided dictionary to find top swap paths for token pair.
|
||||
* @param pools Dictionary of pool data.
|
||||
* @param takerToken Address of taker token.
|
||||
* @param makerToken Address of maker token.
|
||||
* @returns Swap data for pair consisting of assets and swap steps for ExactIn and ExactOut swap types.
|
||||
*/
|
||||
private _getPoolPairSwapInfo(pools: PoolDictionary, takerToken: string, makerToken: string): BalancerSwaps {
|
||||
/*
|
||||
Uses Balancer SDK to construct available paths for pair.
|
||||
Paths can be direct, i.e. both tokens are in same pool or multihop.
|
||||
Will also create paths for the new Balancer Linear pools.
|
||||
These are returned in order of available liquidity which is useful for filtering.
|
||||
*/
|
||||
const paths = this._routeProposer.getCandidatePathsFromDict(
|
||||
takerToken,
|
||||
makerToken,
|
||||
SwapTypes.SwapExactIn,
|
||||
pools,
|
||||
BalancerV2SwapInfoCache._MAX_POOLS_PER_PATH,
|
||||
);
|
||||
|
||||
if (paths.length === 0) {
|
||||
return EMPTY_BALANCER_SWAPS;
|
||||
}
|
||||
|
||||
// Convert paths data to swap information suitable for queryBatchSwap. Only use top 2 liquid paths
|
||||
return formatSwaps(paths.slice(0, BalancerV2SwapInfoCache._MAX_CANDIDATE_PATHS_PER_PAIR));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an array of Balancer paths, returns swap information that can be passed to queryBatchSwap.
|
||||
* @param paths Array of Balancer paths.
|
||||
* @returns Formatted swap data consisting of assets and swap steps for ExactIn and ExactOut swap types.
|
||||
*/
|
||||
function formatSwaps(paths: NewPath[]): BalancerSwaps {
|
||||
const formattedSwapsExactIn: BalancerSwapInfo[] = [];
|
||||
const formattedSwapsExactOut: BalancerSwapInfo[] = [];
|
||||
let assets: string[];
|
||||
paths.forEach(path => {
|
||||
// Add a swap amount for each swap so we can use formatSequence. (This will be overwritten with actual amount during query)
|
||||
path.swaps.forEach(s => (s.swapAmount = '0'));
|
||||
const tokenAddresses = getTokenAddressesForSwap(path.swaps);
|
||||
// Formats for both ExactIn and ExactOut swap types
|
||||
const swapsExactIn = formatSequence(SwapTypes.SwapExactIn, path.swaps, tokenAddresses);
|
||||
const swapsExactOut = formatSequence(SwapTypes.SwapExactOut, path.swaps, tokenAddresses);
|
||||
assets = tokenAddresses;
|
||||
formattedSwapsExactIn.push({
|
||||
assets,
|
||||
swapSteps: swapsExactIn.map(s => ({
|
||||
...s,
|
||||
amount: new BigNumber(s.amount),
|
||||
})),
|
||||
});
|
||||
formattedSwapsExactOut.push({
|
||||
assets,
|
||||
swapSteps: swapsExactOut.map(s => ({
|
||||
...s,
|
||||
amount: new BigNumber(s.amount),
|
||||
})),
|
||||
});
|
||||
});
|
||||
const formattedSwaps: BalancerSwaps = {
|
||||
swapInfoExactIn: formattedSwapsExactIn,
|
||||
swapInfoExactOut: formattedSwapsExactOut,
|
||||
};
|
||||
return formattedSwaps;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import { BalancerSwaps } from '../types';
|
||||
|
||||
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
|
||||
|
||||
export interface CacheValue {
|
||||
expiresAt: number;
|
||||
balancerSwaps: BalancerSwaps;
|
||||
}
|
||||
|
||||
// tslint:disable:custom-no-magic-numbers
|
||||
// Cache results for 30mins
|
||||
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
|
||||
const DEFAULT_TIMEOUT_MS = ONE_SECOND_MS;
|
||||
export const EMPTY_BALANCER_SWAPS = { swapInfoExactIn: [], swapInfoExactOut: [] };
|
||||
// tslint:enable:custom-no-magic-numbers
|
||||
|
||||
/**
|
||||
* Caches SwapInfo for a pair of tokens.
|
||||
* SwapInfo includes swap steps and asset information for those swap steps.
|
||||
*/
|
||||
export abstract class SwapInfoCache {
|
||||
protected static _isExpired(value: CacheValue): boolean {
|
||||
return Date.now() >= value.expiresAt;
|
||||
}
|
||||
constructor(
|
||||
protected readonly _cache: { [key: string]: CacheValue },
|
||||
protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS,
|
||||
) {}
|
||||
|
||||
public async getFreshPoolsForPairAsync(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
timeoutMs: number = DEFAULT_TIMEOUT_MS,
|
||||
): Promise<BalancerSwaps> {
|
||||
const timeout = new Promise<BalancerSwaps>(resolve => setTimeout(resolve, timeoutMs, []));
|
||||
return Promise.race([this._getAndSaveFreshSwapInfoForPairAsync(takerToken, makerToken), timeout]);
|
||||
}
|
||||
|
||||
public getCachedSwapInfoForPair(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
ignoreExpired: boolean = true,
|
||||
): BalancerSwaps | undefined {
|
||||
const key = JSON.stringify([takerToken, makerToken]);
|
||||
const value = this._cache[key];
|
||||
if (ignoreExpired) {
|
||||
return value === undefined ? EMPTY_BALANCER_SWAPS : value.balancerSwaps;
|
||||
}
|
||||
if (!value) {
|
||||
return undefined;
|
||||
}
|
||||
if (SwapInfoCache._isExpired(value)) {
|
||||
return undefined;
|
||||
}
|
||||
return value.balancerSwaps;
|
||||
}
|
||||
|
||||
public isFresh(takerToken: string, makerToken: string): boolean {
|
||||
const cached = this.getCachedSwapInfoForPair(takerToken, makerToken, false);
|
||||
return cached !== undefined;
|
||||
}
|
||||
|
||||
protected async _getAndSaveFreshSwapInfoForPairAsync(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
): Promise<BalancerSwaps> {
|
||||
const key = JSON.stringify([takerToken, makerToken]);
|
||||
const value = this._cache[key];
|
||||
if (value === undefined || value.expiresAt >= Date.now()) {
|
||||
const swapInfo = await this._fetchSwapInfoForPairAsync(takerToken, makerToken);
|
||||
const expiresAt = Date.now() + this._cacheTimeMs;
|
||||
this._cacheSwapInfoForPair(takerToken, makerToken, swapInfo, expiresAt);
|
||||
}
|
||||
return this._cache[key].balancerSwaps;
|
||||
}
|
||||
|
||||
protected _cacheSwapInfoForPair(
|
||||
takerToken: string,
|
||||
makerToken: string,
|
||||
swapInfo: BalancerSwaps,
|
||||
expiresAt: number,
|
||||
): void {
|
||||
const key = JSON.stringify([takerToken, makerToken]);
|
||||
this._cache[key] = {
|
||||
expiresAt,
|
||||
balancerSwaps: swapInfo,
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract _fetchSwapInfoForPairAsync(takerToken: string, makerToken: string): Promise<BalancerSwaps>;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import { ChainId } from '@0x/contract-addresses';
|
||||
import { logUtils } from '@0x/utils';
|
||||
import { PoolDataService, SubgraphPoolBase } from '@balancer-labs/sdk';
|
||||
import { gql, request } from 'graphql-request';
|
||||
|
||||
const queryWithLinear = gql`
|
||||
query fetchTopPoolsWithLinear($maxPoolsFetched: Int!) {
|
||||
pools: pools(
|
||||
first: $maxPoolsFetched
|
||||
where: { swapEnabled: true }
|
||||
orderBy: totalLiquidity
|
||||
orderDirection: desc
|
||||
) {
|
||||
id
|
||||
address
|
||||
poolType
|
||||
swapFee
|
||||
totalShares
|
||||
tokens {
|
||||
address
|
||||
balance
|
||||
decimals
|
||||
weight
|
||||
priceRate
|
||||
}
|
||||
tokensList
|
||||
totalWeight
|
||||
amp
|
||||
expiryTime
|
||||
unitSeconds
|
||||
principalToken
|
||||
baseToken
|
||||
swapEnabled
|
||||
wrappedIndex
|
||||
mainIndex
|
||||
lowerTarget
|
||||
upperTarget
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const queryWithOutLinear = gql`
|
||||
query fetchTopPoolsWithoutLinear($maxPoolsFetched: Int!) {
|
||||
pools: pools(
|
||||
first: $maxPoolsFetched
|
||||
where: { swapEnabled: true }
|
||||
orderBy: totalLiquidity
|
||||
orderDirection: desc
|
||||
) {
|
||||
id
|
||||
address
|
||||
poolType
|
||||
swapFee
|
||||
totalShares
|
||||
tokens {
|
||||
address
|
||||
balance
|
||||
decimals
|
||||
weight
|
||||
priceRate
|
||||
}
|
||||
tokensList
|
||||
totalWeight
|
||||
amp
|
||||
expiryTime
|
||||
unitSeconds
|
||||
principalToken
|
||||
baseToken
|
||||
swapEnabled
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const QUERY_BY_CHAIN_ID: { [chainId: number]: string } = {
|
||||
[ChainId.Mainnet]: queryWithLinear,
|
||||
[ChainId.Polygon]: queryWithOutLinear,
|
||||
};
|
||||
|
||||
const DEFAULT_MAX_POOLS_FETCHED = 96;
|
||||
|
||||
/**
|
||||
* Simple service to query required info from Subgraph for Balancer Pools.
|
||||
* Because Balancer Subgraphs have slightly different schema depending on network the queries are adjusted as needed.
|
||||
*/
|
||||
export class SubgraphPoolDataService implements PoolDataService {
|
||||
private readonly _gqlQuery: string | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly _config: {
|
||||
chainId: number;
|
||||
subgraphUrl: string | null;
|
||||
maxPoolsFetched?: number;
|
||||
},
|
||||
) {
|
||||
this._config.maxPoolsFetched = this._config.maxPoolsFetched || DEFAULT_MAX_POOLS_FETCHED;
|
||||
this._gqlQuery = QUERY_BY_CHAIN_ID[this._config.chainId];
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: async-suffix
|
||||
public async getPools(): Promise<SubgraphPoolBase[]> {
|
||||
if (!this._gqlQuery || !this._config.subgraphUrl) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const { pools } = await request<{ pools: SubgraphPoolBase[] }>(this._config.subgraphUrl, this._gqlQuery, {
|
||||
maxPoolsFetched: this._config.maxPoolsFetched,
|
||||
});
|
||||
return pools;
|
||||
} catch (err) {
|
||||
logUtils.warn(`Failed to fetch BalancerV2 subgraph pools: ${err.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,8 @@ import { SamplerOverrides } from '../../types';
|
||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
|
||||
|
||||
import { BancorService } from './bancor_service';
|
||||
import { PoolsCache } from './pools_cache';
|
||||
import { SamplerOperations } from './sampler_operations';
|
||||
import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
|
||||
import { PoolsCacheMap, SamplerOperations } from './sampler_operations';
|
||||
import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
|
||||
|
||||
/**
|
||||
* Generate sample amounts up to `maxFillAmount`.
|
||||
@@ -37,7 +36,7 @@ export class DexOrderSampler extends SamplerOperations {
|
||||
public readonly chainId: ChainId,
|
||||
_samplerContract: ERC20BridgeSamplerContract,
|
||||
private readonly _samplerOverrides?: SamplerOverrides,
|
||||
poolsCaches?: { [key in ERC20BridgeSource]: PoolsCache },
|
||||
poolsCaches?: PoolsCacheMap,
|
||||
tokenAdjacencyGraph?: TokenAdjacencyGraph,
|
||||
liquidityProviderRegistry?: LiquidityProviderRegistry,
|
||||
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { BigNumber, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
|
||||
import { GeistSampler } from '../../noop_samplers/GeistSampler';
|
||||
import { SamplerCallResult, SignedNativeOrder } from '../../types';
|
||||
import { ERC20BridgeSamplerContract } from '../../wrappers';
|
||||
|
||||
@@ -32,9 +33,7 @@ import {
|
||||
KYBER_CONFIG_BY_CHAIN_ID,
|
||||
KYBER_DMM_ROUTER_BY_CHAIN_ID,
|
||||
LIDO_INFO_BY_CHAIN,
|
||||
LINKSWAP_ROUTER_BY_CHAIN_ID,
|
||||
LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID,
|
||||
MAINNET_TOKENS,
|
||||
MAKER_PSM_INFO_BY_CHAIN_ID,
|
||||
MAX_UINT256,
|
||||
MOONISWAP_REGISTRIES_BY_CHAIN_ID,
|
||||
@@ -46,9 +45,11 @@ import {
|
||||
UNISWAPV3_CONFIG_BY_CHAIN_ID,
|
||||
ZERO_AMOUNT,
|
||||
} from './constants';
|
||||
import { getGeistInfoForPair } from './geist_utils';
|
||||
import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
|
||||
import { getIntermediateTokens } from './multihop_utils';
|
||||
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache';
|
||||
import { BalancerV2SwapInfoCache } from './pools_cache/balancer_v2_utils_new';
|
||||
import { SamplerContractOperation } from './sampler_contract_operation';
|
||||
import { SamplerNoOperation } from './sampler_no_operation';
|
||||
import { SourceFilters } from './source_filters';
|
||||
@@ -56,6 +57,8 @@ import {
|
||||
AaveV2FillData,
|
||||
AaveV2Info,
|
||||
BalancerFillData,
|
||||
BalancerSwapInfo,
|
||||
BalancerV2BatchSwapFillData,
|
||||
BalancerV2FillData,
|
||||
BalancerV2PoolInfo,
|
||||
BancorFillData,
|
||||
@@ -66,6 +69,8 @@ import {
|
||||
DexSample,
|
||||
DODOFillData,
|
||||
ERC20BridgeSource,
|
||||
GeistFillData,
|
||||
GeistInfo,
|
||||
GenericRouterFillData,
|
||||
HopInfo,
|
||||
KyberDmmFillData,
|
||||
@@ -99,6 +104,10 @@ export const TWO_HOP_SOURCE_FILTERS = SourceFilters.all().exclude([
|
||||
*/
|
||||
export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSource.MultiHop, ERC20BridgeSource.Native]);
|
||||
|
||||
export type PoolsCacheMap = { [key in Exclude<SourcesWithPoolsCache, ERC20BridgeSource.BalancerV2>]: PoolsCache } & {
|
||||
[ERC20BridgeSource.BalancerV2]: BalancerV2SwapInfoCache | undefined;
|
||||
};
|
||||
|
||||
// tslint:disable:no-inferred-empty-object-type no-unbound-method
|
||||
|
||||
/**
|
||||
@@ -107,7 +116,7 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
|
||||
*/
|
||||
export class SamplerOperations {
|
||||
public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
|
||||
public readonly poolsCaches: { [key in SourcesWithPoolsCache]: PoolsCache };
|
||||
public readonly poolsCaches: PoolsCacheMap;
|
||||
public readonly aaveReservesCache: AaveV2ReservesCache | undefined;
|
||||
public readonly compoundCTokenCache: CompoundCTokenCache | undefined;
|
||||
protected _bancorService?: BancorService;
|
||||
@@ -122,7 +131,7 @@ export class SamplerOperations {
|
||||
constructor(
|
||||
public readonly chainId: ChainId,
|
||||
protected readonly _samplerContract: ERC20BridgeSamplerContract,
|
||||
poolsCaches?: { [key in SourcesWithPoolsCache]: PoolsCache },
|
||||
poolsCaches?: PoolsCacheMap,
|
||||
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
|
||||
liquidityProviderRegistry: LiquidityProviderRegistry = {},
|
||||
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
|
||||
@@ -134,13 +143,16 @@ export class SamplerOperations {
|
||||
this.poolsCaches = poolsCaches
|
||||
? poolsCaches
|
||||
: {
|
||||
[ERC20BridgeSource.BalancerV2]: new BalancerV2PoolsCache(chainId),
|
||||
[ERC20BridgeSource.Beethovenx]: new BalancerV2PoolsCache(
|
||||
chainId,
|
||||
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN[chainId],
|
||||
),
|
||||
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
|
||||
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
|
||||
[ERC20BridgeSource.BalancerV2]:
|
||||
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[chainId] === NULL_ADDRESS
|
||||
? undefined
|
||||
: new BalancerV2SwapInfoCache(chainId),
|
||||
};
|
||||
|
||||
const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
|
||||
@@ -560,6 +572,49 @@ export class SamplerOperations {
|
||||
});
|
||||
}
|
||||
|
||||
public getBalancerV2MultihopSellQuotes(
|
||||
vault: string,
|
||||
quoteSwaps: BalancerSwapInfo, // Should always be sell swap steps.
|
||||
fillSwaps: BalancerSwapInfo, // Should always be sell swap steps.
|
||||
takerFillAmounts: BigNumber[],
|
||||
source: ERC20BridgeSource,
|
||||
): SourceQuoteOperation<BalancerV2BatchSwapFillData> {
|
||||
const quoteSwapSteps = quoteSwaps.swapSteps.map(s => ({
|
||||
...s,
|
||||
assetInIndex: new BigNumber(s.assetInIndex),
|
||||
assetOutIndex: new BigNumber(s.assetOutIndex),
|
||||
}));
|
||||
return new SamplerContractOperation({
|
||||
source,
|
||||
fillData: { vault, swapSteps: fillSwaps.swapSteps, assets: fillSwaps.assets },
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleMultihopSellsFromBalancerV2,
|
||||
params: [vault, quoteSwapSteps, quoteSwaps.assets, takerFillAmounts],
|
||||
});
|
||||
}
|
||||
|
||||
public getBalancerV2MultihopBuyQuotes(
|
||||
vault: string,
|
||||
quoteSwaps: BalancerSwapInfo, // Should always be buy swap steps.
|
||||
fillSwaps: BalancerSwapInfo, // Should always be a sell quote.
|
||||
makerFillAmounts: BigNumber[],
|
||||
source: ERC20BridgeSource,
|
||||
): SourceQuoteOperation<BalancerV2BatchSwapFillData> {
|
||||
const quoteSwapSteps = quoteSwaps.swapSteps.map(s => ({
|
||||
...s,
|
||||
assetInIndex: new BigNumber(s.assetInIndex),
|
||||
assetOutIndex: new BigNumber(s.assetOutIndex),
|
||||
}));
|
||||
return new SamplerContractOperation({
|
||||
source,
|
||||
// NOTE: fillData is set up for sells but quote function is set up for buys.
|
||||
fillData: { vault, swapSteps: fillSwaps.swapSteps, assets: fillSwaps.assets },
|
||||
contract: this._samplerContract,
|
||||
function: this._samplerContract.sampleMultihopBuysFromBalancerV2,
|
||||
params: [vault, quoteSwapSteps, quoteSwaps.assets, makerFillAmounts],
|
||||
});
|
||||
}
|
||||
|
||||
public getBalancerV2SellQuotes(
|
||||
poolInfo: BalancerV2PoolInfo,
|
||||
makerToken: string,
|
||||
@@ -767,16 +822,17 @@ export class SamplerOperations {
|
||||
function: this._samplerContract.sampleSellsFromUniswapV3,
|
||||
params: [quoter, tokenAddressPath, takerFillAmounts],
|
||||
callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => {
|
||||
const [paths, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>(
|
||||
'sampleSellsFromUniswapV3',
|
||||
callResults,
|
||||
);
|
||||
const [paths, gasUsed, samples] = this._samplerContract.getABIDecodedReturnData<
|
||||
[string[], BigNumber[], BigNumber[]]
|
||||
>('sampleSellsFromUniswapV3', callResults);
|
||||
fillData.router = router;
|
||||
fillData.tokenAddressPath = tokenAddressPath;
|
||||
fillData.pathAmounts = paths.map((uniswapPath, i) => ({
|
||||
uniswapPath,
|
||||
inputAmount: takerFillAmounts[i],
|
||||
gasUsed: gasUsed[i].toNumber(),
|
||||
}));
|
||||
|
||||
return samples;
|
||||
},
|
||||
});
|
||||
@@ -795,15 +851,15 @@ export class SamplerOperations {
|
||||
function: this._samplerContract.sampleBuysFromUniswapV3,
|
||||
params: [quoter, tokenAddressPath, makerFillAmounts],
|
||||
callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => {
|
||||
const [paths, samples] = this._samplerContract.getABIDecodedReturnData<[string[], BigNumber[]]>(
|
||||
'sampleBuysFromUniswapV3',
|
||||
callResults,
|
||||
);
|
||||
const [paths, gasUsed, samples] = this._samplerContract.getABIDecodedReturnData<
|
||||
[string[], BigNumber[], BigNumber[]]
|
||||
>('sampleBuysFromUniswapV3', callResults);
|
||||
fillData.router = router;
|
||||
fillData.tokenAddressPath = tokenAddressPath;
|
||||
fillData.pathAmounts = paths.map((uniswapPath, i) => ({
|
||||
uniswapPath,
|
||||
inputAmount: makerFillAmounts[i],
|
||||
gasUsed: gasUsed[i].toNumber(),
|
||||
}));
|
||||
return samples;
|
||||
},
|
||||
@@ -1151,6 +1207,34 @@ export class SamplerOperations {
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:prefer-function-over-method
|
||||
public getGeistSellQuotes(
|
||||
geistInfo: GeistInfo,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
takerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<GeistFillData> {
|
||||
return new SamplerNoOperation({
|
||||
source: ERC20BridgeSource.Geist,
|
||||
fillData: { ...geistInfo, takerToken },
|
||||
callback: () => GeistSampler.sampleSellsFromGeist(geistInfo, takerToken, makerToken, takerFillAmounts),
|
||||
});
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:prefer-function-over-method
|
||||
public getGeistBuyQuotes(
|
||||
geistInfo: GeistInfo,
|
||||
makerToken: string,
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation<GeistFillData> {
|
||||
return new SamplerNoOperation({
|
||||
source: ERC20BridgeSource.Geist,
|
||||
fillData: { ...geistInfo, takerToken },
|
||||
callback: () => GeistSampler.sampleBuysFromGeist(geistInfo, takerToken, makerToken, makerFillAmounts),
|
||||
});
|
||||
}
|
||||
|
||||
public getCompoundSellQuotes(
|
||||
cToken: string,
|
||||
makerToken: string,
|
||||
@@ -1266,7 +1350,7 @@ export class SamplerOperations {
|
||||
takerFillAmounts: BigNumber[],
|
||||
tokenAdjacencyGraph: TokenAdjacencyGraph = this.tokenAdjacencyGraph,
|
||||
): SourceQuoteOperation[] {
|
||||
// Find the adjacent tokens in the provided tooken adjacency graph,
|
||||
// Find the adjacent tokens in the provided token adjacency graph,
|
||||
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC
|
||||
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, tokenAdjacencyGraph);
|
||||
// Drop out MultiHop and Native as we do not query those here.
|
||||
@@ -1279,8 +1363,6 @@ export class SamplerOperations {
|
||||
return [];
|
||||
}
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.Eth2Dai:
|
||||
return [];
|
||||
case ERC20BridgeSource.Uniswap:
|
||||
return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId])
|
||||
? this.getUniswapSellQuotes(
|
||||
@@ -1313,6 +1395,7 @@ export class SamplerOperations {
|
||||
case ERC20BridgeSource.SpiritSwap:
|
||||
case ERC20BridgeSource.SpookySwap:
|
||||
case ERC20BridgeSource.MorpheusSwap:
|
||||
case ERC20BridgeSource.BiSwap:
|
||||
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
|
||||
if (!isValidAddress(uniLikeRouter)) {
|
||||
return [];
|
||||
@@ -1339,8 +1422,6 @@ export class SamplerOperations {
|
||||
);
|
||||
case ERC20BridgeSource.Curve:
|
||||
case ERC20BridgeSource.CurveV2:
|
||||
case ERC20BridgeSource.Swerve:
|
||||
case ERC20BridgeSource.SnowSwap:
|
||||
case ERC20BridgeSource.Nerve:
|
||||
case ERC20BridgeSource.Synapse:
|
||||
case ERC20BridgeSource.Belt:
|
||||
@@ -1350,6 +1431,7 @@ export class SamplerOperations {
|
||||
case ERC20BridgeSource.FirebirdOneSwap:
|
||||
case ERC20BridgeSource.IronSwap:
|
||||
case ERC20BridgeSource.ACryptos:
|
||||
case ERC20BridgeSource.MobiusMoney:
|
||||
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
|
||||
this.getCurveSellQuotes(
|
||||
pool,
|
||||
@@ -1414,15 +1496,27 @@ export class SamplerOperations {
|
||||
ERC20BridgeSource.Balancer,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.BalancerV2:
|
||||
case ERC20BridgeSource.Beethovenx:
|
||||
case ERC20BridgeSource.BalancerV2: {
|
||||
const cache = this.poolsCaches[source];
|
||||
if (!cache) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const swaps = cache.getCachedSwapInfoForPair(takerToken, makerToken);
|
||||
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
if (!swaps || vault === NULL_ADDRESS) {
|
||||
return [];
|
||||
}
|
||||
// Changed to retrieve queryBatchSwap for swap steps > 1 of length
|
||||
return swaps.swapInfoExactIn.map(swapInfo =>
|
||||
this.getBalancerV2MultihopSellQuotes(vault, swapInfo, swapInfo, takerFillAmounts, source),
|
||||
);
|
||||
}
|
||||
case ERC20BridgeSource.Beethovenx: {
|
||||
const poolIds =
|
||||
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
|
||||
|
||||
const vault =
|
||||
source === ERC20BridgeSource.BalancerV2
|
||||
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
|
||||
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
if (vault === NULL_ADDRESS) {
|
||||
return [];
|
||||
}
|
||||
@@ -1435,6 +1529,7 @@ export class SamplerOperations {
|
||||
source,
|
||||
),
|
||||
);
|
||||
}
|
||||
case ERC20BridgeSource.Cream:
|
||||
return (
|
||||
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
|
||||
@@ -1486,23 +1581,6 @@ export class SamplerOperations {
|
||||
takerToken,
|
||||
takerFillAmounts,
|
||||
);
|
||||
case ERC20BridgeSource.Linkswap:
|
||||
if (!isValidAddress(LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId])) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
[takerToken, makerToken],
|
||||
...getIntermediateTokens(makerToken, takerToken, {
|
||||
default: [MAINNET_TOKENS.LINK, MAINNET_TOKENS.WETH],
|
||||
}).map(t => [takerToken, t, makerToken]),
|
||||
].map(path =>
|
||||
this.getUniswapV2SellQuotes(
|
||||
LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId],
|
||||
path,
|
||||
takerFillAmounts,
|
||||
ERC20BridgeSource.Linkswap,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.MakerPsm:
|
||||
const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId];
|
||||
if (!isValidAddress(psmInfo.psmAddress)) {
|
||||
@@ -1548,6 +1626,13 @@ export class SamplerOperations {
|
||||
};
|
||||
return this.getAaveV2SellQuotes(info, makerToken, takerToken, takerFillAmounts);
|
||||
}
|
||||
case ERC20BridgeSource.Geist: {
|
||||
const info: GeistInfo | undefined = getGeistInfoForPair(takerToken, makerToken);
|
||||
if (!info) {
|
||||
return [];
|
||||
}
|
||||
return this.getGeistSellQuotes(info, makerToken, takerToken, takerFillAmounts);
|
||||
}
|
||||
case ERC20BridgeSource.Compound: {
|
||||
if (!this.compoundCTokenCache) {
|
||||
return [];
|
||||
@@ -1578,15 +1663,13 @@ export class SamplerOperations {
|
||||
takerToken: string,
|
||||
makerFillAmounts: BigNumber[],
|
||||
): SourceQuoteOperation[] {
|
||||
// Find the adjacent tokens in the provided tooken adjacency graph,
|
||||
// Find the adjacent tokens in the provided token adjacency graph,
|
||||
// e.g if this is DAI->USDC we may check for DAI->WETH->USDC
|
||||
const intermediateTokens = getIntermediateTokens(makerToken, takerToken, this.tokenAdjacencyGraph);
|
||||
const _sources = BATCH_SOURCE_FILTERS.getAllowed(sources);
|
||||
return _.flatten(
|
||||
_sources.map((source): SourceQuoteOperation | SourceQuoteOperation[] => {
|
||||
switch (source) {
|
||||
case ERC20BridgeSource.Eth2Dai:
|
||||
return [];
|
||||
case ERC20BridgeSource.Uniswap:
|
||||
return isValidAddress(UNISWAPV1_ROUTER_BY_CHAIN_ID[this.chainId])
|
||||
? this.getUniswapBuyQuotes(
|
||||
@@ -1619,6 +1702,7 @@ export class SamplerOperations {
|
||||
case ERC20BridgeSource.SpiritSwap:
|
||||
case ERC20BridgeSource.SpookySwap:
|
||||
case ERC20BridgeSource.MorpheusSwap:
|
||||
case ERC20BridgeSource.BiSwap:
|
||||
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
|
||||
if (!isValidAddress(uniLikeRouter)) {
|
||||
return [];
|
||||
@@ -1645,8 +1729,6 @@ export class SamplerOperations {
|
||||
);
|
||||
case ERC20BridgeSource.Curve:
|
||||
case ERC20BridgeSource.CurveV2:
|
||||
case ERC20BridgeSource.Swerve:
|
||||
case ERC20BridgeSource.SnowSwap:
|
||||
case ERC20BridgeSource.Nerve:
|
||||
case ERC20BridgeSource.Synapse:
|
||||
case ERC20BridgeSource.Belt:
|
||||
@@ -1656,6 +1738,7 @@ export class SamplerOperations {
|
||||
case ERC20BridgeSource.FirebirdOneSwap:
|
||||
case ERC20BridgeSource.IronSwap:
|
||||
case ERC20BridgeSource.ACryptos:
|
||||
case ERC20BridgeSource.MobiusMoney:
|
||||
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
|
||||
this.getCurveBuyQuotes(
|
||||
pool,
|
||||
@@ -1720,15 +1803,33 @@ export class SamplerOperations {
|
||||
ERC20BridgeSource.Balancer,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.BalancerV2:
|
||||
case ERC20BridgeSource.Beethovenx:
|
||||
case ERC20BridgeSource.BalancerV2: {
|
||||
const cache = this.poolsCaches[source];
|
||||
if (!cache) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const swaps = cache.getCachedSwapInfoForPair(takerToken, makerToken);
|
||||
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
if (!swaps || vault === NULL_ADDRESS) {
|
||||
return [];
|
||||
}
|
||||
// Changed to retrieve queryBatchSwap for swap steps > 1 of length
|
||||
return swaps.swapInfoExactOut.map((quoteSwapInfo, i) =>
|
||||
this.getBalancerV2MultihopBuyQuotes(
|
||||
vault,
|
||||
quoteSwapInfo,
|
||||
swaps.swapInfoExactIn[i],
|
||||
makerFillAmounts,
|
||||
source,
|
||||
),
|
||||
);
|
||||
}
|
||||
case ERC20BridgeSource.Beethovenx: {
|
||||
const poolIds =
|
||||
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
|
||||
|
||||
const vault =
|
||||
source === ERC20BridgeSource.BalancerV2
|
||||
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
|
||||
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
const vault = BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
|
||||
if (vault === NULL_ADDRESS) {
|
||||
return [];
|
||||
}
|
||||
@@ -1741,6 +1842,7 @@ export class SamplerOperations {
|
||||
source,
|
||||
),
|
||||
);
|
||||
}
|
||||
case ERC20BridgeSource.Cream:
|
||||
return (
|
||||
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
|
||||
@@ -1786,24 +1888,6 @@ export class SamplerOperations {
|
||||
// Unimplemented
|
||||
// return this.getBancorBuyQuotes(makerToken, takerToken, makerFillAmounts);
|
||||
return [];
|
||||
case ERC20BridgeSource.Linkswap:
|
||||
if (!isValidAddress(LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId])) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
[takerToken, makerToken],
|
||||
// LINK is the base asset in many of the pools on Linkswap
|
||||
...getIntermediateTokens(makerToken, takerToken, {
|
||||
default: [MAINNET_TOKENS.LINK, MAINNET_TOKENS.WETH],
|
||||
}).map(t => [takerToken, t, makerToken]),
|
||||
].map(path =>
|
||||
this.getUniswapV2BuyQuotes(
|
||||
LINKSWAP_ROUTER_BY_CHAIN_ID[this.chainId],
|
||||
path,
|
||||
makerFillAmounts,
|
||||
ERC20BridgeSource.Linkswap,
|
||||
),
|
||||
);
|
||||
case ERC20BridgeSource.MakerPsm:
|
||||
const psmInfo = MAKER_PSM_INFO_BY_CHAIN_ID[this.chainId];
|
||||
if (!isValidAddress(psmInfo.psmAddress)) {
|
||||
@@ -1849,6 +1933,13 @@ export class SamplerOperations {
|
||||
};
|
||||
return this.getAaveV2BuyQuotes(info, makerToken, takerToken, makerFillAmounts);
|
||||
}
|
||||
case ERC20BridgeSource.Geist: {
|
||||
const info: GeistInfo | undefined = getGeistInfoForPair(takerToken, makerToken);
|
||||
if (!info) {
|
||||
return [];
|
||||
}
|
||||
return this.getGeistBuyQuotes(info, makerToken, takerToken, makerFillAmounts);
|
||||
}
|
||||
case ERC20BridgeSource.Compound: {
|
||||
if (!this.compoundCTokenCache) {
|
||||
return [];
|
||||
|
||||
@@ -8,9 +8,9 @@ import { BigNumber } from '@0x/utils';
|
||||
|
||||
import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types';
|
||||
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor';
|
||||
import { IRfqClient } from '../irfq_client';
|
||||
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
|
||||
|
||||
import { CollapsedPath } from './path';
|
||||
import { SourceFilters } from './source_filters';
|
||||
|
||||
/**
|
||||
@@ -38,7 +38,6 @@ export enum ERC20BridgeSource {
|
||||
Native = 'Native',
|
||||
Uniswap = 'Uniswap',
|
||||
UniswapV2 = 'Uniswap_V2',
|
||||
Eth2Dai = 'Eth2Dai',
|
||||
Kyber = 'Kyber',
|
||||
Curve = 'Curve',
|
||||
LiquidityProvider = 'LiquidityProvider',
|
||||
@@ -52,13 +51,10 @@ export enum ERC20BridgeSource {
|
||||
Mooniswap = 'Mooniswap',
|
||||
MultiHop = 'MultiHop',
|
||||
Shell = 'Shell',
|
||||
Swerve = 'Swerve',
|
||||
SnowSwap = 'SnowSwap',
|
||||
SushiSwap = 'SushiSwap',
|
||||
Dodo = 'DODO',
|
||||
DodoV2 = 'DODO_V2',
|
||||
CryptoCom = 'CryptoCom',
|
||||
Linkswap = 'Linkswap',
|
||||
KyberDmm = 'KyberDMM',
|
||||
Smoothy = 'Smoothy',
|
||||
Component = 'Component',
|
||||
@@ -74,6 +70,7 @@ export enum ERC20BridgeSource {
|
||||
// BSC only
|
||||
PancakeSwap = 'PancakeSwap',
|
||||
PancakeSwapV2 = 'PancakeSwap_V2',
|
||||
BiSwap = 'BiSwap',
|
||||
BakerySwap = 'BakerySwap',
|
||||
Nerve = 'Nerve',
|
||||
Belt = 'Belt',
|
||||
@@ -97,11 +94,13 @@ export enum ERC20BridgeSource {
|
||||
TraderJoe = 'TraderJoe',
|
||||
// Celo only
|
||||
UbeSwap = 'UbeSwap',
|
||||
MobiusMoney = 'MobiusMoney',
|
||||
// Fantom
|
||||
SpiritSwap = 'SpiritSwap',
|
||||
SpookySwap = 'SpookySwap',
|
||||
Beethovenx = 'Beethovenx',
|
||||
MorpheusSwap = 'MorpheusSwap',
|
||||
Geist = 'Geist',
|
||||
}
|
||||
export type SourcesWithPoolsCache =
|
||||
| ERC20BridgeSource.Balancer
|
||||
@@ -181,6 +180,12 @@ export interface AaveV2Info {
|
||||
underlyingToken: string;
|
||||
}
|
||||
|
||||
export interface GeistInfo {
|
||||
lendingPool: string;
|
||||
gToken: string;
|
||||
underlyingToken: string;
|
||||
}
|
||||
|
||||
// Internal `fillData` field for `Fill` objects.
|
||||
export interface FillData {}
|
||||
|
||||
@@ -202,6 +207,23 @@ export interface CurveFillData extends FillData {
|
||||
pool: CurveInfo;
|
||||
}
|
||||
|
||||
export interface BalancerBatchSwapStep {
|
||||
poolId: string;
|
||||
assetInIndex: number;
|
||||
assetOutIndex: number;
|
||||
amount: BigNumber;
|
||||
userData: string;
|
||||
}
|
||||
|
||||
export interface BalancerSwaps {
|
||||
swapInfoExactIn: BalancerSwapInfo[];
|
||||
swapInfoExactOut: BalancerSwapInfo[];
|
||||
}
|
||||
export interface BalancerSwapInfo {
|
||||
assets: string[];
|
||||
swapSteps: BalancerBatchSwapStep[];
|
||||
}
|
||||
|
||||
export interface BalancerFillData extends FillData {
|
||||
poolAddress: string;
|
||||
}
|
||||
@@ -211,6 +233,12 @@ export interface BalancerV2FillData extends FillData {
|
||||
poolId: string;
|
||||
}
|
||||
|
||||
export interface BalancerV2BatchSwapFillData extends FillData {
|
||||
vault: string;
|
||||
swapSteps: BalancerBatchSwapStep[];
|
||||
assets: string[];
|
||||
}
|
||||
|
||||
export interface UniswapV2FillData extends FillData {
|
||||
tokenAddressPath: string[];
|
||||
router: string;
|
||||
@@ -268,19 +296,34 @@ export interface HopInfo {
|
||||
returnData: string;
|
||||
}
|
||||
|
||||
export interface UniswapV3PathAmount {
|
||||
uniswapPath: string;
|
||||
inputAmount: BigNumber;
|
||||
gasUsed: number;
|
||||
}
|
||||
export interface UniswapV3FillData extends FillData {
|
||||
tokenAddressPath: string[];
|
||||
router: string;
|
||||
pathAmounts: Array<{ uniswapPath: string; inputAmount: BigNumber }>;
|
||||
pathAmounts: UniswapV3PathAmount[];
|
||||
}
|
||||
|
||||
export interface KyberDmmFillData extends UniswapV2FillData {
|
||||
poolsPath: string[];
|
||||
}
|
||||
|
||||
export interface FinalUniswapV3FillData extends Omit<UniswapV3FillData, 'uniswapPaths'> {
|
||||
/**
|
||||
* Determines whether FillData is UniswapV3FillData or FinalUniswapV3FillData
|
||||
*/
|
||||
export function isFinalUniswapV3FillData(
|
||||
data: UniswapV3FillData | FinalUniswapV3FillData,
|
||||
): data is FinalUniswapV3FillData {
|
||||
return !!(data as FinalUniswapV3FillData).uniswapPath;
|
||||
}
|
||||
|
||||
export interface FinalUniswapV3FillData extends Omit<UniswapV3FillData, 'pathAmounts'> {
|
||||
// The uniswap-encoded path that can fll the maximum input amount.
|
||||
uniswapPath: string;
|
||||
gasUsed: number;
|
||||
}
|
||||
|
||||
export interface LidoFillData extends FillData {
|
||||
@@ -301,6 +344,13 @@ export interface CompoundFillData extends FillData {
|
||||
makerToken: string;
|
||||
}
|
||||
|
||||
export interface GeistFillData extends FillData {
|
||||
lendingPool: string;
|
||||
gToken: string;
|
||||
underlyingToken: string;
|
||||
takerToken: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a node on a fill path.
|
||||
*/
|
||||
@@ -398,6 +448,7 @@ export type OptimizedMarketOrder =
|
||||
| OptimizedMarketOrderBase<NativeRfqOrderFillData>;
|
||||
|
||||
export interface GetMarketOrdersRfqOpts extends RfqRequestOpts {
|
||||
rfqClient?: IRfqClient;
|
||||
quoteRequestor?: QuoteRequestor;
|
||||
firmQuoteValidator?: RfqFirmQuoteValidator;
|
||||
}
|
||||
@@ -550,7 +601,6 @@ export interface OptimizerResult {
|
||||
liquidityDelivered: CollapsedFill[] | DexSample<MultiHopFillData>;
|
||||
marketSideLiquidity: MarketSideLiquidity;
|
||||
adjustedRate: BigNumber;
|
||||
unoptimizedPath?: CollapsedPath;
|
||||
takerAmountPerEth: BigNumber;
|
||||
makerAmountPerEth: BigNumber;
|
||||
}
|
||||
@@ -582,6 +632,7 @@ export interface MarketSideLiquidity {
|
||||
takerTokenDecimals: number;
|
||||
quotes: RawQuotes;
|
||||
isRfqSupported: boolean;
|
||||
blockNumber: number;
|
||||
}
|
||||
|
||||
export interface RawQuotes {
|
||||
|
||||
@@ -106,6 +106,8 @@ export interface ExtendedQuoteReport {
|
||||
decodedUniqueId?: string;
|
||||
sourcesConsidered: ExtendedQuoteReportIndexedEntryOutbound[];
|
||||
sourcesDelivered: ExtendedQuoteReportIndexedEntryOutbound[] | undefined;
|
||||
blockNumber: number | undefined;
|
||||
estimatedGas: string;
|
||||
}
|
||||
|
||||
export interface PriceComparisonsReport {
|
||||
@@ -205,7 +207,7 @@ export function generateExtendedQuoteReportSources(
|
||||
..._.flatten(
|
||||
quotes.dexQuotes.map(dex =>
|
||||
dex
|
||||
.filter(quote => isDexSampleForTotalAmount(quote, marketOperation, amount))
|
||||
.filter(quote => isDexSampleForTotalAmount(quote, amount))
|
||||
.map(quote => dexSampleToReportSource(quote, marketOperation)),
|
||||
),
|
||||
),
|
||||
@@ -304,16 +306,8 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp
|
||||
* Checks if a DEX sample is the one that represents the whole amount requested by taker
|
||||
* NOTE: this is used for the QuoteReport to filter samples
|
||||
*/
|
||||
function isDexSampleForTotalAmount(ds: DexSample, marketOperation: MarketOperation, amount: BigNumber): boolean {
|
||||
// input and output map to different values
|
||||
// based on the market operation
|
||||
if (marketOperation === MarketOperation.Buy) {
|
||||
return ds.input === amount;
|
||||
} else if (marketOperation === MarketOperation.Sell) {
|
||||
return ds.output === amount;
|
||||
} else {
|
||||
throw new Error(`Unexpected marketOperation ${marketOperation}`);
|
||||
}
|
||||
function isDexSampleForTotalAmount(ds: DexSample, amount: BigNumber): boolean {
|
||||
return ds.input.eq(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
16
packages/asset-swapper/src/utils/rfq_client_mappers.ts
Normal file
16
packages/asset-swapper/src/utils/rfq_client_mappers.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||
|
||||
import { SignedNativeOrder } from '../types';
|
||||
|
||||
import { RfqClientV1Quote } from './irfq_client';
|
||||
|
||||
/**
|
||||
* Converts a RfqClientRfqOrderFirmQuote to a SignedNativeOrder
|
||||
*/
|
||||
export const toSignedNativeOrder = (quote: RfqClientV1Quote): SignedNativeOrder => {
|
||||
return {
|
||||
type: FillQuoteTransformerOrderType.Rfq,
|
||||
order: quote.order,
|
||||
signature: quote.signature,
|
||||
};
|
||||
};
|
||||
@@ -8,16 +8,18 @@ import { ContractArtifact } from 'ethereum-types';
|
||||
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
|
||||
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
|
||||
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
|
||||
import * as BalancerV2BatchSampler from '../test/generated-artifacts/BalancerV2BatchSampler.json';
|
||||
import * as BalancerV2Common from '../test/generated-artifacts/BalancerV2Common.json';
|
||||
import * as BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json';
|
||||
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
|
||||
import * as CompoundSampler from '../test/generated-artifacts/CompoundSampler.json';
|
||||
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
|
||||
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
|
||||
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
|
||||
import * as DummyLiquidityProvider from '../test/generated-artifacts/DummyLiquidityProvider.json';
|
||||
import * as ERC20BridgeSampler from '../test/generated-artifacts/ERC20BridgeSampler.json';
|
||||
import * as FakeTaker from '../test/generated-artifacts/FakeTaker.json';
|
||||
import * as IBalancer from '../test/generated-artifacts/IBalancer.json';
|
||||
import * as IBalancerV2Vault from '../test/generated-artifacts/IBalancerV2Vault.json';
|
||||
import * as IBancor from '../test/generated-artifacts/IBancor.json';
|
||||
import * as ICurve from '../test/generated-artifacts/ICurve.json';
|
||||
import * as IKyberNetwork from '../test/generated-artifacts/IKyberNetwork.json';
|
||||
@@ -35,12 +37,10 @@ import * as LiquidityProviderSampler from '../test/generated-artifacts/Liquidity
|
||||
import * as MakerPSMSampler from '../test/generated-artifacts/MakerPSMSampler.json';
|
||||
import * as MooniswapSampler from '../test/generated-artifacts/MooniswapSampler.json';
|
||||
import * as MStableSampler from '../test/generated-artifacts/MStableSampler.json';
|
||||
import * as MultiBridgeSampler from '../test/generated-artifacts/MultiBridgeSampler.json';
|
||||
import * as NativeOrderSampler from '../test/generated-artifacts/NativeOrderSampler.json';
|
||||
import * as SamplerUtils from '../test/generated-artifacts/SamplerUtils.json';
|
||||
import * as ShellSampler from '../test/generated-artifacts/ShellSampler.json';
|
||||
import * as SmoothySampler from '../test/generated-artifacts/SmoothySampler.json';
|
||||
import * as TestERC20BridgeSampler from '../test/generated-artifacts/TestERC20BridgeSampler.json';
|
||||
import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeOrderSampler.json';
|
||||
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
|
||||
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
|
||||
@@ -51,6 +51,8 @@ export const artifacts = {
|
||||
ApproximateBuys: ApproximateBuys as ContractArtifact,
|
||||
BalanceChecker: BalanceChecker as ContractArtifact,
|
||||
BalancerSampler: BalancerSampler as ContractArtifact,
|
||||
BalancerV2BatchSampler: BalancerV2BatchSampler as ContractArtifact,
|
||||
BalancerV2Common: BalancerV2Common as ContractArtifact,
|
||||
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
|
||||
BancorSampler: BancorSampler as ContractArtifact,
|
||||
CompoundSampler: CompoundSampler as ContractArtifact,
|
||||
@@ -66,7 +68,6 @@ export const artifacts = {
|
||||
MStableSampler: MStableSampler as ContractArtifact,
|
||||
MakerPSMSampler: MakerPSMSampler as ContractArtifact,
|
||||
MooniswapSampler: MooniswapSampler as ContractArtifact,
|
||||
MultiBridgeSampler: MultiBridgeSampler as ContractArtifact,
|
||||
NativeOrderSampler: NativeOrderSampler as ContractArtifact,
|
||||
SamplerUtils: SamplerUtils as ContractArtifact,
|
||||
ShellSampler: ShellSampler as ContractArtifact,
|
||||
@@ -77,6 +78,7 @@ export const artifacts = {
|
||||
UniswapV3Sampler: UniswapV3Sampler as ContractArtifact,
|
||||
UtilitySampler: UtilitySampler as ContractArtifact,
|
||||
IBalancer: IBalancer as ContractArtifact,
|
||||
IBalancerV2Vault: IBalancerV2Vault as ContractArtifact,
|
||||
IBancor: IBancor as ContractArtifact,
|
||||
ICurve: ICurve as ContractArtifact,
|
||||
IKyberNetwork: IKyberNetwork as ContractArtifact,
|
||||
@@ -87,7 +89,5 @@ export const artifacts = {
|
||||
ISmoothy: ISmoothy as ContractArtifact,
|
||||
IUniswapExchangeQuotes: IUniswapExchangeQuotes as ContractArtifact,
|
||||
IUniswapV2Router01: IUniswapV2Router01 as ContractArtifact,
|
||||
DummyLiquidityProvider: DummyLiquidityProvider as ContractArtifact,
|
||||
TestERC20BridgeSampler: TestERC20BridgeSampler as ContractArtifact,
|
||||
TestNativeOrderSampler: TestNativeOrderSampler as ContractArtifact,
|
||||
};
|
||||
|
||||
@@ -66,6 +66,7 @@ const buyMarketSideLiquidity: MarketSideLiquidity = {
|
||||
},
|
||||
quoteSourceFilters: new SourceFilters(),
|
||||
isRfqSupported: false,
|
||||
blockNumber: 1337420,
|
||||
};
|
||||
|
||||
const sellMarketSideLiquidity: MarketSideLiquidity = {
|
||||
@@ -87,6 +88,7 @@ const sellMarketSideLiquidity: MarketSideLiquidity = {
|
||||
},
|
||||
quoteSourceFilters: new SourceFilters(),
|
||||
isRfqSupported: false,
|
||||
blockNumber: 1337420,
|
||||
};
|
||||
|
||||
describe('getComparisonPrices', async () => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -141,6 +141,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
|
||||
...(side === MarketOperation.Buy
|
||||
? { type: MarketOperation.Buy, makerTokenFillAmount }
|
||||
: { type: MarketOperation.Sell, takerTokenFillAmount }),
|
||||
blockNumber: 1337420,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ const MAKER_TOKEN = randomAddress();
|
||||
const TAKER_TOKEN = randomAddress();
|
||||
|
||||
const DEFAULT_INCLUDED = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Kyber,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
@@ -319,7 +319,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const DEFAULT_RATES: RatesBySource = {
|
||||
...ZERO_RATES,
|
||||
[ERC20BridgeSource.Native]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Eth2Dai]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.SushiSwap]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Uniswap]: createDecreasingRates(NUM_SAMPLES),
|
||||
[ERC20BridgeSource.Kyber]: createDecreasingRates(NUM_SAMPLES),
|
||||
};
|
||||
@@ -349,28 +349,6 @@ describe('MarketOperationUtils tests', () => {
|
||||
fromTokenIdx: 0,
|
||||
toTokenIdx: 1,
|
||||
},
|
||||
[ERC20BridgeSource.Swerve]: {
|
||||
pool: {
|
||||
poolAddress: randomAddress(),
|
||||
tokens: [TAKER_TOKEN, MAKER_TOKEN],
|
||||
exchangeFunctionSelector: hexUtils.random(4),
|
||||
sellQuoteFunctionSelector: hexUtils.random(4),
|
||||
buyQuoteFunctionSelector: hexUtils.random(4),
|
||||
},
|
||||
fromTokenIdx: 0,
|
||||
toTokenIdx: 1,
|
||||
},
|
||||
[ERC20BridgeSource.SnowSwap]: {
|
||||
pool: {
|
||||
poolAddress: randomAddress(),
|
||||
tokens: [TAKER_TOKEN, MAKER_TOKEN],
|
||||
exchangeFunctionSelector: hexUtils.random(4),
|
||||
sellQuoteFunctionSelector: hexUtils.random(4),
|
||||
buyQuoteFunctionSelector: hexUtils.random(4),
|
||||
},
|
||||
fromTokenIdx: 0,
|
||||
toTokenIdx: 1,
|
||||
},
|
||||
[ERC20BridgeSource.Smoothy]: {
|
||||
pool: {
|
||||
poolAddress: randomAddress(),
|
||||
@@ -404,9 +382,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.Dodo]: {},
|
||||
[ERC20BridgeSource.DodoV2]: {},
|
||||
[ERC20BridgeSource.CryptoCom]: { tokenAddressPath: [] },
|
||||
[ERC20BridgeSource.Linkswap]: { tokenAddressPath: [] },
|
||||
[ERC20BridgeSource.Uniswap]: { router: randomAddress() },
|
||||
[ERC20BridgeSource.Eth2Dai]: { router: randomAddress() },
|
||||
[ERC20BridgeSource.MakerPsm]: {},
|
||||
[ERC20BridgeSource.KyberDmm]: { tokenAddressPath: [], router: randomAddress(), poolsPath: [] },
|
||||
};
|
||||
@@ -483,7 +459,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('queries `numSamples` samples', async () => {
|
||||
const numSamples = _.random(1, NUM_SAMPLES);
|
||||
// neon-router requires at least 3 samples
|
||||
const numSamples = _.random(3, NUM_SAMPLES);
|
||||
let actualNumSamples = 0;
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -501,6 +478,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
numSamples,
|
||||
neonRouterNumSamples: numSamples,
|
||||
});
|
||||
expect(actualNumSamples).eq(numSamples);
|
||||
});
|
||||
@@ -532,7 +510,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('does not poll DEXes in `excludedSources`', async () => {
|
||||
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
||||
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap];
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -562,7 +540,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('only polls DEXes in `includedSources`', async () => {
|
||||
const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
||||
const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap];
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -746,6 +724,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
],
|
||||
},
|
||||
isRfqSupported: true,
|
||||
blockNumber: 1337420,
|
||||
};
|
||||
});
|
||||
const result = await mockedMarketOpUtils.object.getOptimizerResultAsync(
|
||||
@@ -1054,7 +1033,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const rates: RatesBySource = { ...DEFAULT_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.SushiSwap] = [0.6, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Kyber] = [0, 0, 0, 0]; // unused
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
@@ -1068,7 +1047,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
@@ -1078,14 +1057,15 @@ describe('MarketOperationUtils tests', () => {
|
||||
|
||||
const ETH_TO_MAKER_RATE = 1.5;
|
||||
|
||||
it('factors in fees for native orders', async () => {
|
||||
// TODO: disabled as this is not supported by neon-router
|
||||
it.skip('factors in fees for native orders', async () => {
|
||||
// Native orders will have the best rates but have fees,
|
||||
// dropping their effective rates.
|
||||
const nativeFeeRate = 0.06;
|
||||
const rates: RatesBySource = {
|
||||
[ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, 0.93, 0.92, 0.91]
|
||||
[ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.SushiSwap]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
|
||||
};
|
||||
const feeSchedule = {
|
||||
@@ -1110,7 +1090,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
|
||||
@@ -1123,7 +1103,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const rates: RatesBySource = {
|
||||
[ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.SushiSwap]: [0.92, 0.1, 0.1, 0.1],
|
||||
// Effectively [0.8, ~0.5, ~0, ~0]
|
||||
[ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
|
||||
};
|
||||
@@ -1148,7 +1128,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
];
|
||||
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
|
||||
@@ -1157,7 +1137,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
it('can mix one concave source', async () => {
|
||||
const rates: RatesBySource = {
|
||||
[ERC20BridgeSource.Kyber]: [0, 0, 0, 0], // Won't use
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.5, 0.85, 0.75, 0.75], // Concave
|
||||
[ERC20BridgeSource.SushiSwap]: [0.5, 0.85, 0.75, 0.75], // Concave
|
||||
[ERC20BridgeSource.Uniswap]: [0.96, 0.2, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Native]: [0.95, 0.2, 0.2, 0.1],
|
||||
};
|
||||
@@ -1174,18 +1154,20 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
|
||||
});
|
||||
|
||||
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||
// NOTE: Currently fallbacks for native orders are disabled
|
||||
// TODO: remove this if we remove fallbacks completely
|
||||
it.skip('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||
const rates: RatesBySource = {};
|
||||
rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49];
|
||||
rates[ERC20BridgeSource.SushiSwap] = [0.49, 0.49, 0.49, 0.49];
|
||||
rates[ERC20BridgeSource.Kyber] = [0.35, 0.2, 0.01, 0.01];
|
||||
replaceSamplerOps({
|
||||
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(rates),
|
||||
@@ -1325,7 +1307,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('queries `numSamples` samples', async () => {
|
||||
const numSamples = _.random(1, 16);
|
||||
// neon-router requires at least 3 samples
|
||||
const numSamples = _.random(3, 16);
|
||||
let actualNumSamples = 0;
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -1343,6 +1326,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
|
||||
...DEFAULT_OPTS,
|
||||
numSamples,
|
||||
// Make sure to use same number of samples in neon-router for compatibility
|
||||
neonRouterNumSamples: numSamples,
|
||||
});
|
||||
expect(actualNumSamples).eq(numSamples);
|
||||
});
|
||||
@@ -1377,7 +1362,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('does not poll DEXes in `excludedSources`', async () => {
|
||||
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
||||
const excludedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap];
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -1407,7 +1392,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
});
|
||||
|
||||
it('only polls DEXes in `includedSources`', async () => {
|
||||
const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.Eth2Dai];
|
||||
const includedSources = [ERC20BridgeSource.Uniswap, ERC20BridgeSource.SushiSwap];
|
||||
let sourcesPolled: ERC20BridgeSource[] = [];
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: (sources, makerToken, takerToken, amounts, wethAddress) => {
|
||||
@@ -1494,11 +1479,12 @@ describe('MarketOperationUtils tests', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('can mix convex sources', async () => {
|
||||
// TODO: disabled as this is not supported by neon-router
|
||||
it.skip('can mix convex sources', async () => {
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [0.4, 0.3, 0.2, 0.1];
|
||||
rates[ERC20BridgeSource.Uniswap] = [0.5, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.6, 0.05, 0.05, 0.05];
|
||||
rates[ERC20BridgeSource.SushiSwap] = [0.6, 0.05, 0.05, 0.05];
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||
});
|
||||
@@ -1511,7 +1497,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const improvedOrders = improvedOrdersResponse.optimizedOrders;
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
@@ -1521,7 +1507,8 @@ describe('MarketOperationUtils tests', () => {
|
||||
|
||||
const ETH_TO_TAKER_RATE = 1.5;
|
||||
|
||||
it('factors in fees for native orders', async () => {
|
||||
// TODO: disabled as this is not supported by neon-router
|
||||
it.skip('factors in fees for native orders', async () => {
|
||||
// Native orders will have the best rates but have fees,
|
||||
// dropping their effective rates.
|
||||
const nativeFeeRate = 0.06;
|
||||
@@ -1529,7 +1516,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
...ZERO_RATES,
|
||||
[ERC20BridgeSource.Native]: [1, 0.99, 0.98, 0.97], // Effectively [0.94, ~0.93, ~0.92, ~0.91]
|
||||
[ERC20BridgeSource.Uniswap]: [0.96, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.SushiSwap]: [0.95, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.Kyber]: [0.1, 0.1, 0.1, 0.1],
|
||||
};
|
||||
const feeSchedule = {
|
||||
@@ -1553,7 +1540,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Uniswap,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Native,
|
||||
];
|
||||
@@ -1569,7 +1556,7 @@ describe('MarketOperationUtils tests', () => {
|
||||
[ERC20BridgeSource.Native]: [0.95, 0.1, 0.1, 0.1],
|
||||
// Effectively [0.8, ~0.5, ~0, ~0]
|
||||
[ERC20BridgeSource.Uniswap]: [1, 0.7, 0.2, 0.2],
|
||||
[ERC20BridgeSource.Eth2Dai]: [0.92, 0.1, 0.1, 0.1],
|
||||
[ERC20BridgeSource.SushiSwap]: [0.92, 0.1, 0.1, 0.1],
|
||||
};
|
||||
const feeSchedule = {
|
||||
[ERC20BridgeSource.Uniswap]: _.constant(
|
||||
@@ -1592,17 +1579,19 @@ describe('MarketOperationUtils tests', () => {
|
||||
const orderSources = improvedOrders.map(o => o.fills[0].source);
|
||||
const expectedSources = [
|
||||
ERC20BridgeSource.Native,
|
||||
ERC20BridgeSource.Eth2Dai,
|
||||
ERC20BridgeSource.SushiSwap,
|
||||
ERC20BridgeSource.Uniswap,
|
||||
];
|
||||
expect(orderSources.sort()).to.deep.eq(expectedSources.sort());
|
||||
});
|
||||
|
||||
it('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||
// NOTE: Currently fallbacks for native orders are disabled
|
||||
// TODO: remove this if we remove fallbacks completely
|
||||
it.skip('does not create a fallback if below maxFallbackSlippage', async () => {
|
||||
const rates: RatesBySource = { ...ZERO_RATES };
|
||||
rates[ERC20BridgeSource.Native] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Uniswap] = [1, 1, 0.01, 0.01];
|
||||
rates[ERC20BridgeSource.Eth2Dai] = [0.49, 0.49, 0.49, 0.49];
|
||||
rates[ERC20BridgeSource.SushiSwap] = [0.49, 0.49, 0.49, 0.49];
|
||||
replaceSamplerOps({
|
||||
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(rates),
|
||||
});
|
||||
|
||||
@@ -44,6 +44,7 @@ export async function getFullyFillableSwapQuoteWithNoFeesAsync(
|
||||
makerAmountPerEth: constants.ZERO_AMOUNT,
|
||||
makerTokenDecimals: 18,
|
||||
takerTokenDecimals: 18,
|
||||
blockNumber: 1337420,
|
||||
};
|
||||
|
||||
if (operation === MarketOperation.Buy) {
|
||||
|
||||
@@ -6,16 +6,18 @@
|
||||
export * from '../test/generated-wrappers/approximate_buys';
|
||||
export * from '../test/generated-wrappers/balance_checker';
|
||||
export * from '../test/generated-wrappers/balancer_sampler';
|
||||
export * from '../test/generated-wrappers/balancer_v2_batch_sampler';
|
||||
export * from '../test/generated-wrappers/balancer_v2_common';
|
||||
export * from '../test/generated-wrappers/balancer_v2_sampler';
|
||||
export * from '../test/generated-wrappers/bancor_sampler';
|
||||
export * from '../test/generated-wrappers/compound_sampler';
|
||||
export * from '../test/generated-wrappers/curve_sampler';
|
||||
export * from '../test/generated-wrappers/d_o_d_o_sampler';
|
||||
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';
|
||||
export * from '../test/generated-wrappers/dummy_liquidity_provider';
|
||||
export * from '../test/generated-wrappers/erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/fake_taker';
|
||||
export * from '../test/generated-wrappers/i_balancer';
|
||||
export * from '../test/generated-wrappers/i_balancer_v2_vault';
|
||||
export * from '../test/generated-wrappers/i_bancor';
|
||||
export * from '../test/generated-wrappers/i_curve';
|
||||
export * from '../test/generated-wrappers/i_kyber_network';
|
||||
@@ -33,12 +35,10 @@ export * from '../test/generated-wrappers/liquidity_provider_sampler';
|
||||
export * from '../test/generated-wrappers/m_stable_sampler';
|
||||
export * from '../test/generated-wrappers/maker_p_s_m_sampler';
|
||||
export * from '../test/generated-wrappers/mooniswap_sampler';
|
||||
export * from '../test/generated-wrappers/multi_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/native_order_sampler';
|
||||
export * from '../test/generated-wrappers/sampler_utils';
|
||||
export * from '../test/generated-wrappers/shell_sampler';
|
||||
export * from '../test/generated-wrappers/smoothy_sampler';
|
||||
export * from '../test/generated-wrappers/test_erc20_bridge_sampler';
|
||||
export * from '../test/generated-wrappers/test_native_order_sampler';
|
||||
export * from '../test/generated-wrappers/two_hop_sampler';
|
||||
export * from '../test/generated-wrappers/uniswap_sampler';
|
||||
|
||||
@@ -9,16 +9,18 @@
|
||||
"test/generated-artifacts/ApproximateBuys.json",
|
||||
"test/generated-artifacts/BalanceChecker.json",
|
||||
"test/generated-artifacts/BalancerSampler.json",
|
||||
"test/generated-artifacts/BalancerV2BatchSampler.json",
|
||||
"test/generated-artifacts/BalancerV2Common.json",
|
||||
"test/generated-artifacts/BalancerV2Sampler.json",
|
||||
"test/generated-artifacts/BancorSampler.json",
|
||||
"test/generated-artifacts/CompoundSampler.json",
|
||||
"test/generated-artifacts/CurveSampler.json",
|
||||
"test/generated-artifacts/DODOSampler.json",
|
||||
"test/generated-artifacts/DODOV2Sampler.json",
|
||||
"test/generated-artifacts/DummyLiquidityProvider.json",
|
||||
"test/generated-artifacts/ERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/FakeTaker.json",
|
||||
"test/generated-artifacts/IBalancer.json",
|
||||
"test/generated-artifacts/IBalancerV2Vault.json",
|
||||
"test/generated-artifacts/IBancor.json",
|
||||
"test/generated-artifacts/ICurve.json",
|
||||
"test/generated-artifacts/IKyberNetwork.json",
|
||||
@@ -36,12 +38,10 @@
|
||||
"test/generated-artifacts/MStableSampler.json",
|
||||
"test/generated-artifacts/MakerPSMSampler.json",
|
||||
"test/generated-artifacts/MooniswapSampler.json",
|
||||
"test/generated-artifacts/MultiBridgeSampler.json",
|
||||
"test/generated-artifacts/NativeOrderSampler.json",
|
||||
"test/generated-artifacts/SamplerUtils.json",
|
||||
"test/generated-artifacts/ShellSampler.json",
|
||||
"test/generated-artifacts/SmoothySampler.json",
|
||||
"test/generated-artifacts/TestERC20BridgeSampler.json",
|
||||
"test/generated-artifacts/TestNativeOrderSampler.json",
|
||||
"test/generated-artifacts/TwoHopSampler.json",
|
||||
"test/generated-artifacts/UniswapSampler.json",
|
||||
|
||||
@@ -1,4 +1,33 @@
|
||||
[
|
||||
{
|
||||
"version": "6.13.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Redeploy FQT on mainnet and polygon",
|
||||
"pr": 462
|
||||
}
|
||||
],
|
||||
"timestamp": 1650611093
|
||||
},
|
||||
{
|
||||
"timestamp": 1648739346,
|
||||
"version": "6.12.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "6.12.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Update fantom fillQuoteTransformer addresses",
|
||||
"pr": 398
|
||||
}
|
||||
],
|
||||
"timestamp": 1646225739
|
||||
},
|
||||
{
|
||||
"version": "6.11.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v6.13.0 - _April 22, 2022_
|
||||
|
||||
* Redeploy FQT on mainnet and polygon (#462)
|
||||
|
||||
## v6.12.1 - _March 31, 2022_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.12.0 - _March 2, 2022_
|
||||
|
||||
* Update fantom fillQuoteTransformer addresses (#398)
|
||||
|
||||
## v6.11.0 - _December 24, 2021_
|
||||
|
||||
* Add Optimism addresses (#385)
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"wethTransformer": "0xb2bc06a4efb20fc6553a69dbfa49b7be938034a7",
|
||||
"payTakerTransformer": "0x4638a7ebe75b911b995d0ec73a81e4f85f41f24e",
|
||||
"affiliateFeeTransformer": "0xda6d9fc5998f550a094585cf9171f0e8ee3ac59f",
|
||||
"fillQuoteTransformer": "0xb4fa284689c9784a60d840eb136bb16c5246191f",
|
||||
"fillQuoteTransformer": "0xadbe39f2988a8be1c1120f05e28cc888b150c8a6",
|
||||
"positiveSlippageFeeTransformer": "0xa9416ce1dbde8d331210c07b5c253d94ee4cc3fd"
|
||||
}
|
||||
},
|
||||
@@ -289,7 +289,7 @@
|
||||
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
|
||||
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
|
||||
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
|
||||
"fillQuoteTransformer": "0xd3afdf4a8ea9183e76c9c2306cda03ea4afffea5",
|
||||
"fillQuoteTransformer": "0xd4a518760030dae1adbde9496f8a3b478e83932a",
|
||||
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
|
||||
}
|
||||
},
|
||||
@@ -415,7 +415,7 @@
|
||||
"wethTransformer": "0x9b6aa8f26a92108e7d1f66373d757bb955112703",
|
||||
"payTakerTransformer": "0x32df54951d33d7460e15fa59b1fcc262183ce4c2",
|
||||
"affiliateFeeTransformer": "0x67efa679a4b56c38713d478e649c88247f4f8e88",
|
||||
"fillQuoteTransformer": "0x71de60a1b160094a3f6c7e1b883ff9337d639131",
|
||||
"fillQuoteTransformer": "0x641efe8a57ad39353fe22f77d211ef6b17b0590b",
|
||||
"positiveSlippageFeeTransformer": "0xe87d69b285005cc82b53b844322652c49ed64600"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contract-addresses",
|
||||
"version": "6.11.0",
|
||||
"version": "6.13.0",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -30,7 +30,7 @@
|
||||
"devDependencies": {
|
||||
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
||||
"shx": "^0.2.2",
|
||||
"typescript": "4.2.2"
|
||||
"typescript": "4.6.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
[
|
||||
{
|
||||
"version": "3.18.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Regenerate all artifacts",
|
||||
"pr": 449
|
||||
}
|
||||
],
|
||||
"timestamp": 1648739346
|
||||
},
|
||||
{
|
||||
"version": "3.17.0",
|
||||
"changes": [
|
||||
|
||||
@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.18.0 - _March 31, 2022_
|
||||
|
||||
* Regenerate all artifacts (#449)
|
||||
|
||||
## v3.17.0 - _February 22, 2022_
|
||||
|
||||
* Update IZeroEx artifact (#429)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -135,10 +135,10 @@
|
||||
},
|
||||
"evm": {
|
||||
"bytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b506106bf806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c806370a082311161005057806370a0823114610121578063a9059cbb14610154578063dd62ed3e1461018d57610072565b8063095ea7b31461007757806318160ddd146100c457806323b872dd146100de575b600080fd5b6100b06004803603604081101561008d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356101c8565b604080519115158252519081900360200190f35b6100cc61023b565b60408051918252519081900360200190f35b6100b0600480360360608110156100f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610241565b6100cc6004803603602081101561013757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661049d565b6100b06004803603604081101561016a57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356104c5565b6100cc600480360360408110156101a357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610652565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60025490565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120548211156102d557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8416600090815260016020908152604080832033845290915290205482111561037457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f45524332305f494e53554646494349454e545f414c4c4f57414e434500000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054828101101561040a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff80841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3360009081526020819052604081205482111561054357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110156105d957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220549056fea265627a7a723158200a0e5b4813b81b4a59cf323e9296dbdc12eadc2b35caf21c2052aa9fc5df88b364736f6c63430005110032"
|
||||
"object": "0x608060405234801561001057600080fd5b506106bf806100206000396000f3fe608060405234801561001057600080fd5b50600436106100725760003560e01c806370a082311161005057806370a0823114610121578063a9059cbb14610154578063dd62ed3e1461018d57610072565b8063095ea7b31461007757806318160ddd146100c457806323b872dd146100de575b600080fd5b6100b06004803603604081101561008d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356101c8565b604080519115158252519081900360200190f35b6100cc61023b565b60408051918252519081900360200190f35b6100b0600480360360608110156100f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610241565b6100cc6004803603602081101561013757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661049d565b6100b06004803603604081101561016a57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356104c5565b6100cc600480360360408110156101a357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610652565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60025490565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120548211156102d557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8416600090815260016020908152604080832033845290915290205482111561037457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f45524332305f494e53554646494349454e545f414c4c4f57414e434500000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054828101101561040a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff80841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3360009081526020819052604081205482111561054357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110156105d957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220549056fea265627a7a72315820a51afc8c5cb32c94c3f05c807ed412c5091b6f59e504ef34013b272c3c46a3ee64736f6c63430005110032"
|
||||
},
|
||||
"deployedBytecode": {
|
||||
"object": "0x608060405234801561001057600080fd5b50600436106100725760003560e01c806370a082311161005057806370a0823114610121578063a9059cbb14610154578063dd62ed3e1461018d57610072565b8063095ea7b31461007757806318160ddd146100c457806323b872dd146100de575b600080fd5b6100b06004803603604081101561008d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356101c8565b604080519115158252519081900360200190f35b6100cc61023b565b60408051918252519081900360200190f35b6100b0600480360360608110156100f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610241565b6100cc6004803603602081101561013757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661049d565b6100b06004803603604081101561016a57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356104c5565b6100cc600480360360408110156101a357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610652565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60025490565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120548211156102d557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8416600090815260016020908152604080832033845290915290205482111561037457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f45524332305f494e53554646494349454e545f414c4c4f57414e434500000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054828101101561040a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff80841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3360009081526020819052604081205482111561054357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110156105d957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220549056fea265627a7a723158200a0e5b4813b81b4a59cf323e9296dbdc12eadc2b35caf21c2052aa9fc5df88b364736f6c63430005110032"
|
||||
"object": "0x608060405234801561001057600080fd5b50600436106100725760003560e01c806370a082311161005057806370a0823114610121578063a9059cbb14610154578063dd62ed3e1461018d57610072565b8063095ea7b31461007757806318160ddd146100c457806323b872dd146100de575b600080fd5b6100b06004803603604081101561008d57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356101c8565b604080519115158252519081900360200190f35b6100cc61023b565b60408051918252519081900360200190f35b6100b0600480360360608110156100f457600080fd5b5073ffffffffffffffffffffffffffffffffffffffff813581169160208101359091169060400135610241565b6100cc6004803603602081101561013757600080fd5b503573ffffffffffffffffffffffffffffffffffffffff1661049d565b6100b06004803603604081101561016a57600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81351690602001356104c5565b6100cc600480360360408110156101a357600080fd5b5073ffffffffffffffffffffffffffffffffffffffff81358116916020013516610652565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716808552908352818420869055815186815291519394909390927f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925928290030190a350600192915050565b60025490565b73ffffffffffffffffffffffffffffffffffffffff83166000908152602081905260408120548211156102d557604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8416600090815260016020908152604080832033845290915290205482111561037457604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f45524332305f494e53554646494349454e545f414c4c4f57414e434500000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054828101101561040a57604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff80841660008181526020818152604080832080548801905593881680835284832080548890039055600182528483203384528252918490208054879003905583518681529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35060019392505050565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b3360009081526020819052604081205482111561054357604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f45524332305f494e53554646494349454e545f42414c414e4345000000000000604482015290519081900360640190fd5b73ffffffffffffffffffffffffffffffffffffffff831660009081526020819052604090205482810110156105d957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f55494e543235365f4f564552464c4f5700000000000000000000000000000000604482015290519081900360640190fd5b336000818152602081815260408083208054879003905573ffffffffffffffffffffffffffffffffffffffff871680845292819020805487019055805186815290519293927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a350600192915050565b73ffffffffffffffffffffffffffffffffffffffff91821660009081526001602090815260408083209390941682529190915220549056fea265627a7a72315820a51afc8c5cb32c94c3f05c807ed412c5091b6f59e504ef34013b272c3c46a3ee64736f6c63430005110032"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user