Compare commits
100 Commits
protocol@7
...
protocol@9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ce090c8cd | ||
|
|
980d60deb8 | ||
|
|
d6d79e51e7 | ||
|
|
3ef5de93bb | ||
|
|
ab7dc33ca4 | ||
|
|
14dcee5bb6 | ||
|
|
9856e78609 | ||
|
|
2801b066b3 | ||
|
|
5bc8b13fc3 | ||
|
|
36dba8f5be | ||
|
|
ee2c069889 | ||
|
|
6ca14ed7b2 | ||
|
|
a5babb9a34 | ||
|
|
661cc4669d | ||
|
|
553ba5c868 | ||
|
|
d03d2f254d | ||
|
|
feb91a04b0 | ||
|
|
4d63f33aba | ||
|
|
b72b8b5ffd | ||
|
|
f7cb7a0f51 | ||
|
|
9fcb28f5d8 | ||
|
|
982173471c | ||
|
|
e77958425f | ||
|
|
6af4d71573 | ||
|
|
ee985240fb | ||
|
|
2aadbda527 | ||
|
|
297c73abcc | ||
|
|
4c9e1b21ec | ||
|
|
41685d1545 | ||
|
|
b9c25112ed | ||
|
|
f0738fc122 | ||
|
|
42baf504b7 | ||
|
|
56038d122f | ||
|
|
c4446b6c0e | ||
|
|
eaed2958c3 | ||
|
|
a045a3afb8 | ||
|
|
1cc59ab1ab | ||
|
|
2c6a714b71 | ||
|
|
d8c97d6720 | ||
|
|
d6bc702550 | ||
|
|
2838cb9420 | ||
|
|
b10cfc50d3 | ||
|
|
2e6317b01e | ||
|
|
50e99e6eac | ||
|
|
8f2f4554eb | ||
|
|
67d9678a3a | ||
|
|
f70341fb48 | ||
|
|
14cd24ea47 | ||
|
|
78328056d7 | ||
|
|
0045a60b0f | ||
|
|
e4e71c76e1 | ||
|
|
ca8127545f | ||
|
|
7b709089ce | ||
|
|
190f7e45f2 | ||
|
|
0dcc3a6fc3 | ||
|
|
c2e8cae293 | ||
|
|
83da7caab4 | ||
|
|
fd69a0c273 | ||
|
|
9b131199ad | ||
|
|
f5c486050b | ||
|
|
1f41fe6a20 | ||
|
|
7f4080e0a2 | ||
|
|
db76da58d7 | ||
|
|
cf8fc0ff8e | ||
|
|
2d16f83e37 | ||
|
|
4057bdab91 | ||
|
|
1cd10f0ac9 | ||
|
|
68f87b2432 | ||
|
|
69bafc3bcd | ||
|
|
2c44b06b7b | ||
|
|
0233f00b4e | ||
|
|
fedb53187d | ||
|
|
6774d2f588 | ||
|
|
cf740b74f5 | ||
|
|
177c00463a | ||
|
|
49b0e32129 | ||
|
|
938fc94756 | ||
|
|
1561d91c2b | ||
|
|
9a28e51f51 | ||
|
|
f55eaa867b | ||
|
|
6b2856424a | ||
|
|
da757c4700 | ||
|
|
75e6654884 | ||
|
|
87308e7693 | ||
|
|
d5eef93a76 | ||
|
|
a7f23a982e | ||
|
|
9eadc5fc28 | ||
|
|
92ad1a612e | ||
|
|
09413c0e12 | ||
|
|
23788b41d5 | ||
|
|
ccf999a495 | ||
|
|
aa1016ee5f | ||
|
|
423ef57344 | ||
|
|
c18149e82f | ||
|
|
d14aebf724 | ||
|
|
ba719a9631 | ||
|
|
d36034d958 | ||
|
|
7750c57620 | ||
|
|
4d027e11d1 | ||
|
|
470e9a4697 |
@@ -19,7 +19,6 @@ jobs:
|
|||||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||||
- setup_remote_docker
|
- 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: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:
|
- save_cache:
|
||||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||||
paths:
|
paths:
|
||||||
@@ -111,7 +110,7 @@ jobs:
|
|||||||
- image: node:16
|
- image: node:16
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
environment:
|
environment:
|
||||||
RUST_ROUTER: "true"
|
RUST_ROUTER: 'true'
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
@@ -119,7 +118,6 @@ jobs:
|
|||||||
- run: yarn wsrun -p @0x/contracts-test-utils -m --serial -c test:circleci
|
- run: yarn wsrun -p @0x/contracts-test-utils -m --serial -c test:circleci
|
||||||
- run: yarn wsrun -p @0x/contract-artifacts -m --serial -c test:circleci
|
- run: yarn wsrun -p @0x/contract-artifacts -m --serial -c test:circleci
|
||||||
- run: yarn wsrun -p @0x/contract-wrappers-test -m --serial -c test:circleci
|
- run: yarn wsrun -p @0x/contract-wrappers-test -m --serial -c test:circleci
|
||||||
- run: yarn wsrun -p @0x/migrations -m --serial -c test:circleci
|
|
||||||
- run: yarn wsrun -p @0x/order-utils -m --serial -c test:circleci
|
- run: yarn wsrun -p @0x/order-utils -m --serial -c test:circleci
|
||||||
- run: yarn wsrun -p @0x/asset-swapper -m --serial -c test:circleci
|
- run: yarn wsrun -p @0x/asset-swapper -m --serial -c test:circleci
|
||||||
- save_cache:
|
- save_cache:
|
||||||
|
|||||||
1
.github/autolabeler.yml
vendored
1
.github/autolabeler.yml
vendored
@@ -1,7 +1,6 @@
|
|||||||
python: ['python-packages']
|
python: ['python-packages']
|
||||||
contracts: ['contracts']
|
contracts: ['contracts']
|
||||||
@0x/contract-addresses: ['packages/contract-addresses']
|
@0x/contract-addresses: ['packages/contract-addresses']
|
||||||
@0x/migrations: ['packages/migrations']
|
|
||||||
@0x/order-utils: ['packages/order-utils']
|
@0x/order-utils: ['packages/order-utils']
|
||||||
@0x/contract-artifacts: ['packages/contract-artifacts']
|
@0x/contract-artifacts: ['packages/contract-artifacts']
|
||||||
@0x/contract-wrappers: ['packages/contract-wrappers']
|
@0x/contract-wrappers: ['packages/contract-wrappers']
|
||||||
|
|||||||
18
CODEOWNERS
18
CODEOWNERS
@@ -1,18 +1,20 @@
|
|||||||
# See https://help.github.com/articles/about-codeowners/
|
# See https://help.github.com/articles/about-codeowners/
|
||||||
|
|
||||||
# for more info about CODEOWNERS file
|
# for more info about CODEOWNERS file
|
||||||
|
|
||||||
# It uses the same pattern rule for gitignore file
|
# It uses the same pattern rule for gitignore file
|
||||||
|
|
||||||
# https://git-scm.com/docs/gitignore#_pattern_format
|
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||||
|
|
||||||
# Website
|
packages/asset-swapper/ @dekz @dextracker @kyu-c
|
||||||
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
|
|
||||||
packages/instant/ @BMillman19 @fragosti @dave4506
|
|
||||||
|
|
||||||
# Dev tools & setup
|
# Dev tools & setup
|
||||||
.circleci/ @dorothy-zbornak
|
|
||||||
packages/contract-addresses/ @abandeali1
|
.circleci/ @dekz
|
||||||
packages/contract-artifacts/ @abandeali1
|
packages/contract-addresses/ @dekz @dextracker @kyu-c
|
||||||
packages/order-utils/ @dorothy-zbornak
|
packages/contract-artifacts/ @dekz
|
||||||
|
packages/protocol-utils/ @dekz
|
||||||
|
|
||||||
# Protocol/smart contracts
|
# Protocol/smart contracts
|
||||||
contracts/ @abandeali1 @hysz @dorothy-zbornak @mzhu25
|
|
||||||
|
contracts/ @dekz @dextracker
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
|||||||
| [`@0x/protocol-utils`](/packages/protocol-utils) | [](https://www.npmjs.com/package/@0x/protocol-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
| [`@0x/protocol-utils`](/packages/protocol-utils) | [](https://www.npmjs.com/package/@0x/protocol-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
|
||||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
@@ -1,4 +1,49 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1658950329,
|
||||||
|
"version": "3.3.33",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1655244958,
|
||||||
|
"version": "3.3.32",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1654284040,
|
||||||
|
"version": "3.3.31",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1652919697,
|
||||||
|
"version": "3.3.30",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1650611093,
|
||||||
|
"version": "3.3.29",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1648739346,
|
"timestamp": 1648739346,
|
||||||
"version": "3.3.28",
|
"version": "3.3.28",
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v3.3.33 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.3.32 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.3.31 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.3.30 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v3.3.29 - _April 22, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v3.3.28 - _March 31, 2022_
|
## v3.3.28 - _March 31, 2022_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-erc20",
|
"name": "@0x/contracts-erc20",
|
||||||
"version": "3.3.28",
|
"version": "3.3.33",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -53,8 +53,8 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.8.0",
|
"@0x/abi-gen": "^5.8.0",
|
||||||
"@0x/contracts-gen": "^2.0.46",
|
"@0x/contracts-gen": "^2.0.46",
|
||||||
"@0x/contracts-test-utils": "^5.4.19",
|
"@0x/contracts-test-utils": "^5.4.24",
|
||||||
"@0x/contracts-utils": "^4.8.9",
|
"@0x/contracts-utils": "^4.8.14",
|
||||||
"@0x/dev-utils": "^4.2.14",
|
"@0x/dev-utils": "^4.2.14",
|
||||||
"@0x/sol-compiler": "^4.8.1",
|
"@0x/sol-compiler": "^4.8.1",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
|
|||||||
@@ -1,4 +1,49 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1658950329,
|
||||||
|
"version": "5.4.24",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1655244958,
|
||||||
|
"version": "5.4.23",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1654284040,
|
||||||
|
"version": "5.4.22",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1652919697,
|
||||||
|
"version": "5.4.21",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1650611093,
|
||||||
|
"version": "5.4.20",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1648739346,
|
"timestamp": 1648739346,
|
||||||
"version": "5.4.19",
|
"version": "5.4.19",
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v5.4.24 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v5.4.23 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v5.4.22 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v5.4.21 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v5.4.20 - _April 22, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v5.4.19 - _March 31, 2022_
|
## v5.4.19 - _March 31, 2022_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-test-utils",
|
"name": "@0x/contracts-test-utils",
|
||||||
"version": "5.4.19",
|
"version": "5.4.24",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/assert": "^3.0.34",
|
"@0x/assert": "^3.0.34",
|
||||||
"@0x/base-contract": "^6.5.0",
|
"@0x/base-contract": "^6.5.0",
|
||||||
"@0x/contract-addresses": "^6.12.1",
|
"@0x/contract-addresses": "^6.17.0",
|
||||||
"@0x/dev-utils": "^4.2.14",
|
"@0x/dev-utils": "^4.2.14",
|
||||||
"@0x/json-schemas": "^6.4.4",
|
"@0x/json-schemas": "^6.4.4",
|
||||||
"@0x/order-utils": "^10.4.28",
|
"@0x/order-utils": "^10.4.28",
|
||||||
|
|||||||
@@ -1,4 +1,49 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1658950329,
|
||||||
|
"version": "1.4.16",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1655244958,
|
||||||
|
"version": "1.4.15",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1654284040,
|
||||||
|
"version": "1.4.14",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1652919697,
|
||||||
|
"version": "1.4.13",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1650611093,
|
||||||
|
"version": "1.4.12",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1648739346,
|
"timestamp": 1648739346,
|
||||||
"version": "1.4.11",
|
"version": "1.4.11",
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v1.4.16 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.4.15 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.4.14 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.4.13 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v1.4.12 - _April 22, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v1.4.11 - _March 31, 2022_
|
## v1.4.11 - _March 31, 2022_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-treasury",
|
"name": "@0x/contracts-treasury",
|
||||||
"version": "1.4.11",
|
"version": "1.4.16",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -47,12 +47,12 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.8.0",
|
"@0x/abi-gen": "^5.8.0",
|
||||||
"@0x/contract-addresses": "^6.12.1",
|
"@0x/contract-addresses": "^6.17.0",
|
||||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||||
"@0x/contracts-erc20": "^3.3.28",
|
"@0x/contracts-erc20": "^3.3.33",
|
||||||
"@0x/contracts-gen": "^2.0.46",
|
"@0x/contracts-gen": "^2.0.46",
|
||||||
"@0x/contracts-staking": "^2.0.45",
|
"@0x/contracts-staking": "^2.0.45",
|
||||||
"@0x/contracts-test-utils": "^5.4.19",
|
"@0x/contracts-test-utils": "^5.4.24",
|
||||||
"@0x/sol-compiler": "^4.8.1",
|
"@0x/sol-compiler": "^4.8.1",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
"@0x/tslint-config": "^4.1.4",
|
"@0x/tslint-config": "^4.1.4",
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.5.0",
|
"@0x/base-contract": "^6.5.0",
|
||||||
"@0x/protocol-utils": "^1.11.2",
|
"@0x/protocol-utils": "^11.16.0",
|
||||||
"@0x/subproviders": "^6.6.5",
|
"@0x/subproviders": "^6.6.5",
|
||||||
"@0x/types": "^3.3.6",
|
"@0x/types": "^3.3.6",
|
||||||
"@0x/typescript-typings": "^5.3.1",
|
"@0x/typescript-typings": "^5.3.1",
|
||||||
|
|||||||
@@ -1,4 +1,49 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1658950329,
|
||||||
|
"version": "4.8.14",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1655244958,
|
||||||
|
"version": "4.8.13",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1654284040,
|
||||||
|
"version": "4.8.12",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1652919697,
|
||||||
|
"version": "4.8.11",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1650611093,
|
||||||
|
"version": "4.8.10",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Dependencies updated"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1648739346,
|
"timestamp": 1648739346,
|
||||||
"version": "4.8.9",
|
"version": "4.8.9",
|
||||||
|
|||||||
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v4.8.14 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.8.13 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.8.12 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.8.11 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
|
## v4.8.10 - _April 22, 2022_
|
||||||
|
|
||||||
|
* Dependencies updated
|
||||||
|
|
||||||
## v4.8.9 - _March 31, 2022_
|
## v4.8.9 - _March 31, 2022_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-utils",
|
"name": "@0x/contracts-utils",
|
||||||
"version": "4.8.9",
|
"version": "4.8.14",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.8.0",
|
"@0x/abi-gen": "^5.8.0",
|
||||||
"@0x/contracts-gen": "^2.0.46",
|
"@0x/contracts-gen": "^2.0.46",
|
||||||
"@0x/contracts-test-utils": "^5.4.19",
|
"@0x/contracts-test-utils": "^5.4.24",
|
||||||
"@0x/dev-utils": "^4.2.14",
|
"@0x/dev-utils": "^4.2.14",
|
||||||
"@0x/order-utils": "^10.4.28",
|
"@0x/order-utils": "^10.4.28",
|
||||||
"@0x/sol-compiler": "^4.8.1",
|
"@0x/sol-compiler": "^4.8.1",
|
||||||
|
|||||||
@@ -1,4 +1,62 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.36.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add Synthetix support in Ethereum and Optimism bridge adapters",
|
||||||
|
"pr": 518
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1658950329
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.35.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Adds support for Velodrome OptimismBridgeAdapter",
|
||||||
|
"pr": 494
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1655244958
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.34.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Splits BridgeAdapter up by chain",
|
||||||
|
"pr": 487
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add stETH wrap/unwrap support",
|
||||||
|
"pr": 476
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Adds support for BancorV3 to EthereumBridgeAdapter",
|
||||||
|
"pr": 492
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1654284040
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.33.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add support for GMX and Platypus to bridge adapter",
|
||||||
|
"pr": 478
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1652919697
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "0.32.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add support for `BalancerV2Batch` fills in FQT",
|
||||||
|
"pr": 462
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1650611093
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1648739346,
|
"timestamp": 1648739346,
|
||||||
"version": "0.31.2",
|
"version": "0.31.2",
|
||||||
|
|||||||
@@ -5,6 +5,28 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v0.36.0 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Add Synthetix support in Ethereum and Optimism bridge adapters (#518)
|
||||||
|
|
||||||
|
## v0.35.0 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Adds support for Velodrome OptimismBridgeAdapter (#494)
|
||||||
|
|
||||||
|
## v0.34.0 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Splits BridgeAdapter up by chain (#487)
|
||||||
|
* Add stETH wrap/unwrap support (#476)
|
||||||
|
* Adds support for BancorV3 to EthereumBridgeAdapter (#492)
|
||||||
|
|
||||||
|
## v0.33.0 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Add support for GMX and Platypus to bridge adapter (#478)
|
||||||
|
|
||||||
|
## v0.32.0 - _April 22, 2022_
|
||||||
|
|
||||||
|
* Add support for `BalancerV2Batch` fills in FQT (#462)
|
||||||
|
|
||||||
## v0.31.2 - _March 31, 2022_
|
## v0.31.2 - _March 31, 2022_
|
||||||
|
|
||||||
* Dependencies updated
|
* Dependencies updated
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
import "./IBridgeAdapter.sol";
|
||||||
|
|
||||||
|
abstract contract AbstractBridgeAdapter is IBridgeAdapter {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
uint256 expectedChainId,
|
||||||
|
string memory expectedChainName
|
||||||
|
)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
uint256 chainId;
|
||||||
|
assembly { chainId := chainid() }
|
||||||
|
// Allow testing on Ganache
|
||||||
|
if (chainId != expectedChainId && chainId != 1337) {
|
||||||
|
revert(string(abi.encodePacked(expectedChainName, "BridgeAdapter.constructor: wrong chain ID")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSupportedSource(bytes32 source)
|
||||||
|
external
|
||||||
|
override
|
||||||
|
returns (bool isSupported)
|
||||||
|
{
|
||||||
|
BridgeOrder memory placeholderOrder;
|
||||||
|
placeholderOrder.source = source;
|
||||||
|
IERC20TokenV06 placeholderToken = IERC20TokenV06(address(0));
|
||||||
|
|
||||||
|
(, isSupported) = _trade(
|
||||||
|
placeholderOrder,
|
||||||
|
placeholderToken,
|
||||||
|
placeholderToken,
|
||||||
|
0,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
(boughtAmount, ) = _trade(
|
||||||
|
order,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
virtual
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource);
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinCurve.sol";
|
||||||
|
import "./mixins/MixinCurveV2.sol";
|
||||||
|
import "./mixins/MixinGMX.sol";
|
||||||
|
import "./mixins/MixinKyberDmm.sol";
|
||||||
|
import "./mixins/MixinAaveV2.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinPlatypus.sol";
|
||||||
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract AvalancheBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(43114, "Avalanche"),
|
||||||
|
MixinCurve,
|
||||||
|
MixinCurveV2,
|
||||||
|
MixinGMX,
|
||||||
|
MixinKyberDmm,
|
||||||
|
MixinAaveV2,
|
||||||
|
MixinNerve,
|
||||||
|
MixinPlatypus,
|
||||||
|
MixinUniswapV2,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
MixinCurve(weth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurve(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV2(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeKyberDmm(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeAaveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.GMX) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeGMX(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.PLATYPUS) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradePlatypus(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinCurve.sol";
|
||||||
|
import "./mixins/MixinDodo.sol";
|
||||||
|
import "./mixins/MixinDodoV2.sol";
|
||||||
|
import "./mixins/MixinKyberDmm.sol";
|
||||||
|
import "./mixins/MixinMooniswap.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract BSCBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(56, "BSC"),
|
||||||
|
MixinCurve,
|
||||||
|
MixinDodo,
|
||||||
|
MixinDodoV2,
|
||||||
|
MixinKyberDmm,
|
||||||
|
MixinMooniswap,
|
||||||
|
MixinNerve,
|
||||||
|
MixinUniswapV2,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
MixinCurve(weth)
|
||||||
|
MixinMooniswap(weth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurve(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV2(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.MOONISWAP) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeMooniswap(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.DODO) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeDodo(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeDodoV2(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeKyberDmm(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,29 +27,35 @@ library BridgeProtocols {
|
|||||||
// A incrementally increasing, append-only list of protocol IDs.
|
// 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
|
// 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.
|
// new protocol ID that hasn't been rolled up yet.
|
||||||
uint128 internal constant UNKNOWN = 0;
|
uint128 internal constant UNKNOWN = 0;
|
||||||
uint128 internal constant CURVE = 1;
|
uint128 internal constant CURVE = 1;
|
||||||
uint128 internal constant UNISWAPV2 = 2;
|
uint128 internal constant UNISWAPV2 = 2;
|
||||||
uint128 internal constant UNISWAP = 3;
|
uint128 internal constant UNISWAP = 3;
|
||||||
uint128 internal constant BALANCER = 4;
|
uint128 internal constant BALANCER = 4;
|
||||||
uint128 internal constant KYBER = 5;
|
uint128 internal constant KYBER = 5; // Not used: deprecated.
|
||||||
uint128 internal constant MOONISWAP = 6;
|
uint128 internal constant MOONISWAP = 6;
|
||||||
uint128 internal constant MSTABLE = 7;
|
uint128 internal constant MSTABLE = 7;
|
||||||
uint128 internal constant OASIS = 8;
|
uint128 internal constant OASIS = 8; // Not used: deprecated.
|
||||||
uint128 internal constant SHELL = 9;
|
uint128 internal constant SHELL = 9;
|
||||||
uint128 internal constant DODO = 10;
|
uint128 internal constant DODO = 10;
|
||||||
uint128 internal constant DODOV2 = 11;
|
uint128 internal constant DODOV2 = 11;
|
||||||
uint128 internal constant CRYPTOCOM = 12;
|
uint128 internal constant CRYPTOCOM = 12;
|
||||||
uint128 internal constant BANCOR = 13;
|
uint128 internal constant BANCOR = 13;
|
||||||
uint128 internal constant COFIX = 14;
|
uint128 internal constant COFIX = 14; // Not used: deprecated.
|
||||||
uint128 internal constant NERVE = 15;
|
uint128 internal constant NERVE = 15;
|
||||||
uint128 internal constant MAKERPSM = 16;
|
uint128 internal constant MAKERPSM = 16;
|
||||||
uint128 internal constant BALANCERV2 = 17;
|
uint128 internal constant BALANCERV2 = 17;
|
||||||
uint128 internal constant UNISWAPV3 = 18;
|
uint128 internal constant UNISWAPV3 = 18;
|
||||||
uint128 internal constant KYBERDMM = 19;
|
uint128 internal constant KYBERDMM = 19;
|
||||||
uint128 internal constant CURVEV2 = 20;
|
uint128 internal constant CURVEV2 = 20;
|
||||||
uint128 internal constant LIDO = 21;
|
uint128 internal constant LIDO = 21;
|
||||||
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
|
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
|
||||||
uint128 internal constant AAVEV2 = 23;
|
uint128 internal constant AAVEV2 = 23;
|
||||||
uint128 internal constant COMPOUND = 24;
|
uint128 internal constant COMPOUND = 24;
|
||||||
|
uint128 internal constant BALANCERV2BATCH = 25;
|
||||||
|
uint128 internal constant GMX = 26;
|
||||||
|
uint128 internal constant PLATYPUS = 27;
|
||||||
|
uint128 internal constant BANCORV3 = 28;
|
||||||
|
uint128 internal constant VELODROME = 29;
|
||||||
|
uint128 internal constant SYNTHETIX = 30;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
|
||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract CeloBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(42220, "Celo"),
|
||||||
|
MixinNerve,
|
||||||
|
MixinUniswapV2,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(address _weth)
|
||||||
|
public
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV2(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Copyright 2021 ZeroEx Intl.
|
Copyright 2022 ZeroEx Intl.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@@ -20,55 +20,53 @@
|
|||||||
pragma solidity ^0.6.5;
|
pragma solidity ^0.6.5;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./IBridgeAdapter.sol";
|
import "./AbstractBridgeAdapter.sol";
|
||||||
import "./BridgeProtocols.sol";
|
import "./BridgeProtocols.sol";
|
||||||
import "./mixins/MixinAaveV2.sol";
|
import "./mixins/MixinAaveV2.sol";
|
||||||
import "./mixins/MixinBalancer.sol";
|
import "./mixins/MixinBalancer.sol";
|
||||||
import "./mixins/MixinBalancerV2.sol";
|
import "./mixins/MixinBalancerV2.sol";
|
||||||
|
import "./mixins/MixinBalancerV2Batch.sol";
|
||||||
import "./mixins/MixinBancor.sol";
|
import "./mixins/MixinBancor.sol";
|
||||||
import "./mixins/MixinCoFiX.sol";
|
import "./mixins/MixinBancorV3.sol";
|
||||||
import "./mixins/MixinCompound.sol";
|
import "./mixins/MixinCompound.sol";
|
||||||
import "./mixins/MixinCurve.sol";
|
import "./mixins/MixinCurve.sol";
|
||||||
import "./mixins/MixinCurveV2.sol";
|
import "./mixins/MixinCurveV2.sol";
|
||||||
import "./mixins/MixinCryptoCom.sol";
|
import "./mixins/MixinCryptoCom.sol";
|
||||||
import "./mixins/MixinDodo.sol";
|
import "./mixins/MixinDodo.sol";
|
||||||
import "./mixins/MixinDodoV2.sol";
|
import "./mixins/MixinDodoV2.sol";
|
||||||
import "./mixins/MixinKyber.sol";
|
|
||||||
import "./mixins/MixinKyberDmm.sol";
|
import "./mixins/MixinKyberDmm.sol";
|
||||||
import "./mixins/MixinLido.sol";
|
import "./mixins/MixinLido.sol";
|
||||||
import "./mixins/MixinMakerPSM.sol";
|
import "./mixins/MixinMakerPSM.sol";
|
||||||
import "./mixins/MixinMooniswap.sol";
|
|
||||||
import "./mixins/MixinMStable.sol";
|
import "./mixins/MixinMStable.sol";
|
||||||
import "./mixins/MixinNerve.sol";
|
import "./mixins/MixinNerve.sol";
|
||||||
import "./mixins/MixinOasis.sol";
|
|
||||||
import "./mixins/MixinShell.sol";
|
import "./mixins/MixinShell.sol";
|
||||||
|
import "./mixins/MixinSynthetix.sol";
|
||||||
import "./mixins/MixinUniswap.sol";
|
import "./mixins/MixinUniswap.sol";
|
||||||
import "./mixins/MixinUniswapV2.sol";
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
import "./mixins/MixinUniswapV3.sol";
|
import "./mixins/MixinUniswapV3.sol";
|
||||||
import "./mixins/MixinZeroExBridge.sol";
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
contract BridgeAdapter is
|
contract EthereumBridgeAdapter is
|
||||||
IBridgeAdapter,
|
AbstractBridgeAdapter(1, "Ethereum"),
|
||||||
MixinAaveV2,
|
MixinAaveV2,
|
||||||
MixinBalancer,
|
MixinBalancer,
|
||||||
MixinBalancerV2,
|
MixinBalancerV2,
|
||||||
|
MixinBalancerV2Batch,
|
||||||
MixinBancor,
|
MixinBancor,
|
||||||
MixinCoFiX,
|
MixinBancorV3,
|
||||||
MixinCompound,
|
MixinCompound,
|
||||||
MixinCurve,
|
MixinCurve,
|
||||||
MixinCurveV2,
|
MixinCurveV2,
|
||||||
MixinCryptoCom,
|
MixinCryptoCom,
|
||||||
MixinDodo,
|
MixinDodo,
|
||||||
MixinDodoV2,
|
MixinDodoV2,
|
||||||
MixinKyber,
|
|
||||||
MixinKyberDmm,
|
MixinKyberDmm,
|
||||||
MixinLido,
|
MixinLido,
|
||||||
MixinMakerPSM,
|
MixinMakerPSM,
|
||||||
MixinMooniswap,
|
|
||||||
MixinMStable,
|
MixinMStable,
|
||||||
MixinNerve,
|
MixinNerve,
|
||||||
MixinOasis,
|
|
||||||
MixinShell,
|
MixinShell,
|
||||||
|
MixinSynthetix,
|
||||||
MixinUniswap,
|
MixinUniswap,
|
||||||
MixinUniswapV2,
|
MixinUniswapV2,
|
||||||
MixinUniswapV3,
|
MixinUniswapV3,
|
||||||
@@ -76,43 +74,28 @@ contract BridgeAdapter is
|
|||||||
{
|
{
|
||||||
constructor(IEtherTokenV06 weth)
|
constructor(IEtherTokenV06 weth)
|
||||||
public
|
public
|
||||||
MixinAaveV2()
|
|
||||||
MixinBalancer()
|
|
||||||
MixinBalancerV2()
|
|
||||||
MixinBancor(weth)
|
MixinBancor(weth)
|
||||||
MixinCoFiX()
|
MixinBancorV3(weth)
|
||||||
MixinCompound(weth)
|
MixinCompound(weth)
|
||||||
MixinCurve(weth)
|
MixinCurve(weth)
|
||||||
MixinCurveV2()
|
|
||||||
MixinCryptoCom()
|
|
||||||
MixinDodo()
|
|
||||||
MixinDodoV2()
|
|
||||||
MixinKyber(weth)
|
|
||||||
MixinLido(weth)
|
MixinLido(weth)
|
||||||
MixinMakerPSM()
|
|
||||||
MixinMooniswap(weth)
|
|
||||||
MixinMStable()
|
|
||||||
MixinNerve()
|
|
||||||
MixinOasis()
|
|
||||||
MixinShell()
|
|
||||||
MixinUniswap(weth)
|
MixinUniswap(weth)
|
||||||
MixinUniswapV2()
|
|
||||||
MixinUniswapV3()
|
|
||||||
MixinZeroExBridge()
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
function trade(
|
function _trade(
|
||||||
BridgeOrder memory order,
|
BridgeOrder memory order,
|
||||||
IERC20TokenV06 sellToken,
|
IERC20TokenV06 sellToken,
|
||||||
IERC20TokenV06 buyToken,
|
IERC20TokenV06 buyToken,
|
||||||
uint256 sellAmount
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
)
|
)
|
||||||
public
|
internal
|
||||||
override
|
override
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
{
|
{
|
||||||
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
if (protocolId == BridgeProtocols.CURVE) {
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeCurve(
|
boughtAmount = _tradeCurve(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -120,6 +103,7 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeCurveV2(
|
boughtAmount = _tradeCurveV2(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -127,18 +111,21 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeUniswapV3(
|
boughtAmount = _tradeUniswapV3(
|
||||||
sellToken,
|
sellToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeUniswapV2(
|
boughtAmount = _tradeUniswapV2(
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.UNISWAP) {
|
} else if (protocolId == BridgeProtocols.UNISWAP) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeUniswap(
|
boughtAmount = _tradeUniswap(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -146,6 +133,7 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.BALANCER) {
|
} else if (protocolId == BridgeProtocols.BALANCER) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeBalancer(
|
boughtAmount = _tradeBalancer(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -153,48 +141,37 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeBalancerV2(
|
boughtAmount = _tradeBalancerV2(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.KYBER) {
|
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
|
||||||
boughtAmount = _tradeKyber(
|
if (dryRun) { return (0, true); }
|
||||||
sellToken,
|
boughtAmount = _tradeBalancerV2Batch(
|
||||||
buyToken,
|
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.MAKERPSM) {
|
} else if (protocolId == BridgeProtocols.MAKERPSM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeMakerPsm(
|
boughtAmount = _tradeMakerPsm(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.MOONISWAP) {
|
|
||||||
boughtAmount = _tradeMooniswap(
|
|
||||||
sellToken,
|
|
||||||
buyToken,
|
|
||||||
sellAmount,
|
|
||||||
order.bridgeData
|
|
||||||
);
|
|
||||||
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeMStable(
|
boughtAmount = _tradeMStable(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.OASIS) {
|
|
||||||
boughtAmount = _tradeOasis(
|
|
||||||
sellToken,
|
|
||||||
buyToken,
|
|
||||||
sellAmount,
|
|
||||||
order.bridgeData
|
|
||||||
);
|
|
||||||
} else if (protocolId == BridgeProtocols.SHELL) {
|
} else if (protocolId == BridgeProtocols.SHELL) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeShell(
|
boughtAmount = _tradeShell(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -202,49 +179,49 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.DODO) {
|
} else if (protocolId == BridgeProtocols.DODO) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeDodo(
|
boughtAmount = _tradeDodo(
|
||||||
sellToken,
|
sellToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.DODOV2) {
|
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeDodoV2(
|
boughtAmount = _tradeDodoV2(
|
||||||
sellToken,
|
sellToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.CRYPTOCOM) {
|
} else if (protocolId == BridgeProtocols.CRYPTOCOM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeCryptoCom(
|
boughtAmount = _tradeCryptoCom(
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.BANCOR) {
|
} else if (protocolId == BridgeProtocols.BANCOR) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeBancor(
|
boughtAmount = _tradeBancor(
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.COFIX) {
|
|
||||||
boughtAmount = _tradeCoFiX(
|
|
||||||
sellToken,
|
|
||||||
buyToken,
|
|
||||||
sellAmount,
|
|
||||||
order.bridgeData
|
|
||||||
);
|
|
||||||
} else if (protocolId == BridgeProtocols.NERVE) {
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeNerve(
|
boughtAmount = _tradeNerve(
|
||||||
sellToken,
|
sellToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeKyberDmm(
|
boughtAmount = _tradeKyberDmm(
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.LIDO) {
|
} else if (protocolId == BridgeProtocols.LIDO) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeLido(
|
boughtAmount = _tradeLido(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -252,6 +229,7 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeAaveV2(
|
boughtAmount = _tradeAaveV2(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -259,13 +237,28 @@ contract BridgeAdapter is
|
|||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else if (protocolId == BridgeProtocols.COMPOUND) {
|
} else if (protocolId == BridgeProtocols.COMPOUND) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeCompound(
|
boughtAmount = _tradeCompound(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
sellAmount,
|
sellAmount,
|
||||||
order.bridgeData
|
order.bridgeData
|
||||||
);
|
);
|
||||||
} else {
|
} else if (protocolId == BridgeProtocols.BANCORV3) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeBancorV3(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeSynthetix(
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
boughtAmount = _tradeZeroExBridge(
|
boughtAmount = _tradeZeroExBridge(
|
||||||
sellToken,
|
sellToken,
|
||||||
buyToken,
|
buyToken,
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinAaveV2.sol";
|
||||||
|
import "./mixins/MixinBalancerV2.sol";
|
||||||
|
import "./mixins/MixinCurve.sol";
|
||||||
|
import "./mixins/MixinCurveV2.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract FantomBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(250, "Fantom"),
|
||||||
|
MixinAaveV2,
|
||||||
|
MixinBalancerV2,
|
||||||
|
MixinCurve,
|
||||||
|
MixinCurveV2,
|
||||||
|
MixinNerve,
|
||||||
|
MixinUniswapV2,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
MixinCurve(weth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurve(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV2(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeBalancerV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeAaveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,10 @@ interface IBridgeAdapter {
|
|||||||
uint256 outputTokenAmount
|
uint256 outputTokenAmount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function isSupportedSource(bytes32 source)
|
||||||
|
external
|
||||||
|
returns (bool isSupported);
|
||||||
|
|
||||||
function trade(
|
function trade(
|
||||||
BridgeOrder calldata order,
|
BridgeOrder calldata order,
|
||||||
IERC20TokenV06 sellToken,
|
IERC20TokenV06 sellToken,
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinCurve.sol";
|
||||||
|
import "./mixins/MixinCurveV2.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinSynthetix.sol";
|
||||||
|
import "./mixins/MixinUniswapV3.sol";
|
||||||
|
import "./mixins/MixinVelodrome.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract OptimismBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(10, "Optimism"),
|
||||||
|
MixinCurve,
|
||||||
|
MixinCurveV2,
|
||||||
|
MixinNerve,
|
||||||
|
MixinSynthetix,
|
||||||
|
MixinUniswapV3,
|
||||||
|
MixinVelodrome,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
MixinCurve(weth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurve(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV3(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.VELODROME) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeVelodrome(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.SYNTHETIX) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeSynthetix(
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./AbstractBridgeAdapter.sol";
|
||||||
|
import "./BridgeProtocols.sol";
|
||||||
|
import "./mixins/MixinAaveV2.sol";
|
||||||
|
import "./mixins/MixinBalancerV2.sol";
|
||||||
|
import "./mixins/MixinBalancerV2Batch.sol";
|
||||||
|
import "./mixins/MixinCurve.sol";
|
||||||
|
import "./mixins/MixinCurveV2.sol";
|
||||||
|
import "./mixins/MixinDodo.sol";
|
||||||
|
import "./mixins/MixinDodoV2.sol";
|
||||||
|
import "./mixins/MixinKyberDmm.sol";
|
||||||
|
import "./mixins/MixinMStable.sol";
|
||||||
|
import "./mixins/MixinNerve.sol";
|
||||||
|
import "./mixins/MixinUniswapV2.sol";
|
||||||
|
import "./mixins/MixinUniswapV3.sol";
|
||||||
|
import "./mixins/MixinZeroExBridge.sol";
|
||||||
|
|
||||||
|
contract PolygonBridgeAdapter is
|
||||||
|
AbstractBridgeAdapter(137, "Polygon"),
|
||||||
|
MixinAaveV2,
|
||||||
|
MixinBalancerV2,
|
||||||
|
MixinBalancerV2Batch,
|
||||||
|
MixinCurve,
|
||||||
|
MixinCurveV2,
|
||||||
|
MixinDodo,
|
||||||
|
MixinDodoV2,
|
||||||
|
MixinKyberDmm,
|
||||||
|
MixinMStable,
|
||||||
|
MixinNerve,
|
||||||
|
MixinUniswapV2,
|
||||||
|
MixinUniswapV3,
|
||||||
|
MixinZeroExBridge
|
||||||
|
{
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
MixinCurve(weth)
|
||||||
|
{}
|
||||||
|
|
||||||
|
function _trade(
|
||||||
|
BridgeOrder memory order,
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bool dryRun
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
override
|
||||||
|
returns (uint256 boughtAmount, bool supportedSource)
|
||||||
|
{
|
||||||
|
uint128 protocolId = uint128(uint256(order.source) >> 128);
|
||||||
|
if (protocolId == BridgeProtocols.CURVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurve(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeCurveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV3(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeUniswapV2(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.BALANCERV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeBalancerV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.BALANCERV2BATCH) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeBalancerV2Batch(
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.MSTABLE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeMStable(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.DODO) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeDodo(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.DODOV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeDodoV2(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.NERVE) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeNerve(
|
||||||
|
sellToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.KYBERDMM) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeKyberDmm(
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.AAVEV2) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeAaveV2(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
} else if (protocolId == BridgeProtocols.UNKNOWN) {
|
||||||
|
if (dryRun) { return (0, true); }
|
||||||
|
boughtAmount = _tradeZeroExBridge(
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
order.bridgeData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit BridgeFill(
|
||||||
|
order.source,
|
||||||
|
sellToken,
|
||||||
|
buyToken,
|
||||||
|
sellAmount,
|
||||||
|
boughtAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
// 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";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
BancorV3
|
||||||
|
*/
|
||||||
|
interface IBancorV3 {
|
||||||
|
/**
|
||||||
|
* @dev performs a trade by providing the source amount and returns the target amount and the associated fee
|
||||||
|
*
|
||||||
|
* requirements:
|
||||||
|
*
|
||||||
|
* - the caller must be the network contract
|
||||||
|
*/
|
||||||
|
function tradeBySourceAmount(
|
||||||
|
address sourceToken,
|
||||||
|
address targetToken,
|
||||||
|
uint256 sourceAmount,
|
||||||
|
uint256 minReturnAmount,
|
||||||
|
uint256 deadline,
|
||||||
|
address beneficiary
|
||||||
|
) external payable returns (uint256 amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinBancorV3 {
|
||||||
|
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
|
IERC20TokenV06 constant public BANCORV3_ETH_ADDRESS =
|
||||||
|
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
||||||
|
IEtherTokenV06 private immutable WETH;
|
||||||
|
|
||||||
|
constructor(IEtherTokenV06 weth)
|
||||||
|
public
|
||||||
|
{
|
||||||
|
WETH = weth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tradeBancorV3(
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (uint256 amountOut)
|
||||||
|
|
||||||
|
{
|
||||||
|
IBancorV3 router;
|
||||||
|
IERC20TokenV06[] memory path;
|
||||||
|
address[] memory _path;
|
||||||
|
uint256 payableAmount = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
(router, _path) = abi.decode(bridgeData, (IBancorV3, address[]));
|
||||||
|
// To get around `abi.decode()` not supporting interface array types.
|
||||||
|
assembly { path := _path }
|
||||||
|
}
|
||||||
|
|
||||||
|
require(path.length >= 2, "MixinBancorV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||||
|
require(
|
||||||
|
path[path.length - 1] == buyToken,
|
||||||
|
"MixinBancorV3/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||||
|
);
|
||||||
|
|
||||||
|
//swap WETH->ETH as Bancor only deals in ETH
|
||||||
|
if(_path[0] == address(WETH)) {
|
||||||
|
//withdraw the sell amount of WETH for ETH
|
||||||
|
WETH.withdraw(sellAmount);
|
||||||
|
payableAmount = sellAmount;
|
||||||
|
// set _path[0] to the ETH address if WETH is our buy token
|
||||||
|
_path[0] = address(BANCORV3_ETH_ADDRESS);
|
||||||
|
} else {
|
||||||
|
// Grant the BancorV3 router an allowance to sell the first token.
|
||||||
|
path[0].approveIfBelow(address(router), sellAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we are buying WETH we need to swap to ETH and deposit into WETH after the swap
|
||||||
|
if(_path[1] == address(WETH)){
|
||||||
|
_path[1] = address(BANCORV3_ETH_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint256 amountOut = router.tradeBySourceAmount{value: payableAmount}(
|
||||||
|
_path[0],
|
||||||
|
_path[1],
|
||||||
|
// Sell all tokens we hold.
|
||||||
|
sellAmount,
|
||||||
|
// Minimum buy amount.
|
||||||
|
1,
|
||||||
|
//deadline
|
||||||
|
block.timestamp + 1,
|
||||||
|
// address of the mixin
|
||||||
|
address(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
// if we want to return WETH deposit the ETH amount we sold
|
||||||
|
if(buyToken == WETH){
|
||||||
|
WETH.deposit{value: amountOut}();
|
||||||
|
}
|
||||||
|
|
||||||
|
return amountOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,92 +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.5;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
|
||||||
|
|
||||||
|
|
||||||
interface ICoFiXRouter {
|
|
||||||
// msg.value = fee
|
|
||||||
function swapExactTokensForETH(
|
|
||||||
address token,
|
|
||||||
uint amountIn,
|
|
||||||
uint amountOutMin,
|
|
||||||
address to,
|
|
||||||
address rewardTo,
|
|
||||||
uint deadline
|
|
||||||
) external payable returns (uint _amountIn, uint _amountOut);
|
|
||||||
|
|
||||||
// msg.value = amountIn + fee
|
|
||||||
function swapExactETHForTokens(
|
|
||||||
address token,
|
|
||||||
uint amountIn,
|
|
||||||
uint amountOutMin,
|
|
||||||
address to,
|
|
||||||
address rewardTo,
|
|
||||||
uint deadline
|
|
||||||
) external payable returns (uint _amountIn, uint _amountOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ICoFiXPair {
|
|
||||||
|
|
||||||
function swapWithExact(address outToken, address to)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (
|
|
||||||
uint amountIn,
|
|
||||||
uint amountOut,
|
|
||||||
uint oracleFeeChange,
|
|
||||||
uint256[4] memory tradeInfo
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
contract MixinCoFiX {
|
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
|
||||||
|
|
||||||
function _tradeCoFiX(
|
|
||||||
IERC20TokenV06 sellToken,
|
|
||||||
IERC20TokenV06 buyToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
bytes memory bridgeData
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
returns (uint256 boughtAmount)
|
|
||||||
{
|
|
||||||
(uint256 fee, ICoFiXPair pool) = abi.decode(bridgeData, (uint256, ICoFiXPair));
|
|
||||||
// Transfer tokens into the pool
|
|
||||||
LibERC20TokenV06.compatTransfer(
|
|
||||||
sellToken,
|
|
||||||
address(pool),
|
|
||||||
sellAmount
|
|
||||||
);
|
|
||||||
// Call the swap exact with the tokens now in the pool
|
|
||||||
// pay the NEST Oracle fee with ETH
|
|
||||||
(/* In */, boughtAmount, , ) = pool.swapWithExact{value: fee}(
|
|
||||||
address(buyToken),
|
|
||||||
address(this)
|
|
||||||
);
|
|
||||||
|
|
||||||
return boughtAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// 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";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||||
|
import "../IBridgeAdapter.sol";
|
||||||
|
|
||||||
|
/*
|
||||||
|
UniswapV2
|
||||||
|
*/
|
||||||
|
interface IGmxRouter {
|
||||||
|
|
||||||
|
// /// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path.
|
||||||
|
// /// The first element of path is the input token, the last is the output token, and any intermediate elements represent
|
||||||
|
// /// intermediate pairs to trade through (if, for example, a direct pair does not exist).
|
||||||
|
// /// @param _path An array of token addresses. path.length must be >= 2. Pools for each consecutive pair of addresses must exist and have liquidity.
|
||||||
|
// /// @param _amountIn The amount of input tokens to send.
|
||||||
|
// /// @param _minOut The minimum amount of output tokens that must be received for the transaction not to revert.
|
||||||
|
// /// @param _reciever Recipient of the output tokens.
|
||||||
|
function swap(
|
||||||
|
address[] calldata _path, uint256 _amountIn, uint256 _minOut, address _receiver
|
||||||
|
) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinGMX {
|
||||||
|
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
using LibSafeMathV06 for uint256;
|
||||||
|
|
||||||
|
function _tradeGMX(
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
address _router;
|
||||||
|
address reader;
|
||||||
|
address vault;
|
||||||
|
address[] memory _path;
|
||||||
|
IGmxRouter router;
|
||||||
|
IERC20TokenV06[] memory path;
|
||||||
|
|
||||||
|
{
|
||||||
|
//decode the bridge data
|
||||||
|
(_router, reader, vault, _path) = abi.decode(bridgeData, (address, address, address, address[]));
|
||||||
|
// To get around `abi.decode()` not supporting interface array types.
|
||||||
|
assembly { path := _path }
|
||||||
|
}
|
||||||
|
|
||||||
|
require(path.length >= 2, "MixinGMX/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||||
|
require(
|
||||||
|
path[path.length - 1] == buyToken,
|
||||||
|
"MixinGMX/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||||
|
);
|
||||||
|
|
||||||
|
//connect to the GMX router
|
||||||
|
router = IGmxRouter(_router);
|
||||||
|
|
||||||
|
// Grant the GMX router an allowance to sell the first token.
|
||||||
|
path[0].approveIfBelow(address(router), sellAmount);
|
||||||
|
|
||||||
|
//track the balance to know how much we bought
|
||||||
|
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||||
|
router.swap(
|
||||||
|
// Convert to `buyToken` along this path.
|
||||||
|
_path,
|
||||||
|
// Sell all tokens we hold.
|
||||||
|
sellAmount,
|
||||||
|
// Minimum buy amount.
|
||||||
|
0,
|
||||||
|
// Recipient is `this`.
|
||||||
|
address(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
//calculate the difference in balance from preswap->postswap to find how many tokens out
|
||||||
|
boughtAmount = buyToken.balanceOf(address(this)).safeSub(beforeBalance);
|
||||||
|
|
||||||
|
return boughtAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,124 +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.5;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
|
||||||
import "../IBridgeAdapter.sol";
|
|
||||||
|
|
||||||
interface IKyberNetworkProxy {
|
|
||||||
|
|
||||||
/// @dev Sells `sellTokenAddress` tokens for `buyTokenAddress` tokens
|
|
||||||
/// using a hint for the reserve.
|
|
||||||
/// @param sellToken Token to sell.
|
|
||||||
/// @param sellAmount Amount of tokens to sell.
|
|
||||||
/// @param buyToken Token to buy.
|
|
||||||
/// @param recipientAddress Address to send bought tokens to.
|
|
||||||
/// @param maxBuyTokenAmount A limit on the amount of tokens to buy.
|
|
||||||
/// @param minConversionRate The minimal conversion rate. If actual rate
|
|
||||||
/// is lower, trade is canceled.
|
|
||||||
/// @param walletId The wallet ID to send part of the fees
|
|
||||||
/// @param hint The hint for the selective inclusion (or exclusion) of reserves
|
|
||||||
/// @return boughtAmount Amount of tokens bought.
|
|
||||||
function tradeWithHint(
|
|
||||||
IERC20TokenV06 sellToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
IERC20TokenV06 buyToken,
|
|
||||||
address payable recipientAddress,
|
|
||||||
uint256 maxBuyTokenAmount,
|
|
||||||
uint256 minConversionRate,
|
|
||||||
address payable walletId,
|
|
||||||
bytes calldata hint
|
|
||||||
)
|
|
||||||
external
|
|
||||||
payable
|
|
||||||
returns (uint256 boughtAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
contract MixinKyber {
|
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
|
||||||
|
|
||||||
/// @dev Address indicating the trade is using ETH
|
|
||||||
IERC20TokenV06 private immutable KYBER_ETH_ADDRESS =
|
|
||||||
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
|
|
||||||
/// @dev Mainnet address of the WETH contract.
|
|
||||||
IEtherTokenV06 private immutable WETH;
|
|
||||||
|
|
||||||
constructor(IEtherTokenV06 weth)
|
|
||||||
public
|
|
||||||
{
|
|
||||||
WETH = weth;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _tradeKyber(
|
|
||||||
IERC20TokenV06 sellToken,
|
|
||||||
IERC20TokenV06 buyToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
bytes memory bridgeData
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
returns (uint256 boughtAmount)
|
|
||||||
{
|
|
||||||
(IKyberNetworkProxy kyber, bytes memory hint) =
|
|
||||||
abi.decode(bridgeData, (IKyberNetworkProxy, bytes));
|
|
||||||
|
|
||||||
uint256 payableAmount = 0;
|
|
||||||
if (sellToken != WETH) {
|
|
||||||
// If the input token is not WETH, grant an allowance to the exchange
|
|
||||||
// to spend them.
|
|
||||||
sellToken.approveIfBelow(
|
|
||||||
address(kyber),
|
|
||||||
sellAmount
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// If the input token is WETH, unwrap it and attach it to the call.
|
|
||||||
payableAmount = sellAmount;
|
|
||||||
WETH.withdraw(payableAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to sell all of this contract's input token balance through
|
|
||||||
// `KyberNetworkProxy.trade()`.
|
|
||||||
boughtAmount = kyber.tradeWithHint{ value: payableAmount }(
|
|
||||||
// Input token.
|
|
||||||
sellToken == WETH ? KYBER_ETH_ADDRESS : sellToken,
|
|
||||||
// Sell amount.
|
|
||||||
sellAmount,
|
|
||||||
// Output token.
|
|
||||||
buyToken == WETH ? KYBER_ETH_ADDRESS : buyToken,
|
|
||||||
// Transfer to this contract
|
|
||||||
address(uint160(address(this))),
|
|
||||||
// Buy as much as possible.
|
|
||||||
uint256(-1),
|
|
||||||
// Lowest minimum conversion rate
|
|
||||||
1,
|
|
||||||
// No affiliate address.
|
|
||||||
address(0),
|
|
||||||
hint
|
|
||||||
);
|
|
||||||
// If receving ETH, wrap it to WETH.
|
|
||||||
if (buyToken == WETH) {
|
|
||||||
WETH.deposit{ value: boughtAmount }();
|
|
||||||
}
|
|
||||||
return boughtAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
|||||||
|
|
||||||
|
|
||||||
/// @dev Minimal interface for minting StETH
|
/// @dev Minimal interface for minting StETH
|
||||||
interface ILido {
|
interface IStETH {
|
||||||
/// @dev Adds eth to the pool
|
/// @dev Adds eth to the pool
|
||||||
/// @param _referral optional address for referrals
|
/// @param _referral optional address for referrals
|
||||||
/// @return StETH Amount of shares generated
|
/// @return StETH Amount of shares generated
|
||||||
@@ -37,6 +37,33 @@ interface ILido {
|
|||||||
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
|
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Minimal interface for wrapping/unwrapping stETH.
|
||||||
|
interface IWstETH {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Exchanges stETH to wstETH
|
||||||
|
* @param _stETHAmount amount of stETH to wrap in exchange for wstETH
|
||||||
|
* @dev Requirements:
|
||||||
|
* - `_stETHAmount` must be non-zero
|
||||||
|
* - msg.sender must approve at least `_stETHAmount` stETH to this
|
||||||
|
* contract.
|
||||||
|
* - msg.sender must have at least `_stETHAmount` of stETH.
|
||||||
|
* User should first approve _stETHAmount to the WstETH contract
|
||||||
|
* @return Amount of wstETH user receives after wrap
|
||||||
|
*/
|
||||||
|
function wrap(uint256 _stETHAmount) external returns (uint256);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Exchanges wstETH to stETH
|
||||||
|
* @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
|
||||||
|
* @dev Requirements:
|
||||||
|
* - `_wstETHAmount` must be non-zero
|
||||||
|
* - msg.sender must have at least `_wstETHAmount` wstETH.
|
||||||
|
* @return Amount of stETH user receives after unwrap
|
||||||
|
*/
|
||||||
|
function unwrap(uint256 _wstETHAmount) external returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
contract MixinLido {
|
contract MixinLido {
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
@@ -59,12 +86,43 @@ contract MixinLido {
|
|||||||
internal
|
internal
|
||||||
returns (uint256 boughtAmount)
|
returns (uint256 boughtAmount)
|
||||||
{
|
{
|
||||||
(ILido lido) = abi.decode(bridgeData, (ILido));
|
if (address(sellToken) == address(WETH)) {
|
||||||
if (address(sellToken) == address(WETH) && address(buyToken) == address(lido)) {
|
return _tradeStETH(buyToken, sellAmount, bridgeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _tradeWstETH(sellToken, buyToken, sellAmount, bridgeData);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tradeStETH(
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
) private returns (uint256 boughtAmount) {
|
||||||
|
(IStETH stETH) = abi.decode(bridgeData, (IStETH));
|
||||||
|
if (address(buyToken) == address(stETH)) {
|
||||||
WETH.withdraw(sellAmount);
|
WETH.withdraw(sellAmount);
|
||||||
boughtAmount = lido.getPooledEthByShares(lido.submit{ value: sellAmount}(address(0)));
|
return stETH.getPooledEthByShares(stETH.submit{ value: sellAmount}(address(0)));
|
||||||
} else {
|
|
||||||
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
function _tradeWstETH(
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
|
||||||
|
) private returns(uint256 boughtAmount){
|
||||||
|
(IEtherTokenV06 stETH, IWstETH wstETH) = abi.decode(bridgeData, (IEtherTokenV06, IWstETH));
|
||||||
|
if (address(sellToken) == address(stETH) && address(buyToken) == address(wstETH) ) {
|
||||||
|
sellToken.approveIfBelow(address(wstETH), sellAmount);
|
||||||
|
return wstETH.wrap(sellAmount);
|
||||||
|
}
|
||||||
|
if (address(sellToken) == address(wstETH) && address(buyToken) == address(stETH) ) {
|
||||||
|
return wstETH.unwrap(sellAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,76 +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.5;
|
|
||||||
pragma experimental ABIEncoderV2;
|
|
||||||
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
|
||||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
|
||||||
import "../IBridgeAdapter.sol";
|
|
||||||
|
|
||||||
interface IOasis {
|
|
||||||
|
|
||||||
/// @dev Sell `sellAmount` of `sellToken` token and receive `buyToken` token.
|
|
||||||
/// @param sellToken The token being sold.
|
|
||||||
/// @param sellAmount The amount of `sellToken` token being sold.
|
|
||||||
/// @param buyToken The token being bought.
|
|
||||||
/// @param minBoughtAmount Minimum amount of `buyToken` token to buy.
|
|
||||||
/// @return boughtAmount Amount of `buyToken` bought.
|
|
||||||
function sellAllAmount(
|
|
||||||
IERC20TokenV06 sellToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
IERC20TokenV06 buyToken,
|
|
||||||
uint256 minBoughtAmount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
returns (uint256 boughtAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
contract MixinOasis {
|
|
||||||
|
|
||||||
using LibERC20TokenV06 for IERC20TokenV06;
|
|
||||||
|
|
||||||
function _tradeOasis(
|
|
||||||
IERC20TokenV06 sellToken,
|
|
||||||
IERC20TokenV06 buyToken,
|
|
||||||
uint256 sellAmount,
|
|
||||||
bytes memory bridgeData
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
returns (uint256 boughtAmount)
|
|
||||||
{
|
|
||||||
|
|
||||||
(IOasis oasis) = abi.decode(bridgeData, (IOasis));
|
|
||||||
|
|
||||||
// Grant an allowance to the exchange to spend `sellToken` token.
|
|
||||||
sellToken.approveIfBelow(
|
|
||||||
address(oasis),
|
|
||||||
sellAmount
|
|
||||||
);
|
|
||||||
// Try to sell all of this contract's `sellToken` token balance.
|
|
||||||
boughtAmount = oasis.sellAllAmount(
|
|
||||||
sellToken,
|
|
||||||
sellAmount,
|
|
||||||
buyToken,
|
|
||||||
// min fill amount
|
|
||||||
1
|
|
||||||
);
|
|
||||||
return boughtAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||||
|
|
||||||
|
|
||||||
|
interface IPlatypusRouter {
|
||||||
|
|
||||||
|
function swapTokensForTokens(
|
||||||
|
address[] calldata tokenPath,
|
||||||
|
address[] calldata poolPath,
|
||||||
|
uint256 fromAmount,
|
||||||
|
uint256 minimumToAmount,
|
||||||
|
address to,
|
||||||
|
uint256 deadline
|
||||||
|
) external returns (uint256 amountOut, uint256 haircut);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinPlatypus {
|
||||||
|
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
using LibSafeMathV06 for uint256;
|
||||||
|
|
||||||
|
function _tradePlatypus(
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
public
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
IPlatypusRouter router;
|
||||||
|
address _router;
|
||||||
|
address[] memory _pool;
|
||||||
|
IERC20TokenV06[] memory path;
|
||||||
|
address[] memory _path;
|
||||||
|
|
||||||
|
{
|
||||||
|
(_router, _pool, _path) = abi.decode(bridgeData, (address, address[], address[]));
|
||||||
|
|
||||||
|
// To get around `abi.decode()` not supporting interface array types.
|
||||||
|
assembly { path := _path }
|
||||||
|
}
|
||||||
|
|
||||||
|
//connect to the ptp router
|
||||||
|
router = IPlatypusRouter(_router);
|
||||||
|
|
||||||
|
require(path.length >= 2, "MixinPlatypus/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
|
||||||
|
require(
|
||||||
|
path[path.length - 1] == buyToken,
|
||||||
|
"MixinPlatypus/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
|
||||||
|
);
|
||||||
|
// Grant the Platypus router an allowance to sell the first token.
|
||||||
|
path[0].approveIfBelow(address(router), sellAmount);
|
||||||
|
|
||||||
|
//keep track of the previous balance to confirm amount out
|
||||||
|
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||||
|
|
||||||
|
router.swapTokensForTokens(
|
||||||
|
// Convert to `buyToken` along this path.
|
||||||
|
_path,
|
||||||
|
// pool to swap on
|
||||||
|
_pool,
|
||||||
|
// Sell all tokens we hold.
|
||||||
|
sellAmount,
|
||||||
|
// Minimum buy amount.
|
||||||
|
0,
|
||||||
|
// Recipient is `this`.
|
||||||
|
address(this),
|
||||||
|
|
||||||
|
block.timestamp + 1
|
||||||
|
);
|
||||||
|
//calculate the buy amount from the tokens we recieved
|
||||||
|
boughtAmount = buyToken.balanceOf(address(this)).safeSub(beforeBalance);
|
||||||
|
return boughtAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
interface ISynthetix {
|
||||||
|
// Ethereum Mainnet
|
||||||
|
function exchangeAtomically(
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
uint256 sourceAmount,
|
||||||
|
bytes32 destinationCurrencyKey,
|
||||||
|
bytes32 trackingCode,
|
||||||
|
uint256 minAmount
|
||||||
|
) external returns (uint256 amountReceived);
|
||||||
|
|
||||||
|
// Optimism
|
||||||
|
function exchangeWithTracking(
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
uint256 sourceAmount,
|
||||||
|
bytes32 destinationCurrencyKey,
|
||||||
|
address rewardAddress,
|
||||||
|
bytes32 trackingCode
|
||||||
|
) external returns (uint256 amountReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinSynthetix {
|
||||||
|
address private constant rewardAddress =
|
||||||
|
0x5C80239D97E1eB216b5c3D8fBa5DE5Be5d38e4C9;
|
||||||
|
bytes32 constant trackingCode =
|
||||||
|
0x3058000000000000000000000000000000000000000000000000000000000000;
|
||||||
|
|
||||||
|
function _tradeSynthetix(uint256 sellAmount, bytes memory bridgeData)
|
||||||
|
public
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
(
|
||||||
|
ISynthetix synthetix,
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
bytes32 destinationCurrencyKey
|
||||||
|
) = abi.decode(
|
||||||
|
bridgeData,
|
||||||
|
(ISynthetix, bytes32, bytes32)
|
||||||
|
);
|
||||||
|
|
||||||
|
boughtAmount = exchange(
|
||||||
|
synthetix,
|
||||||
|
sourceCurrencyKey,
|
||||||
|
destinationCurrencyKey,
|
||||||
|
sellAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exchange(
|
||||||
|
ISynthetix synthetix,
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
bytes32 destinationCurrencyKey,
|
||||||
|
uint256 sellAmount
|
||||||
|
) internal returns (uint256 boughtAmount) {
|
||||||
|
uint256 chainId;
|
||||||
|
assembly {
|
||||||
|
chainId := chainid()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chainId == 1) {
|
||||||
|
boughtAmount = synthetix.exchangeAtomically(
|
||||||
|
sourceCurrencyKey,
|
||||||
|
sellAmount,
|
||||||
|
destinationCurrencyKey,
|
||||||
|
trackingCode,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
boughtAmount = synthetix.exchangeWithTracking(
|
||||||
|
sourceCurrencyKey,
|
||||||
|
sellAmount,
|
||||||
|
destinationCurrencyKey,
|
||||||
|
rewardAddress,
|
||||||
|
trackingCode
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
// 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.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
|
||||||
|
interface IVelodromeRouter {
|
||||||
|
function swapExactTokensForTokensSimple(
|
||||||
|
uint256 amountIn,
|
||||||
|
uint256 amountOutMin,
|
||||||
|
address tokenFrom,
|
||||||
|
address tokenTo,
|
||||||
|
bool stable,
|
||||||
|
address to,
|
||||||
|
uint256 deadline
|
||||||
|
) external returns (uint256[] memory amounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract MixinVelodrome {
|
||||||
|
using LibERC20TokenV06 for IERC20TokenV06;
|
||||||
|
|
||||||
|
function _tradeVelodrome(
|
||||||
|
IERC20TokenV06 sellToken,
|
||||||
|
IERC20TokenV06 buyToken,
|
||||||
|
uint256 sellAmount,
|
||||||
|
bytes memory bridgeData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
returns (uint256 boughtAmount)
|
||||||
|
{
|
||||||
|
|
||||||
|
(IVelodromeRouter router, bool stable) = abi.decode(bridgeData, (IVelodromeRouter, bool));
|
||||||
|
sellToken.approveIfBelow(address(router), sellAmount);
|
||||||
|
|
||||||
|
boughtAmount = router.swapExactTokensForTokensSimple(
|
||||||
|
sellAmount,
|
||||||
|
0,
|
||||||
|
address(sellToken),
|
||||||
|
address(buyToken),
|
||||||
|
stable,
|
||||||
|
address(this),
|
||||||
|
block.timestamp + 1
|
||||||
|
)[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/contracts-zero-ex",
|
"name": "@0x/contracts-zero-ex",
|
||||||
"version": "0.31.2",
|
"version": "0.36.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -41,9 +41,9 @@
|
|||||||
"rollback": "node ./lib/scripts/rollback.js"
|
"rollback": "node ./lib/scripts/rollback.js"
|
||||||
},
|
},
|
||||||
"config": {
|
"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",
|
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature,AvalancheBridgeAdapter,BSCBridgeAdapter,CeloBridgeAdapter,EthereumBridgeAdapter,FantomBridgeAdapter,OptimismBridgeAdapter,PolygonBridgeAdapter",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"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/@(AbstractBridgeAdapter|AffiliateFeeTransformer|AvalancheBridgeAdapter|BSCBridgeAdapter|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeProtocols|CeloBridgeAdapter|CurveLiquidityProvider|ERC1155OrdersFeature|ERC165Feature|ERC721OrdersFeature|EthereumBridgeAdapter|FantomBridgeAdapter|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|MixinBancorV3|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinGMX|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinPlatypus|MixinShell|MixinSynthetix|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinVelodrome|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NFTOrders|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OptimismBridgeAdapter|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PolygonBridgeAdapter|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": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -56,10 +56,10 @@
|
|||||||
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.8.0",
|
"@0x/abi-gen": "^5.8.0",
|
||||||
"@0x/contract-addresses": "^6.12.1",
|
"@0x/contract-addresses": "^6.17.0",
|
||||||
"@0x/contracts-erc20": "^3.3.28",
|
"@0x/contracts-erc20": "^3.3.33",
|
||||||
"@0x/contracts-gen": "^2.0.46",
|
"@0x/contracts-gen": "^2.0.46",
|
||||||
"@0x/contracts-test-utils": "^5.4.19",
|
"@0x/contracts-test-utils": "^5.4.24",
|
||||||
"@0x/dev-utils": "^4.2.14",
|
"@0x/dev-utils": "^4.2.14",
|
||||||
"@0x/order-utils": "^10.4.28",
|
"@0x/order-utils": "^10.4.28",
|
||||||
"@0x/sol-compiler": "^4.8.1",
|
"@0x/sol-compiler": "^4.8.1",
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/base-contract": "^6.5.0",
|
"@0x/base-contract": "^6.5.0",
|
||||||
"@0x/protocol-utils": "^1.11.2",
|
"@0x/protocol-utils": "^11.16.0",
|
||||||
"@0x/subproviders": "^6.6.5",
|
"@0x/subproviders": "^6.6.5",
|
||||||
"@0x/types": "^3.3.6",
|
"@0x/types": "^3.3.6",
|
||||||
"@0x/typescript-typings": "^5.3.1",
|
"@0x/typescript-typings": "^5.3.1",
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
import * as AffiliateFeeTransformer from '../generated-artifacts/AffiliateFeeTransformer.json';
|
import * as AffiliateFeeTransformer from '../generated-artifacts/AffiliateFeeTransformer.json';
|
||||||
|
import * as AvalancheBridgeAdapter from '../generated-artifacts/AvalancheBridgeAdapter.json';
|
||||||
import * as BatchFillNativeOrdersFeature from '../generated-artifacts/BatchFillNativeOrdersFeature.json';
|
import * as BatchFillNativeOrdersFeature from '../generated-artifacts/BatchFillNativeOrdersFeature.json';
|
||||||
import * as BridgeAdapter from '../generated-artifacts/BridgeAdapter.json';
|
import * as BSCBridgeAdapter from '../generated-artifacts/BSCBridgeAdapter.json';
|
||||||
|
import * as CeloBridgeAdapter from '../generated-artifacts/CeloBridgeAdapter.json';
|
||||||
import * as CurveLiquidityProvider from '../generated-artifacts/CurveLiquidityProvider.json';
|
import * as CurveLiquidityProvider from '../generated-artifacts/CurveLiquidityProvider.json';
|
||||||
|
import * as EthereumBridgeAdapter from '../generated-artifacts/EthereumBridgeAdapter.json';
|
||||||
|
import * as FantomBridgeAdapter from '../generated-artifacts/FantomBridgeAdapter.json';
|
||||||
import * as FeeCollector from '../generated-artifacts/FeeCollector.json';
|
import * as FeeCollector from '../generated-artifacts/FeeCollector.json';
|
||||||
import * as FeeCollectorController from '../generated-artifacts/FeeCollectorController.json';
|
import * as FeeCollectorController from '../generated-artifacts/FeeCollectorController.json';
|
||||||
import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json';
|
import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json';
|
||||||
@@ -30,9 +34,11 @@ import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTrans
|
|||||||
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
|
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
|
||||||
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.json';
|
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.json';
|
||||||
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
|
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
|
||||||
|
import * as OptimismBridgeAdapter from '../generated-artifacts/OptimismBridgeAdapter.json';
|
||||||
import * as OtcOrdersFeature from '../generated-artifacts/OtcOrdersFeature.json';
|
import * as OtcOrdersFeature from '../generated-artifacts/OtcOrdersFeature.json';
|
||||||
import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
|
import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
|
||||||
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
||||||
|
import * as PolygonBridgeAdapter from '../generated-artifacts/PolygonBridgeAdapter.json';
|
||||||
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
|
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||||
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
|
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||||
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
|
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
|
||||||
@@ -58,7 +64,6 @@ export const artifacts = {
|
|||||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||||
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
|
||||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
|
||||||
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
|
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
|
||||||
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
|
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
|
||||||
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
||||||
@@ -72,4 +77,11 @@ export const artifacts = {
|
|||||||
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
|
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
|
||||||
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
|
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
|
||||||
IOtcOrdersFeature: IOtcOrdersFeature as ContractArtifact,
|
IOtcOrdersFeature: IOtcOrdersFeature as ContractArtifact,
|
||||||
|
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
|
||||||
|
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
|
||||||
|
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
|
||||||
|
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
|
||||||
|
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
|
||||||
|
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
|
||||||
|
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -35,7 +35,11 @@ export * from './bloom_filter_utils';
|
|||||||
export { GREEDY_TOKENS } from './constants';
|
export { GREEDY_TOKENS } from './constants';
|
||||||
export {
|
export {
|
||||||
AffiliateFeeTransformerContract,
|
AffiliateFeeTransformerContract,
|
||||||
BridgeAdapterContract,
|
AvalancheBridgeAdapterContract,
|
||||||
|
BSCBridgeAdapterContract,
|
||||||
|
CeloBridgeAdapterContract,
|
||||||
|
EthereumBridgeAdapterContract,
|
||||||
|
FantomBridgeAdapterContract,
|
||||||
FillQuoteTransformerContract,
|
FillQuoteTransformerContract,
|
||||||
IOwnableFeatureContract,
|
IOwnableFeatureContract,
|
||||||
IOwnableFeatureEvents,
|
IOwnableFeatureEvents,
|
||||||
@@ -45,7 +49,9 @@ export {
|
|||||||
IZeroExContract,
|
IZeroExContract,
|
||||||
LogMetadataTransformerContract,
|
LogMetadataTransformerContract,
|
||||||
MultiplexFeatureContract,
|
MultiplexFeatureContract,
|
||||||
|
OptimismBridgeAdapterContract,
|
||||||
PayTakerTransformerContract,
|
PayTakerTransformerContract,
|
||||||
|
PolygonBridgeAdapterContract,
|
||||||
PositiveSlippageFeeTransformerContract,
|
PositiveSlippageFeeTransformerContract,
|
||||||
TransformERC20FeatureContract,
|
TransformERC20FeatureContract,
|
||||||
WethTransformerContract,
|
WethTransformerContract,
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
export * from '../generated-wrappers/affiliate_fee_transformer';
|
export * from '../generated-wrappers/affiliate_fee_transformer';
|
||||||
|
export * from '../generated-wrappers/avalanche_bridge_adapter';
|
||||||
|
export * from '../generated-wrappers/b_s_c_bridge_adapter';
|
||||||
export * from '../generated-wrappers/batch_fill_native_orders_feature';
|
export * from '../generated-wrappers/batch_fill_native_orders_feature';
|
||||||
export * from '../generated-wrappers/bridge_adapter';
|
export * from '../generated-wrappers/celo_bridge_adapter';
|
||||||
export * from '../generated-wrappers/curve_liquidity_provider';
|
export * from '../generated-wrappers/curve_liquidity_provider';
|
||||||
|
export * from '../generated-wrappers/ethereum_bridge_adapter';
|
||||||
|
export * from '../generated-wrappers/fantom_bridge_adapter';
|
||||||
export * from '../generated-wrappers/fee_collector';
|
export * from '../generated-wrappers/fee_collector';
|
||||||
export * from '../generated-wrappers/fee_collector_controller';
|
export * from '../generated-wrappers/fee_collector_controller';
|
||||||
export * from '../generated-wrappers/fill_quote_transformer';
|
export * from '../generated-wrappers/fill_quote_transformer';
|
||||||
@@ -28,9 +32,11 @@ export * from '../generated-wrappers/log_metadata_transformer';
|
|||||||
export * from '../generated-wrappers/meta_transactions_feature';
|
export * from '../generated-wrappers/meta_transactions_feature';
|
||||||
export * from '../generated-wrappers/multiplex_feature';
|
export * from '../generated-wrappers/multiplex_feature';
|
||||||
export * from '../generated-wrappers/native_orders_feature';
|
export * from '../generated-wrappers/native_orders_feature';
|
||||||
|
export * from '../generated-wrappers/optimism_bridge_adapter';
|
||||||
export * from '../generated-wrappers/otc_orders_feature';
|
export * from '../generated-wrappers/otc_orders_feature';
|
||||||
export * from '../generated-wrappers/ownable_feature';
|
export * from '../generated-wrappers/ownable_feature';
|
||||||
export * from '../generated-wrappers/pay_taker_transformer';
|
export * from '../generated-wrappers/pay_taker_transformer';
|
||||||
|
export * from '../generated-wrappers/polygon_bridge_adapter';
|
||||||
export * from '../generated-wrappers/positive_slippage_fee_transformer';
|
export * from '../generated-wrappers/positive_slippage_fee_transformer';
|
||||||
export * from '../generated-wrappers/simple_function_registry_feature';
|
export * from '../generated-wrappers/simple_function_registry_feature';
|
||||||
export * from '../generated-wrappers/transform_erc20_feature';
|
export * from '../generated-wrappers/transform_erc20_feature';
|
||||||
|
|||||||
@@ -5,15 +5,20 @@
|
|||||||
*/
|
*/
|
||||||
import { ContractArtifact } from 'ethereum-types';
|
import { ContractArtifact } from 'ethereum-types';
|
||||||
|
|
||||||
|
import * as AbstractBridgeAdapter from '../test/generated-artifacts/AbstractBridgeAdapter.json';
|
||||||
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
import * as AffiliateFeeTransformer from '../test/generated-artifacts/AffiliateFeeTransformer.json';
|
||||||
|
import * as AvalancheBridgeAdapter from '../test/generated-artifacts/AvalancheBridgeAdapter.json';
|
||||||
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json';
|
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.json';
|
||||||
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
|
||||||
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
|
|
||||||
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
|
import * as BridgeProtocols from '../test/generated-artifacts/BridgeProtocols.json';
|
||||||
|
import * as BSCBridgeAdapter from '../test/generated-artifacts/BSCBridgeAdapter.json';
|
||||||
|
import * as CeloBridgeAdapter from '../test/generated-artifacts/CeloBridgeAdapter.json';
|
||||||
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
|
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
|
||||||
import * as ERC1155OrdersFeature from '../test/generated-artifacts/ERC1155OrdersFeature.json';
|
import * as ERC1155OrdersFeature from '../test/generated-artifacts/ERC1155OrdersFeature.json';
|
||||||
import * as ERC165Feature from '../test/generated-artifacts/ERC165Feature.json';
|
import * as ERC165Feature from '../test/generated-artifacts/ERC165Feature.json';
|
||||||
import * as ERC721OrdersFeature from '../test/generated-artifacts/ERC721OrdersFeature.json';
|
import * as ERC721OrdersFeature from '../test/generated-artifacts/ERC721OrdersFeature.json';
|
||||||
|
import * as EthereumBridgeAdapter from '../test/generated-artifacts/EthereumBridgeAdapter.json';
|
||||||
|
import * as FantomBridgeAdapter from '../test/generated-artifacts/FantomBridgeAdapter.json';
|
||||||
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
|
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
|
||||||
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
|
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
|
||||||
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
||||||
@@ -101,26 +106,29 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
|
|||||||
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
|
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
|
||||||
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
|
||||||
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.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 MixinBancor from '../test/generated-artifacts/MixinBancor.json';
|
||||||
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
|
import * as MixinBancorV3 from '../test/generated-artifacts/MixinBancorV3.json';
|
||||||
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
|
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
|
||||||
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
|
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
|
||||||
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
|
||||||
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
|
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
|
||||||
import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json';
|
import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json';
|
||||||
import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
|
import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
|
||||||
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
|
import * as MixinGMX from '../test/generated-artifacts/MixinGMX.json';
|
||||||
import * as MixinKyberDmm from '../test/generated-artifacts/MixinKyberDmm.json';
|
import * as MixinKyberDmm from '../test/generated-artifacts/MixinKyberDmm.json';
|
||||||
import * as MixinLido from '../test/generated-artifacts/MixinLido.json';
|
import * as MixinLido from '../test/generated-artifacts/MixinLido.json';
|
||||||
import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json';
|
import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json';
|
||||||
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
|
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
|
||||||
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
||||||
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
|
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
|
||||||
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
|
import * as MixinPlatypus from '../test/generated-artifacts/MixinPlatypus.json';
|
||||||
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
|
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
|
||||||
|
import * as MixinSynthetix from '../test/generated-artifacts/MixinSynthetix.json';
|
||||||
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
|
||||||
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.json';
|
||||||
import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json';
|
import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json';
|
||||||
|
import * as MixinVelodrome from '../test/generated-artifacts/MixinVelodrome.json';
|
||||||
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
||||||
import * as MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json';
|
import * as MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json';
|
||||||
import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json';
|
import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json';
|
||||||
@@ -136,11 +144,13 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.
|
|||||||
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
|
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
|
||||||
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json';
|
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.json';
|
||||||
import * as NFTOrders from '../test/generated-artifacts/NFTOrders.json';
|
import * as NFTOrders from '../test/generated-artifacts/NFTOrders.json';
|
||||||
|
import * as OptimismBridgeAdapter from '../test/generated-artifacts/OptimismBridgeAdapter.json';
|
||||||
import * as OtcOrdersFeature from '../test/generated-artifacts/OtcOrdersFeature.json';
|
import * as OtcOrdersFeature from '../test/generated-artifacts/OtcOrdersFeature.json';
|
||||||
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
|
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
|
||||||
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
|
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
|
||||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||||
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
|
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
|
||||||
|
import * as PolygonBridgeAdapter from '../test/generated-artifacts/PolygonBridgeAdapter.json';
|
||||||
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
|
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
|
||||||
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
|
||||||
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
|
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
|
||||||
@@ -307,32 +317,42 @@ export const artifacts = {
|
|||||||
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
|
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
|
||||||
Transformer: Transformer as ContractArtifact,
|
Transformer: Transformer as ContractArtifact,
|
||||||
WethTransformer: WethTransformer as ContractArtifact,
|
WethTransformer: WethTransformer as ContractArtifact,
|
||||||
BridgeAdapter: BridgeAdapter as ContractArtifact,
|
AbstractBridgeAdapter: AbstractBridgeAdapter as ContractArtifact,
|
||||||
|
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
|
||||||
|
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
|
||||||
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
BridgeProtocols: BridgeProtocols as ContractArtifact,
|
||||||
|
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
|
||||||
|
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
|
||||||
|
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
|
||||||
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
|
||||||
|
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
|
||||||
|
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
|
||||||
MixinAaveV2: MixinAaveV2 as ContractArtifact,
|
MixinAaveV2: MixinAaveV2 as ContractArtifact,
|
||||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||||
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
||||||
|
MixinBalancerV2Batch: MixinBalancerV2Batch as ContractArtifact,
|
||||||
MixinBancor: MixinBancor as ContractArtifact,
|
MixinBancor: MixinBancor as ContractArtifact,
|
||||||
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
MixinBancorV3: MixinBancorV3 as ContractArtifact,
|
||||||
MixinCompound: MixinCompound as ContractArtifact,
|
MixinCompound: MixinCompound as ContractArtifact,
|
||||||
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
||||||
MixinCurve: MixinCurve as ContractArtifact,
|
MixinCurve: MixinCurve as ContractArtifact,
|
||||||
MixinCurveV2: MixinCurveV2 as ContractArtifact,
|
MixinCurveV2: MixinCurveV2 as ContractArtifact,
|
||||||
MixinDodo: MixinDodo as ContractArtifact,
|
MixinDodo: MixinDodo as ContractArtifact,
|
||||||
MixinDodoV2: MixinDodoV2 as ContractArtifact,
|
MixinDodoV2: MixinDodoV2 as ContractArtifact,
|
||||||
MixinKyber: MixinKyber as ContractArtifact,
|
MixinGMX: MixinGMX as ContractArtifact,
|
||||||
MixinKyberDmm: MixinKyberDmm as ContractArtifact,
|
MixinKyberDmm: MixinKyberDmm as ContractArtifact,
|
||||||
MixinLido: MixinLido as ContractArtifact,
|
MixinLido: MixinLido as ContractArtifact,
|
||||||
MixinMStable: MixinMStable as ContractArtifact,
|
MixinMStable: MixinMStable as ContractArtifact,
|
||||||
MixinMakerPSM: MixinMakerPSM as ContractArtifact,
|
MixinMakerPSM: MixinMakerPSM as ContractArtifact,
|
||||||
MixinMooniswap: MixinMooniswap as ContractArtifact,
|
MixinMooniswap: MixinMooniswap as ContractArtifact,
|
||||||
MixinNerve: MixinNerve as ContractArtifact,
|
MixinNerve: MixinNerve as ContractArtifact,
|
||||||
MixinOasis: MixinOasis as ContractArtifact,
|
MixinPlatypus: MixinPlatypus as ContractArtifact,
|
||||||
MixinShell: MixinShell as ContractArtifact,
|
MixinShell: MixinShell as ContractArtifact,
|
||||||
|
MixinSynthetix: MixinSynthetix as ContractArtifact,
|
||||||
MixinUniswap: MixinUniswap as ContractArtifact,
|
MixinUniswap: MixinUniswap as ContractArtifact,
|
||||||
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
|
||||||
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
|
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
|
||||||
|
MixinVelodrome: MixinVelodrome as ContractArtifact,
|
||||||
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
|
||||||
IERC1155Token: IERC1155Token as ContractArtifact,
|
IERC1155Token: IERC1155Token as ContractArtifact,
|
||||||
IERC721Token: IERC721Token as ContractArtifact,
|
IERC721Token: IERC721Token as ContractArtifact,
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import { artifacts } from '../artifacts';
|
|||||||
import { TestFillQuoteTransformerBridgeContract } from '../generated-wrappers/test_fill_quote_transformer_bridge';
|
import { TestFillQuoteTransformerBridgeContract } from '../generated-wrappers/test_fill_quote_transformer_bridge';
|
||||||
import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
|
import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
|
||||||
import {
|
import {
|
||||||
BridgeAdapterContract,
|
EthereumBridgeAdapterContract,
|
||||||
FillQuoteTransformerContract,
|
FillQuoteTransformerContract,
|
||||||
TestFillQuoteTransformerExchangeContract,
|
TestFillQuoteTransformerExchangeContract,
|
||||||
TestFillQuoteTransformerHostContract,
|
TestFillQuoteTransformerHostContract,
|
||||||
@@ -52,7 +52,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
let singleProtocolFee: BigNumber;
|
let singleProtocolFee: BigNumber;
|
||||||
|
|
||||||
const GAS_PRICE = 1337;
|
const GAS_PRICE = 1337;
|
||||||
const TEST_BRIDGE_SOURCE = hexUtils.random(32);
|
// Left half is 0, corresponding to BridgeProtocol.Unknown
|
||||||
|
const TEST_BRIDGE_SOURCE = hexUtils.leftPad(hexUtils.random(16), 32);
|
||||||
const HIGH_BIT = new BigNumber(2).pow(255);
|
const HIGH_BIT = new BigNumber(2).pow(255);
|
||||||
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');
|
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');
|
||||||
|
|
||||||
@@ -64,8 +65,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
|||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
);
|
);
|
||||||
const bridgeAdapter = await BridgeAdapterContract.deployFrom0xArtifactAsync(
|
const bridgeAdapter = await EthereumBridgeAdapterContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.BridgeAdapter,
|
artifacts.EthereumBridgeAdapter,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
|
|||||||
@@ -3,15 +3,20 @@
|
|||||||
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
|
||||||
* -----------------------------------------------------------------------------
|
* -----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
export * from '../test/generated-wrappers/abstract_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
export * from '../test/generated-wrappers/affiliate_fee_transformer';
|
||||||
|
export * from '../test/generated-wrappers/avalanche_bridge_adapter';
|
||||||
|
export * from '../test/generated-wrappers/b_s_c_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/batch_fill_native_orders_feature';
|
export * from '../test/generated-wrappers/batch_fill_native_orders_feature';
|
||||||
export * from '../test/generated-wrappers/bootstrap_feature';
|
export * from '../test/generated-wrappers/bootstrap_feature';
|
||||||
export * from '../test/generated-wrappers/bridge_adapter';
|
|
||||||
export * from '../test/generated-wrappers/bridge_protocols';
|
export * from '../test/generated-wrappers/bridge_protocols';
|
||||||
|
export * from '../test/generated-wrappers/celo_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/curve_liquidity_provider';
|
export * from '../test/generated-wrappers/curve_liquidity_provider';
|
||||||
export * from '../test/generated-wrappers/erc1155_orders_feature';
|
export * from '../test/generated-wrappers/erc1155_orders_feature';
|
||||||
export * from '../test/generated-wrappers/erc165_feature';
|
export * from '../test/generated-wrappers/erc165_feature';
|
||||||
export * from '../test/generated-wrappers/erc721_orders_feature';
|
export * from '../test/generated-wrappers/erc721_orders_feature';
|
||||||
|
export * from '../test/generated-wrappers/ethereum_bridge_adapter';
|
||||||
|
export * from '../test/generated-wrappers/fantom_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/fee_collector';
|
export * from '../test/generated-wrappers/fee_collector';
|
||||||
export * from '../test/generated-wrappers/fee_collector_controller';
|
export * from '../test/generated-wrappers/fee_collector_controller';
|
||||||
export * from '../test/generated-wrappers/fill_quote_transformer';
|
export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||||
@@ -99,26 +104,29 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
|
|||||||
export * from '../test/generated-wrappers/mixin_aave_v2';
|
export * from '../test/generated-wrappers/mixin_aave_v2';
|
||||||
export * from '../test/generated-wrappers/mixin_balancer';
|
export * from '../test/generated-wrappers/mixin_balancer';
|
||||||
export * from '../test/generated-wrappers/mixin_balancer_v2';
|
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_bancor';
|
||||||
export * from '../test/generated-wrappers/mixin_co_fi_x';
|
export * from '../test/generated-wrappers/mixin_bancor_v3';
|
||||||
export * from '../test/generated-wrappers/mixin_compound';
|
export * from '../test/generated-wrappers/mixin_compound';
|
||||||
export * from '../test/generated-wrappers/mixin_crypto_com';
|
export * from '../test/generated-wrappers/mixin_crypto_com';
|
||||||
export * from '../test/generated-wrappers/mixin_curve';
|
export * from '../test/generated-wrappers/mixin_curve';
|
||||||
export * from '../test/generated-wrappers/mixin_curve_v2';
|
export * from '../test/generated-wrappers/mixin_curve_v2';
|
||||||
export * from '../test/generated-wrappers/mixin_dodo';
|
export * from '../test/generated-wrappers/mixin_dodo';
|
||||||
export * from '../test/generated-wrappers/mixin_dodo_v2';
|
export * from '../test/generated-wrappers/mixin_dodo_v2';
|
||||||
export * from '../test/generated-wrappers/mixin_kyber';
|
export * from '../test/generated-wrappers/mixin_g_m_x';
|
||||||
export * from '../test/generated-wrappers/mixin_kyber_dmm';
|
export * from '../test/generated-wrappers/mixin_kyber_dmm';
|
||||||
export * from '../test/generated-wrappers/mixin_lido';
|
export * from '../test/generated-wrappers/mixin_lido';
|
||||||
export * from '../test/generated-wrappers/mixin_m_stable';
|
export * from '../test/generated-wrappers/mixin_m_stable';
|
||||||
export * from '../test/generated-wrappers/mixin_maker_p_s_m';
|
export * from '../test/generated-wrappers/mixin_maker_p_s_m';
|
||||||
export * from '../test/generated-wrappers/mixin_mooniswap';
|
export * from '../test/generated-wrappers/mixin_mooniswap';
|
||||||
export * from '../test/generated-wrappers/mixin_nerve';
|
export * from '../test/generated-wrappers/mixin_nerve';
|
||||||
export * from '../test/generated-wrappers/mixin_oasis';
|
export * from '../test/generated-wrappers/mixin_platypus';
|
||||||
export * from '../test/generated-wrappers/mixin_shell';
|
export * from '../test/generated-wrappers/mixin_shell';
|
||||||
|
export * from '../test/generated-wrappers/mixin_synthetix';
|
||||||
export * from '../test/generated-wrappers/mixin_uniswap';
|
export * from '../test/generated-wrappers/mixin_uniswap';
|
||||||
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
export * from '../test/generated-wrappers/mixin_uniswap_v2';
|
||||||
export * from '../test/generated-wrappers/mixin_uniswap_v3';
|
export * from '../test/generated-wrappers/mixin_uniswap_v3';
|
||||||
|
export * from '../test/generated-wrappers/mixin_velodrome';
|
||||||
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
export * from '../test/generated-wrappers/mixin_zero_ex_bridge';
|
||||||
export * from '../test/generated-wrappers/mooniswap_liquidity_provider';
|
export * from '../test/generated-wrappers/mooniswap_liquidity_provider';
|
||||||
export * from '../test/generated-wrappers/multiplex_feature';
|
export * from '../test/generated-wrappers/multiplex_feature';
|
||||||
@@ -134,11 +142,13 @@ export * from '../test/generated-wrappers/native_orders_feature';
|
|||||||
export * from '../test/generated-wrappers/native_orders_info';
|
export * from '../test/generated-wrappers/native_orders_info';
|
||||||
export * from '../test/generated-wrappers/native_orders_protocol_fees';
|
export * from '../test/generated-wrappers/native_orders_protocol_fees';
|
||||||
export * from '../test/generated-wrappers/native_orders_settlement';
|
export * from '../test/generated-wrappers/native_orders_settlement';
|
||||||
|
export * from '../test/generated-wrappers/optimism_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/otc_orders_feature';
|
export * from '../test/generated-wrappers/otc_orders_feature';
|
||||||
export * from '../test/generated-wrappers/ownable_feature';
|
export * from '../test/generated-wrappers/ownable_feature';
|
||||||
export * from '../test/generated-wrappers/pancake_swap_feature';
|
export * from '../test/generated-wrappers/pancake_swap_feature';
|
||||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||||
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
|
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
|
||||||
|
export * from '../test/generated-wrappers/polygon_bridge_adapter';
|
||||||
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
|
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
|
||||||
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
export * from '../test/generated-wrappers/simple_function_registry_feature';
|
||||||
export * from '../test/generated-wrappers/test_bridge';
|
export * from '../test/generated-wrappers/test_bridge';
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*", "./scripts/**/*"],
|
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*", "./scripts/**/*"],
|
||||||
"files": [
|
"files": [
|
||||||
"generated-artifacts/AffiliateFeeTransformer.json",
|
"generated-artifacts/AffiliateFeeTransformer.json",
|
||||||
|
"generated-artifacts/AvalancheBridgeAdapter.json",
|
||||||
|
"generated-artifacts/BSCBridgeAdapter.json",
|
||||||
"generated-artifacts/BatchFillNativeOrdersFeature.json",
|
"generated-artifacts/BatchFillNativeOrdersFeature.json",
|
||||||
"generated-artifacts/BridgeAdapter.json",
|
"generated-artifacts/CeloBridgeAdapter.json",
|
||||||
"generated-artifacts/CurveLiquidityProvider.json",
|
"generated-artifacts/CurveLiquidityProvider.json",
|
||||||
|
"generated-artifacts/EthereumBridgeAdapter.json",
|
||||||
|
"generated-artifacts/FantomBridgeAdapter.json",
|
||||||
"generated-artifacts/FeeCollector.json",
|
"generated-artifacts/FeeCollector.json",
|
||||||
"generated-artifacts/FeeCollectorController.json",
|
"generated-artifacts/FeeCollectorController.json",
|
||||||
"generated-artifacts/FillQuoteTransformer.json",
|
"generated-artifacts/FillQuoteTransformer.json",
|
||||||
@@ -28,23 +32,30 @@
|
|||||||
"generated-artifacts/MetaTransactionsFeature.json",
|
"generated-artifacts/MetaTransactionsFeature.json",
|
||||||
"generated-artifacts/MultiplexFeature.json",
|
"generated-artifacts/MultiplexFeature.json",
|
||||||
"generated-artifacts/NativeOrdersFeature.json",
|
"generated-artifacts/NativeOrdersFeature.json",
|
||||||
|
"generated-artifacts/OptimismBridgeAdapter.json",
|
||||||
"generated-artifacts/OtcOrdersFeature.json",
|
"generated-artifacts/OtcOrdersFeature.json",
|
||||||
"generated-artifacts/OwnableFeature.json",
|
"generated-artifacts/OwnableFeature.json",
|
||||||
"generated-artifacts/PayTakerTransformer.json",
|
"generated-artifacts/PayTakerTransformer.json",
|
||||||
|
"generated-artifacts/PolygonBridgeAdapter.json",
|
||||||
"generated-artifacts/PositiveSlippageFeeTransformer.json",
|
"generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||||
"generated-artifacts/SimpleFunctionRegistryFeature.json",
|
"generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||||
"generated-artifacts/TransformERC20Feature.json",
|
"generated-artifacts/TransformERC20Feature.json",
|
||||||
"generated-artifacts/WethTransformer.json",
|
"generated-artifacts/WethTransformer.json",
|
||||||
"generated-artifacts/ZeroEx.json",
|
"generated-artifacts/ZeroEx.json",
|
||||||
|
"test/generated-artifacts/AbstractBridgeAdapter.json",
|
||||||
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
"test/generated-artifacts/AffiliateFeeTransformer.json",
|
||||||
|
"test/generated-artifacts/AvalancheBridgeAdapter.json",
|
||||||
|
"test/generated-artifacts/BSCBridgeAdapter.json",
|
||||||
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
|
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
|
||||||
"test/generated-artifacts/BootstrapFeature.json",
|
"test/generated-artifacts/BootstrapFeature.json",
|
||||||
"test/generated-artifacts/BridgeAdapter.json",
|
|
||||||
"test/generated-artifacts/BridgeProtocols.json",
|
"test/generated-artifacts/BridgeProtocols.json",
|
||||||
|
"test/generated-artifacts/CeloBridgeAdapter.json",
|
||||||
"test/generated-artifacts/CurveLiquidityProvider.json",
|
"test/generated-artifacts/CurveLiquidityProvider.json",
|
||||||
"test/generated-artifacts/ERC1155OrdersFeature.json",
|
"test/generated-artifacts/ERC1155OrdersFeature.json",
|
||||||
"test/generated-artifacts/ERC165Feature.json",
|
"test/generated-artifacts/ERC165Feature.json",
|
||||||
"test/generated-artifacts/ERC721OrdersFeature.json",
|
"test/generated-artifacts/ERC721OrdersFeature.json",
|
||||||
|
"test/generated-artifacts/EthereumBridgeAdapter.json",
|
||||||
|
"test/generated-artifacts/FantomBridgeAdapter.json",
|
||||||
"test/generated-artifacts/FeeCollector.json",
|
"test/generated-artifacts/FeeCollector.json",
|
||||||
"test/generated-artifacts/FeeCollectorController.json",
|
"test/generated-artifacts/FeeCollectorController.json",
|
||||||
"test/generated-artifacts/FillQuoteTransformer.json",
|
"test/generated-artifacts/FillQuoteTransformer.json",
|
||||||
@@ -132,26 +143,29 @@
|
|||||||
"test/generated-artifacts/MixinAaveV2.json",
|
"test/generated-artifacts/MixinAaveV2.json",
|
||||||
"test/generated-artifacts/MixinBalancer.json",
|
"test/generated-artifacts/MixinBalancer.json",
|
||||||
"test/generated-artifacts/MixinBalancerV2.json",
|
"test/generated-artifacts/MixinBalancerV2.json",
|
||||||
|
"test/generated-artifacts/MixinBalancerV2Batch.json",
|
||||||
"test/generated-artifacts/MixinBancor.json",
|
"test/generated-artifacts/MixinBancor.json",
|
||||||
"test/generated-artifacts/MixinCoFiX.json",
|
"test/generated-artifacts/MixinBancorV3.json",
|
||||||
"test/generated-artifacts/MixinCompound.json",
|
"test/generated-artifacts/MixinCompound.json",
|
||||||
"test/generated-artifacts/MixinCryptoCom.json",
|
"test/generated-artifacts/MixinCryptoCom.json",
|
||||||
"test/generated-artifacts/MixinCurve.json",
|
"test/generated-artifacts/MixinCurve.json",
|
||||||
"test/generated-artifacts/MixinCurveV2.json",
|
"test/generated-artifacts/MixinCurveV2.json",
|
||||||
"test/generated-artifacts/MixinDodo.json",
|
"test/generated-artifacts/MixinDodo.json",
|
||||||
"test/generated-artifacts/MixinDodoV2.json",
|
"test/generated-artifacts/MixinDodoV2.json",
|
||||||
"test/generated-artifacts/MixinKyber.json",
|
"test/generated-artifacts/MixinGMX.json",
|
||||||
"test/generated-artifacts/MixinKyberDmm.json",
|
"test/generated-artifacts/MixinKyberDmm.json",
|
||||||
"test/generated-artifacts/MixinLido.json",
|
"test/generated-artifacts/MixinLido.json",
|
||||||
"test/generated-artifacts/MixinMStable.json",
|
"test/generated-artifacts/MixinMStable.json",
|
||||||
"test/generated-artifacts/MixinMakerPSM.json",
|
"test/generated-artifacts/MixinMakerPSM.json",
|
||||||
"test/generated-artifacts/MixinMooniswap.json",
|
"test/generated-artifacts/MixinMooniswap.json",
|
||||||
"test/generated-artifacts/MixinNerve.json",
|
"test/generated-artifacts/MixinNerve.json",
|
||||||
"test/generated-artifacts/MixinOasis.json",
|
"test/generated-artifacts/MixinPlatypus.json",
|
||||||
"test/generated-artifacts/MixinShell.json",
|
"test/generated-artifacts/MixinShell.json",
|
||||||
|
"test/generated-artifacts/MixinSynthetix.json",
|
||||||
"test/generated-artifacts/MixinUniswap.json",
|
"test/generated-artifacts/MixinUniswap.json",
|
||||||
"test/generated-artifacts/MixinUniswapV2.json",
|
"test/generated-artifacts/MixinUniswapV2.json",
|
||||||
"test/generated-artifacts/MixinUniswapV3.json",
|
"test/generated-artifacts/MixinUniswapV3.json",
|
||||||
|
"test/generated-artifacts/MixinVelodrome.json",
|
||||||
"test/generated-artifacts/MixinZeroExBridge.json",
|
"test/generated-artifacts/MixinZeroExBridge.json",
|
||||||
"test/generated-artifacts/MooniswapLiquidityProvider.json",
|
"test/generated-artifacts/MooniswapLiquidityProvider.json",
|
||||||
"test/generated-artifacts/MultiplexFeature.json",
|
"test/generated-artifacts/MultiplexFeature.json",
|
||||||
@@ -167,11 +181,13 @@
|
|||||||
"test/generated-artifacts/NativeOrdersInfo.json",
|
"test/generated-artifacts/NativeOrdersInfo.json",
|
||||||
"test/generated-artifacts/NativeOrdersProtocolFees.json",
|
"test/generated-artifacts/NativeOrdersProtocolFees.json",
|
||||||
"test/generated-artifacts/NativeOrdersSettlement.json",
|
"test/generated-artifacts/NativeOrdersSettlement.json",
|
||||||
|
"test/generated-artifacts/OptimismBridgeAdapter.json",
|
||||||
"test/generated-artifacts/OtcOrdersFeature.json",
|
"test/generated-artifacts/OtcOrdersFeature.json",
|
||||||
"test/generated-artifacts/OwnableFeature.json",
|
"test/generated-artifacts/OwnableFeature.json",
|
||||||
"test/generated-artifacts/PancakeSwapFeature.json",
|
"test/generated-artifacts/PancakeSwapFeature.json",
|
||||||
"test/generated-artifacts/PayTakerTransformer.json",
|
"test/generated-artifacts/PayTakerTransformer.json",
|
||||||
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
|
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
|
||||||
|
"test/generated-artifacts/PolygonBridgeAdapter.json",
|
||||||
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
|
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
|
||||||
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
|
||||||
"test/generated-artifacts/TestBridge.json",
|
"test/generated-artifacts/TestBridge.json",
|
||||||
|
|||||||
@@ -52,10 +52,10 @@
|
|||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
|
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
|
||||||
"nonContractPackages": "@0x/migrations @0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
|
"nonContractPackages": "@0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
|
||||||
"ignoreTestsForPackages": "",
|
"ignoreTestsForPackages": "",
|
||||||
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
|
||||||
"packagesWithDocPages": "@0x/contract-wrappers @0x/migrations",
|
"packagesWithDocPages": "@0x/contract-wrappers",
|
||||||
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
"ignoreDependencyVersions": "@types/styled-components @types/node",
|
||||||
"ignoreDependencyVersionsForPackage": "contract-wrappers"
|
"ignoreDependencyVersionsForPackage": "contract-wrappers"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,233 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "16.65.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Use 0x gas api instead of eth gas station api",
|
||||||
|
"pr": 532
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1659391840
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.64.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Refactor `TokenAdjacency` and `TokenAdjacencyBuilder`",
|
||||||
|
"pr": 517
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add Synthetix support`",
|
||||||
|
"pr": 518
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Replace Beethoven X subgraph URL",
|
||||||
|
"pr": 519
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Remove Mooniswap on Ethereum mainnet",
|
||||||
|
"pr": 529
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1658950329
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.63.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Better error handling for balancer cache",
|
||||||
|
"pr": 515
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1657661207
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.63.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Remove JS router",
|
||||||
|
"pr": 480
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Removed Median price in favour of best gas adjusted price",
|
||||||
|
"pr": 480
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1656491792
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.62.2",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Offboard Smoothy and ComethSwap",
|
||||||
|
"pr": 509
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.62.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Remove nUSD from intermediate liquidity to save on sampler gas",
|
||||||
|
"pr": 505
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1655253622
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.62.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add MDEX on BSC",
|
||||||
|
"pr": 496
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add KnightSwap on BSC",
|
||||||
|
"pr": 498
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add Velodrome support on Optimism",
|
||||||
|
"pr": 494
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Do not send empty entries on Quote Report",
|
||||||
|
"pr": 501
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "KnightSwap/Mdex cosmetic change",
|
||||||
|
"pr": 502
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Offboard JetSwap, CafeSwap, JulSwap, and PolyDex",
|
||||||
|
"pr": 503
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1655244958
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.61.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add stETH wrap/unwrap support",
|
||||||
|
"pr": 476
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Offboard/clean up Oasis, CoFix, and legacy Kyber",
|
||||||
|
"pr": 482
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add MeshSwap on Polygon",
|
||||||
|
"pr": 491
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1654284040
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.60.1",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Alias Balancer sor to the old version",
|
||||||
|
"pr": 481
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1652931596
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "16.60.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add BiSwap on BSC",
|
||||||
|
"pr": 467
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add GMX and Platypus on Avalanche and Enable KyberDMM on bsc",
|
||||||
|
"pr": 478
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add Yoshi Exchange support in Fantom",
|
||||||
|
"pr": 473
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Fix KyberDMM gas underestimation",
|
||||||
|
"pr": 479
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timestamp": 1652919697
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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",
|
"version": "16.56.0",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
|||||||
@@ -5,6 +5,88 @@ Edit the package's CHANGELOG.json file only.
|
|||||||
|
|
||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
## v16.65.0 - _August 1, 2022_
|
||||||
|
|
||||||
|
* Use 0x gas api instead of eth gas station api (#532)
|
||||||
|
|
||||||
|
## v16.64.0 - _July 27, 2022_
|
||||||
|
|
||||||
|
* Refactor `TokenAdjacency` and `TokenAdjacencyBuilder` (#517)
|
||||||
|
* Add Synthetix support` (#518)
|
||||||
|
* Replace Beethoven X subgraph URL (#519)
|
||||||
|
* Remove Mooniswap on Ethereum mainnet (#529)
|
||||||
|
|
||||||
|
## v16.63.1 - _July 12, 2022_
|
||||||
|
|
||||||
|
* Better error handling for balancer cache (#515)
|
||||||
|
|
||||||
|
## v16.63.0 - _June 29, 2022_
|
||||||
|
|
||||||
|
* Remove JS router (#480)
|
||||||
|
* Removed Median price in favour of best gas adjusted price (#480)
|
||||||
|
|
||||||
|
## v16.62.2 - _Invalid date_
|
||||||
|
|
||||||
|
* Offboard Smoothy and ComethSwap (#509)
|
||||||
|
|
||||||
|
## v16.62.1 - _June 15, 2022_
|
||||||
|
|
||||||
|
* Remove nUSD from intermediate liquidity to save on sampler gas (#505)
|
||||||
|
|
||||||
|
## v16.62.0 - _June 14, 2022_
|
||||||
|
|
||||||
|
* Add MDEX on BSC (#496)
|
||||||
|
* Add KnightSwap on BSC (#498)
|
||||||
|
* Add Velodrome support on Optimism (#494)
|
||||||
|
* Do not send empty entries on Quote Report (#501)
|
||||||
|
* KnightSwap/Mdex cosmetic change (#502)
|
||||||
|
* Offboard JetSwap, CafeSwap, JulSwap, and PolyDex (#503)
|
||||||
|
|
||||||
|
## v16.61.0 - _June 3, 2022_
|
||||||
|
|
||||||
|
* Add stETH wrap/unwrap support (#476)
|
||||||
|
* Offboard/clean up Oasis, CoFix, and legacy Kyber (#482)
|
||||||
|
* Add MeshSwap on Polygon (#491)
|
||||||
|
|
||||||
|
## v16.60.1 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Alias Balancer sor to the old version (#481)
|
||||||
|
|
||||||
|
## v16.60.0 - _May 19, 2022_
|
||||||
|
|
||||||
|
* Add BiSwap on BSC (#467)
|
||||||
|
* Add GMX and Platypus on Avalanche and Enable KyberDMM on bsc (#478)
|
||||||
|
* Add Yoshi Exchange support in Fantom (#473)
|
||||||
|
* Fix KyberDMM gas underestimation (#479)
|
||||||
|
|
||||||
|
## 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_
|
## v16.56.0 - _April 21, 2022_
|
||||||
|
|
||||||
* Add estimatedGas to ExtendedQuoteReport (#463)
|
* Add estimatedGas to ExtendedQuoteReport (#463)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"shouldSaveStandardInput": true,
|
"shouldSaveStandardInput": true,
|
||||||
"compilerSettings": {
|
"compilerSettings": {
|
||||||
"evmVersion": "istanbul",
|
"evmVersion": "istanbul",
|
||||||
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": true, "deduplicate": true } },
|
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": false, "deduplicate": true } },
|
||||||
"outputSelection": {
|
"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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,28 +18,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
pragma solidity ^0.6;
|
pragma solidity ^0.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./interfaces/IBalancerV2Vault.sol";
|
||||||
|
|
||||||
|
|
||||||
interface ISmoothy {
|
contract BalancerV2Common {
|
||||||
|
|
||||||
function getBalance (
|
function _createSwapFunds()
|
||||||
uint256 tid
|
internal
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
view
|
||||||
returns (uint256 balance);
|
returns (IBalancerV2Vault.FundManagement memory)
|
||||||
|
{
|
||||||
function _yBalances (
|
return
|
||||||
uint256 tid
|
IBalancerV2Vault.FundManagement({
|
||||||
)
|
sender: address(this),
|
||||||
external
|
fromInternalBalance: false,
|
||||||
view
|
recipient: payable(address(this)),
|
||||||
returns (uint256 balance);
|
toInternalBalance: false
|
||||||
|
});
|
||||||
function getTokenStats (
|
}
|
||||||
uint256 tid
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 softWeight, uint256 hardWeight, uint256 balance, uint256 decimals);
|
|
||||||
}
|
}
|
||||||
@@ -21,44 +21,11 @@ pragma solidity ^0.6;
|
|||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
import "./SamplerUtils.sol";
|
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 {
|
contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Balancer V2.
|
/// @dev Sample sell quotes from Balancer V2.
|
||||||
/// @param poolInfo Struct with pool related data
|
/// @param poolInfo Struct with pool related data
|
||||||
@@ -68,7 +35,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleSellsFromBalancerV2(
|
function sampleSellsFromBalancerV2(
|
||||||
BalancerV2PoolInfo memory poolInfo,
|
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] memory takerTokenAmounts
|
uint256[] memory takerTokenAmounts
|
||||||
@@ -78,9 +45,9 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||||
IAsset[] memory swapAssets = new IAsset[](2);
|
address[] memory swapAssets = new address[](2);
|
||||||
swapAssets[0] = IAsset(takerToken);
|
swapAssets[0] = takerToken;
|
||||||
swapAssets[1] = IAsset(makerToken);
|
swapAssets[1] = makerToken;
|
||||||
|
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
@@ -97,7 +64,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
|
||||||
returns (int256[] memory amounts) {
|
returns (int256[] memory amounts) {
|
||||||
// Outgoing balance is negative so we need to flip the sign
|
// 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) {
|
if (amountOutFromPool <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -117,7 +84,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
/// amount.
|
/// amount.
|
||||||
function sampleBuysFromBalancerV2(
|
function sampleBuysFromBalancerV2(
|
||||||
BalancerV2PoolInfo memory poolInfo,
|
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||||
address takerToken,
|
address takerToken,
|
||||||
address makerToken,
|
address makerToken,
|
||||||
uint256[] memory makerTokenAmounts
|
uint256[] memory makerTokenAmounts
|
||||||
@@ -127,9 +94,9 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
|
||||||
IAsset[] memory swapAssets = new IAsset[](2);
|
address[] memory swapAssets = new address[](2);
|
||||||
swapAssets[0] = IAsset(takerToken);
|
swapAssets[0] = takerToken;
|
||||||
swapAssets[1] = IAsset(makerToken);
|
swapAssets[1] = makerToken;
|
||||||
|
|
||||||
uint256 numSamples = makerTokenAmounts.length;
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
takerTokenAmounts = new uint256[](numSamples);
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
@@ -157,7 +124,7 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _createSwapSteps(
|
function _createSwapSteps(
|
||||||
BalancerV2PoolInfo memory poolInfo,
|
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
|
||||||
uint256 amount
|
uint256 amount
|
||||||
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
|
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
|
||||||
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
|
||||||
@@ -172,18 +139,4 @@ contract BalancerV2Sampler is SamplerUtils {
|
|||||||
|
|
||||||
return swapSteps;
|
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";
|
import "./interfaces/IBancor.sol";
|
||||||
|
|
||||||
contract CompilerHack {}
|
|
||||||
|
|
||||||
contract BancorSampler is CompilerHack {
|
contract BancorSampler {
|
||||||
|
|
||||||
/// @dev Base gas limit for Bancor calls.
|
/// @dev Base gas limit for Bancor calls.
|
||||||
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
|
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
|
||||||
|
|||||||
120
packages/asset-swapper/contracts/src/BancorV3Sampler.sol
Normal file
120
packages/asset-swapper/contracts/src/BancorV3Sampler.sol
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// 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 "./interfaces/IBancorV3.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract BancorV3Sampler
|
||||||
|
{
|
||||||
|
/// @dev Gas limit for BancorV3 calls.
|
||||||
|
uint256 constant private BancorV3_CALL_GAS = 150e3; // 150k
|
||||||
|
|
||||||
|
address constant public ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from BancorV3.
|
||||||
|
/// @param weth The WETH contract address
|
||||||
|
/// @param router Router to look up tokens and amounts
|
||||||
|
/// @param path Token route. Should be takerToken -> makerToken
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token
|
||||||
|
/// amount.
|
||||||
|
function sampleSellsFromBancorV3(
|
||||||
|
address weth,
|
||||||
|
address router,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
if(path[0] == weth){
|
||||||
|
path[0] = ETH;
|
||||||
|
}
|
||||||
|
if(path[1] == weth){
|
||||||
|
path[1] = ETH;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
try
|
||||||
|
IBancorV3(router).tradeOutputBySourceAmount(path[0], path[1], takerTokenAmounts[i])
|
||||||
|
returns (uint256 amount)
|
||||||
|
{
|
||||||
|
makerTokenAmounts[i] = amount;
|
||||||
|
// Break early if there are 0 amounts
|
||||||
|
if (makerTokenAmounts[i] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (bytes memory) {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from BancorV3.
|
||||||
|
/// @param weth The WETH contract address
|
||||||
|
/// @param router Router to look up tokens and amounts
|
||||||
|
/// @param path Token route. Should be takerToken -> makerToken.
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
||||||
|
/// amount.
|
||||||
|
function sampleBuysFromBancorV3(
|
||||||
|
address weth,
|
||||||
|
address router,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
if(path[0] == weth){
|
||||||
|
path[0] = ETH;
|
||||||
|
}
|
||||||
|
if(path[1] == weth){
|
||||||
|
path[1] = ETH;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
try
|
||||||
|
IBancorV3(router).tradeInputByTargetAmount(path[0], path[1], makerTokenAmounts[i])
|
||||||
|
returns (uint256 amount)
|
||||||
|
{
|
||||||
|
takerTokenAmounts[i] = amount;
|
||||||
|
// Break early if there are 0 amounts
|
||||||
|
if (takerTokenAmounts[i] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (bytes memory) {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,52 +22,58 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "./BalancerSampler.sol";
|
import "./BalancerSampler.sol";
|
||||||
import "./BalancerV2Sampler.sol";
|
import "./BalancerV2Sampler.sol";
|
||||||
|
import "./BalancerV2BatchSampler.sol";
|
||||||
import "./BancorSampler.sol";
|
import "./BancorSampler.sol";
|
||||||
|
import "./BancorV3Sampler.sol";
|
||||||
import "./CompoundSampler.sol";
|
import "./CompoundSampler.sol";
|
||||||
import "./CurveSampler.sol";
|
import "./CurveSampler.sol";
|
||||||
import "./DODOSampler.sol";
|
import "./DODOSampler.sol";
|
||||||
import "./DODOV2Sampler.sol";
|
import "./DODOV2Sampler.sol";
|
||||||
import "./KyberSampler.sol";
|
import "./GMXSampler.sol";
|
||||||
import "./KyberDmmSampler.sol";
|
import "./KyberDmmSampler.sol";
|
||||||
import "./LidoSampler.sol";
|
import "./LidoSampler.sol";
|
||||||
import "./LiquidityProviderSampler.sol";
|
import "./LiquidityProviderSampler.sol";
|
||||||
import "./MakerPSMSampler.sol";
|
import "./MakerPSMSampler.sol";
|
||||||
import "./MultiBridgeSampler.sol";
|
|
||||||
import "./MStableSampler.sol";
|
import "./MStableSampler.sol";
|
||||||
import "./MooniswapSampler.sol";
|
import "./MooniswapSampler.sol";
|
||||||
import "./NativeOrderSampler.sol";
|
import "./NativeOrderSampler.sol";
|
||||||
|
import "./PlatypusSampler.sol";
|
||||||
import "./ShellSampler.sol";
|
import "./ShellSampler.sol";
|
||||||
import "./SmoothySampler.sol";
|
import "./SynthetixSampler.sol";
|
||||||
import "./TwoHopSampler.sol";
|
import "./TwoHopSampler.sol";
|
||||||
import "./UniswapSampler.sol";
|
import "./UniswapSampler.sol";
|
||||||
import "./UniswapV2Sampler.sol";
|
import "./UniswapV2Sampler.sol";
|
||||||
import "./UniswapV3Sampler.sol";
|
import "./UniswapV3Sampler.sol";
|
||||||
|
import "./VelodromeSampler.sol";
|
||||||
import "./UtilitySampler.sol";
|
import "./UtilitySampler.sol";
|
||||||
|
|
||||||
|
|
||||||
contract ERC20BridgeSampler is
|
contract ERC20BridgeSampler is
|
||||||
BalancerSampler,
|
BalancerSampler,
|
||||||
BalancerV2Sampler,
|
BalancerV2Sampler,
|
||||||
|
BalancerV2BatchSampler,
|
||||||
BancorSampler,
|
BancorSampler,
|
||||||
|
BancorV3Sampler,
|
||||||
CompoundSampler,
|
CompoundSampler,
|
||||||
CurveSampler,
|
CurveSampler,
|
||||||
DODOSampler,
|
DODOSampler,
|
||||||
DODOV2Sampler,
|
DODOV2Sampler,
|
||||||
KyberSampler,
|
GMXSampler,
|
||||||
KyberDmmSampler,
|
KyberDmmSampler,
|
||||||
LidoSampler,
|
LidoSampler,
|
||||||
LiquidityProviderSampler,
|
LiquidityProviderSampler,
|
||||||
MakerPSMSampler,
|
MakerPSMSampler,
|
||||||
MStableSampler,
|
MStableSampler,
|
||||||
MooniswapSampler,
|
MooniswapSampler,
|
||||||
MultiBridgeSampler,
|
|
||||||
NativeOrderSampler,
|
NativeOrderSampler,
|
||||||
|
PlatypusSampler,
|
||||||
ShellSampler,
|
ShellSampler,
|
||||||
SmoothySampler,
|
SynthetixSampler,
|
||||||
TwoHopSampler,
|
TwoHopSampler,
|
||||||
UniswapSampler,
|
UniswapSampler,
|
||||||
UniswapV2Sampler,
|
UniswapV2Sampler,
|
||||||
UniswapV3Sampler,
|
UniswapV3Sampler,
|
||||||
|
VelodromeSampler,
|
||||||
UtilitySampler
|
UtilitySampler
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -92,4 +98,6 @@ contract ERC20BridgeSampler is
|
|||||||
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
|
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
receive() external payable {}
|
||||||
}
|
}
|
||||||
|
|||||||
96
packages/asset-swapper/contracts/src/GMXSampler.sol
Normal file
96
packages/asset-swapper/contracts/src/GMXSampler.sol
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
pragma solidity ^0.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./interfaces/IGMX.sol";
|
||||||
|
import "./ApproximateBuys.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
contract GMXSampler is
|
||||||
|
SamplerUtils,
|
||||||
|
ApproximateBuys
|
||||||
|
{
|
||||||
|
struct GMXInfo {
|
||||||
|
address reader;
|
||||||
|
address vault;
|
||||||
|
address[] path;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleSellsFromGMX(
|
||||||
|
address reader,
|
||||||
|
address vault,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
try
|
||||||
|
IGMX(reader).getAmountOut(IVault(vault), path[0], path[1], takerTokenAmounts[i])
|
||||||
|
returns (uint256 amountAfterFees, uint256 feeAmount)
|
||||||
|
{
|
||||||
|
makerTokenAmounts[i] = amountAfterFees;
|
||||||
|
// Break early if there are 0 amounts
|
||||||
|
if (makerTokenAmounts[i] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (bytes memory) {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleBuysFromGMX(
|
||||||
|
address reader,
|
||||||
|
address vault,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
address[] memory invertBuyPath = new address[](2);
|
||||||
|
invertBuyPath[0] = path[1];
|
||||||
|
invertBuyPath[1] = path[0];
|
||||||
|
return _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
makerTokenData: abi.encode(reader, vault, invertBuyPath),
|
||||||
|
takerTokenData: abi.encode(reader, vault, path),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromGMX
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromGMX(
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
bytes memory makerTokenData,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
(address _reader, address _vault, address[] memory _path ) = abi.decode(takerTokenData, (address, address, address[]));
|
||||||
|
|
||||||
|
(bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromGMX.selector,
|
||||||
|
_reader,
|
||||||
|
_vault,
|
||||||
|
_path,
|
||||||
|
_toSingleValueArray(sellAmount)
|
||||||
|
));
|
||||||
|
if(!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
return abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,301 +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/IKyberNetwork.sol";
|
|
||||||
import "./ApproximateBuys.sol";
|
|
||||||
import "./SamplerUtils.sol";
|
|
||||||
|
|
||||||
|
|
||||||
contract KyberSampler is
|
|
||||||
SamplerUtils,
|
|
||||||
ApproximateBuys
|
|
||||||
{
|
|
||||||
/// @dev Gas limit for Kyber calls.
|
|
||||||
uint256 constant private KYBER_CALL_GAS = 500e3; // 500k
|
|
||||||
/// @dev Kyber ETH pseudo-address.
|
|
||||||
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
|
||||||
|
|
||||||
struct KyberSamplerOpts {
|
|
||||||
uint256 reserveOffset;
|
|
||||||
address hintHandler;
|
|
||||||
address networkProxy;
|
|
||||||
address weth;
|
|
||||||
bytes hint;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Kyber.
|
|
||||||
/// @param opts KyberSamplerOpts The nth reserve
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param takerTokenAmounts Taker token sell amount for each sample.
|
|
||||||
/// @return reserveId The id of the reserve found at reserveOffset
|
|
||||||
/// @return hint The hint for the selected reserve
|
|
||||||
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
|
|
||||||
function sampleSellsFromKyberNetwork(
|
|
||||||
KyberSamplerOpts memory opts,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (bytes32 reserveId, bytes memory hint, uint256[] memory makerTokenAmounts)
|
|
||||||
{
|
|
||||||
_assertValidPair(makerToken, takerToken);
|
|
||||||
reserveId = _getNextReserveId(opts, takerToken, makerToken);
|
|
||||||
if (reserveId == 0x0) {
|
|
||||||
return (reserveId, hint, makerTokenAmounts);
|
|
||||||
}
|
|
||||||
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
|
|
||||||
hint = opts.hint;
|
|
||||||
|
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
|
||||||
uint256 value = this.sampleSellFromKyberNetwork(
|
|
||||||
opts,
|
|
||||||
takerToken,
|
|
||||||
makerToken,
|
|
||||||
takerTokenAmounts[i]
|
|
||||||
);
|
|
||||||
makerTokenAmounts[i] = value;
|
|
||||||
// Break early if there are 0 amounts
|
|
||||||
if (makerTokenAmounts[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Kyber.
|
|
||||||
/// @param opts KyberSamplerOpts The nth reserve
|
|
||||||
/// @param takerToken Address of the taker token (what to sell).
|
|
||||||
/// @param makerToken Address of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @return reserveId The id of the reserve found at reserveOffset
|
|
||||||
/// @return hint The hint for the selected reserve
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
|
|
||||||
function sampleBuysFromKyberNetwork(
|
|
||||||
KyberSamplerOpts memory opts,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256[] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (bytes32 reserveId, bytes memory hint, uint256[] memory takerTokenAmounts)
|
|
||||||
{
|
|
||||||
_assertValidPair(makerToken, takerToken);
|
|
||||||
|
|
||||||
reserveId = _getNextReserveId(opts, takerToken, makerToken);
|
|
||||||
if (reserveId == 0x0) {
|
|
||||||
return (reserveId, hint, takerTokenAmounts);
|
|
||||||
}
|
|
||||||
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
|
|
||||||
hint = opts.hint;
|
|
||||||
|
|
||||||
takerTokenAmounts = _sampleApproximateBuys(
|
|
||||||
ApproximateBuyQuoteOpts({
|
|
||||||
makerTokenData: abi.encode(makerToken, opts),
|
|
||||||
takerTokenData: abi.encode(takerToken, opts),
|
|
||||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
|
|
||||||
}),
|
|
||||||
makerTokenAmounts
|
|
||||||
);
|
|
||||||
return (reserveId, hint, takerTokenAmounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeKyberHint(
|
|
||||||
KyberSamplerOpts memory opts,
|
|
||||||
bytes32 reserveId,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (bytes memory hint)
|
|
||||||
{
|
|
||||||
// Build a hint selecting the single reserve
|
|
||||||
IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler);
|
|
||||||
|
|
||||||
// All other reserves should be ignored with this hint
|
|
||||||
bytes32[] memory selectedReserves = new bytes32[](1);
|
|
||||||
selectedReserves[0] = reserveId;
|
|
||||||
uint256[] memory emptySplits = new uint256[](0);
|
|
||||||
|
|
||||||
if (takerToken == opts.weth) {
|
|
||||||
// ETH to Token
|
|
||||||
try
|
|
||||||
kyberHint.buildEthToTokenHint
|
|
||||||
{gas: KYBER_CALL_GAS}
|
|
||||||
(
|
|
||||||
makerToken,
|
|
||||||
IKyberHintHandler.TradeType.MaskIn,
|
|
||||||
selectedReserves,
|
|
||||||
emptySplits
|
|
||||||
)
|
|
||||||
returns (bytes memory result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
} catch (bytes memory) {
|
|
||||||
// Swallow failures, leaving all results as zero.
|
|
||||||
}
|
|
||||||
} else if (makerToken == opts.weth) {
|
|
||||||
// Token to ETH
|
|
||||||
try
|
|
||||||
kyberHint.buildTokenToEthHint
|
|
||||||
{gas: KYBER_CALL_GAS}
|
|
||||||
(
|
|
||||||
takerToken,
|
|
||||||
IKyberHintHandler.TradeType.MaskIn,
|
|
||||||
selectedReserves,
|
|
||||||
emptySplits
|
|
||||||
)
|
|
||||||
returns (bytes memory result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
} catch (bytes memory) {
|
|
||||||
// Swallow failures, leaving all results as zero.
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Token to Token
|
|
||||||
// We use the same reserve both ways
|
|
||||||
try
|
|
||||||
kyberHint.buildTokenToTokenHint
|
|
||||||
{gas: KYBER_CALL_GAS}
|
|
||||||
(
|
|
||||||
takerToken,
|
|
||||||
IKyberHintHandler.TradeType.MaskIn,
|
|
||||||
selectedReserves,
|
|
||||||
emptySplits,
|
|
||||||
makerToken,
|
|
||||||
IKyberHintHandler.TradeType.MaskIn,
|
|
||||||
selectedReserves,
|
|
||||||
emptySplits
|
|
||||||
)
|
|
||||||
returns (bytes memory result)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
} catch (bytes memory) {
|
|
||||||
// Swallow failures, leaving all results as zero.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sampleSellForApproximateBuyFromKyber(
|
|
||||||
bytes memory takerTokenData,
|
|
||||||
bytes memory makerTokenData,
|
|
||||||
uint256 sellAmount
|
|
||||||
)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (uint256)
|
|
||||||
{
|
|
||||||
(address makerToken, KyberSamplerOpts memory opts) =
|
|
||||||
abi.decode(makerTokenData, (address, KyberSamplerOpts));
|
|
||||||
(address takerToken, ) =
|
|
||||||
abi.decode(takerTokenData, (address, KyberSamplerOpts));
|
|
||||||
try
|
|
||||||
this.sampleSellFromKyberNetwork
|
|
||||||
(opts, takerToken, makerToken, sellAmount)
|
|
||||||
returns (uint256 amount)
|
|
||||||
{
|
|
||||||
return amount;
|
|
||||||
} catch (bytes memory) {
|
|
||||||
// Swallow failures, leaving all results as zero.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sampleSellFromKyberNetwork(
|
|
||||||
KyberSamplerOpts memory opts,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken,
|
|
||||||
uint256 takerTokenAmount
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256 makerTokenAmount)
|
|
||||||
{
|
|
||||||
// If there is no hint do not continue
|
|
||||||
if (opts.hint.length == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
IKyberNetworkProxy(opts.networkProxy).getExpectedRateAfterFee
|
|
||||||
{gas: KYBER_CALL_GAS}
|
|
||||||
(
|
|
||||||
takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
|
|
||||||
makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
|
|
||||||
takerTokenAmount,
|
|
||||||
0, // fee
|
|
||||||
opts.hint
|
|
||||||
)
|
|
||||||
returns (uint256 rate)
|
|
||||||
{
|
|
||||||
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
|
|
||||||
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
|
|
||||||
makerTokenAmount =
|
|
||||||
rate *
|
|
||||||
takerTokenAmount *
|
|
||||||
10 ** makerTokenDecimals /
|
|
||||||
10 ** takerTokenDecimals /
|
|
||||||
10 ** 18;
|
|
||||||
return makerTokenAmount;
|
|
||||||
} catch (bytes memory) {
|
|
||||||
// Swallow failures, leaving all results as zero.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function _getNextReserveId(
|
|
||||||
KyberSamplerOpts memory opts,
|
|
||||||
address takerToken,
|
|
||||||
address makerToken
|
|
||||||
)
|
|
||||||
internal
|
|
||||||
view
|
|
||||||
returns (bytes32 reserveId)
|
|
||||||
{
|
|
||||||
// Fetch the registered reserves for this pair
|
|
||||||
IKyberHintHandler kyberHint = IKyberHintHandler(opts.hintHandler);
|
|
||||||
(bytes32[] memory reserveIds, ,) = kyberHint.getTradingReserves(
|
|
||||||
takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
|
|
||||||
makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
|
|
||||||
true,
|
|
||||||
new bytes(0) // empty hint
|
|
||||||
);
|
|
||||||
|
|
||||||
if (opts.reserveOffset >= reserveIds.length) {
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
reserveId = reserveIds[opts.reserveOffset];
|
|
||||||
// Ignore Kyber Bridged Reserves (0xbb)
|
|
||||||
if (uint256(reserveId >> 248) == 0xbb) {
|
|
||||||
return 0x0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return reserveId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,10 +22,18 @@ pragma experimental ABIEncoderV2;
|
|||||||
|
|
||||||
import "./SamplerUtils.sol";
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
interface IWstETH {
|
||||||
|
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256);
|
||||||
|
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
contract LidoSampler is SamplerUtils {
|
contract LidoSampler is SamplerUtils {
|
||||||
struct LidoInfo {
|
struct LidoInfo {
|
||||||
address stEthToken;
|
address stEthToken;
|
||||||
address wethToken;
|
address wethToken;
|
||||||
|
address wstEthToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Lido
|
/// @dev Sample sell quotes from Lido
|
||||||
@@ -42,20 +50,17 @@ contract LidoSampler is SamplerUtils {
|
|||||||
uint256[] memory takerTokenAmounts
|
uint256[] memory takerTokenAmounts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
pure
|
view
|
||||||
returns (uint256[] memory)
|
returns (uint256[] memory)
|
||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
|
||||||
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
|
if (takerToken == lidoInfo.wethToken && makerToken == address(lidoInfo.stEthToken)) {
|
||||||
// Return 0 values if not selling WETH for stETH
|
// Minting stETH is always 1:1 therefore we can just return the same amounts back.
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
return takerTokenAmounts;
|
||||||
uint256[] memory makerTokenAmounts = new uint256[](numSamples);
|
|
||||||
return makerTokenAmounts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minting stETH is always 1:1 therefore we can just return the same amounts back
|
return _sampleSellsForWrapped(lidoInfo, takerToken, makerToken, takerTokenAmounts);
|
||||||
return takerTokenAmounts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Lido.
|
/// @dev Sample buy quotes from Lido.
|
||||||
@@ -72,20 +77,43 @@ contract LidoSampler is SamplerUtils {
|
|||||||
uint256[] memory makerTokenAmounts
|
uint256[] memory makerTokenAmounts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
pure
|
view
|
||||||
returns (uint256[] memory)
|
returns (uint256[] memory)
|
||||||
{
|
{
|
||||||
_assertValidPair(makerToken, takerToken);
|
if (takerToken == lidoInfo.wethToken && makerToken == address(lidoInfo.stEthToken)) {
|
||||||
|
// Minting stETH is always 1:1 therefore we can just return the same amounts back.
|
||||||
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
|
return makerTokenAmounts;
|
||||||
// Return 0 values if not buying stETH for WETH
|
|
||||||
uint256 numSamples = makerTokenAmounts.length;
|
|
||||||
uint256[] memory takerTokenAmounts = new uint256[](numSamples);
|
|
||||||
return takerTokenAmounts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Minting stETH is always 1:1 therefore we can just return the same amounts back
|
// Swap out `makerToken` and `takerToken` and re-use `_sampleSellsForWrapped`.
|
||||||
return makerTokenAmounts;
|
return _sampleSellsForWrapped(lidoInfo, makerToken, takerToken, makerTokenAmounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _sampleSellsForWrapped(
|
||||||
|
LidoInfo memory lidoInfo,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
) private view returns (uint256[] memory) {
|
||||||
|
IWstETH wstETH = IWstETH(lidoInfo.wstEthToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
uint256[] memory makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
if (takerToken == lidoInfo.stEthToken && makerToken == lidoInfo.wstEthToken) {
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = wstETH.getWstETHByStETH(takerTokenAmounts[i]);
|
||||||
|
}
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (takerToken == lidoInfo.wstEthToken && makerToken == lidoInfo.stEthToken) {
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = wstETH.getStETHByWstETH(takerTokenAmounts[i]);
|
||||||
|
}
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 values.
|
||||||
|
return makerTokenAmounts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
89
packages/asset-swapper/contracts/src/PlatypusSampler.sol
Normal file
89
packages/asset-swapper/contracts/src/PlatypusSampler.sol
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
pragma solidity ^0.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./interfaces/IPlatypus.sol";
|
||||||
|
import "./ApproximateBuys.sol";
|
||||||
|
import "./SamplerUtils.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract PlatypusSampler is
|
||||||
|
SamplerUtils,
|
||||||
|
ApproximateBuys
|
||||||
|
{
|
||||||
|
|
||||||
|
function sampleSellsFromPlatypus(
|
||||||
|
address pool,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory makerTokenAmounts)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
try
|
||||||
|
IPlatypus(pool).quotePotentialSwap(path[0], path[1], takerTokenAmounts[i])
|
||||||
|
returns (uint256 amountAfterFees, uint256 feeAmount)
|
||||||
|
{
|
||||||
|
makerTokenAmounts[i] = amountAfterFees;
|
||||||
|
// Break early if there are 0 amounts
|
||||||
|
if (makerTokenAmounts[i] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (bytes memory result) {
|
||||||
|
// Swallow failures, leaving all results as zero.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleBuysFromPlatypus(
|
||||||
|
address pool,
|
||||||
|
address[] memory path,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
returns (uint256[] memory takerTokenAmounts)
|
||||||
|
{
|
||||||
|
address[] memory invertBuyPath = new address[](2);
|
||||||
|
invertBuyPath[0] = path[1];
|
||||||
|
invertBuyPath[1] = path[0];
|
||||||
|
return _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
makerTokenData: abi.encode(pool, invertBuyPath),
|
||||||
|
takerTokenData: abi.encode(pool, path),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromPlatypus
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromPlatypus(
|
||||||
|
bytes memory makerTokenData,
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
uint256 sellAmount
|
||||||
|
)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (uint256 buyAmount)
|
||||||
|
{
|
||||||
|
(address _pool, address[] memory _path ) = abi.decode(makerTokenData, (address, address[]));
|
||||||
|
|
||||||
|
(bool success, bytes memory resultData) = address(this).staticcall(abi.encodeWithSelector(
|
||||||
|
this.sampleSellsFromPlatypus.selector,
|
||||||
|
_pool,
|
||||||
|
_path,
|
||||||
|
_toSingleValueArray(sellAmount)
|
||||||
|
));
|
||||||
|
if(!success) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// solhint-disable-next-line indent
|
||||||
|
return abi.decode(resultData, (uint256[]))[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,156 +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/ISmoothy.sol";
|
|
||||||
import "./ApproximateBuys.sol";
|
|
||||||
import "./SamplerUtils.sol";
|
|
||||||
import "./interfaces/ISmoothy.sol";
|
|
||||||
|
|
||||||
contract SmoothySampler is
|
|
||||||
SamplerUtils,
|
|
||||||
ApproximateBuys
|
|
||||||
{
|
|
||||||
/// @dev Information for sampling from smoothy sources.
|
|
||||||
struct SmoothyInfo {
|
|
||||||
address poolAddress;
|
|
||||||
bytes4 sellQuoteFunctionSelector;
|
|
||||||
bytes4 buyQuoteFunctionSelector;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Base gas limit for Smoothy calls.
|
|
||||||
uint256 constant private SMOOTHY_CALL_GAS = 600e3;
|
|
||||||
|
|
||||||
/// @dev Sample sell quotes from Smoothy.
|
|
||||||
/// @param smoothyInfo Smoothy information specific to this token pair.
|
|
||||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
|
||||||
/// @param toTokenIdx Index 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 sampleSellsFromSmoothy(
|
|
||||||
SmoothyInfo memory smoothyInfo,
|
|
||||||
int128 fromTokenIdx,
|
|
||||||
int128 toTokenIdx,
|
|
||||||
uint256[] memory takerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256[] memory makerTokenAmounts)
|
|
||||||
{
|
|
||||||
// Basically a Curve fork
|
|
||||||
|
|
||||||
// Smoothy only keep a percentage of its tokens available in reserve
|
|
||||||
uint256 poolReserveMakerAmount = ISmoothy(smoothyInfo.poolAddress).getBalance(uint256(toTokenIdx)) -
|
|
||||||
ISmoothy(smoothyInfo.poolAddress)._yBalances(uint256(toTokenIdx));
|
|
||||||
(, , , uint256 decimals) = ISmoothy(smoothyInfo.poolAddress).getTokenStats(uint256(toTokenIdx));
|
|
||||||
poolReserveMakerAmount = poolReserveMakerAmount/(10**(18-decimals));
|
|
||||||
|
|
||||||
uint256 numSamples = takerTokenAmounts.length;
|
|
||||||
makerTokenAmounts = new uint256[](numSamples);
|
|
||||||
for (uint256 i = 0; i < numSamples; i++) {
|
|
||||||
(bool didSucceed, bytes memory resultData) =
|
|
||||||
smoothyInfo.poolAddress.staticcall.gas(SMOOTHY_CALL_GAS)(
|
|
||||||
abi.encodeWithSelector(
|
|
||||||
smoothyInfo.sellQuoteFunctionSelector,
|
|
||||||
fromTokenIdx,
|
|
||||||
toTokenIdx,
|
|
||||||
takerTokenAmounts[i]
|
|
||||||
));
|
|
||||||
uint256 buyAmount = 0;
|
|
||||||
if (didSucceed) {
|
|
||||||
buyAmount = abi.decode(resultData, (uint256));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the quoted buyAmount is available in the pool reserve
|
|
||||||
if (buyAmount >= poolReserveMakerAmount) {
|
|
||||||
// Assign pool reserve amount for all higher samples to break early
|
|
||||||
for (uint256 j = i; j < numSamples; j++) {
|
|
||||||
makerTokenAmounts[j] = poolReserveMakerAmount;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
makerTokenAmounts[i] = buyAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Break early if there are 0 amounts
|
|
||||||
if (makerTokenAmounts[i] == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @dev Sample buy quotes from Smoothy.
|
|
||||||
/// @param smoothyInfo Smoothy information specific to this token pair.
|
|
||||||
/// @param fromTokenIdx Index of the taker token (what to sell).
|
|
||||||
/// @param toTokenIdx Index of the maker token (what to buy).
|
|
||||||
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
|
||||||
/// @return takerTokenAmounts Taker amounts sold at each maker token
|
|
||||||
/// amount.
|
|
||||||
function sampleBuysFromSmoothy(
|
|
||||||
SmoothyInfo memory smoothyInfo,
|
|
||||||
int128 fromTokenIdx,
|
|
||||||
int128 toTokenIdx,
|
|
||||||
uint256[] memory makerTokenAmounts
|
|
||||||
)
|
|
||||||
public
|
|
||||||
view
|
|
||||||
returns (uint256[] memory takerTokenAmounts)
|
|
||||||
{
|
|
||||||
// Buys not supported so approximate it.
|
|
||||||
return _sampleApproximateBuys(
|
|
||||||
ApproximateBuyQuoteOpts({
|
|
||||||
makerTokenData: abi.encode(toTokenIdx, smoothyInfo),
|
|
||||||
takerTokenData: abi.encode(fromTokenIdx, smoothyInfo),
|
|
||||||
getSellQuoteCallback: _sampleSellForApproximateBuyFromSmoothy
|
|
||||||
}),
|
|
||||||
makerTokenAmounts
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _sampleSellForApproximateBuyFromSmoothy(
|
|
||||||
bytes memory takerTokenData,
|
|
||||||
bytes memory makerTokenData,
|
|
||||||
uint256 sellAmount
|
|
||||||
)
|
|
||||||
private
|
|
||||||
view
|
|
||||||
returns (uint256 buyAmount)
|
|
||||||
{
|
|
||||||
(int128 takerTokenIdx, SmoothyInfo memory smoothyInfo) =
|
|
||||||
abi.decode(takerTokenData, (int128, SmoothyInfo));
|
|
||||||
(int128 makerTokenIdx) =
|
|
||||||
abi.decode(makerTokenData, (int128));
|
|
||||||
(bool success, bytes memory resultData) =
|
|
||||||
address(this).staticcall(abi.encodeWithSelector(
|
|
||||||
this.sampleSellsFromSmoothy.selector,
|
|
||||||
smoothyInfo,
|
|
||||||
takerTokenIdx,
|
|
||||||
makerTokenIdx,
|
|
||||||
_toSingleValueArray(sellAmount)
|
|
||||||
));
|
|
||||||
if (!success) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
// solhint-disable-next-line indent
|
|
||||||
return abi.decode(resultData, (uint256[]))[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
173
packages/asset-swapper/contracts/src/SynthetixSampler.sol
Normal file
173
packages/asset-swapper/contracts/src/SynthetixSampler.sol
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
interface IReadProxyAddressResolver {
|
||||||
|
function target() external view returns (address);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IAddressResolver {
|
||||||
|
function getAddress(bytes32 name) external view returns (address);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IExchanger {
|
||||||
|
// Ethereum Mainnet
|
||||||
|
function getAmountsForAtomicExchange(
|
||||||
|
uint256 sourceAmount,
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
bytes32 destinationCurrencyKey
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
uint256 amountReceived,
|
||||||
|
uint256 fee,
|
||||||
|
uint256 exchangeFeeRate
|
||||||
|
);
|
||||||
|
|
||||||
|
// Optimism
|
||||||
|
function getAmountsForExchange(
|
||||||
|
uint256 sourceAmount,
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
bytes32 destinationCurrencyKey
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (
|
||||||
|
uint256 amountReceived,
|
||||||
|
uint256 fee,
|
||||||
|
uint256 exchangeFeeRate
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract SynthetixSampler {
|
||||||
|
|
||||||
|
/// @dev Sample sell quotes from Synthetix Atomic Swap.
|
||||||
|
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
|
||||||
|
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order).
|
||||||
|
/// @return synthetix Synthetix address.
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
|
||||||
|
function sampleSellsFromSynthetix(
|
||||||
|
IReadProxyAddressResolver readProxy,
|
||||||
|
bytes32 takerTokenSymbol,
|
||||||
|
bytes32 makerTokenSymbol,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
) public view returns (address synthetix, uint256[] memory makerTokenAmounts) {
|
||||||
|
synthetix = getSynthetixAddress(readProxy);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
if (numSamples == 0) {
|
||||||
|
return (synthetix, makerTokenAmounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
makerTokenAmounts[0] = exchange(
|
||||||
|
readProxy,
|
||||||
|
takerTokenAmounts[0],
|
||||||
|
takerTokenSymbol,
|
||||||
|
makerTokenSymbol
|
||||||
|
);
|
||||||
|
|
||||||
|
// Synthetix atomic swap has a fixed rate. Calculate the rest based on the first value (and save gas).
|
||||||
|
for (uint256 i = 1; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] =
|
||||||
|
(makerTokenAmounts[0] * takerTokenAmounts[i]) /
|
||||||
|
takerTokenAmounts[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Synthetix Atomic Swap.
|
||||||
|
/// @param takerTokenSymbol Symbol (currency key) of the taker token (what to sell).
|
||||||
|
/// @param makerTokenSymbol Symbol (currency key) of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample (sorted in ascending order).
|
||||||
|
/// @return synthetix Synthetix address.
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
function sampleBuysFromSynthetix(
|
||||||
|
IReadProxyAddressResolver readProxy,
|
||||||
|
bytes32 takerTokenSymbol,
|
||||||
|
bytes32 makerTokenSymbol,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
) public view returns (address synthetix, uint256[] memory takerTokenAmounts) {
|
||||||
|
synthetix = getSynthetixAddress(readProxy);
|
||||||
|
// Since Synthetix atomic have a fixed rate, we can pick any reasonablely size takerTokenAmount (fixed to 1 ether here) and calculate the rest.
|
||||||
|
uint256 amountReceivedForEther = exchange(
|
||||||
|
readProxy,
|
||||||
|
1 ether,
|
||||||
|
takerTokenSymbol,
|
||||||
|
makerTokenSymbol
|
||||||
|
);
|
||||||
|
|
||||||
|
uint256 numSamples = makerTokenAmounts.length;
|
||||||
|
takerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
takerTokenAmounts[i] =
|
||||||
|
(1 ether * makerTokenAmounts[i]) /
|
||||||
|
amountReceivedForEther;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exchange(
|
||||||
|
IReadProxyAddressResolver readProxy,
|
||||||
|
uint256 sourceAmount,
|
||||||
|
bytes32 sourceCurrencyKey,
|
||||||
|
bytes32 destinationCurrencyKey
|
||||||
|
) private view returns (uint256 amountReceived) {
|
||||||
|
IExchanger exchanger = getExchanger(readProxy);
|
||||||
|
uint256 chainId;
|
||||||
|
assembly {
|
||||||
|
chainId := chainid()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chainId == 1) {
|
||||||
|
(amountReceived, , ) = exchanger.getAmountsForAtomicExchange(
|
||||||
|
sourceAmount,
|
||||||
|
sourceCurrencyKey,
|
||||||
|
destinationCurrencyKey
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
(amountReceived, , ) = exchanger.getAmountsForExchange(
|
||||||
|
sourceAmount,
|
||||||
|
sourceCurrencyKey,
|
||||||
|
destinationCurrencyKey
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSynthetixAddress(IReadProxyAddressResolver readProxy)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (address)
|
||||||
|
{
|
||||||
|
return IAddressResolver(readProxy.target()).getAddress("Synthetix");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExchanger(IReadProxyAddressResolver readProxy)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
returns (IExchanger)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
IExchanger(
|
||||||
|
IAddressResolver(readProxy.target()).getAddress("Exchanger")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
134
packages/asset-swapper/contracts/src/VelodromeSampler.sol
Normal file
134
packages/asset-swapper/contracts/src/VelodromeSampler.sol
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
// 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 './ApproximateBuys.sol';
|
||||||
|
import './SamplerUtils.sol';
|
||||||
|
|
||||||
|
struct VeloRoute {
|
||||||
|
address from;
|
||||||
|
address to;
|
||||||
|
bool stable;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVelodromeRouter {
|
||||||
|
function getAmountOut(
|
||||||
|
uint256 amountIn,
|
||||||
|
address tokenIn,
|
||||||
|
address tokenOut
|
||||||
|
) external view returns (uint256 amount, bool stable);
|
||||||
|
|
||||||
|
function getAmountsOut(uint256 amountIn, VeloRoute[] calldata routes)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256[] memory amounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract VelodromeSampler is SamplerUtils, ApproximateBuys {
|
||||||
|
/// @dev Sample sell quotes from Velodrome
|
||||||
|
/// @param router Address of Velodrome router.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token sell amount for each sample (sorted in ascending order).
|
||||||
|
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||||
|
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
|
||||||
|
function sampleSellsFromVelodrome(
|
||||||
|
IVelodromeRouter router,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
) public view returns (bool stable, uint256[] memory makerTokenAmounts) {
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
makerTokenAmounts = new uint256[](numSamples);
|
||||||
|
|
||||||
|
// Sampling should not mix stable and volatile pools.
|
||||||
|
// Find the most liquid pool based on max(takerTokenAmounts) and stick with it.
|
||||||
|
stable = _isMostLiquidPoolStablePool(router, takerToken, makerToken, takerTokenAmounts);
|
||||||
|
VeloRoute[] memory routes = new VeloRoute[](1);
|
||||||
|
routes[0] = VeloRoute({ from: takerToken, to: makerToken, stable: stable });
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < numSamples; i++) {
|
||||||
|
makerTokenAmounts[i] = router.getAmountsOut(takerTokenAmounts[i], routes)[1];
|
||||||
|
// Break early if there are 0 amounts
|
||||||
|
if (makerTokenAmounts[i] == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Sample buy quotes from Velodrome.
|
||||||
|
/// @param router Address of Velodrome router.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param makerTokenAmounts Maker token buy amount for each sample.
|
||||||
|
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||||
|
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
|
||||||
|
function sampleBuysFromVelodrome(
|
||||||
|
IVelodromeRouter router,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory makerTokenAmounts
|
||||||
|
) public view returns (bool stable, uint256[] memory takerTokenAmounts) {
|
||||||
|
_assertValidPair(makerToken, takerToken);
|
||||||
|
|
||||||
|
// Sampling should not mix stable and volatile pools.
|
||||||
|
// Find the most liquid pool based on the reverse swap (maker -> taker) and stick with it.
|
||||||
|
stable = _isMostLiquidPoolStablePool(router, makerToken, takerToken, makerTokenAmounts);
|
||||||
|
|
||||||
|
takerTokenAmounts = _sampleApproximateBuys(
|
||||||
|
ApproximateBuyQuoteOpts({
|
||||||
|
takerTokenData: abi.encode(router, VeloRoute({ from: takerToken, to: makerToken, stable: stable })),
|
||||||
|
makerTokenData: abi.encode(router, VeloRoute({ from: makerToken, to: takerToken, stable: stable })),
|
||||||
|
getSellQuoteCallback: _sampleSellForApproximateBuyFromVelodrome
|
||||||
|
}),
|
||||||
|
makerTokenAmounts
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _sampleSellForApproximateBuyFromVelodrome(
|
||||||
|
bytes memory takerTokenData,
|
||||||
|
bytes memory, /* makerTokenData */
|
||||||
|
uint256 sellAmount
|
||||||
|
) internal view returns (uint256) {
|
||||||
|
(IVelodromeRouter router, VeloRoute memory route) = abi.decode(takerTokenData, (IVelodromeRouter, VeloRoute));
|
||||||
|
|
||||||
|
VeloRoute[] memory routes = new VeloRoute[](1);
|
||||||
|
routes[0] = route;
|
||||||
|
return router.getAmountsOut(sellAmount, routes)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Returns whether the most liquid pool is a stable pool.
|
||||||
|
/// @param router Address of Velodrome router.
|
||||||
|
/// @param takerToken Address of the taker token (what to sell).
|
||||||
|
/// @param makerToken Address of the maker token (what to buy).
|
||||||
|
/// @param takerTokenAmounts Taker token buy amount for each sample (sorted in ascending order)
|
||||||
|
/// @return stable Whether the pool is a stable pool (vs volatile).
|
||||||
|
function _isMostLiquidPoolStablePool(
|
||||||
|
IVelodromeRouter router,
|
||||||
|
address takerToken,
|
||||||
|
address makerToken,
|
||||||
|
uint256[] memory takerTokenAmounts
|
||||||
|
) internal view returns (bool stable) {
|
||||||
|
uint256 numSamples = takerTokenAmounts.length;
|
||||||
|
(, stable) = router.getAmountOut(takerTokenAmounts[numSamples - 1], takerToken, makerToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
interface IBancorV3 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns the output amount when trading by providing the source amount
|
||||||
|
*/
|
||||||
|
function tradeOutputBySourceAmount(
|
||||||
|
address sourceToken,
|
||||||
|
address targetToken,
|
||||||
|
uint256 sourceAmount
|
||||||
|
) external view returns (uint256);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev returns the input amount when trading by providing the target amount
|
||||||
|
*/
|
||||||
|
function tradeInputByTargetAmount(
|
||||||
|
address sourceToken,
|
||||||
|
address targetToken,
|
||||||
|
uint256 targetAmount
|
||||||
|
) external view returns (uint256);
|
||||||
|
|
||||||
|
}
|
||||||
23
packages/asset-swapper/contracts/src/interfaces/IGMX.sol
Normal file
23
packages/asset-swapper/contracts/src/interfaces/IGMX.sol
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
pragma solidity ^0.6;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
interface IGMX {
|
||||||
|
function getMaxAmountIn(IVault _vault, address _tokenIn, address _tokenOut)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256);
|
||||||
|
|
||||||
|
function getAmountOut(IVault _vault, address _tokenIn, address _tokenOut, uint256 _amountIn)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256, uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IVault {
|
||||||
|
function getFeeBasisPoints(address _token, uint256 _usdgDelta, uint256 _feeBasisPoints, uint256 _taxBasisPoints, bool _increment) external view returns (uint256);
|
||||||
|
function stableSwapFeeBasisPoints() external view returns (uint256);
|
||||||
|
function stableTokens(address _token) external view returns (bool);
|
||||||
|
function tokenDecimals(address _token) external view returns (uint256);
|
||||||
|
function getMaxPrice(address _token) external view returns (uint256);
|
||||||
|
function getMinPrice(address _token) external view returns (uint256);
|
||||||
|
}
|
||||||
@@ -1,96 +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;
|
|
||||||
|
|
||||||
// Keepin everything together
|
|
||||||
interface IKyberNetwork {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface IKyberNetworkProxy {
|
|
||||||
|
|
||||||
function getExpectedRateAfterFee(
|
|
||||||
address src,
|
|
||||||
address dest,
|
|
||||||
uint256 srcQty,
|
|
||||||
uint256 platformFeeBps,
|
|
||||||
bytes calldata hint
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (uint256 expectedRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IKyberHintHandler {
|
|
||||||
|
|
||||||
enum TradeType {BestOfAll, MaskIn, MaskOut, Split}
|
|
||||||
|
|
||||||
enum ProcessWithRate {NotRequired, Required}
|
|
||||||
|
|
||||||
function getTradingReserves(
|
|
||||||
address tokenSrc,
|
|
||||||
address tokenDest,
|
|
||||||
bool isTokenToToken,
|
|
||||||
bytes calldata hint
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (
|
|
||||||
bytes32[] memory reserveIds,
|
|
||||||
uint256[] memory splitValuesBps,
|
|
||||||
ProcessWithRate processWithRate
|
|
||||||
);
|
|
||||||
|
|
||||||
function buildTokenToEthHint(
|
|
||||||
address tokenSrc,
|
|
||||||
TradeType tokenToEthType,
|
|
||||||
bytes32[] calldata tokenToEthReserveIds,
|
|
||||||
uint256[] calldata tokenToEthSplits
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes memory hint);
|
|
||||||
|
|
||||||
function buildEthToTokenHint(
|
|
||||||
address tokenDest,
|
|
||||||
TradeType ethToTokenType,
|
|
||||||
bytes32[] calldata ethToTokenReserveIds,
|
|
||||||
uint256[] calldata ethToTokenSplits
|
|
||||||
)
|
|
||||||
external
|
|
||||||
view
|
|
||||||
returns (bytes memory hint);
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
pragma solidity ^0.6;
|
||||||
|
|
||||||
|
interface IPlatypus {
|
||||||
|
function quotePotentialSwap(
|
||||||
|
address fromToken,
|
||||||
|
address toToken,
|
||||||
|
uint256 fromAmount
|
||||||
|
) external view returns (uint256 potentialOutcome, uint256 haircut);
|
||||||
|
|
||||||
|
function assetOf(address token) external view returns (address);
|
||||||
|
}
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1897,7 +1897,7 @@ ___
|
|||||||
|
|
||||||
# Interface: SwapQuoteRequestOpts
|
# Interface: SwapQuoteRequestOpts
|
||||||
|
|
||||||
slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.2 (20%).
|
slippagePercentage: The percentage buffer to add to account for slippage. Affects max ETH price estimates. Defaults to 0.01 (1%).
|
||||||
gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
|
gasPrice: gas price to determine protocolFee amount, default to ethGasStation fast amount
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@0x/asset-swapper",
|
"name": "@0x/asset-swapper",
|
||||||
"version": "16.56.0",
|
"version": "16.65.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.12"
|
"node": ">=6.12"
|
||||||
},
|
},
|
||||||
@@ -34,12 +34,13 @@
|
|||||||
"contracts:gen": "contracts-gen generate",
|
"contracts:gen": "contracts-gen generate",
|
||||||
"contracts:copy": "contracts-gen copy",
|
"contracts:copy": "contracts-gen copy",
|
||||||
"publish:private": "yarn build && gitpkg publish",
|
"publish:private": "yarn build && gitpkg publish",
|
||||||
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))"
|
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))",
|
||||||
|
"list:deps": "yarn lerna list -l"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
|
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"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|BancorV3Sampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|ERC20BridgeSampler|FakeTaker|GMXSampler|IBalancer|IBalancerV2Vault|IBancor|IBancorV3|ICurve|IGMX|IMStable|IMooniswap|IMultiBridge|IPlatypus|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|NativeOrderSampler|PlatypusSampler|SamplerUtils|ShellSampler|SynthetixSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json",
|
||||||
"postpublish": {
|
"postpublish": {
|
||||||
"assets": []
|
"assets": []
|
||||||
}
|
}
|
||||||
@@ -60,20 +61,19 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/assert": "^3.0.34",
|
"@0x/assert": "^3.0.34",
|
||||||
"@0x/base-contract": "^6.5.0",
|
"@0x/base-contract": "^6.5.0",
|
||||||
"@0x/contract-addresses": "^6.12.1",
|
"@0x/contract-addresses": "^6.17.0",
|
||||||
"@0x/contract-wrappers": "^13.20.0",
|
"@0x/contract-wrappers": "^13.20.5",
|
||||||
"@0x/contracts-erc20": "^3.3.28",
|
"@0x/contracts-erc20": "^3.3.33",
|
||||||
"@0x/contracts-zero-ex": "^0.31.2",
|
"@0x/contracts-zero-ex": "^0.36.0",
|
||||||
"@0x/dev-utils": "^4.2.14",
|
"@0x/dev-utils": "^4.2.14",
|
||||||
"@0x/json-schemas": "^6.4.4",
|
"@0x/json-schemas": "^6.4.4",
|
||||||
"@0x/neon-router": "^0.3.5",
|
"@0x/neon-router": "^0.3.5",
|
||||||
"@0x/protocol-utils": "^1.11.2",
|
"@0x/protocol-utils": "^11.16.0",
|
||||||
"@0x/quote-server": "^6.0.6",
|
"@0x/quote-server": "^6.0.6",
|
||||||
"@0x/types": "^3.3.6",
|
"@0x/types": "^3.3.6",
|
||||||
"@0x/typescript-typings": "^5.3.1",
|
|
||||||
"@0x/utils": "^6.5.3",
|
"@0x/utils": "^6.5.3",
|
||||||
"@0x/web3-wrapper": "^7.6.5",
|
"@0x/web3-wrapper": "^7.6.5",
|
||||||
"@balancer-labs/sor": "0.3.2",
|
"@balancer-labs/sdk": "0.1.6",
|
||||||
"@bancor/sdk": "0.2.9",
|
"@bancor/sdk": "0.2.9",
|
||||||
"@ethersproject/abi": "^5.0.1",
|
"@ethersproject/abi": "^5.0.1",
|
||||||
"@ethersproject/address": "^5.0.1",
|
"@ethersproject/address": "^5.0.1",
|
||||||
@@ -82,10 +82,9 @@
|
|||||||
"@ethersproject/strings": "^5.0.10",
|
"@ethersproject/strings": "^5.0.10",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
"axios-mock-adapter": "^1.19.0",
|
"axios-mock-adapter": "^1.19.0",
|
||||||
|
"balancer-labs-sor-v1": "npm:@balancer-labs/sor@0.3.2",
|
||||||
"cream-sor": "^0.3.3",
|
"cream-sor": "^0.3.3",
|
||||||
"decimal.js": "^10.2.0",
|
|
||||||
"ethereum-types": "^3.7.0",
|
"ethereum-types": "^3.7.0",
|
||||||
"ethereumjs-util": "^7.0.10",
|
|
||||||
"fast-abi": "^0.0.4",
|
"fast-abi": "^0.0.4",
|
||||||
"graphql": "^15.4.0",
|
"graphql": "^15.4.0",
|
||||||
"graphql-request": "^3.4.0",
|
"graphql-request": "^3.4.0",
|
||||||
@@ -94,14 +93,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/abi-gen": "^5.8.0",
|
"@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.46",
|
"@0x/contracts-gen": "^2.0.46",
|
||||||
"@0x/contracts-test-utils": "^5.4.19",
|
"@0x/contracts-test-utils": "^5.4.24",
|
||||||
"@0x/contracts-utils": "^4.8.9",
|
|
||||||
"@0x/mesh-rpc-client": "^9.4.2",
|
|
||||||
"@0x/migrations": "^8.1.17",
|
|
||||||
"@0x/sol-compiler": "^4.8.1",
|
"@0x/sol-compiler": "^4.8.1",
|
||||||
"@0x/subproviders": "^6.6.5",
|
"@0x/subproviders": "^6.6.5",
|
||||||
"@0x/ts-doc-gen": "^0.0.28",
|
"@0x/ts-doc-gen": "^0.0.28",
|
||||||
@@ -115,8 +108,8 @@
|
|||||||
"chai-bignumber": "^3.0.0",
|
"chai-bignumber": "^3.0.0",
|
||||||
"dirty-chai": "^2.0.1",
|
"dirty-chai": "^2.0.1",
|
||||||
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
"gitpkg": "https://github.com/0xProject/gitpkg.git",
|
||||||
"make-promises-safe": "^1.1.0",
|
|
||||||
"mocha": "^6.2.0",
|
"mocha": "^6.2.0",
|
||||||
|
"msw": "^0.44.2",
|
||||||
"npm-run-all": "^4.1.2",
|
"npm-run-all": "^4.1.2",
|
||||||
"nyc": "^11.0.1",
|
"nyc": "^11.0.1",
|
||||||
"shx": "^0.2.2",
|
"shx": "^0.2.2",
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import {
|
|||||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||||
} from './utils/market_operation_utils/constants';
|
} from './utils/market_operation_utils/constants';
|
||||||
|
|
||||||
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
|
const ZERO_EX_GAS_API_URL = 'https://gas.api.0x.org/source/median';
|
||||||
const NULL_BYTES = '0x';
|
const NULL_BYTES = '0x';
|
||||||
const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000';
|
const NULL_ERC20_ASSET_DATA = '0xf47261b00000000000000000000000000000000000000000000000000000000000000000';
|
||||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||||
@@ -48,7 +48,7 @@ const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
|
|||||||
orderRefreshIntervalMs: 10000, // 10 seconds
|
orderRefreshIntervalMs: 10000, // 10 seconds
|
||||||
...DEFAULT_ORDER_PRUNER_OPTS,
|
...DEFAULT_ORDER_PRUNER_OPTS,
|
||||||
samplerGasLimit: 500e6,
|
samplerGasLimit: 500e6,
|
||||||
ethGasStationUrl: ETH_GAS_STATION_API_URL,
|
zeroExGasApiUrl: ZERO_EX_GAS_API_URL,
|
||||||
rfqt: {
|
rfqt: {
|
||||||
integratorsWhitelist: [],
|
integratorsWhitelist: [],
|
||||||
makerAssetOfferings: {},
|
makerAssetOfferings: {},
|
||||||
@@ -99,7 +99,7 @@ export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
|
|||||||
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
|
export const KEEP_ALIVE_TTL = 5 * 60 * ONE_SECOND_MS;
|
||||||
|
|
||||||
export const constants = {
|
export const constants = {
|
||||||
ETH_GAS_STATION_API_URL,
|
ZERO_EX_GAS_API_URL,
|
||||||
PROTOCOL_FEE_MULTIPLIER,
|
PROTOCOL_FEE_MULTIPLIER,
|
||||||
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
|
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
|
||||||
NULL_BYTES,
|
NULL_BYTES,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export {
|
|||||||
ContractTxFunctionObj,
|
ContractTxFunctionObj,
|
||||||
SendTransactionOpts,
|
SendTransactionOpts,
|
||||||
} from '@0x/base-contract';
|
} from '@0x/base-contract';
|
||||||
export { ContractAddresses } from '@0x/contract-addresses';
|
export { ContractAddresses, ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
export {
|
export {
|
||||||
V4RFQFirmQuote,
|
V4RFQFirmQuote,
|
||||||
V4RFQIndicativeQuote,
|
V4RFQIndicativeQuote,
|
||||||
@@ -116,6 +116,15 @@ export {
|
|||||||
SamplerMetrics,
|
SamplerMetrics,
|
||||||
} from './types';
|
} from './types';
|
||||||
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
|
||||||
|
export {
|
||||||
|
IRfqClient,
|
||||||
|
RfqClientV1Price,
|
||||||
|
RfqClientV1PriceRequest,
|
||||||
|
RfqClientV1PriceResponse,
|
||||||
|
RfqClientV1Quote,
|
||||||
|
RfqClientV1QuoteRequest,
|
||||||
|
RfqClientV1QuoteResponse,
|
||||||
|
} from './utils/irfq_client';
|
||||||
export {
|
export {
|
||||||
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
|
||||||
DEFAULT_GAS_SCHEDULE,
|
DEFAULT_GAS_SCHEDULE,
|
||||||
@@ -123,6 +132,7 @@ export {
|
|||||||
BUY_SOURCE_FILTER_BY_CHAIN_ID,
|
BUY_SOURCE_FILTER_BY_CHAIN_ID,
|
||||||
SELL_SOURCE_FILTER_BY_CHAIN_ID,
|
SELL_SOURCE_FILTER_BY_CHAIN_ID,
|
||||||
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
||||||
|
ZERO_AMOUNT,
|
||||||
} from './utils/market_operation_utils/constants';
|
} from './utils/market_operation_utils/constants';
|
||||||
export {
|
export {
|
||||||
Parameters,
|
Parameters,
|
||||||
@@ -132,7 +142,6 @@ export {
|
|||||||
export {
|
export {
|
||||||
BalancerFillData,
|
BalancerFillData,
|
||||||
BancorFillData,
|
BancorFillData,
|
||||||
CollapsedFill,
|
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
CurveFunctionSelectors,
|
CurveFunctionSelectors,
|
||||||
CurveInfo,
|
CurveInfo,
|
||||||
@@ -141,25 +150,25 @@ export {
|
|||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
ExchangeProxyOverhead,
|
ExchangeProxyOverhead,
|
||||||
FeeSchedule,
|
FeeSchedule,
|
||||||
|
GasSchedule,
|
||||||
Fill,
|
Fill,
|
||||||
|
FillAdjustor,
|
||||||
FillData,
|
FillData,
|
||||||
GetMarketOrdersRfqOpts,
|
GetMarketOrdersRfqOpts,
|
||||||
KyberFillData,
|
|
||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
LiquidityProviderRegistry,
|
LiquidityProviderRegistry,
|
||||||
MarketDepth,
|
|
||||||
MarketDepthSide,
|
|
||||||
MooniswapFillData,
|
MooniswapFillData,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
NativeCollapsedFill,
|
|
||||||
NativeRfqOrderFillData,
|
NativeRfqOrderFillData,
|
||||||
NativeLimitOrderFillData,
|
NativeLimitOrderFillData,
|
||||||
NativeFillData,
|
NativeFillData,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
SourceQuoteOperation,
|
SourceQuoteOperation,
|
||||||
TokenAdjacencyGraph,
|
|
||||||
UniswapV2FillData,
|
UniswapV2FillData,
|
||||||
} from './utils/market_operation_utils/types';
|
} from './utils/market_operation_utils/types';
|
||||||
|
|
||||||
|
export { TokenAdjacencyGraph, TokenAdjacencyGraphBuilder } from './utils/token_adjacency_graph';
|
||||||
|
export { IdentityFillAdjustor } from './utils/market_operation_utils/identity_fill_adjustor';
|
||||||
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
|
||||||
export {
|
export {
|
||||||
BridgeQuoteReportEntry,
|
BridgeQuoteReportEntry,
|
||||||
@@ -183,3 +192,5 @@ export type Native = ERC20BridgeSource.Native;
|
|||||||
export type MultiHop = ERC20BridgeSource.MultiHop;
|
export type MultiHop = ERC20BridgeSource.MultiHop;
|
||||||
|
|
||||||
export { rfqtMocker, RfqtQuoteEndpoint } from './utils/rfqt_mocker';
|
export { rfqtMocker, RfqtQuoteEndpoint } from './utils/rfqt_mocker';
|
||||||
|
|
||||||
|
export { adjustOutput } from './utils/market_operation_utils/fills';
|
||||||
|
|||||||
@@ -32,16 +32,13 @@ import {
|
|||||||
import { assert } from '../utils/assert';
|
import { assert } from '../utils/assert';
|
||||||
import {
|
import {
|
||||||
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
|
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
|
||||||
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID,
|
|
||||||
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
|
||||||
} from '../utils/market_operation_utils/constants';
|
} from '../utils/market_operation_utils/constants';
|
||||||
import { poolEncoder } from '../utils/market_operation_utils/orders';
|
|
||||||
import {
|
import {
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
FinalUniswapV3FillData,
|
FinalUniswapV3FillData,
|
||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
MooniswapFillData,
|
|
||||||
NativeRfqOrderFillData,
|
NativeRfqOrderFillData,
|
||||||
OptimizedMarketBridgeOrder,
|
OptimizedMarketBridgeOrder,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
@@ -75,9 +72,7 @@ const PANCAKE_SWAP_FORKS = [
|
|||||||
ERC20BridgeSource.BakerySwap,
|
ERC20BridgeSource.BakerySwap,
|
||||||
ERC20BridgeSource.SushiSwap,
|
ERC20BridgeSource.SushiSwap,
|
||||||
ERC20BridgeSource.ApeSwap,
|
ERC20BridgeSource.ApeSwap,
|
||||||
ERC20BridgeSource.CafeSwap,
|
|
||||||
ERC20BridgeSource.CheeseSwap,
|
ERC20BridgeSource.CheeseSwap,
|
||||||
ERC20BridgeSource.JulSwap,
|
|
||||||
];
|
];
|
||||||
const FAKE_PROVIDER: any = {
|
const FAKE_PROVIDER: any = {
|
||||||
sendAsync(): void {
|
sendAsync(): void {
|
||||||
@@ -222,9 +217,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
ERC20BridgeSource.BakerySwap,
|
ERC20BridgeSource.BakerySwap,
|
||||||
ERC20BridgeSource.SushiSwap,
|
ERC20BridgeSource.SushiSwap,
|
||||||
ERC20BridgeSource.ApeSwap,
|
ERC20BridgeSource.ApeSwap,
|
||||||
ERC20BridgeSource.CafeSwap,
|
|
||||||
ERC20BridgeSource.CheeseSwap,
|
ERC20BridgeSource.CheeseSwap,
|
||||||
ERC20BridgeSource.JulSwap,
|
|
||||||
])
|
])
|
||||||
) {
|
) {
|
||||||
const source = slippedOrders[0].source;
|
const source = slippedOrders[0].source;
|
||||||
@@ -280,13 +273,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
this.chainId === ChainId.Mainnet &&
|
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
|
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
|
||||||
// into WETH prior/post the trade.
|
// into WETH prior/post the trade.
|
||||||
// ETH buy/sell is supported
|
// ETH buy/sell is supported
|
||||||
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
|
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
|
||||||
) {
|
) {
|
||||||
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
|
const fillData = slippedOrders[0].fillData as CurveFillData;
|
||||||
return {
|
return {
|
||||||
calldataHexString: this._exchangeProxy
|
calldataHexString: this._exchangeProxy
|
||||||
.sellToLiquidityProvider(
|
.sellToLiquidityProvider(
|
||||||
@@ -311,30 +304,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
this.chainId === ChainId.Mainnet &&
|
|
||||||
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
|
|
||||||
) {
|
|
||||||
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
|
|
||||||
return {
|
|
||||||
calldataHexString: this._exchangeProxy
|
|
||||||
.sellToLiquidityProvider(
|
|
||||||
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
|
|
||||||
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
|
|
||||||
MOONISWAP_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
|
|
||||||
NULL_ADDRESS,
|
|
||||||
sellAmount,
|
|
||||||
minBuyAmount,
|
|
||||||
poolEncoder.encode([fillData.poolAddress]),
|
|
||||||
)
|
|
||||||
.getABIEncodedTransactionData(),
|
|
||||||
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
|
|
||||||
toAddress: this._exchangeProxy.address,
|
|
||||||
allowanceTarget: this.contractAddresses.exchangeProxy,
|
|
||||||
gasOverhead: ZERO_AMOUNT,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// RFQT VIP
|
// RFQT VIP
|
||||||
if (
|
if (
|
||||||
[ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&
|
[ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
SwapQuoterRfqOpts,
|
SwapQuoterRfqOpts,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { assert } from './utils/assert';
|
import { assert } from './utils/assert';
|
||||||
|
import { IRfqClient } from './utils/irfq_client';
|
||||||
import { MarketOperationUtils } from './utils/market_operation_utils';
|
import { MarketOperationUtils } from './utils/market_operation_utils';
|
||||||
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
import { BancorService } from './utils/market_operation_utils/bancor_service';
|
||||||
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
|
||||||
@@ -32,12 +33,9 @@ import { DexOrderSampler } from './utils/market_operation_utils/sampler';
|
|||||||
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
import { SourceFilters } from './utils/market_operation_utils/source_filters';
|
||||||
import {
|
import {
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
FeeSchedule,
|
|
||||||
FillData,
|
FillData,
|
||||||
|
GasSchedule,
|
||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
MarketDepth,
|
|
||||||
MarketDepthSide,
|
|
||||||
MarketSideLiquidity,
|
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
OptimizerResultWithReport,
|
OptimizerResultWithReport,
|
||||||
} from './utils/market_operation_utils/types';
|
} from './utils/market_operation_utils/types';
|
||||||
@@ -111,7 +109,7 @@ export class SwapQuoter {
|
|||||||
};
|
};
|
||||||
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
|
||||||
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
|
||||||
options.ethGasStationUrl,
|
options.zeroExGasApiUrl,
|
||||||
);
|
);
|
||||||
// Allow the sampler bytecode to be overwritten using geths override functionality
|
// Allow the sampler bytecode to be overwritten using geths override functionality
|
||||||
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
|
||||||
@@ -227,67 +225,6 @@ export class SwapQuoter {
|
|||||||
return batchSwapQuotes.filter(x => x !== undefined) as MarketBuySwapQuote[];
|
return batchSwapQuotes.filter(x => x !== undefined) as MarketBuySwapQuote[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the bids and asks liquidity for the entire market.
|
|
||||||
* For certain sources (like AMM's) it is recommended to provide a practical maximum takerAssetAmount.
|
|
||||||
* @param makerTokenAddress The address of the maker asset
|
|
||||||
* @param takerTokenAddress The address of the taker asset
|
|
||||||
* @param takerAssetAmount The amount to sell and buy for the bids and asks.
|
|
||||||
*
|
|
||||||
* @return An object that conforms to MarketDepth that contains all of the samples and liquidity
|
|
||||||
* information for the source.
|
|
||||||
*/
|
|
||||||
public async getBidAskLiquidityForMakerTakerAssetPairAsync(
|
|
||||||
makerToken: string,
|
|
||||||
takerToken: string,
|
|
||||||
takerAssetAmount: BigNumber,
|
|
||||||
options: Partial<SwapQuoteRequestOpts> = {},
|
|
||||||
): Promise<MarketDepth> {
|
|
||||||
assert.isString('makerToken', makerToken);
|
|
||||||
assert.isString('takerToken', takerToken);
|
|
||||||
const sourceFilters = new SourceFilters([], options.excludedSources, options.includedSources);
|
|
||||||
|
|
||||||
let [sellOrders, buyOrders] = !sourceFilters.isAllowed(ERC20BridgeSource.Native)
|
|
||||||
? [[], []]
|
|
||||||
: await Promise.all([
|
|
||||||
this.orderbook.getOrdersAsync(makerToken, takerToken),
|
|
||||||
this.orderbook.getOrdersAsync(takerToken, makerToken),
|
|
||||||
]);
|
|
||||||
if (!sellOrders || sellOrders.length === 0) {
|
|
||||||
sellOrders = [createDummyOrder(makerToken, takerToken)];
|
|
||||||
}
|
|
||||||
if (!buyOrders || buyOrders.length === 0) {
|
|
||||||
buyOrders = [createDummyOrder(takerToken, makerToken)];
|
|
||||||
}
|
|
||||||
|
|
||||||
const getMarketDepthSide = (marketSideLiquidity: MarketSideLiquidity): MarketDepthSide => {
|
|
||||||
const { dexQuotes, nativeOrders } = marketSideLiquidity.quotes;
|
|
||||||
const { side } = marketSideLiquidity;
|
|
||||||
|
|
||||||
return [
|
|
||||||
...dexQuotes,
|
|
||||||
nativeOrders.map(o => {
|
|
||||||
return {
|
|
||||||
input: side === MarketOperation.Sell ? o.fillableTakerAmount : o.fillableMakerAmount,
|
|
||||||
output: side === MarketOperation.Sell ? o.fillableMakerAmount : o.fillableTakerAmount,
|
|
||||||
fillData: o,
|
|
||||||
source: ERC20BridgeSource.Native,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
};
|
|
||||||
const [bids, asks] = await Promise.all([
|
|
||||||
this._marketOperationUtils.getMarketBuyLiquidityAsync(buyOrders, takerAssetAmount, options),
|
|
||||||
this._marketOperationUtils.getMarketSellLiquidityAsync(sellOrders, takerAssetAmount, options),
|
|
||||||
]);
|
|
||||||
return {
|
|
||||||
bids: getMarketDepthSide(bids),
|
|
||||||
asks: getMarketDepthSide(asks),
|
|
||||||
makerTokenDecimals: asks.makerTokenDecimals,
|
|
||||||
takerTokenDecimals: asks.takerTokenDecimals,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the recommended gas price for a fast transaction
|
* Returns the recommended gas price for a fast transaction
|
||||||
*/
|
*/
|
||||||
@@ -327,6 +264,7 @@ export class SwapQuoter {
|
|||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
marketOperation: MarketOperation,
|
marketOperation: MarketOperation,
|
||||||
options: Partial<SwapQuoteRequestOpts>,
|
options: Partial<SwapQuoteRequestOpts>,
|
||||||
|
rfqClient?: IRfqClient | undefined,
|
||||||
): Promise<SwapQuote> {
|
): Promise<SwapQuote> {
|
||||||
assert.isETHAddressHex('makerToken', makerToken);
|
assert.isETHAddressHex('makerToken', makerToken);
|
||||||
assert.isETHAddressHex('takerToken', takerToken);
|
assert.isETHAddressHex('takerToken', takerToken);
|
||||||
@@ -364,9 +302,11 @@ export class SwapQuoter {
|
|||||||
const calcOpts: GetMarketOrdersOpts = {
|
const calcOpts: GetMarketOrdersOpts = {
|
||||||
...cloneOpts,
|
...cloneOpts,
|
||||||
gasPrice,
|
gasPrice,
|
||||||
feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData: FillData) =>
|
feeSchedule: _.mapValues(opts.gasSchedule, gasCost => (fillData: FillData) => {
|
||||||
gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)),
|
const gas = gasCost ? gasCost(fillData) : 0;
|
||||||
),
|
const fee = gasPrice.times(gas);
|
||||||
|
return { gas, fee };
|
||||||
|
}),
|
||||||
exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
|
exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
|
||||||
};
|
};
|
||||||
// pass the QuoteRequestor on if rfqt enabled
|
// pass the QuoteRequestor on if rfqt enabled
|
||||||
@@ -381,6 +321,7 @@ export class SwapQuoter {
|
|||||||
this.expiryBufferMs,
|
this.expiryBufferMs,
|
||||||
rfqtOptions?.metricsProxy,
|
rfqtOptions?.metricsProxy,
|
||||||
);
|
);
|
||||||
|
calcOpts.rfqt.rfqClient = rfqClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
const result: OptimizerResultWithReport = await this._marketOperationUtils.getOptimizerResultAsync(
|
const result: OptimizerResultWithReport = await this._marketOperationUtils.getOptimizerResultAsync(
|
||||||
@@ -499,7 +440,7 @@ function createSwapQuote(
|
|||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
gasSchedule: FeeSchedule,
|
gasSchedule: GasSchedule,
|
||||||
slippage: number,
|
slippage: number,
|
||||||
): SwapQuote {
|
): SwapQuote {
|
||||||
const {
|
const {
|
||||||
@@ -559,7 +500,7 @@ function calculateQuoteInfo(
|
|||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
assetFillAmount: BigNumber,
|
assetFillAmount: BigNumber,
|
||||||
gasPrice: BigNumber,
|
gasPrice: BigNumber,
|
||||||
gasSchedule: FeeSchedule,
|
gasSchedule: GasSchedule,
|
||||||
slippage: number,
|
slippage: number,
|
||||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
||||||
const bestCaseFillResult = simulateBestCaseFill({
|
const bestCaseFillResult = simulateBestCaseFill({
|
||||||
@@ -588,25 +529,23 @@ function calculateQuoteInfo(
|
|||||||
function calculateTwoHopQuoteInfo(
|
function calculateTwoHopQuoteInfo(
|
||||||
optimizedOrders: OptimizedMarketOrder[],
|
optimizedOrders: OptimizedMarketOrder[],
|
||||||
operation: MarketOperation,
|
operation: MarketOperation,
|
||||||
gasSchedule: FeeSchedule,
|
gasSchedule: GasSchedule,
|
||||||
slippage: number,
|
slippage: number,
|
||||||
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
|
||||||
const [firstHopOrder, secondHopOrder] = optimizedOrders;
|
const [firstHopOrder, secondHopOrder] = optimizedOrders;
|
||||||
const [firstHopFill] = firstHopOrder.fills;
|
|
||||||
const [secondHopFill] = secondHopOrder.fills;
|
|
||||||
const gas = new BigNumber(
|
const gas = new BigNumber(
|
||||||
gasSchedule[ERC20BridgeSource.MultiHop]!({
|
gasSchedule[ERC20BridgeSource.MultiHop]!({
|
||||||
firstHopSource: _.pick(firstHopFill, 'source', 'fillData'),
|
firstHopSource: _.pick(firstHopOrder, 'source', 'fillData'),
|
||||||
secondHopSource: _.pick(secondHopFill, 'source', 'fillData'),
|
secondHopSource: _.pick(secondHopOrder, 'source', 'fillData'),
|
||||||
}),
|
}),
|
||||||
).toNumber();
|
).toNumber();
|
||||||
const isSell = operation === MarketOperation.Sell;
|
const isSell = operation === MarketOperation.Sell;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
bestCaseQuoteInfo: {
|
bestCaseQuoteInfo: {
|
||||||
makerAmount: isSell ? secondHopFill.output : secondHopFill.input,
|
makerAmount: isSell ? secondHopOrder.fill.output : secondHopOrder.fill.input,
|
||||||
takerAmount: isSell ? firstHopFill.input : firstHopFill.output,
|
takerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output,
|
||||||
totalTakerAmount: isSell ? firstHopFill.input : firstHopFill.output,
|
totalTakerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output,
|
||||||
feeTakerTokenAmount: constants.ZERO_AMOUNT,
|
feeTakerTokenAmount: constants.ZERO_AMOUNT,
|
||||||
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
|
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
|
||||||
gas,
|
gas,
|
||||||
@@ -632,7 +571,7 @@ function calculateTwoHopQuoteInfo(
|
|||||||
[ERC20BridgeSource.MultiHop]: {
|
[ERC20BridgeSource.MultiHop]: {
|
||||||
proportion: new BigNumber(1),
|
proportion: new BigNumber(1),
|
||||||
intermediateToken: secondHopOrder.takerToken,
|
intermediateToken: secondHopOrder.takerToken,
|
||||||
hops: [firstHopFill.source, secondHopFill.source],
|
hops: [firstHopOrder.source, secondHopOrder.source],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,11 +17,13 @@ import {
|
|||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
LiquidityProviderRegistry,
|
LiquidityProviderRegistry,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
TokenAdjacencyGraph,
|
|
||||||
} from './utils/market_operation_utils/types';
|
} from './utils/market_operation_utils/types';
|
||||||
export { SamplerMetrics } from './utils/market_operation_utils/types';
|
export { SamplerMetrics } from './utils/market_operation_utils/types';
|
||||||
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
|
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
|
||||||
import { MetricsProxy } from './utils/quote_requestor';
|
import { MetricsProxy } from './utils/quote_requestor';
|
||||||
|
import { TokenAdjacencyGraph } from './utils/token_adjacency_graph';
|
||||||
|
|
||||||
|
export type Address = string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
* expiryBufferMs: The number of seconds to add when calculating whether an order is expired or not. Defaults to 300s (5m).
|
||||||
@@ -335,7 +337,7 @@ export interface SwapQuoterOpts extends OrderPrunerOpts {
|
|||||||
contractAddresses?: AssetSwapperContractAddresses;
|
contractAddresses?: AssetSwapperContractAddresses;
|
||||||
samplerGasLimit?: number;
|
samplerGasLimit?: number;
|
||||||
multiBridgeAddress?: string;
|
multiBridgeAddress?: string;
|
||||||
ethGasStationUrl?: string;
|
zeroExGasApiUrl?: string;
|
||||||
rfqt?: SwapQuoterRfqOpts;
|
rfqt?: SwapQuoterRfqOpts;
|
||||||
samplerOverrides?: SamplerOverrides;
|
samplerOverrides?: SamplerOverrides;
|
||||||
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
tokenAdjacencyGraph?: TokenAdjacencyGraph;
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ import {
|
|||||||
|
|
||||||
const SUCCESS_CODE = 201;
|
const SUCCESS_CODE = 201;
|
||||||
|
|
||||||
function getAltMarketInfo(
|
/**
|
||||||
|
* Returns the AltOffering if it exists for a given pair
|
||||||
|
*/
|
||||||
|
export function getAltMarketInfo(
|
||||||
offerings: AltOffering[],
|
offerings: AltOffering[],
|
||||||
buyTokenAddress: string,
|
buyTokenAddress: string,
|
||||||
sellTokenAddress: 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>;
|
||||||
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
import { ChainId } from '@0x/contract-addresses';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { BigNumber, NULL_BYTES } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ACRYPTOS_BSC_INFOS,
|
ACRYPTOS_BSC_INFOS,
|
||||||
APESWAP_ROUTER_BY_CHAIN_ID,
|
APESWAP_ROUTER_BY_CHAIN_ID,
|
||||||
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
|
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
BELT_BSC_INFOS,
|
BELT_BSC_INFOS,
|
||||||
CAFESWAP_ROUTER_BY_CHAIN_ID,
|
BISWAP_ROUTER_BY_CHAIN_ID,
|
||||||
CHEESESWAP_ROUTER_BY_CHAIN_ID,
|
CHEESESWAP_ROUTER_BY_CHAIN_ID,
|
||||||
COMETHSWAP_ROUTER_BY_CHAIN_ID,
|
|
||||||
COMPONENT_POOLS_BY_CHAIN_ID,
|
COMPONENT_POOLS_BY_CHAIN_ID,
|
||||||
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
|
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
|
||||||
CURVE_AVALANCHE_INFOS,
|
CURVE_AVALANCHE_INFOS,
|
||||||
@@ -25,12 +24,10 @@ import {
|
|||||||
FIREBIRDONESWAP_BSC_INFOS,
|
FIREBIRDONESWAP_BSC_INFOS,
|
||||||
FIREBIRDONESWAP_POLYGON_INFOS,
|
FIREBIRDONESWAP_POLYGON_INFOS,
|
||||||
IRONSWAP_POLYGON_INFOS,
|
IRONSWAP_POLYGON_INFOS,
|
||||||
JETSWAP_ROUTER_BY_CHAIN_ID,
|
KNIGHTSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
JULSWAP_ROUTER_BY_CHAIN_ID,
|
|
||||||
KYBER_BANNED_RESERVES,
|
|
||||||
KYBER_BRIDGED_LIQUIDITY_PREFIX,
|
|
||||||
MAX_DODOV2_POOLS_QUERIED,
|
MAX_DODOV2_POOLS_QUERIED,
|
||||||
MAX_KYBER_RESERVES_QUERIED,
|
MDEX_ROUTER_BY_CHAIN_ID,
|
||||||
|
MESHSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
MOBIUSMONEY_CELO_INFOS,
|
MOBIUSMONEY_CELO_INFOS,
|
||||||
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
|
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
MSTABLE_POOLS_BY_CHAIN_ID,
|
MSTABLE_POOLS_BY_CHAIN_ID,
|
||||||
@@ -39,18 +36,14 @@ import {
|
|||||||
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
|
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
|
||||||
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
|
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
|
||||||
PANGOLIN_ROUTER_BY_CHAIN_ID,
|
PANGOLIN_ROUTER_BY_CHAIN_ID,
|
||||||
POLYDEX_ROUTER_BY_CHAIN_ID,
|
PLATYPUS_AVALANCHE_INFOS,
|
||||||
QUICKSWAP_ROUTER_BY_CHAIN_ID,
|
QUICKSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
SADDLE_MAINNET_INFOS,
|
SADDLE_MAINNET_INFOS,
|
||||||
SHELL_POOLS_BY_CHAIN_ID,
|
SHELL_POOLS_BY_CHAIN_ID,
|
||||||
SHIBASWAP_ROUTER_BY_CHAIN_ID,
|
SHIBASWAP_ROUTER_BY_CHAIN_ID,
|
||||||
SMOOTHY_BSC_INFOS,
|
|
||||||
SMOOTHY_MAINNET_INFOS,
|
|
||||||
SNOWSWAP_MAINNET_INFOS,
|
|
||||||
SPIRITSWAP_ROUTER_BY_CHAIN_ID,
|
SPIRITSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
SPOOKYSWAP_ROUTER_BY_CHAIN_ID,
|
SPOOKYSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
SUSHISWAP_ROUTER_BY_CHAIN_ID,
|
SUSHISWAP_ROUTER_BY_CHAIN_ID,
|
||||||
SWERVE_MAINNET_INFOS,
|
|
||||||
SYNAPSE_AVALANCHE_INFOS,
|
SYNAPSE_AVALANCHE_INFOS,
|
||||||
SYNAPSE_BSC_INFOS,
|
SYNAPSE_BSC_INFOS,
|
||||||
SYNAPSE_FANTOM_INFOS,
|
SYNAPSE_FANTOM_INFOS,
|
||||||
@@ -62,35 +55,15 @@ import {
|
|||||||
UNISWAPV2_ROUTER_BY_CHAIN_ID,
|
UNISWAPV2_ROUTER_BY_CHAIN_ID,
|
||||||
WAULTSWAP_ROUTER_BY_CHAIN_ID,
|
WAULTSWAP_ROUTER_BY_CHAIN_ID,
|
||||||
XSIGMA_MAINNET_INFOS,
|
XSIGMA_MAINNET_INFOS,
|
||||||
|
YOSHI_ROUTER_BY_CHAIN_ID,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { CurveInfo, ERC20BridgeSource } from './types';
|
import { CurveInfo, ERC20BridgeSource, PlatypusInfo } from './types';
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter Kyber reserves which should not be used (0xbb bridged reserves)
|
|
||||||
* @param reserveId Kyber reserveId
|
|
||||||
*/
|
|
||||||
export function isAllowedKyberReserveId(reserveId: string): boolean {
|
|
||||||
return (
|
|
||||||
reserveId !== NULL_BYTES &&
|
|
||||||
!reserveId.startsWith(KYBER_BRIDGED_LIQUIDITY_PREFIX) &&
|
|
||||||
!KYBER_BANNED_RESERVES.includes(reserveId)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line: completed-docs ban-types
|
// tslint:disable-next-line: completed-docs ban-types
|
||||||
export function isValidAddress(address: string | String): address is string {
|
export function isValidAddress(address: string | String): address is string {
|
||||||
return (typeof address === 'string' || address instanceof String) && address.toString() !== NULL_ADDRESS;
|
return (typeof address === 'string' || address instanceof String) && address.toString() !== NULL_ADDRESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the offsets to be used to discover Kyber reserves
|
|
||||||
*/
|
|
||||||
export function getKyberOffsets(): BigNumber[] {
|
|
||||||
return Array(MAX_KYBER_RESERVES_QUERIED)
|
|
||||||
.fill(0)
|
|
||||||
.map((_v, i) => new BigNumber(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable completed-docs
|
// tslint:disable completed-docs
|
||||||
export function getDodoV2Offsets(): BigNumber[] {
|
export function getDodoV2Offsets(): BigNumber[] {
|
||||||
return Array(MAX_DODOV2_POOLS_QUERIED)
|
return Array(MAX_DODOV2_POOLS_QUERIED)
|
||||||
@@ -225,32 +198,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[] {
|
export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||||
if (chainId !== ChainId.BSC) {
|
if (chainId !== ChainId.BSC) {
|
||||||
return [];
|
return [];
|
||||||
@@ -375,30 +322,6 @@ export function getEllipsisInfosForPair(chainId: ChainId, takerToken: string, ma
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSmoothyInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
|
||||||
if (chainId === ChainId.BSC) {
|
|
||||||
return Object.values(SMOOTHY_BSC_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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else if (chainId === ChainId.Mainnet) {
|
|
||||||
return Object.values(SMOOTHY_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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getSaddleInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
export function getSaddleInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
|
||||||
if (chainId !== ChainId.Mainnet) {
|
if (chainId !== ChainId.Mainnet) {
|
||||||
return [];
|
return [];
|
||||||
@@ -463,6 +386,15 @@ export function getMobiusMoneyInfoForPair(chainId: ChainId, takerToken: string,
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPlatypusInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): PlatypusInfo[] {
|
||||||
|
if (chainId !== ChainId.Avalanche) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return Object.values(PLATYPUS_AVALANCHE_INFOS).filter(c =>
|
||||||
|
[makerToken, takerToken].every(t => c.tokens.includes(t)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function getShellLikeInfosForPair(
|
export function getShellLikeInfosForPair(
|
||||||
chainId: ChainId,
|
chainId: ChainId,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
@@ -493,13 +425,10 @@ export function getCurveLikeInfosForPair(
|
|||||||
source:
|
source:
|
||||||
| ERC20BridgeSource.Curve
|
| ERC20BridgeSource.Curve
|
||||||
| ERC20BridgeSource.CurveV2
|
| ERC20BridgeSource.CurveV2
|
||||||
| ERC20BridgeSource.Swerve
|
|
||||||
| ERC20BridgeSource.SnowSwap
|
|
||||||
| ERC20BridgeSource.Nerve
|
| ERC20BridgeSource.Nerve
|
||||||
| ERC20BridgeSource.Synapse
|
| ERC20BridgeSource.Synapse
|
||||||
| ERC20BridgeSource.Belt
|
| ERC20BridgeSource.Belt
|
||||||
| ERC20BridgeSource.Ellipsis
|
| ERC20BridgeSource.Ellipsis
|
||||||
| ERC20BridgeSource.Smoothy
|
|
||||||
| ERC20BridgeSource.Saddle
|
| ERC20BridgeSource.Saddle
|
||||||
| ERC20BridgeSource.IronSwap
|
| ERC20BridgeSource.IronSwap
|
||||||
| ERC20BridgeSource.XSigma
|
| ERC20BridgeSource.XSigma
|
||||||
@@ -515,12 +444,6 @@ export function getCurveLikeInfosForPair(
|
|||||||
case ERC20BridgeSource.CurveV2:
|
case ERC20BridgeSource.CurveV2:
|
||||||
pools = getCurveV2InfosForPair(chainId, takerToken, makerToken);
|
pools = getCurveV2InfosForPair(chainId, takerToken, makerToken);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Swerve:
|
|
||||||
pools = getSwerveInfosForPair(chainId, takerToken, makerToken);
|
|
||||||
break;
|
|
||||||
case ERC20BridgeSource.SnowSwap:
|
|
||||||
pools = getSnowSwapInfosForPair(chainId, takerToken, makerToken);
|
|
||||||
break;
|
|
||||||
case ERC20BridgeSource.Nerve:
|
case ERC20BridgeSource.Nerve:
|
||||||
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
|
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
|
||||||
break;
|
break;
|
||||||
@@ -533,9 +456,6 @@ export function getCurveLikeInfosForPair(
|
|||||||
case ERC20BridgeSource.Ellipsis:
|
case ERC20BridgeSource.Ellipsis:
|
||||||
pools = getEllipsisInfosForPair(chainId, takerToken, makerToken);
|
pools = getEllipsisInfosForPair(chainId, takerToken, makerToken);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Smoothy:
|
|
||||||
pools = getSmoothyInfosForPair(chainId, takerToken, makerToken);
|
|
||||||
break;
|
|
||||||
case ERC20BridgeSource.Saddle:
|
case ERC20BridgeSource.Saddle:
|
||||||
pools = getSaddleInfosForPair(chainId, takerToken, makerToken);
|
pools = getSaddleInfosForPair(chainId, takerToken, makerToken);
|
||||||
break;
|
break;
|
||||||
@@ -574,22 +494,22 @@ export function uniswapV2LikeRouterAddress(
|
|||||||
| ERC20BridgeSource.PancakeSwapV2
|
| ERC20BridgeSource.PancakeSwapV2
|
||||||
| ERC20BridgeSource.BakerySwap
|
| ERC20BridgeSource.BakerySwap
|
||||||
| ERC20BridgeSource.ApeSwap
|
| ERC20BridgeSource.ApeSwap
|
||||||
| ERC20BridgeSource.CafeSwap
|
|
||||||
| ERC20BridgeSource.CheeseSwap
|
| ERC20BridgeSource.CheeseSwap
|
||||||
| ERC20BridgeSource.JulSwap
|
|
||||||
| ERC20BridgeSource.QuickSwap
|
| ERC20BridgeSource.QuickSwap
|
||||||
| ERC20BridgeSource.ComethSwap
|
|
||||||
| ERC20BridgeSource.Dfyn
|
| ERC20BridgeSource.Dfyn
|
||||||
| ERC20BridgeSource.WaultSwap
|
| ERC20BridgeSource.WaultSwap
|
||||||
| ERC20BridgeSource.Polydex
|
|
||||||
| ERC20BridgeSource.ShibaSwap
|
| ERC20BridgeSource.ShibaSwap
|
||||||
| ERC20BridgeSource.JetSwap
|
|
||||||
| ERC20BridgeSource.TraderJoe
|
| ERC20BridgeSource.TraderJoe
|
||||||
| ERC20BridgeSource.Pangolin
|
| ERC20BridgeSource.Pangolin
|
||||||
| ERC20BridgeSource.UbeSwap
|
| ERC20BridgeSource.UbeSwap
|
||||||
| ERC20BridgeSource.MorpheusSwap
|
| ERC20BridgeSource.MorpheusSwap
|
||||||
| ERC20BridgeSource.SpookySwap
|
| ERC20BridgeSource.SpookySwap
|
||||||
| ERC20BridgeSource.SpiritSwap,
|
| ERC20BridgeSource.SpiritSwap
|
||||||
|
| ERC20BridgeSource.BiSwap
|
||||||
|
| ERC20BridgeSource.Yoshi
|
||||||
|
| ERC20BridgeSource.MDex
|
||||||
|
| ERC20BridgeSource.KnightSwap
|
||||||
|
| ERC20BridgeSource.MeshSwap,
|
||||||
): string {
|
): string {
|
||||||
switch (source) {
|
switch (source) {
|
||||||
case ERC20BridgeSource.UniswapV2:
|
case ERC20BridgeSource.UniswapV2:
|
||||||
@@ -606,26 +526,16 @@ export function uniswapV2LikeRouterAddress(
|
|||||||
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.ApeSwap:
|
case ERC20BridgeSource.ApeSwap:
|
||||||
return APESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return APESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.CafeSwap:
|
|
||||||
return CAFESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
|
||||||
case ERC20BridgeSource.CheeseSwap:
|
case ERC20BridgeSource.CheeseSwap:
|
||||||
return CHEESESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return CHEESESWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.JulSwap:
|
|
||||||
return JULSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
|
||||||
case ERC20BridgeSource.QuickSwap:
|
case ERC20BridgeSource.QuickSwap:
|
||||||
return QUICKSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return QUICKSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.ComethSwap:
|
|
||||||
return COMETHSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
|
||||||
case ERC20BridgeSource.Dfyn:
|
case ERC20BridgeSource.Dfyn:
|
||||||
return DFYN_ROUTER_BY_CHAIN_ID[chainId];
|
return DFYN_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.WaultSwap:
|
case ERC20BridgeSource.WaultSwap:
|
||||||
return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.Polydex:
|
|
||||||
return POLYDEX_ROUTER_BY_CHAIN_ID[chainId];
|
|
||||||
case ERC20BridgeSource.ShibaSwap:
|
case ERC20BridgeSource.ShibaSwap:
|
||||||
return SHIBASWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return SHIBASWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.JetSwap:
|
|
||||||
return JETSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
|
||||||
case ERC20BridgeSource.Pangolin:
|
case ERC20BridgeSource.Pangolin:
|
||||||
return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId];
|
return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.TraderJoe:
|
case ERC20BridgeSource.TraderJoe:
|
||||||
@@ -638,6 +548,16 @@ export function uniswapV2LikeRouterAddress(
|
|||||||
return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
case ERC20BridgeSource.SpiritSwap:
|
case ERC20BridgeSource.SpiritSwap:
|
||||||
return SPIRITSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
return SPIRITSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
|
case ERC20BridgeSource.BiSwap:
|
||||||
|
return BISWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
|
case ERC20BridgeSource.Yoshi:
|
||||||
|
return YOSHI_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
|
case ERC20BridgeSource.MeshSwap:
|
||||||
|
return MESHSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
|
case ERC20BridgeSource.MDex:
|
||||||
|
return MDEX_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
|
case ERC20BridgeSource.KnightSwap:
|
||||||
|
return KNIGHTSWAP_ROUTER_BY_CHAIN_ID[chainId];
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown UniswapV2 like source ${source}`);
|
throw new Error(`Unknown UniswapV2 like source ${source}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export function getComparisonPrices(
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
const fillFeeInEth = new BigNumber(
|
const fillFeeInEth = new BigNumber(
|
||||||
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }),
|
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }).fee,
|
||||||
);
|
);
|
||||||
const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
|
const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
|
||||||
feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);
|
feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,74 +3,17 @@ import { BigNumber, hexUtils } from '@0x/utils';
|
|||||||
|
|
||||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
||||||
|
|
||||||
import { POSITIVE_INF, SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
|
import { DEFAULT_FEE_ESTIMATE, POSITIVE_INF, SOURCE_FLAGS } from './constants';
|
||||||
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
|
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
|
||||||
|
|
||||||
// tslint:disable: prefer-for-of no-bitwise completed-docs
|
// tslint:disable: prefer-for-of no-bitwise completed-docs
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create `Fill` objects from orders and dex quotes.
|
* Converts the ETH value to an amount in output tokens.
|
||||||
|
*
|
||||||
|
* By default this prefers the outputAmountPerEth, but if this value
|
||||||
|
* is zero it will utilize the inputAmountPerEth and input.
|
||||||
*/
|
*/
|
||||||
export function createFills(opts: {
|
|
||||||
side: MarketOperation;
|
|
||||||
orders?: NativeOrderWithFillableAmounts[];
|
|
||||||
dexQuotes?: DexSample[][];
|
|
||||||
targetInput?: BigNumber;
|
|
||||||
outputAmountPerEth?: BigNumber;
|
|
||||||
inputAmountPerEth?: BigNumber;
|
|
||||||
excludedSources?: ERC20BridgeSource[];
|
|
||||||
feeSchedule?: FeeSchedule;
|
|
||||||
}): Fill[][] {
|
|
||||||
const { side } = opts;
|
|
||||||
const excludedSources = opts.excludedSources || [];
|
|
||||||
const feeSchedule = opts.feeSchedule || {};
|
|
||||||
const orders = opts.orders || [];
|
|
||||||
const dexQuotes = opts.dexQuotes || [];
|
|
||||||
const outputAmountPerEth = opts.outputAmountPerEth || ZERO_AMOUNT;
|
|
||||||
const inputAmountPerEth = opts.inputAmountPerEth || ZERO_AMOUNT;
|
|
||||||
// Create native fills.
|
|
||||||
const nativeFills = nativeOrdersToFills(
|
|
||||||
side,
|
|
||||||
orders.filter(o => o.fillableTakerAmount.isGreaterThan(0)),
|
|
||||||
opts.targetInput,
|
|
||||||
outputAmountPerEth,
|
|
||||||
inputAmountPerEth,
|
|
||||||
feeSchedule,
|
|
||||||
);
|
|
||||||
// Create DEX fills.
|
|
||||||
const dexFills = dexQuotes.map(singleSourceSamples =>
|
|
||||||
dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, feeSchedule),
|
|
||||||
);
|
|
||||||
return [...dexFills, nativeFills]
|
|
||||||
.map(p => clipFillsToInput(p, opts.targetInput))
|
|
||||||
.filter(fills => hasLiquidity(fills) && !excludedSources.includes(fills[0].source));
|
|
||||||
}
|
|
||||||
|
|
||||||
function clipFillsToInput(fills: Fill[], targetInput: BigNumber = POSITIVE_INF): Fill[] {
|
|
||||||
const clipped: Fill[] = [];
|
|
||||||
let input = ZERO_AMOUNT;
|
|
||||||
for (const fill of fills) {
|
|
||||||
if (input.gte(targetInput)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
input = input.plus(fill.input);
|
|
||||||
clipped.push(fill);
|
|
||||||
}
|
|
||||||
return clipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasLiquidity(fills: Fill[]): boolean {
|
|
||||||
if (fills.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const totalInput = BigNumber.sum(...fills.map(fill => fill.input));
|
|
||||||
const totalOutput = BigNumber.sum(...fills.map(fill => fill.output));
|
|
||||||
if (totalInput.isZero() || totalOutput.isZero()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ethToOutputAmount({
|
export function ethToOutputAmount({
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
@@ -85,122 +28,106 @@ export function ethToOutputAmount({
|
|||||||
ethAmount: BigNumber | number;
|
ethAmount: BigNumber | number;
|
||||||
}): BigNumber {
|
}): BigNumber {
|
||||||
return !outputAmountPerEth.isZero()
|
return !outputAmountPerEth.isZero()
|
||||||
? outputAmountPerEth.times(ethAmount)
|
? outputAmountPerEth.times(ethAmount).integerValue()
|
||||||
: inputAmountPerEth.times(ethAmount).times(output.dividedToIntegerBy(input));
|
: inputAmountPerEth.times(ethAmount).times(output.dividedToIntegerBy(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function nativeOrdersToFills(
|
export function nativeOrderToFill(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
orders: NativeOrderWithFillableAmounts[],
|
order: NativeOrderWithFillableAmounts,
|
||||||
targetInput: BigNumber = POSITIVE_INF,
|
targetInput: BigNumber = POSITIVE_INF,
|
||||||
outputAmountPerEth: BigNumber,
|
outputAmountPerEth: BigNumber,
|
||||||
inputAmountPerEth: BigNumber,
|
inputAmountPerEth: BigNumber,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
filterNegativeAdjustedRateOrders: boolean = true,
|
filterNegativeAdjustedRateOrders: boolean = true,
|
||||||
): Fill[] {
|
): Fill | undefined {
|
||||||
const sourcePathId = hexUtils.random();
|
const sourcePathId = hexUtils.random();
|
||||||
// Create a single path from all orders.
|
// Create a single path from all orders.
|
||||||
let fills: Array<Fill & { adjustedRate: BigNumber }> = [];
|
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = order;
|
||||||
for (const o of orders) {
|
const makerAmount = fillableMakerAmount;
|
||||||
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = o;
|
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
|
||||||
const makerAmount = fillableMakerAmount;
|
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
||||||
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
|
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
||||||
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
|
const { fee, gas } =
|
||||||
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
|
fees[ERC20BridgeSource.Native] === undefined ? DEFAULT_FEE_ESTIMATE : fees[ERC20BridgeSource.Native]!(order);
|
||||||
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
|
const outputPenalty = ethToOutputAmount({
|
||||||
const outputPenalty = ethToOutputAmount({
|
input,
|
||||||
input,
|
output,
|
||||||
output,
|
inputAmountPerEth,
|
||||||
inputAmountPerEth,
|
outputAmountPerEth,
|
||||||
outputAmountPerEth,
|
ethAmount: fee,
|
||||||
ethAmount: fee,
|
});
|
||||||
});
|
// targetInput can be less than the order size
|
||||||
// targetInput can be less than the order size
|
// whilst the penalty is constant, it affects the adjusted output
|
||||||
// whilst the penalty is constant, it affects the adjusted output
|
// only up until the target has been exhausted.
|
||||||
// only up until the target has been exhausted.
|
// A large order and an order at the exact target should be penalized
|
||||||
// A large order and an order at the exact target should be penalized
|
// the same.
|
||||||
// the same.
|
const clippedInput = BigNumber.min(targetInput, input);
|
||||||
const clippedInput = BigNumber.min(targetInput, input);
|
// scale the clipped output inline with the input
|
||||||
// scale the clipped output inline with the input
|
const clippedOutput = clippedInput.dividedBy(input).times(output);
|
||||||
const clippedOutput = clippedInput.dividedBy(input).times(output);
|
const adjustedOutput =
|
||||||
const adjustedOutput =
|
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
|
||||||
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
|
const adjustedRate =
|
||||||
const adjustedRate =
|
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
||||||
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
|
// Optionally skip orders with rates that are <= 0.
|
||||||
// Optionally skip orders with rates that are <= 0.
|
if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) {
|
||||||
if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) {
|
return undefined;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fills.push({
|
|
||||||
sourcePathId,
|
|
||||||
adjustedRate,
|
|
||||||
adjustedOutput,
|
|
||||||
input: clippedInput,
|
|
||||||
output: clippedOutput,
|
|
||||||
flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
|
|
||||||
index: 0, // TBD
|
|
||||||
parent: undefined, // TBD
|
|
||||||
source: ERC20BridgeSource.Native,
|
|
||||||
type,
|
|
||||||
fillData: { ...o },
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Sort by descending adjusted rate.
|
|
||||||
fills = fills.sort((a, b) => b.adjustedRate.comparedTo(a.adjustedRate));
|
return {
|
||||||
// Re-index fills.
|
sourcePathId,
|
||||||
for (let i = 0; i < fills.length; ++i) {
|
adjustedOutput,
|
||||||
fills[i].parent = i === 0 ? undefined : fills[i - 1];
|
input: clippedInput,
|
||||||
fills[i].index = i;
|
output: clippedOutput,
|
||||||
}
|
flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
|
||||||
return fills;
|
source: ERC20BridgeSource.Native,
|
||||||
|
type,
|
||||||
|
fillData: { ...order },
|
||||||
|
gas,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dexSamplesToFills(
|
export function dexSampleToFill(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
samples: DexSample[],
|
sample: DexSample,
|
||||||
outputAmountPerEth: BigNumber,
|
outputAmountPerEth: BigNumber,
|
||||||
inputAmountPerEth: BigNumber,
|
inputAmountPerEth: BigNumber,
|
||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
): Fill[] {
|
): Fill {
|
||||||
const sourcePathId = hexUtils.random();
|
const sourcePathId = hexUtils.random();
|
||||||
const fills: Fill[] = [];
|
const { source, fillData } = sample;
|
||||||
// Drop any non-zero entries. This can occur if the any fills on Kyber were UniswapReserves
|
const input = sample.input;
|
||||||
// We need not worry about Kyber fills going to UniswapReserve as the input amount
|
const output = sample.output;
|
||||||
// we fill is the same as we sampled. I.e we received [0,20,30] output from [1,2,3] input
|
const { fee, gas } =
|
||||||
// and we only fill [2,3] on Kyber (as 1 returns 0 output)
|
fees[source] === undefined ? DEFAULT_FEE_ESTIMATE : fees[source]!(sample.fillData) || DEFAULT_FEE_ESTIMATE;
|
||||||
const nonzeroSamples = samples.filter(q => !q.output.isZero());
|
|
||||||
for (let i = 0; i < nonzeroSamples.length; i++) {
|
|
||||||
const sample = nonzeroSamples[i];
|
|
||||||
const prevSample = i === 0 ? undefined : nonzeroSamples[i - 1];
|
|
||||||
const { source, fillData } = sample;
|
|
||||||
const input = sample.input.minus(prevSample ? prevSample.input : 0);
|
|
||||||
const output = sample.output.minus(prevSample ? prevSample.output : 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,
|
|
||||||
output,
|
|
||||||
inputAmountPerEth,
|
|
||||||
outputAmountPerEth,
|
|
||||||
ethAmount: fee,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
|
||||||
|
|
||||||
fills.push({
|
const penalty = ethToOutputAmount({
|
||||||
sourcePathId,
|
input,
|
||||||
input,
|
output,
|
||||||
output,
|
inputAmountPerEth,
|
||||||
adjustedOutput,
|
outputAmountPerEth,
|
||||||
source,
|
ethAmount: fee,
|
||||||
fillData,
|
});
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
|
||||||
index: i,
|
return {
|
||||||
parent: i !== 0 ? fills[fills.length - 1] : undefined,
|
sourcePathId,
|
||||||
flags: SOURCE_FLAGS[source],
|
input,
|
||||||
});
|
output,
|
||||||
}
|
adjustedOutput: adjustOutput(side, output, penalty),
|
||||||
return fills;
|
source,
|
||||||
|
fillData,
|
||||||
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
|
flags: SOURCE_FLAGS[source],
|
||||||
|
gas,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts the output depending on whether this is a buy or a sell.
|
||||||
|
*
|
||||||
|
* If it is a sell, than output is lowered by the adjustment.
|
||||||
|
* If it is a buy, than output is increased by adjustment.
|
||||||
|
*/
|
||||||
|
export function adjustOutput(side: MarketOperation, output: BigNumber, penalty: BigNumber): BigNumber {
|
||||||
|
return side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
|
import { MarketOperation } from '../../types';
|
||||||
|
|
||||||
|
import { Fill, FillAdjustor } from './types';
|
||||||
|
|
||||||
|
// tslint:disable:prefer-function-over-method
|
||||||
|
|
||||||
|
export class IdentityFillAdjustor implements FillAdjustor {
|
||||||
|
public adjustFills(side: MarketOperation, fills: Fill[], amount: BigNumber): Fill[] {
|
||||||
|
return fills;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,15 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants';
|
import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants';
|
||||||
import {
|
import {
|
||||||
|
AltRfqMakerAssetOfferings,
|
||||||
AssetSwapperContractAddresses,
|
AssetSwapperContractAddresses,
|
||||||
MarketOperation,
|
MarketOperation,
|
||||||
NativeOrderWithFillableAmounts,
|
NativeOrderWithFillableAmounts,
|
||||||
SignedNativeOrder,
|
SignedNativeOrder,
|
||||||
} from '../../types';
|
} 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 {
|
import {
|
||||||
getNativeAdjustedFillableAmountsFromMakerAmount,
|
getNativeAdjustedFillableAmountsFromMakerAmount,
|
||||||
getNativeAdjustedFillableAmountsFromTakerAmount,
|
getNativeAdjustedFillableAmountsFromTakerAmount,
|
||||||
@@ -38,19 +41,17 @@ import {
|
|||||||
SOURCE_FLAGS,
|
SOURCE_FLAGS,
|
||||||
ZERO_AMOUNT,
|
ZERO_AMOUNT,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import { createFills } from './fills';
|
import { IdentityFillAdjustor } from './identity_fill_adjustor';
|
||||||
import { getBestTwoHopQuote } from './multihop_utils';
|
import { getBestTwoHopQuote } from './multihop_utils';
|
||||||
import { createOrdersFromTwoHopSample } from './orders';
|
import { createOrdersFromTwoHopSample } from './orders';
|
||||||
import { Path, PathPenaltyOpts } from './path';
|
import { Path, PathPenaltyOpts } from './path';
|
||||||
import { findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
|
import { findOptimalPathFromSamples } from './path_optimizer';
|
||||||
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
import { DexOrderSampler, getSampleAmounts } from './sampler';
|
||||||
import { SourceFilters } from './source_filters';
|
import { SourceFilters } from './source_filters';
|
||||||
import {
|
import {
|
||||||
AggregationError,
|
AggregationError,
|
||||||
CollapsedFill,
|
|
||||||
DexSample,
|
DexSample,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
Fill,
|
|
||||||
GenerateOptimizedOrdersOpts,
|
GenerateOptimizedOrdersOpts,
|
||||||
GetMarketOrdersOpts,
|
GetMarketOrdersOpts,
|
||||||
MarketSideLiquidity,
|
MarketSideLiquidity,
|
||||||
@@ -59,8 +60,6 @@ import {
|
|||||||
OrderDomain,
|
OrderDomain,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
const SHOULD_USE_RUST_ROUTER = process.env.RUST_ROUTER === 'true';
|
|
||||||
|
|
||||||
// tslint:disable:boolean-naming
|
// tslint:disable:boolean-naming
|
||||||
|
|
||||||
export class MarketOperationUtils {
|
export class MarketOperationUtils {
|
||||||
@@ -164,18 +163,20 @@ export class MarketOperationUtils {
|
|||||||
// Get native order fillable amounts.
|
// Get native order fillable amounts.
|
||||||
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
||||||
// Get ETH -> maker token price.
|
// Get ETH -> maker token price.
|
||||||
this._sampler.getMedianSellRate(
|
this._sampler.getBestNativeTokenSellRate(
|
||||||
feeSourceFilters.sources,
|
feeSourceFilters.sources,
|
||||||
makerToken,
|
makerToken,
|
||||||
this._nativeFeeToken,
|
this._nativeFeeToken,
|
||||||
this._nativeFeeTokenAmount,
|
this._nativeFeeTokenAmount,
|
||||||
|
_opts.feeSchedule,
|
||||||
),
|
),
|
||||||
// Get ETH -> taker token price.
|
// Get ETH -> taker token price.
|
||||||
this._sampler.getMedianSellRate(
|
this._sampler.getBestNativeTokenSellRate(
|
||||||
feeSourceFilters.sources,
|
feeSourceFilters.sources,
|
||||||
takerToken,
|
takerToken,
|
||||||
this._nativeFeeToken,
|
this._nativeFeeToken,
|
||||||
this._nativeFeeTokenAmount,
|
this._nativeFeeTokenAmount,
|
||||||
|
_opts.feeSchedule,
|
||||||
),
|
),
|
||||||
// Get sell quotes for taker -> maker.
|
// Get sell quotes for taker -> maker.
|
||||||
this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
||||||
@@ -275,18 +276,20 @@ export class MarketOperationUtils {
|
|||||||
// Get native order fillable amounts.
|
// Get native order fillable amounts.
|
||||||
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
|
||||||
// Get ETH -> makerToken token price.
|
// Get ETH -> makerToken token price.
|
||||||
this._sampler.getMedianSellRate(
|
this._sampler.getBestNativeTokenSellRate(
|
||||||
feeSourceFilters.sources,
|
feeSourceFilters.sources,
|
||||||
makerToken,
|
makerToken,
|
||||||
this._nativeFeeToken,
|
this._nativeFeeToken,
|
||||||
this._nativeFeeTokenAmount,
|
this._nativeFeeTokenAmount,
|
||||||
|
_opts.feeSchedule,
|
||||||
),
|
),
|
||||||
// Get ETH -> taker token price.
|
// Get ETH -> taker token price.
|
||||||
this._sampler.getMedianSellRate(
|
this._sampler.getBestNativeTokenSellRate(
|
||||||
feeSourceFilters.sources,
|
feeSourceFilters.sources,
|
||||||
takerToken,
|
takerToken,
|
||||||
this._nativeFeeToken,
|
this._nativeFeeToken,
|
||||||
this._nativeFeeTokenAmount,
|
this._nativeFeeTokenAmount,
|
||||||
|
_opts.feeSchedule,
|
||||||
),
|
),
|
||||||
// Get buy quotes for taker -> maker.
|
// Get buy quotes for taker -> maker.
|
||||||
this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
|
||||||
@@ -381,11 +384,12 @@ export class MarketOperationUtils {
|
|||||||
this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
|
this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
|
||||||
),
|
),
|
||||||
...batchNativeOrders.map(orders =>
|
...batchNativeOrders.map(orders =>
|
||||||
this._sampler.getMedianSellRate(
|
this._sampler.getBestNativeTokenSellRate(
|
||||||
feeSourceFilters.sources,
|
feeSourceFilters.sources,
|
||||||
orders[0].order.takerToken,
|
orders[0].order.takerToken,
|
||||||
this._nativeFeeToken,
|
this._nativeFeeToken,
|
||||||
this._nativeFeeTokenAmount,
|
this._nativeFeeTokenAmount,
|
||||||
|
_opts.feeSchedule,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
...batchNativeOrders.map((orders, i) =>
|
...batchNativeOrders.map((orders, i) =>
|
||||||
@@ -452,6 +456,7 @@ export class MarketOperationUtils {
|
|||||||
allowFallback: _opts.allowFallback,
|
allowFallback: _opts.allowFallback,
|
||||||
gasPrice: _opts.gasPrice,
|
gasPrice: _opts.gasPrice,
|
||||||
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
||||||
|
fillAdjustor: _opts.fillAdjustor,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return optimizerResult;
|
return optimizerResult;
|
||||||
@@ -513,60 +518,38 @@ export class MarketOperationUtils {
|
|||||||
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
|
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
|
||||||
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
|
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
|
||||||
|
|
||||||
let fills: Fill[][];
|
|
||||||
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
|
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
|
||||||
let optimalPath: Path | undefined;
|
let optimalPath: Path | undefined;
|
||||||
if (SHOULD_USE_RUST_ROUTER) {
|
optimalPath = findOptimalPathFromSamples(
|
||||||
fills = [[]];
|
side,
|
||||||
optimalPath = findOptimalRustPathFromSamples(
|
dexQuotes,
|
||||||
side,
|
[...nativeOrders, ...augmentedRfqtIndicativeQuotes],
|
||||||
dexQuotes,
|
inputAmount,
|
||||||
[...nativeOrders, ...augmentedRfqtIndicativeQuotes],
|
penaltyOpts,
|
||||||
inputAmount,
|
opts.feeSchedule,
|
||||||
penaltyOpts,
|
this._sampler.chainId,
|
||||||
opts.feeSchedule,
|
opts.neonRouterNumSamples,
|
||||||
this._sampler.chainId,
|
opts.fillAdjustor,
|
||||||
opts.neonRouterNumSamples,
|
opts.samplerMetrics,
|
||||||
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(
|
const optimalPathAdjustedRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
|
||||||
side,
|
|
||||||
fills,
|
|
||||||
inputAmount,
|
|
||||||
opts.runLimit,
|
|
||||||
opts.samplerMetrics,
|
|
||||||
penaltyOpts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
|
const { adjustedRate: bestTwoHopAdjustedRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
|
||||||
|
|
||||||
const { adjustedRate: bestTwoHopRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
|
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
opts.feeSchedule,
|
opts.feeSchedule,
|
||||||
opts.exchangeProxyOverhead,
|
opts.exchangeProxyOverhead,
|
||||||
|
opts.fillAdjustor,
|
||||||
);
|
);
|
||||||
if (bestTwoHopQuote && bestTwoHopRate.isGreaterThan(optimalPathRate)) {
|
|
||||||
|
if (bestTwoHopQuote && bestTwoHopAdjustedRate.isGreaterThan(optimalPathAdjustedRate)) {
|
||||||
const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts);
|
const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts);
|
||||||
return {
|
return {
|
||||||
optimizedOrders: twoHopOrders,
|
optimizedOrders: twoHopOrders,
|
||||||
liquidityDelivered: bestTwoHopQuote,
|
liquidityDelivered: bestTwoHopQuote,
|
||||||
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
adjustedRate: bestTwoHopRate,
|
adjustedRate: bestTwoHopAdjustedRate,
|
||||||
takerAmountPerEth,
|
takerAmountPerEth,
|
||||||
makerAmountPerEth,
|
makerAmountPerEth,
|
||||||
};
|
};
|
||||||
@@ -577,19 +560,14 @@ export class MarketOperationUtils {
|
|||||||
throw new Error(AggregationError.NoOptimalPath);
|
throw new Error(AggregationError.NoOptimalPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a fallback path if required
|
const finalizedPath = optimalPath.finalize(orderOpts);
|
||||||
// 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 {
|
return {
|
||||||
optimizedOrders: collapsedPath.orders,
|
optimizedOrders: finalizedPath.orders,
|
||||||
liquidityDelivered: collapsedPath.collapsedFills as CollapsedFill[],
|
liquidityDelivered: finalizedPath.fills,
|
||||||
sourceFlags: collapsedPath.sourceFlags,
|
sourceFlags: finalizedPath.sourceFlags,
|
||||||
marketSideLiquidity,
|
marketSideLiquidity,
|
||||||
adjustedRate: optimalPathRate,
|
adjustedRate: optimalPathAdjustedRate,
|
||||||
takerAmountPerEth,
|
takerAmountPerEth,
|
||||||
makerAmountPerEth,
|
makerAmountPerEth,
|
||||||
};
|
};
|
||||||
@@ -615,6 +593,7 @@ export class MarketOperationUtils {
|
|||||||
gasPrice: _opts.gasPrice,
|
gasPrice: _opts.gasPrice,
|
||||||
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
neonRouterNumSamples: _opts.neonRouterNumSamples,
|
||||||
samplerMetrics: _opts.samplerMetrics,
|
samplerMetrics: _opts.samplerMetrics,
|
||||||
|
fillAdjustor: _opts.fillAdjustor,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (nativeOrders.length === 0) {
|
if (nativeOrders.length === 0) {
|
||||||
@@ -627,9 +606,15 @@ export class MarketOperationUtils {
|
|||||||
? this.getMarketSellLiquidityAsync.bind(this)
|
? this.getMarketSellLiquidityAsync.bind(this)
|
||||||
: this.getMarketBuyLiquidityAsync.bind(this);
|
: this.getMarketBuyLiquidityAsync.bind(this);
|
||||||
const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
const marketSideLiquidity: MarketSideLiquidity = await marketLiquidityFnAsync(nativeOrders, amount, _opts);
|
||||||
|
|
||||||
|
// Phase 1 Routing
|
||||||
|
// We find an optimized path for ALL the DEX and open-orderbook liquidity
|
||||||
let optimizerResult: OptimizerResult | undefined;
|
let optimizerResult: OptimizerResult | undefined;
|
||||||
try {
|
try {
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, {
|
||||||
|
...optimizerOpts,
|
||||||
|
fillAdjustor: new IdentityFillAdjustor(),
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// If no on-chain or off-chain Open Orderbook orders are present, a `NoOptimalPath` will be thrown.
|
// If no on-chain or off-chain Open Orderbook orders are present, a `NoOptimalPath` will be thrown.
|
||||||
// If this happens at this stage, there is still a chance that an RFQ order is fillable, therefore
|
// If this happens at this stage, there is still a chance that an RFQ order is fillable, therefore
|
||||||
@@ -653,6 +638,17 @@ export class MarketOperationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If RFQ liquidity is enabled, make a request to check RFQ liquidity against the first optimizer result
|
// If RFQ liquidity is enabled, make a request to check RFQ liquidity against the first optimizer result
|
||||||
|
|
||||||
|
// Phase 2 Routing
|
||||||
|
// Mix in any off-chain RFQ quotes
|
||||||
|
// Apply any fill adjustments i
|
||||||
|
const phaseTwoOptimizerOpts = {
|
||||||
|
...optimizerOpts,
|
||||||
|
// Pass in the FillAdjustor for Phase 2 adjustment, in the future we may perform this adjustment
|
||||||
|
// in Phase 1.
|
||||||
|
fillAdjustor: _opts.fillAdjustor,
|
||||||
|
};
|
||||||
|
|
||||||
const { rfqt } = _opts;
|
const { rfqt } = _opts;
|
||||||
if (
|
if (
|
||||||
marketSideLiquidity.isRfqSupported &&
|
marketSideLiquidity.isRfqSupported &&
|
||||||
@@ -663,17 +659,49 @@ export class MarketOperationUtils {
|
|||||||
// Timing of RFQT lifecycle
|
// Timing of RFQT lifecycle
|
||||||
const timeStart = new Date().getTime();
|
const timeStart = new Date().getTime();
|
||||||
const { makerToken, takerToken } = nativeOrders[0].order;
|
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) {
|
if (rfqt.isIndicative) {
|
||||||
// An indicative quote is being requested, and indicative quotes price-aware enabled
|
// 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.
|
// Make the RFQT request and then re-run the sampler if new orders come back.
|
||||||
const indicativeQuotes = await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
|
|
||||||
makerToken,
|
const indicativeQuotes =
|
||||||
takerToken,
|
rfqt.rfqClient !== undefined
|
||||||
amount,
|
? ((
|
||||||
side,
|
await rfqt.rfqClient.getV1PricesAsync({
|
||||||
wholeOrderPrice,
|
altRfqAssetOfferings: filteredOfferings,
|
||||||
rfqt,
|
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;
|
const deltaTime = new Date().getTime() - timeStart;
|
||||||
DEFAULT_INFO_LOGGER({
|
DEFAULT_INFO_LOGGER({
|
||||||
rfqQuoteType: 'indicative',
|
rfqQuoteType: 'indicative',
|
||||||
@@ -681,20 +709,57 @@ export class MarketOperationUtils {
|
|||||||
});
|
});
|
||||||
// Re-run optimizer with the new indicative quote
|
// Re-run optimizer with the new indicative quote
|
||||||
if (indicativeQuotes.length > 0) {
|
if (indicativeQuotes.length > 0) {
|
||||||
|
// Attach the indicative quotes to the market side liquidity
|
||||||
marketSideLiquidity.quotes.rfqtIndicativeQuotes = indicativeQuotes;
|
marketSideLiquidity.quotes.rfqtIndicativeQuotes = indicativeQuotes;
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
|
||||||
|
// Phase 2 Routing
|
||||||
|
const phase1OptimalSources = optimizerResult
|
||||||
|
? optimizerResult.optimizedOrders.map(o => o.source)
|
||||||
|
: [];
|
||||||
|
const phase2MarketSideLiquidity: MarketSideLiquidity = {
|
||||||
|
...marketSideLiquidity,
|
||||||
|
quotes: {
|
||||||
|
...marketSideLiquidity.quotes,
|
||||||
|
// Select only the quotes that were chosen in Phase 1
|
||||||
|
dexQuotes: marketSideLiquidity.quotes.dexQuotes.filter(
|
||||||
|
q => q.length > 0 && phase1OptimalSources.includes(q[0].source),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
optimizerResult = await this._generateOptimizedOrdersAsync(
|
||||||
|
phase2MarketSideLiquidity,
|
||||||
|
phaseTwoOptimizerOpts,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// A firm quote is being requested, and firm quotes price-aware enabled.
|
// A firm quote is being requested, and firm quotes price-aware enabled.
|
||||||
// Ensure that `intentOnFilling` is enabled and make the request.
|
// Ensure that `intentOnFilling` is enabled and make the request.
|
||||||
const firmQuotes = await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
|
const firmQuotes =
|
||||||
makerToken,
|
rfqt.rfqClient !== undefined
|
||||||
takerToken,
|
? (
|
||||||
amount,
|
await rfqt.rfqClient.getV1QuotesAsync({
|
||||||
side,
|
altRfqAssetOfferings: filteredOfferings,
|
||||||
wholeOrderPrice,
|
assetFillAmount: amount,
|
||||||
rfqt,
|
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;
|
const deltaTime = new Date().getTime() - timeStart;
|
||||||
DEFAULT_INFO_LOGGER({
|
DEFAULT_INFO_LOGGER({
|
||||||
rfqQuoteType: 'firm',
|
rfqQuoteType: 'firm',
|
||||||
@@ -723,6 +788,8 @@ export class MarketOperationUtils {
|
|||||||
fillableTakerFeeAmount: ZERO_AMOUNT,
|
fillableTakerFeeAmount: ZERO_AMOUNT,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Attach the firm RFQt quotes to the market side liquidity
|
||||||
marketSideLiquidity.quotes.nativeOrders = [
|
marketSideLiquidity.quotes.nativeOrders = [
|
||||||
...quotesWithOrderFillableAmounts,
|
...quotesWithOrderFillableAmounts,
|
||||||
...marketSideLiquidity.quotes.nativeOrders,
|
...marketSideLiquidity.quotes.nativeOrders,
|
||||||
@@ -731,7 +798,27 @@ export class MarketOperationUtils {
|
|||||||
// Re-run optimizer with the new firm quote. This is the second and last time
|
// Re-run optimizer with the new firm quote. This is the second and last time
|
||||||
// we run the optimized in a block of code. In this case, we don't catch a potential `NoOptimalPath` exception
|
// we run the optimized in a block of code. In this case, we don't catch a potential `NoOptimalPath` exception
|
||||||
// and we let it bubble up if it happens.
|
// and we let it bubble up if it happens.
|
||||||
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
|
|
||||||
|
// Phase 2 Routing
|
||||||
|
// Optimization: Filter by what is already currently in the Phase1 output as it doesn't
|
||||||
|
// seem possible that inclusion of RFQT could impact the sources chosen from Phase 1.
|
||||||
|
const phase1OptimalSources = optimizerResult
|
||||||
|
? optimizerResult.optimizedOrders.map(o => o.source)
|
||||||
|
: [];
|
||||||
|
const phase2MarketSideLiquidity: MarketSideLiquidity = {
|
||||||
|
...marketSideLiquidity,
|
||||||
|
quotes: {
|
||||||
|
...marketSideLiquidity.quotes,
|
||||||
|
// Select only the quotes that were chosen in Phase 1
|
||||||
|
dexQuotes: marketSideLiquidity.quotes.dexQuotes.filter(
|
||||||
|
q => q.length > 0 && phase1OptimalSources.includes(q[0].source),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
optimizerResult = await this._generateOptimizedOrdersAsync(
|
||||||
|
phase2MarketSideLiquidity,
|
||||||
|
phaseTwoOptimizerOpts,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -775,84 +862,10 @@ export class MarketOperationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
|
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
|
||||||
void Promise.all(
|
_.values(this._sampler.poolsCaches)
|
||||||
Object.values(this._sampler.poolsCaches).map(async cache => {
|
.filter(cache => cache !== undefined && !cache.isFresh(takerToken, makerToken))
|
||||||
if (cache.isFresh(takerToken, makerToken)) {
|
.forEach(cache => cache?.getFreshPoolsForPairAsync(takerToken, makerToken));
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* 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,
|
|
||||||
inputAmount: BigNumber,
|
|
||||||
optimalPath: Path,
|
|
||||||
dexQuotes: DexSample[][],
|
|
||||||
fills: Fill[][],
|
|
||||||
opts: GenerateOptimizedOrdersOpts,
|
|
||||||
penaltyOpts: PathPenaltyOpts,
|
|
||||||
): Promise<void> {
|
|
||||||
const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
|
|
||||||
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
|
|
||||||
// Generate a fallback path if sources requiring a fallback (fragile) are in the optimal path.
|
|
||||||
// Native is relatively fragile (limit order collision, expiry, or lack of available maker balance)
|
|
||||||
// LiquidityProvider is relatively fragile (collision)
|
|
||||||
const fragileSources = [ERC20BridgeSource.Native, ERC20BridgeSource.LiquidityProvider];
|
|
||||||
const fragileFills = optimalPath.fills.filter(f => fragileSources.includes(f.source));
|
|
||||||
if (opts.allowFallback && fragileFills.length !== 0) {
|
|
||||||
// We create a fallback path that is exclusive of Native liquidity
|
|
||||||
// This is the optimal on-chain path for the entire input amount
|
|
||||||
const sturdyPenaltyOpts = {
|
|
||||||
...penaltyOpts,
|
|
||||||
exchangeProxyOverhead: (sourceFlags: bigint) =>
|
|
||||||
// tslint:disable-next-line: no-bitwise
|
|
||||||
penaltyOpts.exchangeProxyOverhead(sourceFlags | optimalPath.sourceFlags),
|
|
||||||
};
|
|
||||||
|
|
||||||
let sturdyOptimalPath: Path | undefined;
|
|
||||||
if (SHOULD_USE_RUST_ROUTER) {
|
|
||||||
const sturdySamples = dexQuotes.filter(
|
|
||||||
samples => samples.length > 0 && !fragileSources.includes(samples[0].source),
|
|
||||||
);
|
|
||||||
sturdyOptimalPath = findOptimalRustPathFromSamples(
|
|
||||||
side,
|
|
||||||
sturdySamples,
|
|
||||||
[],
|
|
||||||
inputAmount,
|
|
||||||
sturdyPenaltyOpts,
|
|
||||||
opts.feeSchedule,
|
|
||||||
this._sampler.chainId,
|
|
||||||
opts.neonRouterNumSamples,
|
|
||||||
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
|
|
||||||
sturdyOptimalPath = await findOptimalPathJSAsync(
|
|
||||||
side,
|
|
||||||
sturdyFills,
|
|
||||||
inputAmount,
|
|
||||||
opts.runLimit,
|
|
||||||
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
|
|
||||||
sturdyPenaltyOpts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Calculate the slippage of on-chain sources compared to the most optimal path
|
|
||||||
// if within an acceptable threshold we enable a fallback to prevent reverts
|
|
||||||
if (
|
|
||||||
sturdyOptimalPath !== undefined &&
|
|
||||||
(fragileFills.length === optimalPath.fills.length ||
|
|
||||||
sturdyOptimalPath.adjustedSlippage(optimalPathRate) <= maxFallbackSlippage)
|
|
||||||
) {
|
|
||||||
optimalPath.addFallback(sturdyOptimalPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable: max-file-line-count
|
// tslint:disable: max-file-line-count
|
||||||
|
|||||||
@@ -9,28 +9,11 @@ import {
|
|||||||
DexSample,
|
DexSample,
|
||||||
ExchangeProxyOverhead,
|
ExchangeProxyOverhead,
|
||||||
FeeSchedule,
|
FeeSchedule,
|
||||||
|
FillAdjustor,
|
||||||
MarketSideLiquidity,
|
MarketSideLiquidity,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
TokenAdjacencyGraph,
|
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a token pair, returns the intermediate tokens to consider for two-hop routes.
|
|
||||||
*/
|
|
||||||
export function getIntermediateTokens(
|
|
||||||
makerToken: string,
|
|
||||||
takerToken: string,
|
|
||||||
tokenAdjacencyGraph: TokenAdjacencyGraph,
|
|
||||||
): string[] {
|
|
||||||
const intermediateTokens = _.union(
|
|
||||||
_.get(tokenAdjacencyGraph, takerToken, tokenAdjacencyGraph.default),
|
|
||||||
_.get(tokenAdjacencyGraph, makerToken, tokenAdjacencyGraph.default),
|
|
||||||
);
|
|
||||||
return _.uniqBy(intermediateTokens, a => a.toLowerCase()).filter(
|
|
||||||
token => token.toLowerCase() !== makerToken.toLowerCase() && token.toLowerCase() !== takerToken.toLowerCase(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the best two-hop quote and the fee-adjusted rate of that quote.
|
* Returns the best two-hop quote and the fee-adjusted rate of that quote.
|
||||||
*/
|
*/
|
||||||
@@ -38,6 +21,7 @@ export function getBestTwoHopQuote(
|
|||||||
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
|
||||||
feeSchedule?: FeeSchedule,
|
feeSchedule?: FeeSchedule,
|
||||||
exchangeProxyOverhead?: ExchangeProxyOverhead,
|
exchangeProxyOverhead?: ExchangeProxyOverhead,
|
||||||
|
fillAdjustor?: FillAdjustor,
|
||||||
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
|
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
|
||||||
const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
|
const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
|
||||||
const { twoHopQuotes } = quotes;
|
const { twoHopQuotes } = quotes;
|
||||||
@@ -57,7 +41,15 @@ export function getBestTwoHopQuote(
|
|||||||
}
|
}
|
||||||
const best = filteredQuotes
|
const best = filteredQuotes
|
||||||
.map(quote =>
|
.map(quote =>
|
||||||
getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
|
getTwoHopAdjustedRate(
|
||||||
|
side,
|
||||||
|
quote,
|
||||||
|
inputAmount,
|
||||||
|
outputAmountPerEth,
|
||||||
|
feeSchedule,
|
||||||
|
exchangeProxyOverhead,
|
||||||
|
fillAdjustor,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.reduce(
|
.reduce(
|
||||||
(prev, curr, i) =>
|
(prev, curr, i) =>
|
||||||
@@ -70,6 +62,7 @@ export function getBestTwoHopQuote(
|
|||||||
outputAmountPerEth,
|
outputAmountPerEth,
|
||||||
feeSchedule,
|
feeSchedule,
|
||||||
exchangeProxyOverhead,
|
exchangeProxyOverhead,
|
||||||
|
fillAdjustor,
|
||||||
),
|
),
|
||||||
quote: filteredQuotes[0],
|
quote: filteredQuotes[0],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||||
import { AbiEncoder, BigNumber } from '@0x/utils';
|
import { AbiEncoder, BigNumber } from '@0x/utils';
|
||||||
|
import _ = require('lodash');
|
||||||
|
|
||||||
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
|
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
|
||||||
|
|
||||||
@@ -8,36 +9,40 @@ import {
|
|||||||
AaveV2FillData,
|
AaveV2FillData,
|
||||||
AggregationError,
|
AggregationError,
|
||||||
BalancerFillData,
|
BalancerFillData,
|
||||||
|
BalancerV2BatchSwapFillData,
|
||||||
BalancerV2FillData,
|
BalancerV2FillData,
|
||||||
BancorFillData,
|
BancorFillData,
|
||||||
CollapsedFill,
|
|
||||||
CompoundFillData,
|
CompoundFillData,
|
||||||
CurveFillData,
|
CurveFillData,
|
||||||
DexSample,
|
DexSample,
|
||||||
DODOFillData,
|
DODOFillData,
|
||||||
ERC20BridgeSource,
|
ERC20BridgeSource,
|
||||||
|
Fill,
|
||||||
FillData,
|
FillData,
|
||||||
FinalUniswapV3FillData,
|
FinalUniswapV3FillData,
|
||||||
GeistFillData,
|
GeistFillData,
|
||||||
GenericRouterFillData,
|
GenericRouterFillData,
|
||||||
|
GMXFillData,
|
||||||
KyberDmmFillData,
|
KyberDmmFillData,
|
||||||
KyberFillData,
|
|
||||||
LidoFillData,
|
LidoFillData,
|
||||||
LiquidityProviderFillData,
|
LiquidityProviderFillData,
|
||||||
MakerPsmFillData,
|
MakerPsmFillData,
|
||||||
MooniswapFillData,
|
MooniswapFillData,
|
||||||
MultiHopFillData,
|
MultiHopFillData,
|
||||||
NativeCollapsedFill,
|
NativeFillData,
|
||||||
NativeLimitOrderFillData,
|
NativeLimitOrderFillData,
|
||||||
NativeRfqOrderFillData,
|
NativeRfqOrderFillData,
|
||||||
OptimizedMarketBridgeOrder,
|
OptimizedMarketBridgeOrder,
|
||||||
OptimizedMarketOrder,
|
OptimizedMarketOrder,
|
||||||
OptimizedMarketOrderBase,
|
OptimizedMarketOrderBase,
|
||||||
OrderDomain,
|
OrderDomain,
|
||||||
|
PlatypusFillData,
|
||||||
ShellFillData,
|
ShellFillData,
|
||||||
|
SynthetixFillData,
|
||||||
UniswapV2FillData,
|
UniswapV2FillData,
|
||||||
UniswapV3FillData,
|
UniswapV3FillData,
|
||||||
UniswapV3PathAmount,
|
UniswapV3PathAmount,
|
||||||
|
VelodromeFillData,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
// tslint:disable completed-docs
|
// tslint:disable completed-docs
|
||||||
@@ -57,23 +62,27 @@ export function createOrdersFromTwoHopSample(
|
|||||||
): OptimizedMarketOrder[] {
|
): OptimizedMarketOrder[] {
|
||||||
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
||||||
const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData;
|
const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData;
|
||||||
const firstHopFill: CollapsedFill = {
|
const firstHopFill: Fill = {
|
||||||
sourcePathId: '',
|
sourcePathId: '',
|
||||||
source: firstHopSource.source,
|
source: firstHopSource.source,
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT,
|
input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT,
|
||||||
output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
|
output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
|
||||||
subFills: [],
|
adjustedOutput: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
|
||||||
fillData: firstHopSource.fillData,
|
fillData: firstHopSource.fillData,
|
||||||
|
flags: BigInt(0),
|
||||||
|
gas: 1,
|
||||||
};
|
};
|
||||||
const secondHopFill: CollapsedFill = {
|
const secondHopFill: Fill = {
|
||||||
sourcePathId: '',
|
sourcePathId: '',
|
||||||
source: secondHopSource.source,
|
source: secondHopSource.source,
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input,
|
input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input,
|
||||||
output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
|
output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
|
||||||
subFills: [],
|
adjustedOutput: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
|
||||||
fillData: secondHopSource.fillData,
|
fillData: secondHopSource.fillData,
|
||||||
|
flags: BigInt(0),
|
||||||
|
gas: 1,
|
||||||
};
|
};
|
||||||
return [
|
return [
|
||||||
createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
|
createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
|
||||||
@@ -86,11 +95,9 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
case ERC20BridgeSource.Balancer:
|
case ERC20BridgeSource.Balancer:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
|
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
|
||||||
case ERC20BridgeSource.BalancerV2:
|
case ERC20BridgeSource.BalancerV2:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'BalancerV2');
|
return encodeBridgeSourceId(BridgeProtocol.BalancerV2Batch, 'BalancerV2');
|
||||||
case ERC20BridgeSource.Bancor:
|
case ERC20BridgeSource.Bancor:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
|
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
|
||||||
// case ERC20BridgeSource.CoFiX:
|
|
||||||
// return encodeBridgeSourceId(BridgeProtocol.CoFiX, 'CoFiX');
|
|
||||||
case ERC20BridgeSource.Curve:
|
case ERC20BridgeSource.Curve:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
|
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
|
||||||
case ERC20BridgeSource.Cream:
|
case ERC20BridgeSource.Cream:
|
||||||
@@ -99,8 +106,6 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom');
|
return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom');
|
||||||
case ERC20BridgeSource.Dodo:
|
case ERC20BridgeSource.Dodo:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo');
|
return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo');
|
||||||
case ERC20BridgeSource.Kyber:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber');
|
|
||||||
case ERC20BridgeSource.LiquidityProvider:
|
case ERC20BridgeSource.LiquidityProvider:
|
||||||
// "LiquidityProvider" is too long to encode (17 characters).
|
// "LiquidityProvider" is too long to encode (17 characters).
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP');
|
return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP');
|
||||||
@@ -110,24 +115,16 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap');
|
return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap');
|
||||||
case ERC20BridgeSource.MStable:
|
case ERC20BridgeSource.MStable:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable');
|
return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable');
|
||||||
case ERC20BridgeSource.Eth2Dai:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Oasis, 'Eth2Dai');
|
|
||||||
case ERC20BridgeSource.Shell:
|
case ERC20BridgeSource.Shell:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell');
|
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell');
|
||||||
case ERC20BridgeSource.SnowSwap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'SnowSwap');
|
|
||||||
case ERC20BridgeSource.SushiSwap:
|
case ERC20BridgeSource.SushiSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap');
|
||||||
case ERC20BridgeSource.Swerve:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Swerve');
|
|
||||||
case ERC20BridgeSource.Uniswap:
|
case ERC20BridgeSource.Uniswap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap');
|
return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap');
|
||||||
case ERC20BridgeSource.UniswapV2:
|
case ERC20BridgeSource.UniswapV2:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2');
|
||||||
case ERC20BridgeSource.DodoV2:
|
case ERC20BridgeSource.DodoV2:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2');
|
return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2');
|
||||||
case ERC20BridgeSource.Linkswap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Linkswap');
|
|
||||||
case ERC20BridgeSource.PancakeSwap:
|
case ERC20BridgeSource.PancakeSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap');
|
||||||
case ERC20BridgeSource.PancakeSwapV2:
|
case ERC20BridgeSource.PancakeSwapV2:
|
||||||
@@ -144,44 +141,32 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis');
|
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis');
|
||||||
case ERC20BridgeSource.Component:
|
case ERC20BridgeSource.Component:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Component');
|
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Component');
|
||||||
case ERC20BridgeSource.Smoothy:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Smoothy');
|
|
||||||
case ERC20BridgeSource.Saddle:
|
case ERC20BridgeSource.Saddle:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle');
|
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle');
|
||||||
case ERC20BridgeSource.XSigma:
|
case ERC20BridgeSource.XSigma:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Curve, 'xSigma');
|
return encodeBridgeSourceId(BridgeProtocol.Curve, 'xSigma');
|
||||||
case ERC20BridgeSource.ApeSwap:
|
case ERC20BridgeSource.ApeSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ApeSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ApeSwap');
|
||||||
case ERC20BridgeSource.CafeSwap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CafeSwap');
|
|
||||||
case ERC20BridgeSource.CheeseSwap:
|
case ERC20BridgeSource.CheeseSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CheeseSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CheeseSwap');
|
||||||
case ERC20BridgeSource.JulSwap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JulSwap');
|
|
||||||
case ERC20BridgeSource.UniswapV3:
|
case ERC20BridgeSource.UniswapV3:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3');
|
||||||
case ERC20BridgeSource.KyberDmm:
|
case ERC20BridgeSource.KyberDmm:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.KyberDmm, 'KyberDmm');
|
return encodeBridgeSourceId(BridgeProtocol.KyberDmm, 'KyberDmm');
|
||||||
case ERC20BridgeSource.QuickSwap:
|
case ERC20BridgeSource.QuickSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'QuickSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'QuickSwap');
|
||||||
case ERC20BridgeSource.ComethSwap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ComethSwap');
|
|
||||||
case ERC20BridgeSource.Dfyn:
|
case ERC20BridgeSource.Dfyn:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Dfyn');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Dfyn');
|
||||||
case ERC20BridgeSource.CurveV2:
|
case ERC20BridgeSource.CurveV2:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.CurveV2, 'CurveV2');
|
return encodeBridgeSourceId(BridgeProtocol.CurveV2, 'CurveV2');
|
||||||
case ERC20BridgeSource.WaultSwap:
|
case ERC20BridgeSource.WaultSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'WaultSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'WaultSwap');
|
||||||
case ERC20BridgeSource.Polydex:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Polydex');
|
|
||||||
case ERC20BridgeSource.FirebirdOneSwap:
|
case ERC20BridgeSource.FirebirdOneSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'FirebirdOneSwap');
|
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'FirebirdOneSwap');
|
||||||
case ERC20BridgeSource.Lido:
|
case ERC20BridgeSource.Lido:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Lido, 'Lido');
|
return encodeBridgeSourceId(BridgeProtocol.Lido, 'Lido');
|
||||||
case ERC20BridgeSource.ShibaSwap:
|
case ERC20BridgeSource.ShibaSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ShibaSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ShibaSwap');
|
||||||
case ERC20BridgeSource.JetSwap:
|
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JetSwap');
|
|
||||||
case ERC20BridgeSource.IronSwap:
|
case ERC20BridgeSource.IronSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap');
|
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap');
|
||||||
case ERC20BridgeSource.ACryptos:
|
case ERC20BridgeSource.ACryptos:
|
||||||
@@ -200,6 +185,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
|
||||||
case ERC20BridgeSource.MorpheusSwap:
|
case ERC20BridgeSource.MorpheusSwap:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
|
||||||
|
case ERC20BridgeSource.Yoshi:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Yoshi');
|
||||||
case ERC20BridgeSource.AaveV2:
|
case ERC20BridgeSource.AaveV2:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
|
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
|
||||||
case ERC20BridgeSource.Compound:
|
case ERC20BridgeSource.Compound:
|
||||||
@@ -208,6 +195,24 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
|
|||||||
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'Geist');
|
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'Geist');
|
||||||
case ERC20BridgeSource.MobiusMoney:
|
case ERC20BridgeSource.MobiusMoney:
|
||||||
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
|
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
|
||||||
|
case ERC20BridgeSource.BiSwap:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BiSwap');
|
||||||
|
case ERC20BridgeSource.MDex:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MDex');
|
||||||
|
case ERC20BridgeSource.KnightSwap:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'KnightSwap');
|
||||||
|
case ERC20BridgeSource.GMX:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.GMX, 'GMX');
|
||||||
|
case ERC20BridgeSource.Platypus:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.Platypus, 'Platypus');
|
||||||
|
case ERC20BridgeSource.MeshSwap:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MeshSwap');
|
||||||
|
case ERC20BridgeSource.BancorV3:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.BancorV3, 'BancorV3');
|
||||||
|
case ERC20BridgeSource.Velodrome:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.Velodrome, 'Velodrome');
|
||||||
|
case ERC20BridgeSource.Synthetix:
|
||||||
|
return encodeBridgeSourceId(BridgeProtocol.Synthetix, 'Synthetix');
|
||||||
default:
|
default:
|
||||||
throw new Error(AggregationError.NoBridgeForSource);
|
throw new Error(AggregationError.NoBridgeForSource);
|
||||||
}
|
}
|
||||||
@@ -231,13 +236,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
switch (order.source) {
|
switch (order.source) {
|
||||||
case ERC20BridgeSource.Curve:
|
case ERC20BridgeSource.Curve:
|
||||||
case ERC20BridgeSource.CurveV2:
|
case ERC20BridgeSource.CurveV2:
|
||||||
case ERC20BridgeSource.Swerve:
|
|
||||||
case ERC20BridgeSource.SnowSwap:
|
|
||||||
case ERC20BridgeSource.Nerve:
|
case ERC20BridgeSource.Nerve:
|
||||||
case ERC20BridgeSource.Synapse:
|
case ERC20BridgeSource.Synapse:
|
||||||
case ERC20BridgeSource.Belt:
|
case ERC20BridgeSource.Belt:
|
||||||
case ERC20BridgeSource.Ellipsis:
|
case ERC20BridgeSource.Ellipsis:
|
||||||
case ERC20BridgeSource.Smoothy:
|
|
||||||
case ERC20BridgeSource.Saddle:
|
case ERC20BridgeSource.Saddle:
|
||||||
case ERC20BridgeSource.XSigma:
|
case ERC20BridgeSource.XSigma:
|
||||||
case ERC20BridgeSource.FirebirdOneSwap:
|
case ERC20BridgeSource.FirebirdOneSwap:
|
||||||
@@ -258,9 +260,18 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
bridgeData = encoder.encode([balancerFillData.poolAddress]);
|
bridgeData = encoder.encode([balancerFillData.poolAddress]);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.BalancerV2:
|
case ERC20BridgeSource.BalancerV2:
|
||||||
|
{
|
||||||
|
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2BatchSwapFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([
|
||||||
|
balancerV2FillData.vault,
|
||||||
|
balancerV2FillData.swapSteps,
|
||||||
|
balancerV2FillData.assets,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ERC20BridgeSource.Beethovenx:
|
case ERC20BridgeSource.Beethovenx:
|
||||||
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
|
const beethovenFillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
|
||||||
const { vault, poolId } = balancerV2FillData;
|
const { vault, poolId } = beethovenFillData;
|
||||||
bridgeData = encoder.encode([vault, poolId]);
|
bridgeData = encoder.encode([vault, poolId]);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Bancor:
|
case ERC20BridgeSource.Bancor:
|
||||||
@@ -270,34 +281,29 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
case ERC20BridgeSource.UniswapV2:
|
case ERC20BridgeSource.UniswapV2:
|
||||||
case ERC20BridgeSource.SushiSwap:
|
case ERC20BridgeSource.SushiSwap:
|
||||||
case ERC20BridgeSource.CryptoCom:
|
case ERC20BridgeSource.CryptoCom:
|
||||||
case ERC20BridgeSource.Linkswap:
|
|
||||||
case ERC20BridgeSource.PancakeSwap:
|
case ERC20BridgeSource.PancakeSwap:
|
||||||
case ERC20BridgeSource.PancakeSwapV2:
|
case ERC20BridgeSource.PancakeSwapV2:
|
||||||
case ERC20BridgeSource.BakerySwap:
|
case ERC20BridgeSource.BakerySwap:
|
||||||
case ERC20BridgeSource.ApeSwap:
|
case ERC20BridgeSource.ApeSwap:
|
||||||
case ERC20BridgeSource.CafeSwap:
|
|
||||||
case ERC20BridgeSource.CheeseSwap:
|
case ERC20BridgeSource.CheeseSwap:
|
||||||
case ERC20BridgeSource.JulSwap:
|
|
||||||
case ERC20BridgeSource.QuickSwap:
|
case ERC20BridgeSource.QuickSwap:
|
||||||
case ERC20BridgeSource.ComethSwap:
|
|
||||||
case ERC20BridgeSource.Dfyn:
|
case ERC20BridgeSource.Dfyn:
|
||||||
case ERC20BridgeSource.WaultSwap:
|
case ERC20BridgeSource.WaultSwap:
|
||||||
case ERC20BridgeSource.Polydex:
|
|
||||||
case ERC20BridgeSource.ShibaSwap:
|
case ERC20BridgeSource.ShibaSwap:
|
||||||
case ERC20BridgeSource.JetSwap:
|
|
||||||
case ERC20BridgeSource.Pangolin:
|
case ERC20BridgeSource.Pangolin:
|
||||||
case ERC20BridgeSource.TraderJoe:
|
case ERC20BridgeSource.TraderJoe:
|
||||||
case ERC20BridgeSource.UbeSwap:
|
case ERC20BridgeSource.UbeSwap:
|
||||||
case ERC20BridgeSource.SpiritSwap:
|
case ERC20BridgeSource.SpiritSwap:
|
||||||
case ERC20BridgeSource.SpookySwap:
|
case ERC20BridgeSource.SpookySwap:
|
||||||
case ERC20BridgeSource.MorpheusSwap:
|
case ERC20BridgeSource.MorpheusSwap:
|
||||||
|
case ERC20BridgeSource.BiSwap:
|
||||||
|
case ERC20BridgeSource.MDex:
|
||||||
|
case ERC20BridgeSource.KnightSwap:
|
||||||
|
case ERC20BridgeSource.Yoshi:
|
||||||
|
case ERC20BridgeSource.MeshSwap:
|
||||||
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
|
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
|
||||||
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
|
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Kyber:
|
|
||||||
const kyberFillData = (order as OptimizedMarketBridgeOrder<KyberFillData>).fillData;
|
|
||||||
bridgeData = encoder.encode([kyberFillData.networkProxy, kyberFillData.hint]);
|
|
||||||
break;
|
|
||||||
case ERC20BridgeSource.Mooniswap:
|
case ERC20BridgeSource.Mooniswap:
|
||||||
const mooniswapFillData = (order as OptimizedMarketBridgeOrder<MooniswapFillData>).fillData;
|
const mooniswapFillData = (order as OptimizedMarketBridgeOrder<MooniswapFillData>).fillData;
|
||||||
bridgeData = encoder.encode([mooniswapFillData.poolAddress]);
|
bridgeData = encoder.encode([mooniswapFillData.poolAddress]);
|
||||||
@@ -327,10 +333,6 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
const uniFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
const uniFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
||||||
bridgeData = encoder.encode([uniFillData.router]);
|
bridgeData = encoder.encode([uniFillData.router]);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Eth2Dai:
|
|
||||||
const oasisFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
|
||||||
bridgeData = encoder.encode([oasisFillData.router]);
|
|
||||||
break;
|
|
||||||
case ERC20BridgeSource.MStable:
|
case ERC20BridgeSource.MStable:
|
||||||
const mStableFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
const mStableFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
|
||||||
bridgeData = encoder.encode([mStableFillData.router]);
|
bridgeData = encoder.encode([mStableFillData.router]);
|
||||||
@@ -353,7 +355,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.Lido:
|
case ERC20BridgeSource.Lido:
|
||||||
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
|
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
|
||||||
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
|
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress, lidoFillData.wstEthTokenAddress]);
|
||||||
break;
|
break;
|
||||||
case ERC20BridgeSource.AaveV2:
|
case ERC20BridgeSource.AaveV2:
|
||||||
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
|
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
|
||||||
@@ -367,34 +369,223 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
|
|||||||
const geistFillData = (order as OptimizedMarketBridgeOrder<GeistFillData>).fillData;
|
const geistFillData = (order as OptimizedMarketBridgeOrder<GeistFillData>).fillData;
|
||||||
bridgeData = encoder.encode([geistFillData.lendingPool, geistFillData.gToken]);
|
bridgeData = encoder.encode([geistFillData.lendingPool, geistFillData.gToken]);
|
||||||
break;
|
break;
|
||||||
|
case ERC20BridgeSource.GMX:
|
||||||
|
const gmxFillData = (order as OptimizedMarketBridgeOrder<GMXFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([
|
||||||
|
gmxFillData.router,
|
||||||
|
gmxFillData.reader,
|
||||||
|
gmxFillData.vault,
|
||||||
|
gmxFillData.tokenAddressPath,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case ERC20BridgeSource.Platypus:
|
||||||
|
const platypusFillData = (order as OptimizedMarketBridgeOrder<PlatypusFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([
|
||||||
|
platypusFillData.router,
|
||||||
|
platypusFillData.pool,
|
||||||
|
platypusFillData.tokenAddressPath,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
case ERC20BridgeSource.BancorV3:
|
||||||
|
const bancorV3FillData = (order as OptimizedMarketBridgeOrder<BancorFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([bancorV3FillData.networkAddress, bancorV3FillData.path]);
|
||||||
|
break;
|
||||||
|
case ERC20BridgeSource.Velodrome:
|
||||||
|
const velodromeFillData = (order as OptimizedMarketBridgeOrder<VelodromeFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([velodromeFillData.router, velodromeFillData.stable]);
|
||||||
|
break;
|
||||||
|
case ERC20BridgeSource.Synthetix:
|
||||||
|
const fillData = (order as OptimizedMarketBridgeOrder<SynthetixFillData>).fillData;
|
||||||
|
bridgeData = encoder.encode([
|
||||||
|
fillData.synthetix,
|
||||||
|
fillData.takerTokenSymbolBytes32,
|
||||||
|
fillData.makerTokenSymbolBytes32,
|
||||||
|
]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(AggregationError.NoBridgeForSource);
|
throw new Error(AggregationError.NoBridgeForSource);
|
||||||
}
|
}
|
||||||
return bridgeData;
|
return bridgeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const poolEncoder = AbiEncoder.create([{ name: 'poolAddress', type: 'address' }]);
|
||||||
|
const curveEncoder = AbiEncoder.create([
|
||||||
|
{ name: 'curveAddress', type: 'address' },
|
||||||
|
{ name: 'exchangeFunctionSelector', type: 'bytes4' },
|
||||||
|
{ name: 'fromTokenIdx', type: 'int128' },
|
||||||
|
{ name: 'toTokenIdx', type: 'int128' },
|
||||||
|
]);
|
||||||
|
const makerPsmEncoder = AbiEncoder.create([
|
||||||
|
{ name: 'psmAddress', type: 'address' },
|
||||||
|
{ name: 'gemTokenAddress', type: 'address' },
|
||||||
|
]);
|
||||||
|
const balancerV2Encoder = AbiEncoder.create([
|
||||||
|
{ name: 'vault', type: 'address' },
|
||||||
|
{ name: 'poolId', type: 'bytes32' },
|
||||||
|
]);
|
||||||
|
const routerAddressPathEncoder = AbiEncoder.create('(address,address[])');
|
||||||
|
const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]);
|
||||||
|
const gmxAddressPathEncoder = AbiEncoder.create('(address,address,address,address[])');
|
||||||
|
const platypusAddressPathEncoder = AbiEncoder.create('(address,address[],address[])');
|
||||||
|
|
||||||
|
export const BRIDGE_ENCODERS: {
|
||||||
|
[key in Exclude<
|
||||||
|
ERC20BridgeSource,
|
||||||
|
ERC20BridgeSource.Native | ERC20BridgeSource.MultiHop | ERC20BridgeSource.MultiBridge
|
||||||
|
>]: AbiEncoder.DataType;
|
||||||
|
} = {
|
||||||
|
[ERC20BridgeSource.LiquidityProvider]: AbiEncoder.create([
|
||||||
|
{ name: 'provider', type: 'address' },
|
||||||
|
{ name: 'data', type: 'bytes' },
|
||||||
|
]),
|
||||||
|
[ERC20BridgeSource.Dodo]: AbiEncoder.create([
|
||||||
|
{ name: 'helper', type: 'address' },
|
||||||
|
{ name: 'poolAddress', type: 'address' },
|
||||||
|
{ name: 'isSellBase', type: 'bool' },
|
||||||
|
]),
|
||||||
|
[ERC20BridgeSource.DodoV2]: AbiEncoder.create([
|
||||||
|
{ name: 'poolAddress', type: 'address' },
|
||||||
|
{ name: 'isSellBase', type: 'bool' },
|
||||||
|
]),
|
||||||
|
// Curve like
|
||||||
|
[ERC20BridgeSource.Curve]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.CurveV2]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.Nerve]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.Synapse]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.Belt]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.Ellipsis]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.Saddle]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.XSigma]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.FirebirdOneSwap]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.IronSwap]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.ACryptos]: curveEncoder,
|
||||||
|
[ERC20BridgeSource.MobiusMoney]: curveEncoder,
|
||||||
|
// UniswapV2 like, (router, address[])
|
||||||
|
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.BancorV3]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.ShibaSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.Pangolin]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.SpiritSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.SpookySwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.BiSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.MDex]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.KnightSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.Yoshi]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.MeshSwap]: routerAddressPathEncoder,
|
||||||
|
// Avalanche
|
||||||
|
[ERC20BridgeSource.GMX]: gmxAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.Platypus]: platypusAddressPathEncoder,
|
||||||
|
// Celo
|
||||||
|
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
|
||||||
|
// BSC
|
||||||
|
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.ApeSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.CheeseSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.WaultSwap]: routerAddressPathEncoder,
|
||||||
|
// Polygon
|
||||||
|
[ERC20BridgeSource.QuickSwap]: routerAddressPathEncoder,
|
||||||
|
[ERC20BridgeSource.Dfyn]: routerAddressPathEncoder,
|
||||||
|
// Generic pools
|
||||||
|
[ERC20BridgeSource.Shell]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.Component]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.Mooniswap]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.MStable]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.Balancer]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.Cream]: poolEncoder,
|
||||||
|
[ERC20BridgeSource.Uniswap]: poolEncoder,
|
||||||
|
// Custom integrations
|
||||||
|
[ERC20BridgeSource.MakerPsm]: makerPsmEncoder,
|
||||||
|
[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' },
|
||||||
|
{ name: 'path', type: 'bytes' },
|
||||||
|
]),
|
||||||
|
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
|
||||||
|
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address,address)'),
|
||||||
|
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
|
||||||
|
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
|
||||||
|
[ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'),
|
||||||
|
[ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'),
|
||||||
|
[ERC20BridgeSource.Synthetix]: AbiEncoder.create('(address,bytes32,bytes32)'),
|
||||||
|
};
|
||||||
|
|
||||||
|
function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] {
|
||||||
|
return [
|
||||||
|
// Maker asset amount.
|
||||||
|
side === MarketOperation.Sell ? fill.output.integerValue(BigNumber.ROUND_DOWN) : fill.input,
|
||||||
|
// Taker asset amount.
|
||||||
|
side === MarketOperation.Sell ? fill.input : fill.output.integerValue(BigNumber.ROUND_UP),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNativeOptimizedOrder(
|
||||||
|
fill: Fill<NativeFillData>,
|
||||||
|
side: MarketOperation,
|
||||||
|
): OptimizedMarketOrderBase<NativeLimitOrderFillData> | OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
||||||
|
const fillData = fill.fillData;
|
||||||
|
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
||||||
|
const base = {
|
||||||
|
type: fill.type,
|
||||||
|
source: ERC20BridgeSource.Native,
|
||||||
|
makerToken: fillData.order.makerToken,
|
||||||
|
takerToken: fillData.order.takerToken,
|
||||||
|
makerAmount,
|
||||||
|
takerAmount,
|
||||||
|
fillData,
|
||||||
|
fill: cleanFillForExport(fill),
|
||||||
|
};
|
||||||
|
return fill.type === FillQuoteTransformerOrderType.Rfq
|
||||||
|
? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData }
|
||||||
|
: { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData };
|
||||||
|
}
|
||||||
|
|
||||||
export function createBridgeOrder(
|
export function createBridgeOrder(
|
||||||
fill: CollapsedFill,
|
fill: Fill,
|
||||||
makerToken: string,
|
makerToken: string,
|
||||||
takerToken: string,
|
takerToken: string,
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
): OptimizedMarketBridgeOrder {
|
): OptimizedMarketBridgeOrder {
|
||||||
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
||||||
return {
|
return {
|
||||||
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
|
source: fill.source,
|
||||||
makerToken,
|
makerToken,
|
||||||
takerToken,
|
takerToken,
|
||||||
makerAmount,
|
makerAmount,
|
||||||
takerAmount,
|
takerAmount,
|
||||||
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
|
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
|
||||||
source: fill.source,
|
fill: cleanFillForExport(fill),
|
||||||
sourcePathId: fill.sourcePathId,
|
sourcePathId: fill.sourcePathId,
|
||||||
type: FillQuoteTransformerOrderType.Bridge,
|
|
||||||
fills: [fill],
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFinalBridgeOrderFillDataFromCollapsedFill(fill: CollapsedFill): FillData {
|
function cleanFillForExport(fill: Fill): Fill {
|
||||||
|
return _.omit(fill, ['flags', 'fillData', 'sourcePathId', 'source', 'type']) as Fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFinalBridgeOrderFillDataFromCollapsedFill(fill: Fill): FillData {
|
||||||
switch (fill.source) {
|
switch (fill.source) {
|
||||||
case ERC20BridgeSource.UniswapV3: {
|
case ERC20BridgeSource.UniswapV3: {
|
||||||
const fd = fill.fillData as UniswapV3FillData;
|
const fd = fill.fillData as UniswapV3FillData;
|
||||||
@@ -435,143 +626,3 @@ export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, str
|
|||||||
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
|
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
|
||||||
return [makerToken, takerToken];
|
return [makerToken, takerToken];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const poolEncoder = AbiEncoder.create([{ name: 'poolAddress', type: 'address' }]);
|
|
||||||
const curveEncoder = AbiEncoder.create([
|
|
||||||
{ name: 'curveAddress', type: 'address' },
|
|
||||||
{ name: 'exchangeFunctionSelector', type: 'bytes4' },
|
|
||||||
{ name: 'fromTokenIdx', type: 'int128' },
|
|
||||||
{ name: 'toTokenIdx', type: 'int128' },
|
|
||||||
]);
|
|
||||||
const makerPsmEncoder = AbiEncoder.create([
|
|
||||||
{ name: 'psmAddress', type: 'address' },
|
|
||||||
{ name: 'gemTokenAddress', type: 'address' },
|
|
||||||
]);
|
|
||||||
const balancerV2Encoder = AbiEncoder.create([
|
|
||||||
{ name: 'vault', type: 'address' },
|
|
||||||
{ name: 'poolId', type: 'bytes32' },
|
|
||||||
]);
|
|
||||||
const routerAddressPathEncoder = AbiEncoder.create('(address,address[])');
|
|
||||||
const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]);
|
|
||||||
|
|
||||||
export const BRIDGE_ENCODERS: {
|
|
||||||
[key in Exclude<
|
|
||||||
ERC20BridgeSource,
|
|
||||||
ERC20BridgeSource.Native | ERC20BridgeSource.MultiHop | ERC20BridgeSource.MultiBridge
|
|
||||||
>]: AbiEncoder.DataType;
|
|
||||||
} = {
|
|
||||||
[ERC20BridgeSource.LiquidityProvider]: AbiEncoder.create([
|
|
||||||
{ name: 'provider', type: 'address' },
|
|
||||||
{ name: 'data', type: 'bytes' },
|
|
||||||
]),
|
|
||||||
[ERC20BridgeSource.Kyber]: AbiEncoder.create([
|
|
||||||
{ name: 'kyberNetworkProxy', type: 'address' },
|
|
||||||
{ name: 'hint', type: 'bytes' },
|
|
||||||
]),
|
|
||||||
[ERC20BridgeSource.Dodo]: AbiEncoder.create([
|
|
||||||
{ name: 'helper', type: 'address' },
|
|
||||||
{ name: 'poolAddress', type: 'address' },
|
|
||||||
{ name: 'isSellBase', type: 'bool' },
|
|
||||||
]),
|
|
||||||
[ERC20BridgeSource.DodoV2]: AbiEncoder.create([
|
|
||||||
{ name: 'poolAddress', type: 'address' },
|
|
||||||
{ name: 'isSellBase', type: 'bool' },
|
|
||||||
]),
|
|
||||||
// Curve like
|
|
||||||
[ERC20BridgeSource.Curve]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.CurveV2]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Swerve]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.SnowSwap]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Nerve]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Synapse]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Belt]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Ellipsis]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Smoothy]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.Saddle]: curveEncoder,
|
|
||||||
[ERC20BridgeSource.XSigma]: curveEncoder,
|
|
||||||
[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,
|
|
||||||
// Celo
|
|
||||||
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
|
|
||||||
// BSC
|
|
||||||
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.BakerySwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.ApeSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.CafeSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.CheeseSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.JulSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.WaultSwap]: routerAddressPathEncoder,
|
|
||||||
// Polygon
|
|
||||||
[ERC20BridgeSource.QuickSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.ComethSwap]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.Dfyn]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.Polydex]: routerAddressPathEncoder,
|
|
||||||
[ERC20BridgeSource.JetSwap]: routerAddressPathEncoder,
|
|
||||||
// Generic pools
|
|
||||||
[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.Beethovenx]: balancerV2Encoder,
|
|
||||||
[ERC20BridgeSource.UniswapV3]: AbiEncoder.create([
|
|
||||||
{ name: 'router', type: 'address' },
|
|
||||||
{ name: 'path', type: 'bytes' },
|
|
||||||
]),
|
|
||||||
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
|
|
||||||
[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] {
|
|
||||||
return [
|
|
||||||
// Maker asset amount.
|
|
||||||
side === MarketOperation.Sell ? fill.output.integerValue(BigNumber.ROUND_DOWN) : fill.input,
|
|
||||||
// Taker asset amount.
|
|
||||||
side === MarketOperation.Sell ? fill.input : fill.output.integerValue(BigNumber.ROUND_UP),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createNativeOptimizedOrder(
|
|
||||||
fill: NativeCollapsedFill,
|
|
||||||
side: MarketOperation,
|
|
||||||
): OptimizedMarketOrderBase<NativeLimitOrderFillData> | OptimizedMarketOrderBase<NativeRfqOrderFillData> {
|
|
||||||
const fillData = fill.fillData;
|
|
||||||
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
|
|
||||||
const base = {
|
|
||||||
type: fill.type,
|
|
||||||
source: ERC20BridgeSource.Native,
|
|
||||||
makerToken: fillData.order.makerToken,
|
|
||||||
takerToken: fillData.order.takerToken,
|
|
||||||
makerAmount,
|
|
||||||
takerAmount,
|
|
||||||
fills: [fill],
|
|
||||||
fillData,
|
|
||||||
};
|
|
||||||
return fill.type === FillQuoteTransformerOrderType.Rfq
|
|
||||||
? { ...base, type: FillQuoteTransformerOrderType.Rfq, fillData: fillData as NativeRfqOrderFillData }
|
|
||||||
: { ...base, type: FillQuoteTransformerOrderType.Limit, fillData: fillData as NativeLimitOrderFillData };
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
import _ = require('lodash');
|
||||||
|
|
||||||
import { MarketOperation } from '../../types';
|
import { MarketOperation } from '../../types';
|
||||||
|
|
||||||
@@ -6,14 +7,7 @@ import { POSITIVE_INF, ZERO_AMOUNT } from './constants';
|
|||||||
import { ethToOutputAmount } from './fills';
|
import { ethToOutputAmount } from './fills';
|
||||||
import { createBridgeOrder, createNativeOptimizedOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders';
|
import { createBridgeOrder, createNativeOptimizedOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders';
|
||||||
import { getCompleteRate, getRate } from './rate_utils';
|
import { getCompleteRate, getRate } from './rate_utils';
|
||||||
import {
|
import { ERC20BridgeSource, ExchangeProxyOverhead, Fill, NativeFillData, OptimizedMarketOrder } from './types';
|
||||||
CollapsedFill,
|
|
||||||
ERC20BridgeSource,
|
|
||||||
ExchangeProxyOverhead,
|
|
||||||
Fill,
|
|
||||||
NativeCollapsedFill,
|
|
||||||
OptimizedMarketOrder,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
// tslint:disable: prefer-for-of no-bitwise completed-docs
|
// tslint:disable: prefer-for-of no-bitwise completed-docs
|
||||||
|
|
||||||
@@ -37,7 +31,6 @@ export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class Path {
|
export class Path {
|
||||||
public collapsedFills?: ReadonlyArray<CollapsedFill>;
|
|
||||||
public orders?: OptimizedMarketOrder[];
|
public orders?: OptimizedMarketOrder[];
|
||||||
public sourceFlags: bigint = BigInt(0);
|
public sourceFlags: bigint = BigInt(0);
|
||||||
protected _size: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT };
|
protected _size: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT };
|
||||||
@@ -57,16 +50,6 @@ export class Path {
|
|||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static clone(base: Path): Path {
|
|
||||||
const clonedPath = new Path(base.side, base.fills.slice(), base.targetInput, base.pathPenaltyOpts);
|
|
||||||
clonedPath.sourceFlags = base.sourceFlags;
|
|
||||||
clonedPath._size = { ...base._size };
|
|
||||||
clonedPath._adjustedSize = { ...base._adjustedSize };
|
|
||||||
clonedPath.collapsedFills = base.collapsedFills === undefined ? undefined : base.collapsedFills.slice();
|
|
||||||
clonedPath.orders = base.orders === undefined ? undefined : base.orders.slice();
|
|
||||||
return clonedPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected constructor(
|
protected constructor(
|
||||||
protected readonly side: MarketOperation,
|
protected readonly side: MarketOperation,
|
||||||
public fills: ReadonlyArray<Fill>,
|
public fills: ReadonlyArray<Fill>,
|
||||||
@@ -74,68 +57,33 @@ export class Path {
|
|||||||
public readonly pathPenaltyOpts: PathPenaltyOpts,
|
public readonly pathPenaltyOpts: PathPenaltyOpts,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public append(fill: Fill): this {
|
|
||||||
(this.fills as Fill[]).push(fill);
|
|
||||||
this.sourceFlags |= fill.flags;
|
|
||||||
this._addFillSize(fill);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a fallback path to the current path
|
* Finalizes this path, creating fillable orders with the information required
|
||||||
* Fallback must contain exclusive fills that are
|
* for settlement
|
||||||
* not present in this path
|
|
||||||
*/
|
*/
|
||||||
public addFallback(fallback: Path): this {
|
public finalize(opts: CreateOrderFromPathOpts): FinalizedPath {
|
||||||
// We pre-pend the sources which have a higher probability of failure
|
|
||||||
// This allows us to continue on to the remaining fills
|
|
||||||
// If the "flakey" sources like Native were at the end, we may have a failure
|
|
||||||
// as the last fill and then either revert, or go back to a source we previously
|
|
||||||
// filled against
|
|
||||||
const nativeFills = this.fills.filter(f => f.source === ERC20BridgeSource.Native);
|
|
||||||
const otherFills = this.fills.filter(f => f.source !== ERC20BridgeSource.Native);
|
|
||||||
|
|
||||||
// Map to the unique source id and the index to represent a unique fill
|
|
||||||
const fillToFillId = (fill: Fill) => `${fill.sourcePathId}${fill.index}`;
|
|
||||||
const otherFillIds = otherFills.map(f => fillToFillId(f));
|
|
||||||
|
|
||||||
this.fills = [
|
|
||||||
// Append all of the native fills first
|
|
||||||
...nativeFills,
|
|
||||||
// Add the other fills that are not native in the optimal path
|
|
||||||
...otherFills,
|
|
||||||
// Add the fills to the end that aren't already included
|
|
||||||
...fallback.fills.filter(f => !otherFillIds.includes(fillToFillId(f))),
|
|
||||||
];
|
|
||||||
// Recompute the source flags
|
|
||||||
this.sourceFlags = this.fills.reduce((flags, fill) => flags | fill.flags, BigInt(0));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public collapse(opts: CreateOrderFromPathOpts): CollapsedPath {
|
|
||||||
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
const [makerToken, takerToken] = getMakerTakerTokens(opts);
|
||||||
const collapsedFills = this.collapsedFills === undefined ? this._collapseFills() : this.collapsedFills;
|
|
||||||
this.orders = [];
|
this.orders = [];
|
||||||
for (let i = 0; i < collapsedFills.length; ) {
|
for (const fill of this.fills) {
|
||||||
if (collapsedFills[i].source === ERC20BridgeSource.Native) {
|
// internal BigInt flag field is not supported JSON and is tricky
|
||||||
this.orders.push(createNativeOptimizedOrder(collapsedFills[i] as NativeCollapsedFill, opts.side));
|
// to remove upstream. Since it's not needed in a FinalizedPath we just drop it.
|
||||||
++i;
|
const normalizedFill = _.omit(fill, 'flags') as Fill;
|
||||||
continue;
|
if (fill.source === ERC20BridgeSource.Native) {
|
||||||
|
this.orders.push(createNativeOptimizedOrder(normalizedFill as Fill<NativeFillData>, opts.side));
|
||||||
|
} else {
|
||||||
|
this.orders.push(createBridgeOrder(normalizedFill, makerToken, takerToken, opts.side));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.orders.push(createBridgeOrder(collapsedFills[i], makerToken, takerToken, opts.side));
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
return this as CollapsedPath;
|
return this as FinalizedPath;
|
||||||
}
|
|
||||||
|
|
||||||
public size(): PathSize {
|
|
||||||
return this._size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public adjustedSize(): PathSize {
|
public adjustedSize(): PathSize {
|
||||||
|
// Adjusted input/output has been adjusted by the cost of the DEX, but not by any
|
||||||
|
// overhead added by the exchange proxy.
|
||||||
const { input, output } = this._adjustedSize;
|
const { input, output } = this._adjustedSize;
|
||||||
const { exchangeProxyOverhead, outputAmountPerEth, inputAmountPerEth } = this.pathPenaltyOpts;
|
const { exchangeProxyOverhead, outputAmountPerEth, inputAmountPerEth } = this.pathPenaltyOpts;
|
||||||
|
// Calculate the additional penalty from the ways this path can be filled
|
||||||
|
// by the exchange proxy, e.g VIPs (small) or FillQuoteTransformer (large)
|
||||||
const gasOverhead = exchangeProxyOverhead(this.sourceFlags);
|
const gasOverhead = exchangeProxyOverhead(this.sourceFlags);
|
||||||
const pathPenalty = ethToOutputAmount({
|
const pathPenalty = ethToOutputAmount({
|
||||||
input,
|
input,
|
||||||
@@ -155,6 +103,10 @@ export class Path {
|
|||||||
return getCompleteRate(this.side, input, output, this.targetInput);
|
return getCompleteRate(this.side, input, output, this.targetInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the rate of this path, where the output has been
|
||||||
|
* adjusted for penalties (e.g cost)
|
||||||
|
*/
|
||||||
public adjustedRate(): BigNumber {
|
public adjustedRate(): BigNumber {
|
||||||
const { input, output } = this.adjustedSize();
|
const { input, output } = this.adjustedSize();
|
||||||
return getRate(this.side, input, output);
|
return getRate(this.side, input, output);
|
||||||
@@ -171,16 +123,11 @@ export class Path {
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
public adjustedSlippage(maxRate: BigNumber): number {
|
/**
|
||||||
if (maxRate.eq(0)) {
|
* Compares two paths returning if this adjusted path
|
||||||
return 0;
|
* is better than the other adjusted path
|
||||||
}
|
*/
|
||||||
const totalRate = this.adjustedRate();
|
public isAdjustedBetterThan(other: Path): boolean {
|
||||||
const rateChange = maxRate.minus(totalRate);
|
|
||||||
return rateChange.div(maxRate).toNumber();
|
|
||||||
}
|
|
||||||
|
|
||||||
public isBetterThan(other: Path): boolean {
|
|
||||||
if (!this.targetInput.isEqualTo(other.targetInput)) {
|
if (!this.targetInput.isEqualTo(other.targetInput)) {
|
||||||
throw new Error(`Target input mismatch: ${this.targetInput} !== ${other.targetInput}`);
|
throw new Error(`Target input mismatch: ${this.targetInput} !== ${other.targetInput}`);
|
||||||
}
|
}
|
||||||
@@ -192,78 +139,6 @@ export class Path {
|
|||||||
} else {
|
} else {
|
||||||
return this.adjustedCompleteRate().isGreaterThan(other.adjustedCompleteRate());
|
return this.adjustedCompleteRate().isGreaterThan(other.adjustedCompleteRate());
|
||||||
}
|
}
|
||||||
// if (otherInput.isLessThan(targetInput)) {
|
|
||||||
// return input.isGreaterThan(otherInput);
|
|
||||||
// } else if (input.isGreaterThanOrEqualTo(targetInput)) {
|
|
||||||
// return this.adjustedCompleteRate().isGreaterThan(other.adjustedCompleteRate());
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isComplete(): boolean {
|
|
||||||
const { input } = this._size;
|
|
||||||
return input.gte(this.targetInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public isValid(skipDuplicateCheck: boolean = false): boolean {
|
|
||||||
for (let i = 0; i < this.fills.length; ++i) {
|
|
||||||
// Fill must immediately follow its parent.
|
|
||||||
if (this.fills[i].parent) {
|
|
||||||
if (i === 0 || this.fills[i - 1] !== this.fills[i].parent) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!skipDuplicateCheck) {
|
|
||||||
// Fill must not be duplicated.
|
|
||||||
for (let j = 0; j < i; ++j) {
|
|
||||||
if (this.fills[i] === this.fills[j]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public isValidNextFill(fill: Fill): boolean {
|
|
||||||
if (this.fills.length === 0) {
|
|
||||||
return !fill.parent;
|
|
||||||
}
|
|
||||||
if (this.fills[this.fills.length - 1] === fill.parent) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (fill.parent) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _collapseFills(): ReadonlyArray<CollapsedFill> {
|
|
||||||
this.collapsedFills = [];
|
|
||||||
for (const fill of this.fills) {
|
|
||||||
const source = fill.source;
|
|
||||||
if (this.collapsedFills.length !== 0 && source !== ERC20BridgeSource.Native) {
|
|
||||||
const prevFill = this.collapsedFills[this.collapsedFills.length - 1];
|
|
||||||
// If the last fill is from the same source, merge them.
|
|
||||||
if (prevFill.sourcePathId === fill.sourcePathId) {
|
|
||||||
prevFill.input = prevFill.input.plus(fill.input);
|
|
||||||
prevFill.output = prevFill.output.plus(fill.output);
|
|
||||||
prevFill.fillData = fill.fillData;
|
|
||||||
prevFill.subFills.push(fill);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(this.collapsedFills as CollapsedFill[]).push({
|
|
||||||
sourcePathId: fill.sourcePathId,
|
|
||||||
source: fill.source,
|
|
||||||
type: fill.type,
|
|
||||||
fillData: fill.fillData,
|
|
||||||
input: fill.input,
|
|
||||||
output: fill.output,
|
|
||||||
subFills: [fill],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this.collapsedFills;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addFillSize(fill: Fill): void {
|
private _addFillSize(fill: Fill): void {
|
||||||
@@ -285,7 +160,6 @@ export class Path {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CollapsedPath extends Path {
|
export interface FinalizedPath extends Path {
|
||||||
readonly collapsedFills: ReadonlyArray<CollapsedFill>;
|
|
||||||
readonly orders: OptimizedMarketOrder[];
|
readonly orders: OptimizedMarketOrder[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { assert } from '@0x/assert';
|
import { assert } from '@0x/assert';
|
||||||
import { ChainId } from '@0x/contract-addresses';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
|
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
|
||||||
|
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||||
import { BigNumber, hexUtils } from '@0x/utils';
|
import { BigNumber, hexUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { performance } from 'perf_hooks';
|
import { performance } from 'perf_hooks';
|
||||||
@@ -9,13 +10,12 @@ import { DEFAULT_WARNING_LOGGER } from '../../constants';
|
|||||||
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
|
||||||
|
|
||||||
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from './constants';
|
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from './constants';
|
||||||
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
|
import { dexSampleToFill, ethToOutputAmount, nativeOrderToFill } from './fills';
|
||||||
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
|
import { Path, PathPenaltyOpts } from './path';
|
||||||
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types';
|
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillAdjustor, FillData, SamplerMetrics } from './types';
|
||||||
|
|
||||||
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
|
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
|
||||||
|
|
||||||
const RUN_LIMIT_DECAY_FACTOR = 0.5;
|
|
||||||
// NOTE: The Rust router will panic with less than 3 samples
|
// NOTE: The Rust router will panic with less than 3 samples
|
||||||
const MIN_NUM_SAMPLE_INPUTS = 3;
|
const MIN_NUM_SAMPLE_INPUTS = 3;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ function calculateOuputFee(
|
|||||||
): BigNumber {
|
): BigNumber {
|
||||||
if (isDexSample(sampleOrNativeOrder)) {
|
if (isDexSample(sampleOrNativeOrder)) {
|
||||||
const { input, output, source, fillData } = sampleOrNativeOrder;
|
const { input, output, source, fillData } = sampleOrNativeOrder;
|
||||||
const fee = fees[source]?.(fillData) || 0;
|
const fee = fees[source]?.(fillData).fee || ZERO_AMOUNT;
|
||||||
const outputFee = ethToOutputAmount({
|
const outputFee = ethToOutputAmount({
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
@@ -56,7 +56,7 @@ function calculateOuputFee(
|
|||||||
return outputFee;
|
return outputFee;
|
||||||
} else {
|
} else {
|
||||||
const { input, output } = nativeOrderToNormalizedAmounts(side, sampleOrNativeOrder);
|
const { input, output } = nativeOrderToNormalizedAmounts(side, sampleOrNativeOrder);
|
||||||
const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder) || 0;
|
const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder).fee || ZERO_AMOUNT;
|
||||||
const outputFee = ethToOutputAmount({
|
const outputFee = ethToOutputAmount({
|
||||||
input,
|
input,
|
||||||
output,
|
output,
|
||||||
@@ -77,6 +77,7 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
neonRouterNumSamples: number,
|
neonRouterNumSamples: number,
|
||||||
vipSourcesSet: Set<ERC20BridgeSource>,
|
vipSourcesSet: Set<ERC20BridgeSource>,
|
||||||
|
fillAdjustor: FillAdjustor,
|
||||||
): { allSourcesPath: Path | undefined; vipSourcesPath: Path | undefined } | undefined {
|
): { allSourcesPath: Path | undefined; vipSourcesPath: Path | undefined } | undefined {
|
||||||
// Currently the rust router is unable to handle 1 base unit sized quotes and will error out
|
// 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
|
// To avoid flooding the logs with these errors we just return an insufficient liquidity error
|
||||||
@@ -85,31 +86,44 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const createFill = (sample: DexSample): Fill | undefined => {
|
// Create a `Fill` from a dex sample and adjust it with any passed in
|
||||||
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
|
// adjustor
|
||||||
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
|
const createFillFromDexSample = (sample: DexSample): Fill => {
|
||||||
if (fills.length === 0) {
|
const fill = dexSampleToFill(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
|
||||||
return undefined;
|
const adjustedFills = fillAdjustor.adjustFills(side, [fill], input);
|
||||||
}
|
return adjustedFills[0];
|
||||||
|
|
||||||
return fills[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createPathFromStrategy = (sourcesRustRoute: Float64Array, sourcesOutputAmounts: Float64Array) => {
|
const createPathFromStrategy = (optimalRouteInputs: Float64Array, optimalRouteOutputs: Float64Array) => {
|
||||||
|
/**
|
||||||
|
* inputs are the amounts to fill at each source index
|
||||||
|
* e.g fill 2076 at index 4
|
||||||
|
* [ 0, 0, 0, 0, 2076, 464, 230,
|
||||||
|
* 230, 0, 0, 0 ]
|
||||||
|
* the sum represents the total input amount
|
||||||
|
*
|
||||||
|
* outputs are the amounts we expect out at each source index
|
||||||
|
* [ 0, 0, 0, 0, 42216, 9359, 4677,
|
||||||
|
* 4674, 0, 0, 0 ]
|
||||||
|
* the sum represents the total expected output amount
|
||||||
|
*/
|
||||||
|
|
||||||
const routesAndSamplesAndOutputs = _.zip(
|
const routesAndSamplesAndOutputs = _.zip(
|
||||||
sourcesRustRoute,
|
optimalRouteInputs,
|
||||||
|
optimalRouteOutputs,
|
||||||
samplesAndNativeOrdersWithResults,
|
samplesAndNativeOrdersWithResults,
|
||||||
sourcesOutputAmounts,
|
|
||||||
sampleSourcePathIds,
|
sampleSourcePathIds,
|
||||||
);
|
);
|
||||||
const adjustedFills: Fill[] = [];
|
const adjustedFills: Fill[] = [];
|
||||||
const totalRoutedAmount = BigNumber.sum(...sourcesRustRoute);
|
const totalRoutedAmount = BigNumber.sum(...optimalRouteInputs);
|
||||||
|
|
||||||
|
// Due to precision errors we can end up with a totalRoutedAmount that is not exactly equal to the input
|
||||||
|
const precisionErrorScalar = input.dividedBy(totalRoutedAmount);
|
||||||
|
|
||||||
const scale = input.dividedBy(totalRoutedAmount);
|
|
||||||
for (const [
|
for (const [
|
||||||
routeInput,
|
routeInput,
|
||||||
routeSamplesAndNativeOrders,
|
|
||||||
outputAmount,
|
outputAmount,
|
||||||
|
routeSamplesAndNativeOrders,
|
||||||
sourcePathId,
|
sourcePathId,
|
||||||
] of routesAndSamplesAndOutputs) {
|
] of routesAndSamplesAndOutputs) {
|
||||||
if (!Number.isFinite(outputAmount)) {
|
if (!Number.isFinite(outputAmount)) {
|
||||||
@@ -119,26 +133,27 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
|
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
|
// TODO: [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
|
// 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(
|
const routeInputCorrected = BigNumber.min(
|
||||||
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
|
precisionErrorScalar.multipliedBy(routeInput).integerValue(BigNumber.ROUND_CEIL),
|
||||||
input,
|
input,
|
||||||
);
|
);
|
||||||
|
|
||||||
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
|
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
|
||||||
|
// If it is a native single order we only have one Input/output
|
||||||
|
// we want to convert this to an array of samples
|
||||||
if (!isDexSample(current)) {
|
if (!isDexSample(current)) {
|
||||||
const nativeFill = nativeOrdersToFills(
|
const nativeFill = nativeOrderToFill(
|
||||||
side,
|
side,
|
||||||
[current],
|
current,
|
||||||
rustInputAdjusted,
|
routeInputCorrected,
|
||||||
opts.outputAmountPerEth,
|
opts.outputAmountPerEth,
|
||||||
opts.inputAmountPerEth,
|
opts.inputAmountPerEth,
|
||||||
fees,
|
fees,
|
||||||
false,
|
false,
|
||||||
)[0] as Fill | undefined;
|
);
|
||||||
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
|
// Note: If the order has an adjusted rate of less than or equal to 0 it will be undefined
|
||||||
// and nativeFill will be `undefined`
|
|
||||||
if (nativeFill) {
|
if (nativeFill) {
|
||||||
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
|
||||||
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
|
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
|
||||||
@@ -147,62 +162,54 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: For DexSamples only
|
// NOTE: For DexSamples only
|
||||||
let fill = createFill(current);
|
let fill = createFillFromDexSample(current);
|
||||||
if (!fill) {
|
if (!fill) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
|
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
|
|
||||||
|
|
||||||
|
// From the output of the router, find the closest Sample in terms of input.
|
||||||
|
// The Router may have chosen an amount to fill that we do not have a measured sample of
|
||||||
|
// Choosing this accurately is required in some sources where the `FillData` may change depending
|
||||||
|
// on the size of the trade. For example, UniswapV3 has variable gas cost
|
||||||
|
// which increases with input.
|
||||||
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
|
||||||
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
for (let k = routeSamples.length - 1; k >= 0; k--) {
|
||||||
|
// If we're at the last remaining sample that's all we have left to use
|
||||||
if (k === 0) {
|
if (k === 0) {
|
||||||
fill = createFill(routeSamples[0]) ?? fill;
|
fill = createFillFromDexSample(routeSamples[0]) ?? fill;
|
||||||
}
|
}
|
||||||
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
|
if (routeInputCorrected.isGreaterThan(routeSamples[k].input)) {
|
||||||
const left = routeSamples[k];
|
const left = routeSamples[k];
|
||||||
const right = routeSamples[k + 1];
|
const right = routeSamples[k + 1];
|
||||||
if (left && right) {
|
if (left && right) {
|
||||||
fill =
|
fill =
|
||||||
createFill({
|
createFillFromDexSample({
|
||||||
...right, // default to the greater (for gas used)
|
...right, // default to the greater (for gas used)
|
||||||
input: rustInputAdjusted,
|
input: routeInputCorrected,
|
||||||
output: new BigNumber(outputAmount),
|
output: new BigNumber(outputAmount).integerValue(),
|
||||||
}) ?? fill;
|
}) ?? fill;
|
||||||
} else {
|
} else {
|
||||||
assert.assert(Boolean(left || right), 'No valid sample to use');
|
assert.assert(Boolean(left || right), 'No valid sample to use');
|
||||||
fill = createFill(left || right) ?? fill;
|
fill = createFillFromDexSample(left || right) ?? fill;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
|
// TODO: remove once we have solved the rounding/precision loss issues in the Rust router
|
||||||
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output));
|
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output)).integerValue();
|
||||||
// Scale output by scale factor but never go above the largest sample in sell quotes (unknown liquidity) or below 1 base unit (unfillable)
|
// 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) => {
|
const scaleOutput = (output: BigNumber) => {
|
||||||
// Don't try to scale 0 output as it will be clamped to 1
|
const capped = BigNumber.min(output.integerValue(), maxSampledOutput);
|
||||||
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);
|
return BigNumber.max(capped, 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
adjustedFills.push({
|
adjustedFills.push({
|
||||||
...fill,
|
...fill,
|
||||||
input: rustInputAdjusted,
|
input: routeInputCorrected,
|
||||||
output: scaleOutput(fill.output),
|
output: scaleOutput(fill.output),
|
||||||
adjustedOutput: scaleOutput(fill.adjustedOutput),
|
adjustedOutput: scaleOutput(fill.adjustedOutput),
|
||||||
index: 0,
|
|
||||||
parent: undefined,
|
|
||||||
sourcePathId: sourcePathId ?? hexUtils.random(),
|
sourcePathId: sourcePathId ?? hexUtils.random(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -224,7 +231,6 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourcePathId = hexUtils.random();
|
|
||||||
const singleSourceSamplesWithOutput = [...singleSourceSamples];
|
const singleSourceSamplesWithOutput = [...singleSourceSamples];
|
||||||
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
|
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
|
||||||
const currentOutput = singleSourceSamples[i].output;
|
const currentOutput = singleSourceSamples[i].output;
|
||||||
@@ -240,17 +246,23 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
|
// TODO: Do we need to handle 0 entries, from eg Kyber?
|
||||||
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
|
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
|
||||||
(memo, sample, sampleIdx) => {
|
(memo, sample, sampleIdx) => {
|
||||||
memo.ids.push(`${sample.source}-${serializedPaths.length}-${sampleIdx}`);
|
// Use the fill from createFillFromDexSample to apply
|
||||||
memo.inputs.push(sample.input.integerValue().toNumber());
|
// any user supplied adjustments
|
||||||
memo.outputs.push(sample.output.integerValue().toNumber());
|
const f = createFillFromDexSample(sample);
|
||||||
memo.outputFees.push(
|
memo.ids.push(`${f.source}-${serializedPaths.length}-${sampleIdx}`);
|
||||||
calculateOuputFee(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
|
memo.inputs.push(f.input.integerValue().toNumber());
|
||||||
.integerValue()
|
memo.outputs.push(f.output.integerValue().toNumber());
|
||||||
.toNumber(),
|
// Calculate the penalty of this sample as the diff between the
|
||||||
);
|
// output and the adjusted output
|
||||||
|
const outputFee = f.output
|
||||||
|
.minus(f.adjustedOutput)
|
||||||
|
.absoluteValue()
|
||||||
|
.integerValue()
|
||||||
|
.toNumber();
|
||||||
|
memo.outputFees.push(outputFee);
|
||||||
|
|
||||||
return memo;
|
return memo;
|
||||||
},
|
},
|
||||||
@@ -265,6 +277,8 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
|
|
||||||
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
|
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
|
||||||
serializedPaths.push(serializedPath);
|
serializedPaths.push(serializedPath);
|
||||||
|
|
||||||
|
const sourcePathId = hexUtils.random();
|
||||||
sampleSourcePathIds.push(sourcePathId);
|
sampleSourcePathIds.push(sourcePathId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,19 +320,22 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
normalizedOrderOutput.times(scaleToInput).times(fraction),
|
normalizedOrderOutput.times(scaleToInput).times(fraction),
|
||||||
normalizedOrderOutput,
|
normalizedOrderOutput,
|
||||||
);
|
);
|
||||||
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
|
const id = `${ERC20BridgeSource.Native}-${nativeOrder.type}-${serializedPaths.length}-${idx}-${i}`;
|
||||||
inputs.push(currentInput.integerValue().toNumber());
|
inputs.push(currentInput.integerValue().toNumber());
|
||||||
outputs.push(currentOutput.integerValue().toNumber());
|
outputs.push(currentOutput.integerValue().toNumber());
|
||||||
outputFees.push(fee);
|
outputFees.push(fee);
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have a VIP for the Rfq order type, Limit order currently goes through FQT
|
||||||
|
const isVip = nativeOrder.type !== FillQuoteTransformerOrderType.Limit;
|
||||||
|
|
||||||
const serializedPath: SerializedPath = {
|
const serializedPath: SerializedPath = {
|
||||||
ids,
|
ids,
|
||||||
inputs,
|
inputs,
|
||||||
outputs,
|
outputs,
|
||||||
outputFees,
|
outputFees,
|
||||||
isVip: true,
|
isVip,
|
||||||
};
|
};
|
||||||
|
|
||||||
samplesAndNativeOrdersWithResults.push([nativeOrder]);
|
samplesAndNativeOrdersWithResults.push([nativeOrder]);
|
||||||
@@ -375,7 +392,7 @@ function findRoutesAndCreateOptimalPath(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function findOptimalRustPathFromSamples(
|
export function findOptimalPathFromSamples(
|
||||||
side: MarketOperation,
|
side: MarketOperation,
|
||||||
samples: DexSample[][],
|
samples: DexSample[][],
|
||||||
nativeOrders: NativeOrderWithFillableAmounts[],
|
nativeOrders: NativeOrderWithFillableAmounts[],
|
||||||
@@ -384,6 +401,7 @@ export function findOptimalRustPathFromSamples(
|
|||||||
fees: FeeSchedule,
|
fees: FeeSchedule,
|
||||||
chainId: ChainId,
|
chainId: ChainId,
|
||||||
neonRouterNumSamples: number,
|
neonRouterNumSamples: number,
|
||||||
|
fillAdjustor: FillAdjustor,
|
||||||
samplerMetrics?: SamplerMetrics,
|
samplerMetrics?: SamplerMetrics,
|
||||||
): Path | undefined {
|
): Path | undefined {
|
||||||
const beforeTimeMs = performance.now();
|
const beforeTimeMs = performance.now();
|
||||||
@@ -406,6 +424,7 @@ export function findOptimalRustPathFromSamples(
|
|||||||
fees,
|
fees,
|
||||||
neonRouterNumSamples,
|
neonRouterNumSamples,
|
||||||
vipSourcesSet,
|
vipSourcesSet,
|
||||||
|
fillAdjustor,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!paths) {
|
if (!paths) {
|
||||||
@@ -415,7 +434,7 @@ export function findOptimalRustPathFromSamples(
|
|||||||
|
|
||||||
const { allSourcesPath, vipSourcesPath } = paths;
|
const { allSourcesPath, vipSourcesPath } = paths;
|
||||||
|
|
||||||
if (!allSourcesPath || vipSourcesPath?.isBetterThan(allSourcesPath)) {
|
if (!allSourcesPath || vipSourcesPath?.isAdjustedBetterThan(allSourcesPath)) {
|
||||||
sendMetrics();
|
sendMetrics();
|
||||||
return vipSourcesPath;
|
return vipSourcesPath;
|
||||||
}
|
}
|
||||||
@@ -423,143 +442,3 @@ export function findOptimalRustPathFromSamples(
|
|||||||
sendMetrics();
|
sendMetrics();
|
||||||
return allSourcesPath;
|
return allSourcesPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the optimal mixture of fills that maximizes (for sells) or minimizes
|
|
||||||
* (for buys) output, while meeting the input requirement.
|
|
||||||
*/
|
|
||||||
export async function findOptimalPathJSAsync(
|
|
||||||
side: MarketOperation,
|
|
||||||
fills: Fill[][],
|
|
||||||
targetInput: BigNumber,
|
|
||||||
runLimit: number = 2 ** 8,
|
|
||||||
samplerMetrics?: SamplerMetrics,
|
|
||||||
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
|
|
||||||
): Promise<Path | undefined> {
|
|
||||||
const beforeTimeMs = performance.now();
|
|
||||||
// Sort fill arrays by descending adjusted completed rate.
|
|
||||||
// Remove any paths which cannot impact the optimal path
|
|
||||||
const sortedPaths = reducePaths(fillsToSortedPaths(fills, side, targetInput, opts), side);
|
|
||||||
if (sortedPaths.length === 0) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const rates = rateBySourcePathId(sortedPaths);
|
|
||||||
let optimalPath = sortedPaths[0];
|
|
||||||
for (const [i, path] of sortedPaths.slice(1).entries()) {
|
|
||||||
optimalPath = mixPaths(side, optimalPath, path, targetInput, runLimit * RUN_LIMIT_DECAY_FACTOR ** i, rates);
|
|
||||||
// Yield to event loop.
|
|
||||||
await Promise.resolve();
|
|
||||||
}
|
|
||||||
const finalPath = optimalPath.isComplete() ? optimalPath : undefined;
|
|
||||||
// tslint:disable-next-line: no-unused-expression
|
|
||||||
samplerMetrics &&
|
|
||||||
samplerMetrics.logRouterDetails({
|
|
||||||
router: 'js',
|
|
||||||
type: 'total',
|
|
||||||
timingMs: performance.now() - beforeTimeMs,
|
|
||||||
});
|
|
||||||
return finalPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort fill arrays by descending adjusted completed rate.
|
|
||||||
export function fillsToSortedPaths(
|
|
||||||
fills: Fill[][],
|
|
||||||
side: MarketOperation,
|
|
||||||
targetInput: BigNumber,
|
|
||||||
opts: PathPenaltyOpts,
|
|
||||||
): Path[] {
|
|
||||||
const paths = fills.map(singleSourceFills => Path.create(side, singleSourceFills, targetInput, opts));
|
|
||||||
const sortedPaths = paths.sort((a, b) => {
|
|
||||||
const aRate = a.adjustedCompleteRate();
|
|
||||||
const bRate = b.adjustedCompleteRate();
|
|
||||||
// There is a case where the adjusted completed rate isn't sufficient for the desired amount
|
|
||||||
// resulting in a NaN div by 0 (output)
|
|
||||||
if (bRate.isNaN()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (aRate.isNaN()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return bRate.comparedTo(aRate);
|
|
||||||
});
|
|
||||||
return sortedPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove paths which have no impact on the optimal path
|
|
||||||
export function reducePaths(sortedPaths: Path[], side: MarketOperation): Path[] {
|
|
||||||
// Any path which has a min rate that is less than the best adjusted completed rate has no chance of improving
|
|
||||||
// the overall route.
|
|
||||||
const bestNonNativeCompletePath = sortedPaths.filter(
|
|
||||||
p => p.isComplete() && p.fills[0].source !== ERC20BridgeSource.Native,
|
|
||||||
)[0];
|
|
||||||
|
|
||||||
// If there is no complete path then just go ahead with the sorted paths
|
|
||||||
// I.e if the token only exists on sources which cannot sell to infinity
|
|
||||||
// or buys where X is greater than all the tokens available in the pools
|
|
||||||
if (!bestNonNativeCompletePath) {
|
|
||||||
return sortedPaths;
|
|
||||||
}
|
|
||||||
const bestNonNativeCompletePathAdjustedRate = bestNonNativeCompletePath.adjustedCompleteRate();
|
|
||||||
if (!bestNonNativeCompletePathAdjustedRate.isGreaterThan(0)) {
|
|
||||||
return sortedPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredPaths = sortedPaths.filter(p =>
|
|
||||||
p.bestRate().isGreaterThanOrEqualTo(bestNonNativeCompletePathAdjustedRate),
|
|
||||||
);
|
|
||||||
return filteredPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mixPaths(
|
|
||||||
side: MarketOperation,
|
|
||||||
pathA: Path,
|
|
||||||
pathB: Path,
|
|
||||||
targetInput: BigNumber,
|
|
||||||
maxSteps: number,
|
|
||||||
rates: { [id: string]: BigNumber },
|
|
||||||
): Path {
|
|
||||||
const _maxSteps = Math.max(maxSteps, 32);
|
|
||||||
let steps = 0;
|
|
||||||
// We assume pathA is the better of the two initially.
|
|
||||||
let bestPath: Path = pathA;
|
|
||||||
|
|
||||||
const _walk = (path: Path, remainingFills: Fill[]) => {
|
|
||||||
steps += 1;
|
|
||||||
if (path.isBetterThan(bestPath)) {
|
|
||||||
bestPath = path;
|
|
||||||
}
|
|
||||||
const remainingInput = targetInput.minus(path.size().input);
|
|
||||||
if (remainingInput.isGreaterThan(0)) {
|
|
||||||
for (let i = 0; i < remainingFills.length && steps < _maxSteps; ++i) {
|
|
||||||
const fill = remainingFills[i];
|
|
||||||
// Only walk valid paths.
|
|
||||||
if (!path.isValidNextFill(fill)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Remove this fill from the next list of candidate fills.
|
|
||||||
const nextRemainingFills = remainingFills.slice();
|
|
||||||
nextRemainingFills.splice(i, 1);
|
|
||||||
// Recurse.
|
|
||||||
_walk(Path.clone(path).append(fill), nextRemainingFills);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const allFills = [...pathA.fills, ...pathB.fills];
|
|
||||||
// Sort subpaths by rate and keep fills contiguous to improve our
|
|
||||||
// chances of walking ideal, valid paths first.
|
|
||||||
const sortedFills = allFills.sort((a, b) => {
|
|
||||||
if (a.sourcePathId !== b.sourcePathId) {
|
|
||||||
return rates[b.sourcePathId].comparedTo(rates[a.sourcePathId]);
|
|
||||||
}
|
|
||||||
return a.index - b.index;
|
|
||||||
});
|
|
||||||
_walk(Path.create(side, [], targetInput, pathA.pathPenaltyOpts), sortedFills);
|
|
||||||
if (!bestPath.isValid()) {
|
|
||||||
throw new Error('nooope');
|
|
||||||
}
|
|
||||||
return bestPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
function rateBySourcePathId(paths: Path[]): { [id: string]: BigNumber } {
|
|
||||||
return _.fromPairs(paths.map(p => [p.fills[0].sourcePathId, p.adjustedRate()]));
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
import { getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { Pool } from '@balancer-labs/sor/dist/types';
|
import { getPoolsWithTokens, parsePoolData } from 'balancer-labs-sor-v1';
|
||||||
|
import { Pool } from 'balancer-labs-sor-v1/dist/types';
|
||||||
import { gql, request } from 'graphql-request';
|
import { gql, request } from 'graphql-request';
|
||||||
|
|
||||||
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants';
|
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
|
||||||
|
import { LogFunction } from '../../../types';
|
||||||
|
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
|
||||||
|
|
||||||
import { CacheValue, PoolsCache } from './pools_cache';
|
import { NoOpPoolsCache } from './no_op_pools_cache';
|
||||||
|
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
|
||||||
|
|
||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
||||||
// tslint:enable:custom-no-magic-numbers
|
// tslint:enable:custom-no-magic-numbers
|
||||||
|
// tslint:disable: member-ordering
|
||||||
|
|
||||||
|
const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
|
||||||
|
|
||||||
interface BalancerPoolResponse {
|
interface BalancerPoolResponse {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -18,12 +25,21 @@ interface BalancerPoolResponse {
|
|||||||
totalWeight: string;
|
totalWeight: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BalancerPoolsCache extends PoolsCache {
|
export class BalancerPoolsCache extends AbstractPoolsCache {
|
||||||
constructor(
|
public static create(chainId: ChainId): PoolsCache {
|
||||||
|
if (chainId !== ChainId.Mainnet) {
|
||||||
|
return new NoOpPoolsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BalancerPoolsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(
|
||||||
private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL,
|
private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL,
|
||||||
cache: { [key: string]: CacheValue } = {},
|
cache: Map<string, CacheValue> = new Map(),
|
||||||
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
||||||
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
|
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
|
||||||
|
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
|
||||||
) {
|
) {
|
||||||
super(cache);
|
super(cache);
|
||||||
void this._loadTopPoolsAsync();
|
void this._loadTopPoolsAsync();
|
||||||
@@ -49,7 +65,14 @@ export class BalancerPoolsCache extends PoolsCache {
|
|||||||
[from: string]: { [to: string]: Pool[] };
|
[from: string]: { [to: string]: Pool[] };
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
const pools = await this._fetchTopPoolsAsync();
|
let pools: BalancerPoolResponse[];
|
||||||
|
try {
|
||||||
|
pools = await this._fetchTopPoolsAsync();
|
||||||
|
} catch (err) {
|
||||||
|
this._warningLogger(err, 'Failed to fetch top pools for Balancer V1');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const pool of pools) {
|
for (const pool of pools) {
|
||||||
const { tokensList } = pool;
|
const { tokensList } = pool;
|
||||||
for (const from of tokensList) {
|
for (const from of tokensList) {
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import { ChainId } from '@0x/contract-addresses';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
// import { parsePoolData } from '@balancer-labs'; // TODO - upgrade to v2
|
// import { parsePoolData } from '@balancer-labs'; // TODO - upgrade to v2
|
||||||
import { Pool } from '@balancer-labs/sor/dist/types';
|
import { Pool } from 'balancer-labs-sor-v1/dist/types';
|
||||||
import { gql, request } from 'graphql-request';
|
import { gql, request } from 'graphql-request';
|
||||||
|
|
||||||
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
|
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
|
||||||
import { LogFunction } from '../../../types';
|
import { LogFunction } from '../../../types';
|
||||||
import {
|
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_TOP_POOLS_FETCHED } from '../constants';
|
||||||
BALANCER_MAX_POOLS_FETCHED,
|
|
||||||
BALANCER_TOP_POOLS_FETCHED,
|
|
||||||
BALANCER_V2_SUBGRAPH_URL_BY_CHAIN,
|
|
||||||
} from '../constants';
|
|
||||||
|
|
||||||
import { parsePoolData } from './balancer_sor_v2';
|
import { parsePoolData } from './balancer_sor_v2';
|
||||||
import { CacheValue, PoolsCache } from './pools_cache';
|
import { NoOpPoolsCache } from './no_op_pools_cache';
|
||||||
|
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
|
||||||
|
|
||||||
|
// tslint:disable: member-ordering
|
||||||
|
|
||||||
|
const BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN = new Map<ChainId, string>([
|
||||||
|
[ChainId.Fantom, 'https://api.thegraph.com/subgraphs/name/beethovenxfi/beethovenx'],
|
||||||
|
]);
|
||||||
|
|
||||||
// tslint:disable-next-line:custom-no-magic-numbers
|
// tslint:disable-next-line:custom-no-magic-numbers
|
||||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
||||||
@@ -28,7 +31,16 @@ interface BalancerPoolResponse {
|
|||||||
amp: string | null;
|
amp: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BalancerV2PoolsCache extends PoolsCache {
|
export class BalancerV2PoolsCache extends AbstractPoolsCache {
|
||||||
|
public static createBeethovenXPoolCache(chainId: ChainId): PoolsCache {
|
||||||
|
const subgraphUrl = BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN.get(chainId);
|
||||||
|
if (subgraphUrl === undefined) {
|
||||||
|
return new NoOpPoolsCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BalancerV2PoolsCache(subgraphUrl);
|
||||||
|
}
|
||||||
|
|
||||||
private static _parseSubgraphPoolData(pool: any, takerToken: string, makerToken: string): Pool {
|
private static _parseSubgraphPoolData(pool: any, takerToken: string, makerToken: string): Pool {
|
||||||
const tToken = pool.tokens.find((t: any) => t.address === takerToken);
|
const tToken = pool.tokens.find((t: any) => t.address === takerToken);
|
||||||
const mToken = pool.tokens.find((t: any) => t.address === makerToken);
|
const mToken = pool.tokens.find((t: any) => t.address === makerToken);
|
||||||
@@ -49,13 +61,12 @@ export class BalancerV2PoolsCache extends PoolsCache {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
private constructor(
|
||||||
chainId: ChainId,
|
private readonly subgraphUrl: string,
|
||||||
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId],
|
|
||||||
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
||||||
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
|
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
|
||||||
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
|
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,
|
||||||
cache: { [key: string]: CacheValue } = {},
|
cache: Map<string, CacheValue> = new Map(),
|
||||||
) {
|
) {
|
||||||
super(cache);
|
super(cache);
|
||||||
void this._loadTopPoolsAsync();
|
void this._loadTopPoolsAsync();
|
||||||
@@ -63,19 +74,6 @@ export class BalancerV2PoolsCache extends PoolsCache {
|
|||||||
setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2);
|
setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
|
|
||||||
// try {
|
|
||||||
// const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
|
|
||||||
// // Sort by maker token balance (descending)
|
|
||||||
// const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) =>
|
|
||||||
// b.balanceOut.minus(a.balanceOut).toNumber(),
|
|
||||||
// );
|
|
||||||
// return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools;
|
|
||||||
// } catch (err) {
|
|
||||||
// return [];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
protected async _fetchTopPoolsAsync(): Promise<BalancerPoolResponse[]> {
|
protected async _fetchTopPoolsAsync(): Promise<BalancerPoolResponse[]> {
|
||||||
const query = gql`
|
const query = gql`
|
||||||
query fetchTopPools($topPoolsFetched: Int!) {
|
query fetchTopPools($topPoolsFetched: Int!) {
|
||||||
@@ -114,7 +112,14 @@ export class BalancerV2PoolsCache extends PoolsCache {
|
|||||||
[from: string]: { [to: string]: Pool[] };
|
[from: string]: { [to: string]: Pool[] };
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
const pools = await this._fetchTopPoolsAsync();
|
let pools: BalancerPoolResponse[];
|
||||||
|
try {
|
||||||
|
pools = await this._fetchTopPoolsAsync();
|
||||||
|
} catch (err) {
|
||||||
|
this._warningLogger(err, 'Failed to fetch top pools for Balancer V2');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const pool of pools) {
|
for (const pool of pools) {
|
||||||
const { tokensList } = pool;
|
const { tokensList } = pool;
|
||||||
for (const from of tokensList) {
|
for (const from of tokensList) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
import { Pool } from '@balancer-labs/sor/dist/types';
|
import { ChainId } from '@0x/contract-addresses';
|
||||||
|
import { Pool } from 'balancer-labs-sor-v1/dist/types';
|
||||||
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
|
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
|
||||||
|
|
||||||
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';
|
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';
|
||||||
|
|
||||||
import { CacheValue, PoolsCache } from './pools_cache';
|
import { NoOpPoolsCache } from './no_op_pools_cache';
|
||||||
|
import { AbstractPoolsCache, CacheValue, PoolsCache } from './pools_cache';
|
||||||
|
|
||||||
export class CreamPoolsCache extends PoolsCache {
|
export class CreamPoolsCache extends AbstractPoolsCache {
|
||||||
constructor(
|
public static create(chainId: ChainId): PoolsCache {
|
||||||
_cache: { [key: string]: CacheValue } = {},
|
if (chainId !== ChainId.Mainnet) {
|
||||||
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
return new NoOpPoolsCache();
|
||||||
) {
|
}
|
||||||
super(_cache);
|
|
||||||
|
return new CreamPoolsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
|
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
|
||||||
try {
|
try {
|
||||||
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
|
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
|
||||||
@@ -25,4 +27,10 @@ export class CreamPoolsCache extends PoolsCache {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private constructor(
|
||||||
|
_cache: Map<string, CacheValue> = new Map(),
|
||||||
|
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
|
||||||
|
) {
|
||||||
|
super(_cache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export { BalancerPoolsCache } from './balancer_utils';
|
export { BalancerPoolsCache } from './balancer_utils';
|
||||||
export { BalancerV2PoolsCache } from './balancer_v2_utils';
|
export { BalancerV2PoolsCache } from './balancer_v2_utils';
|
||||||
export { CreamPoolsCache } from './cream_utils';
|
export { CreamPoolsCache } from './cream_utils';
|
||||||
export { PoolsCache } from './pools_cache';
|
export { AbstractPoolsCache, PoolsCache } from './pools_cache';
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Pool, PoolsCache } from './pools_cache';
|
||||||
|
|
||||||
|
// tslint:disable:prefer-function-over-method
|
||||||
|
|
||||||
|
export class NoOpPoolsCache implements PoolsCache {
|
||||||
|
public async getFreshPoolsForPairAsync(
|
||||||
|
_takerToken: string,
|
||||||
|
_makerToken: string,
|
||||||
|
_timeoutMs?: number | undefined,
|
||||||
|
): Promise<Pool[]> {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPoolAddressesForPair(_takerToken: string, _makerToken: string): string[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public isFresh(_takerToken: string, _makerToken: string): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Pool } from '@balancer-labs/sor/dist/types';
|
import { Pool } from 'balancer-labs-sor-v1/dist/types';
|
||||||
|
|
||||||
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
|
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
|
||||||
export { Pool };
|
export { Pool };
|
||||||
@@ -10,15 +10,29 @@ export interface CacheValue {
|
|||||||
// tslint:disable:custom-no-magic-numbers
|
// tslint:disable:custom-no-magic-numbers
|
||||||
// Cache results for 30mins
|
// Cache results for 30mins
|
||||||
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
|
const DEFAULT_CACHE_TIME_MS = (ONE_HOUR_IN_SECONDS / 2) * ONE_SECOND_MS;
|
||||||
const DEFAULT_TIMEOUT_MS = 1000;
|
const DEFAULT_TIMEOUT_MS = 3000;
|
||||||
// tslint:enable:custom-no-magic-numbers
|
// tslint:enable:custom-no-magic-numbers
|
||||||
|
|
||||||
export abstract class PoolsCache {
|
export interface PoolsCache {
|
||||||
protected static _isExpired(value: CacheValue): boolean {
|
getFreshPoolsForPairAsync(takerToken: string, makerToken: string, timeoutMs?: number): Promise<Pool[]>;
|
||||||
|
getPoolAddressesForPair(takerToken: string, makerToken: string): string[];
|
||||||
|
isFresh(takerToken: string, makerToken: string): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractPoolsCache implements PoolsCache {
|
||||||
|
protected static _getKey(takerToken: string, makerToken: string): string {
|
||||||
|
return `${takerToken}-${makerToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static _isExpired(value: CacheValue | undefined): boolean {
|
||||||
|
if (value === undefined) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return Date.now() >= value.expiresAt;
|
return Date.now() >= value.expiresAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected readonly _cache: { [key: string]: CacheValue },
|
protected readonly _cache: Map<string, CacheValue>,
|
||||||
protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS,
|
protected readonly _cacheTimeMs: number = DEFAULT_CACHE_TIME_MS,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -31,47 +45,42 @@ export abstract class PoolsCache {
|
|||||||
return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]);
|
return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCachedPoolAddressesForPair(
|
/**
|
||||||
takerToken: string,
|
* Returns pool addresses (can be stale) for a pair.
|
||||||
makerToken: string,
|
*
|
||||||
ignoreExpired: boolean = true,
|
* An empty array will be returned if cache does not exist.
|
||||||
): string[] | undefined {
|
*/
|
||||||
const key = JSON.stringify([takerToken, makerToken]);
|
public getPoolAddressesForPair(takerToken: string, makerToken: string): string[] {
|
||||||
const value = this._cache[key];
|
const value = this._getValue(takerToken, makerToken);
|
||||||
if (ignoreExpired) {
|
return value === undefined ? [] : value.pools.map(pool => pool.id);
|
||||||
return value === undefined ? [] : value.pools.map(pool => pool.id);
|
|
||||||
}
|
|
||||||
if (!value) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (PoolsCache._isExpired(value)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return (value || []).pools.map(pool => pool.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public isFresh(takerToken: string, makerToken: string): boolean {
|
public isFresh(takerToken: string, makerToken: string): boolean {
|
||||||
const cached = this.getCachedPoolAddressesForPair(takerToken, makerToken, false);
|
const value = this._getValue(takerToken, makerToken);
|
||||||
return cached !== undefined;
|
return !AbstractPoolsCache._isExpired(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _getValue(takerToken: string, makerToken: string): CacheValue | undefined {
|
||||||
|
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
|
||||||
|
return this._cache.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
|
protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
|
||||||
const key = JSON.stringify([takerToken, makerToken]);
|
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
|
||||||
const value = this._cache[key];
|
const value = this._cache.get(key);
|
||||||
if (value === undefined || value.expiresAt >= Date.now()) {
|
if (!AbstractPoolsCache._isExpired(value)) {
|
||||||
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
|
return value!.pools;
|
||||||
const expiresAt = Date.now() + this._cacheTimeMs;
|
|
||||||
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
|
|
||||||
}
|
}
|
||||||
return this._cache[key].pools;
|
|
||||||
|
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
|
||||||
|
const expiresAt = Date.now() + this._cacheTimeMs;
|
||||||
|
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
|
||||||
|
return pools;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void {
|
protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void {
|
||||||
const key = JSON.stringify([takerToken, makerToken]);
|
const key = AbstractPoolsCache._getKey(takerToken, makerToken);
|
||||||
this._cache[key] = {
|
this._cache.set(key, { pools, expiresAt });
|
||||||
pools,
|
|
||||||
expiresAt,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]>;
|
protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]>;
|
||||||
|
|||||||
@@ -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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,20 @@
|
|||||||
|
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
|
||||||
import { BigNumber } from '@0x/utils';
|
import { BigNumber } from '@0x/utils';
|
||||||
|
|
||||||
import { MarketOperation } from '../../types';
|
import { MarketOperation } from '../../types';
|
||||||
|
|
||||||
import { SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
|
import { SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
|
||||||
import { DexSample, ERC20BridgeSource, ExchangeProxyOverhead, FeeSchedule, MultiHopFillData } from './types';
|
import { adjustOutput } from './fills';
|
||||||
|
import { IdentityFillAdjustor } from './identity_fill_adjustor';
|
||||||
|
import {
|
||||||
|
DexSample,
|
||||||
|
ERC20BridgeSource,
|
||||||
|
ExchangeProxyOverhead,
|
||||||
|
FeeSchedule,
|
||||||
|
Fill,
|
||||||
|
FillAdjustor,
|
||||||
|
MultiHopFillData,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
// tslint:disable:no-bitwise
|
// tslint:disable:no-bitwise
|
||||||
|
|
||||||
@@ -18,20 +29,55 @@ export function getTwoHopAdjustedRate(
|
|||||||
outputAmountPerEth: BigNumber,
|
outputAmountPerEth: BigNumber,
|
||||||
fees: FeeSchedule = {},
|
fees: FeeSchedule = {},
|
||||||
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
|
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
|
||||||
|
fillAdjustor: FillAdjustor = new IdentityFillAdjustor(),
|
||||||
): BigNumber {
|
): BigNumber {
|
||||||
const { output, input, fillData } = twoHopQuote;
|
const { output, input, fillData } = twoHopQuote;
|
||||||
if (input.isLessThan(targetInput) || output.isZero()) {
|
if (input.isLessThan(targetInput) || output.isZero()) {
|
||||||
return ZERO_AMOUNT;
|
return ZERO_AMOUNT;
|
||||||
}
|
}
|
||||||
const penalty = outputAmountPerEth.times(
|
|
||||||
exchangeProxyOverhead(
|
// Flags to indicate which sources are used
|
||||||
SOURCE_FLAGS.MultiHop |
|
const flags =
|
||||||
SOURCE_FLAGS[fillData.firstHopSource.source] |
|
SOURCE_FLAGS.MultiHop |
|
||||||
SOURCE_FLAGS[fillData.secondHopSource.source],
|
SOURCE_FLAGS[fillData.firstHopSource.source] |
|
||||||
).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
|
SOURCE_FLAGS[fillData.secondHopSource.source];
|
||||||
);
|
|
||||||
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
|
// Penalty of going to those sources in terms of output
|
||||||
return side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
|
const sourcePenalty = outputAmountPerEth.times(fees[ERC20BridgeSource.MultiHop]!(fillData).fee).integerValue();
|
||||||
|
|
||||||
|
// Create a Fill so it can be adjusted by the `FillAdjustor`
|
||||||
|
const fill: Fill = {
|
||||||
|
...twoHopQuote,
|
||||||
|
flags,
|
||||||
|
type: FillQuoteTransformerOrderType.Bridge,
|
||||||
|
adjustedOutput: adjustOutput(side, twoHopQuote.output, sourcePenalty),
|
||||||
|
sourcePathId: `${ERC20BridgeSource.MultiHop}-${fillData.firstHopSource.source}-${fillData.secondHopSource.source}`,
|
||||||
|
// We don't have this information at this stage
|
||||||
|
gas: 0,
|
||||||
|
};
|
||||||
|
// Adjust the individual Fill
|
||||||
|
// HACK: Chose the worst of slippage between the two sources in multihop
|
||||||
|
const adjustedOutputLeft = fillAdjustor.adjustFills(
|
||||||
|
side,
|
||||||
|
[{ ...fill, source: fillData.firstHopSource.source }],
|
||||||
|
targetInput,
|
||||||
|
)[0].adjustedOutput;
|
||||||
|
const adjustedOutputRight = fillAdjustor.adjustFills(
|
||||||
|
side,
|
||||||
|
[{ ...fill, source: fillData.secondHopSource.source }],
|
||||||
|
targetInput,
|
||||||
|
)[0].adjustedOutput;
|
||||||
|
// In Sells, output smaller is worse (you're getting less out)
|
||||||
|
// In Buys, output larger is worse (it's costing you more)
|
||||||
|
const fillAdjustedOutput =
|
||||||
|
side === MarketOperation.Sell
|
||||||
|
? BigNumber.min(adjustedOutputLeft, adjustedOutputRight)
|
||||||
|
: BigNumber.max(adjustedOutputLeft, adjustedOutputRight);
|
||||||
|
|
||||||
|
const pathPenalty = outputAmountPerEth.times(exchangeProxyOverhead(flags)).integerValue();
|
||||||
|
const pathAdjustedOutput = adjustOutput(side, fillAdjustedOutput, pathPenalty);
|
||||||
|
|
||||||
|
return getRate(side, input, pathAdjustedOutput);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,6 +105,8 @@ export function getCompleteRate(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the rate given the input/output of a path.
|
* Computes the rate given the input/output of a path.
|
||||||
|
*
|
||||||
|
* If it is a sell, output/input. If it is a buy, input/output.
|
||||||
*/
|
*/
|
||||||
export function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber {
|
export function getRate(side: MarketOperation, input: BigNumber, output: BigNumber): BigNumber {
|
||||||
if (input.eq(0) || output.eq(0)) {
|
if (input.eq(0) || output.eq(0)) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user