Compare commits

..

6 Commits

Author SHA1 Message Date
Noah Khamliche
9b9c47ef56 Merge branch 'development' into feat/RedactedCurvePool 2022-02-28 15:37:33 -05:00
Noah Khamliche
dfcde47ab3 added WETH/BTRFLY pool on curve mainnet 2022-02-23 20:35:18 -05:00
Noah Khamliche
424066c1a2 remove comments 2022-02-17 10:26:51 -05:00
Noah Khamliche
d8a489843e removed ohmv2/dai and ohmv2/weth, routing is the same without the search overhead 2022-02-17 10:21:17 -05:00
Noah Khamliche
914449025c updated changelog 2022-02-17 10:07:37 -05:00
Noah Khamliche
49f129aa30 fixed btrfly routing to include the ohmV2/dai, ohmV2/btfly, and ohmV2/weth pools 2022-02-17 10:05:28 -05:00
183 changed files with 9794 additions and 7980 deletions

View File

@@ -4,7 +4,7 @@ jobs:
build:
resource_class: xlarge
docker:
- image: node:16
- image: node:12
environment:
NODE_OPTIONS: '--max-old-space-size=16384'
working_directory: ~/repo
@@ -19,6 +19,7 @@ jobs:
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
- setup_remote_docker
- run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci
- run: yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts
- save_cache:
key: repo-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -30,7 +31,7 @@ jobs:
test-exchange-ganache:
resource_class: medium+
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -40,7 +41,7 @@ jobs:
test-integrations-ganache:
resource_class: medium+
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -50,7 +51,7 @@ jobs:
test-contracts-staking-ganache:
resource_class: medium+
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -60,7 +61,7 @@ jobs:
test-contracts-extra-ganache:
resource_class: medium+
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -70,7 +71,7 @@ jobs:
test-contracts-rest-ganache:
resource_class: medium+
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -82,7 +83,7 @@ jobs:
environment:
NODE_OPTIONS: '--max-old-space-size=6442'
docker:
- image: node:16
- image: node:12
- image: 0xorg/verdaccio
working_directory: ~/repo
steps:
@@ -96,7 +97,7 @@ jobs:
path: ~/.npm/_logs
test-doc-generation:
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -107,10 +108,8 @@ jobs:
no_output_timeout: 1200
test-rest:
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
environment:
RUST_ROUTER: 'true'
steps:
- restore_cache:
keys:
@@ -118,6 +117,7 @@ jobs:
- 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-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/asset-swapper -m --serial -c test:circleci
- save_cache:
@@ -136,7 +136,7 @@ jobs:
resource_class: large
working_directory: ~/repo
docker:
- image: node:16
- image: node:12
steps:
- restore_cache:
keys:
@@ -147,7 +147,7 @@ jobs:
- run: yarn diff_md_docs:ci
submit-coverage:
docker:
- image: node:16
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:

View File

@@ -1,6 +1,7 @@
python: ['python-packages']
contracts: ['contracts']
@0x/contract-addresses: ['packages/contract-addresses']
@0x/migrations: ['packages/migrations']
@0x/order-utils: ['packages/order-utils']
@0x/contract-artifacts: ['packages/contract-artifacts']
@0x/contract-wrappers: ['packages/contract-wrappers']

View File

@@ -28,7 +28,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-node@v1
with:
node-version: 16
node-version: 10
- uses: actions/setup-python@v2
- name: 'configure git'
run: |

View File

@@ -1,20 +1,18 @@
# See https://help.github.com/articles/about-codeowners/
# for more info about CODEOWNERS file
# It uses the same pattern rule for gitignore file
# https://git-scm.com/docs/gitignore#_pattern_format
packages/asset-swapper/ @dekz @mzhu25 @dextracker @kh-chang
# Website
packages/asset-swapper/ @BMillman19 @fragosti @dave4506
packages/instant/ @BMillman19 @fragosti @dave4506
# Dev tools & setup
.circleci/ @dekz @mzhu25
packages/contract-addresses/ @dekz @mzhu25 @dextracker @kh-chang
packages/contract-artifacts/ @dekz @mzhu25
packages/protocol-utils/ @dekz @mzhu25
.circleci/ @dorothy-zbornak
packages/contract-addresses/ @abandeali1
packages/contract-artifacts/ @abandeali1
packages/order-utils/ @dorothy-zbornak
# Protocol/smart contracts
contracts/ @dekz @mzhu25 @dextracker
contracts/ @abandeali1 @hysz @dorothy-zbornak @mzhu25

View File

@@ -38,6 +38,7 @@ These packages are all under development. See [/contracts/README.md](/contracts/
| [`@0x/protocol-utils`](/packages/protocol-utils) | [![npm](https://img.shields.io/npm/v/@0x/protocol-utils.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contract-addresses.svg)](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) | [![npm](https://img.shields.io/npm/v/@0x/contract-wrappers.svg)](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
| [`@0x/migrations`](/packages/migrations) | [![npm](https://img.shields.io/npm/v/@0x/migrations.svg)](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [![npm](https://img.shields.io/npm/v/@0x/contract-artifacts.svg)](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
## Usage

View File

@@ -1,58 +1,4 @@
[
{
"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,
"version": "3.3.28",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1646225739,
"version": "3.3.27",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1645569128,
"version": "3.3.26",

View File

@@ -5,30 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Dependencies updated
## v3.3.27 - _March 2, 2022_
* Dependencies updated
## v3.3.26 - _February 22, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.32",
"version": "3.3.26",
"engines": {
"node": ">=6.12"
},
@@ -51,18 +51,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/contracts-utils": "^4.8.13",
"@0x/dev-utils": "^4.2.14",
"@0x/sol-compiler": "^4.8.1",
"@0x/abi-gen": "^5.7.2",
"@0x/contracts-gen": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.17",
"@0x/contracts-utils": "^4.8.7",
"@0x/dev-utils": "^4.2.11",
"@0x/sol-compiler": "^4.7.8",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"@0x/web3-wrapper": "^7.6.2",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -70,7 +70,7 @@
"chai-as-promised": "^7.1.0",
"chai-bignumber": "^3.0.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.7.0",
"ethereum-types": "^3.6.0",
"lodash": "^4.17.11",
"make-promises-safe": "^1.1.0",
"mocha": "^6.2.0",
@@ -79,10 +79,10 @@
"solhint": "^1.4.1",
"tslint": "5.11.0",
"typedoc": "~0.16.11",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.5.0",
"@0x/base-contract": "^6.4.5",
"ethers": "~4.0.4"
},
"publishConfig": {

View File

@@ -1,58 +1,4 @@
[
{
"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,
"version": "5.4.19",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1646225739,
"version": "5.4.18",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1645569128,
"version": "5.4.17",

View File

@@ -5,30 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Dependencies updated
## v5.4.18 - _March 2, 2022_
* Dependencies updated
## v5.4.17 - _February 22, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.23",
"version": "5.4.17",
"engines": {
"node": ">=6.12"
},
@@ -34,28 +34,28 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
"devDependencies": {
"@0x/sol-compiler": "^4.8.1",
"@0x/sol-compiler": "^4.7.8",
"@0x/tslint-config": "^4.1.4",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
"tslint": "5.11.0",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/base-contract": "^6.5.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/dev-utils": "^4.2.14",
"@0x/json-schemas": "^6.4.4",
"@0x/assert": "^3.0.31",
"@0x/base-contract": "^6.4.5",
"@0x/contract-addresses": "^6.11.0",
"@0x/dev-utils": "^4.2.11",
"@0x/json-schemas": "^6.4.1",
"@0x/order-utils": "^10.4.28",
"@0x/sol-coverage": "^4.0.45",
"@0x/sol-profiler": "^4.1.35",
"@0x/sol-trace": "^3.0.45",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"@0x/sol-coverage": "^4.0.42",
"@0x/sol-profiler": "^4.1.32",
"@0x/sol-trace": "^3.0.42",
"@0x/subproviders": "^6.6.2",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"@0x/web3-wrapper": "^7.6.2",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",
@@ -67,7 +67,7 @@
"chai-bignumber": "^3.0.0",
"decimal.js": "^10.2.0",
"dirty-chai": "^2.0.1",
"ethereum-types": "^3.7.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4",
"js-combinatorics": "^0.5.3",

View File

@@ -1,58 +1,4 @@
[
{
"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,
"version": "1.4.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1646225739,
"version": "1.4.10",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1645569128,
"version": "1.4.9",

View File

@@ -5,30 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Dependencies updated
## v1.4.10 - _March 2, 2022_
* Dependencies updated
## v1.4.9 - _February 22, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.4.15",
"version": "1.4.9",
"engines": {
"node": ">=6.12"
},
@@ -46,14 +46,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/abi-gen": "^5.7.2",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.32",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-erc20": "^3.3.26",
"@0x/contracts-gen": "^2.0.43",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/sol-compiler": "^4.8.1",
"@0x/contracts-test-utils": "^5.4.17",
"@0x/sol-compiler": "^4.7.8",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -69,17 +69,17 @@
"solhint": "^1.4.1",
"tslint": "5.11.0",
"typedoc": "~0.16.11",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.5.0",
"@0x/protocol-utils": "^11.15.0",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"ethereum-types": "^3.7.0",
"@0x/base-contract": "^6.4.5",
"@0x/protocol-utils": "^1.11.0",
"@0x/subproviders": "^6.6.2",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"@0x/web3-wrapper": "^7.6.2",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10"
},
"publishConfig": {

View File

@@ -1,58 +1,4 @@
[
{
"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,
"version": "4.8.9",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1646225739,
"version": "4.8.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1645569128,
"version": "4.8.7",

View File

@@ -5,30 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Dependencies updated
## v4.8.8 - _March 2, 2022_
* Dependencies updated
## v4.8.7 - _February 22, 2022_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.8.13",
"version": "4.8.7",
"engines": {
"node": ">=6.12"
},
@@ -50,15 +50,15 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/dev-utils": "^4.2.14",
"@0x/abi-gen": "^5.7.2",
"@0x/contracts-gen": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.17",
"@0x/dev-utils": "^4.2.11",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.8.1",
"@0x/sol-compiler": "^4.7.8",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.6",
"@0x/web3-wrapper": "^7.6.5",
"@0x/types": "^3.3.4",
"@0x/web3-wrapper": "^7.6.2",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -76,14 +76,14 @@
"solhint": "^1.4.1",
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.5.0",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/base-contract": "^6.4.5",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"bn.js": "^4.11.8",
"ethereum-types": "^3.7.0"
"ethereum-types": "^3.6.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,70 +1,4 @@
[
{
"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,
"version": "0.31.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1646225739,
"version": "0.31.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.31.0",
"changes": [

View File

@@ -5,32 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Dependencies updated
## v0.31.1 - _March 2, 2022_
* Dependencies updated
## v0.31.0 - _February 22, 2022_
* Add ERC721OrdersFeature, ERC1155OrdersFeature, and ERC165Feature (#429)

View File

@@ -1,88 +0,0 @@
// 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);
}

View File

@@ -1,141 +0,0 @@
// 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
);
}
}

View File

@@ -1,132 +0,0 @@
// 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
);
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2022 ZeroEx Intl.
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.
@@ -20,52 +20,54 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "./AbstractBridgeAdapter.sol";
import "./IBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBalancerV2Batch.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinBancorV3.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCompound.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinCryptoCom.sol";
import "./mixins/MixinDodo.sol";
import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyber.sol";
import "./mixins/MixinKyberDmm.sol";
import "./mixins/MixinLido.sol";
import "./mixins/MixinMakerPSM.sol";
import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinMStable.sol";
import "./mixins/MixinNerve.sol";
import "./mixins/MixinOasis.sol";
import "./mixins/MixinShell.sol";
import "./mixins/MixinUniswap.sol";
import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinUniswapV3.sol";
import "./mixins/MixinZeroExBridge.sol";
contract EthereumBridgeAdapter is
AbstractBridgeAdapter(1, "Ethereum"),
contract BridgeAdapter is
IBridgeAdapter,
MixinAaveV2,
MixinBalancer,
MixinBalancerV2,
MixinBalancerV2Batch,
MixinBancor,
MixinBancorV3,
MixinCoFiX,
MixinCompound,
MixinCurve,
MixinCurveV2,
MixinCryptoCom,
MixinDodo,
MixinDodoV2,
MixinKyber,
MixinKyberDmm,
MixinLido,
MixinMakerPSM,
MixinMooniswap,
MixinMStable,
MixinNerve,
MixinOasis,
MixinShell,
MixinUniswap,
MixinUniswapV2,
@@ -74,29 +76,43 @@ contract EthereumBridgeAdapter is
{
constructor(IEtherTokenV06 weth)
public
MixinAaveV2()
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinBancorV3(weth)
MixinCoFiX()
MixinCompound(weth)
MixinCurve(weth)
MixinCurveV2()
MixinCryptoCom()
MixinDodo()
MixinDodoV2()
MixinKyber(weth)
MixinLido(weth)
MixinMakerPSM()
MixinMooniswap(weth)
MixinMStable()
MixinNerve()
MixinOasis()
MixinShell()
MixinUniswap(weth)
MixinUniswapV2()
MixinUniswapV3()
MixinZeroExBridge()
{}
function _trade(
function trade(
BridgeOrder memory order,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bool dryRun
uint256 sellAmount
)
internal
public
override
returns (uint256 boughtAmount, bool supportedSource)
returns (uint256 boughtAmount)
{
uint128 protocolId = uint128(uint256(order.source) >> 128);
if (protocolId == BridgeProtocols.CURVE) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeCurve(
sellToken,
buyToken,
@@ -104,7 +120,6 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.CURVEV2) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeCurveV2(
sellToken,
buyToken,
@@ -112,21 +127,18 @@ contract EthereumBridgeAdapter is
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.UNISWAP) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeUniswap(
sellToken,
buyToken,
@@ -134,7 +146,6 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.BALANCER) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeBalancer(
sellToken,
buyToken,
@@ -142,21 +153,20 @@ contract EthereumBridgeAdapter is
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(
} else if (protocolId == BridgeProtocols.KYBER) {
boughtAmount = _tradeKyber(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.MAKERPSM) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeMakerPsm(
sellToken,
buyToken,
@@ -164,7 +174,6 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.MOONISWAP) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeMooniswap(
sellToken,
buyToken,
@@ -172,15 +181,20 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.MSTABLE) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeMStable(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.OASIS) {
boughtAmount = _tradeOasis(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.SHELL) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeShell(
sellToken,
buyToken,
@@ -188,49 +202,49 @@ contract EthereumBridgeAdapter is
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.CRYPTOCOM) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeCryptoCom(
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.BANCOR) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeBancor(
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.COFIX) {
boughtAmount = _tradeCoFiX(
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.KYBERDMM) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeKyberDmm(
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.LIDO) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeLido(
sellToken,
buyToken,
@@ -238,7 +252,6 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.AAVEV2) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeAaveV2(
sellToken,
buyToken,
@@ -246,22 +259,13 @@ contract EthereumBridgeAdapter is
order.bridgeData
);
} else if (protocolId == BridgeProtocols.COMPOUND) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeCompound(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.BANCORV3) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeBancorV3(
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNKNOWN) {
if (dryRun) { return (0, true); }
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,
buyToken,

View File

@@ -27,34 +27,29 @@ library BridgeProtocols {
// A incrementally increasing, append-only list of protocol IDs.
// We don't use an enum so solidity doesn't throw when we pass in a
// new protocol ID that hasn't been rolled up yet.
uint128 internal constant UNKNOWN = 0;
uint128 internal constant CURVE = 1;
uint128 internal constant UNISWAPV2 = 2;
uint128 internal constant UNISWAP = 3;
uint128 internal constant BALANCER = 4;
uint128 internal constant KYBER = 5; // Not used: deprecated.
uint128 internal constant MOONISWAP = 6;
uint128 internal constant MSTABLE = 7;
uint128 internal constant OASIS = 8; // Not used: deprecated.
uint128 internal constant SHELL = 9;
uint128 internal constant DODO = 10;
uint128 internal constant DODOV2 = 11;
uint128 internal constant CRYPTOCOM = 12;
uint128 internal constant BANCOR = 13;
uint128 internal constant COFIX = 14; // Not used: deprecated.
uint128 internal constant NERVE = 15;
uint128 internal constant MAKERPSM = 16;
uint128 internal constant BALANCERV2 = 17;
uint128 internal constant UNISWAPV3 = 18;
uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
uint128 internal constant AAVEV2 = 23;
uint128 internal constant COMPOUND = 24;
uint128 internal constant BALANCERV2BATCH = 25;
uint128 internal constant GMX = 26;
uint128 internal constant PLATYPUS = 27;
uint128 internal constant BANCORV3 = 28;
uint128 internal constant VELODROME = 29;
uint128 internal constant UNKNOWN = 0;
uint128 internal constant CURVE = 1;
uint128 internal constant UNISWAPV2 = 2;
uint128 internal constant UNISWAP = 3;
uint128 internal constant BALANCER = 4;
uint128 internal constant KYBER = 5;
uint128 internal constant MOONISWAP = 6;
uint128 internal constant MSTABLE = 7;
uint128 internal constant OASIS = 8;
uint128 internal constant SHELL = 9;
uint128 internal constant DODO = 10;
uint128 internal constant DODOV2 = 11;
uint128 internal constant CRYPTOCOM = 12;
uint128 internal constant BANCOR = 13;
uint128 internal constant COFIX = 14;
uint128 internal constant NERVE = 15;
uint128 internal constant MAKERPSM = 16;
uint128 internal constant BALANCERV2 = 17;
uint128 internal constant UNISWAPV3 = 18;
uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
uint128 internal constant AAVEV2 = 23;
uint128 internal constant COMPOUND = 24;
}

View File

@@ -1,84 +0,0 @@
// 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
);
}
}

View File

@@ -1,124 +0,0 @@
// 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
);
}
}

View File

@@ -50,10 +50,6 @@ interface IBridgeAdapter {
uint256 outputTokenAmount
);
function isSupportedSource(bytes32 source)
external
returns (bool isSupported);
function trade(
BridgeOrder calldata order,
IERC20TokenV06 sellToken,

View File

@@ -1,114 +0,0 @@
// 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/MixinUniswapV3.sol";
import "./mixins/MixinVelodrome.sol";
import "./mixins/MixinZeroExBridge.sol";
contract OptimismBridgeAdapter is
AbstractBridgeAdapter(10, "Optimism"),
MixinCurve,
MixinCurveV2,
MixinNerve,
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.UNKNOWN) {
if (dryRun) { return (0, true); }
boughtAmount = _tradeZeroExBridge(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
}
emit BridgeFill(
order.source,
sellToken,
buyToken,
sellAmount,
boughtAmount
);
}
}

View File

@@ -1,178 +0,0 @@
// 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
);
}
}

View File

@@ -1,107 +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";
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);
}
}

View File

@@ -1,128 +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";
/*
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;
}
}

View File

@@ -0,0 +1,92 @@
// 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;
}
}

View File

@@ -1,98 +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-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;
}
}

View File

@@ -0,0 +1,124 @@
// 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;
}
}

View File

@@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
/// @dev Minimal interface for minting StETH
interface IStETH {
interface ILido {
/// @dev Adds eth to the pool
/// @param _referral optional address for referrals
/// @return StETH Amount of shares generated
@@ -37,33 +37,6 @@ interface IStETH {
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 {
using LibERC20TokenV06 for IERC20TokenV06;
@@ -86,43 +59,12 @@ contract MixinLido {
internal
returns (uint256 boughtAmount)
{
if (address(sellToken) == address(WETH)) {
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)) {
(ILido lido) = abi.decode(bridgeData, (ILido));
if (address(sellToken) == address(WETH) && address(buyToken) == address(lido)) {
WETH.withdraw(sellAmount);
return stETH.getPooledEthByShares(stETH.submit{ value: sellAmount}(address(0)));
boughtAmount = lido.getPooledEthByShares(lido.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");
}
}

View File

@@ -0,0 +1,76 @@
// 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;
}
}

View File

@@ -1,98 +0,0 @@
// 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;
}
}

View File

@@ -1,64 +0,0 @@
// 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];
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.35.0",
"version": "0.31.0",
"engines": {
"node": ">=6.12"
},
@@ -41,9 +41,9 @@
"rollback": "node ./lib/scripts/rollback.js"
},
"config": {
"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",
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(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|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"
"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"
},
"repository": {
"type": "git",
@@ -55,14 +55,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/contracts-erc20": "^3.3.32",
"@0x/contracts-gen": "^2.0.46",
"@0x/contracts-test-utils": "^5.4.23",
"@0x/dev-utils": "^4.2.14",
"@0x/abi-gen": "^5.7.2",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-erc20": "^3.3.26",
"@0x/contracts-gen": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.17",
"@0x/dev-utils": "^4.2.11",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.8.1",
"@0x/sol-compiler": "^4.7.8",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -79,17 +79,17 @@
"truffle": "^5.0.32",
"tslint": "5.11.0",
"typedoc": "~0.16.11",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.5.0",
"@0x/protocol-utils": "^11.15.0",
"@0x/subproviders": "^6.6.5",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"ethereum-types": "^3.7.0",
"@0x/base-contract": "^6.4.5",
"@0x/protocol-utils": "^1.11.0",
"@0x/subproviders": "^6.6.2",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"@0x/web3-wrapper": "^7.6.2",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"ethers": "~4.0.4"
},

View File

@@ -6,13 +6,9 @@
import { ContractArtifact } from 'ethereum-types';
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 BSCBridgeAdapter from '../generated-artifacts/BSCBridgeAdapter.json';
import * as CeloBridgeAdapter from '../generated-artifacts/CeloBridgeAdapter.json';
import * as BridgeAdapter from '../generated-artifacts/BridgeAdapter.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 FeeCollectorController from '../generated-artifacts/FeeCollectorController.json';
import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json';
@@ -34,11 +30,9 @@ import * as LogMetadataTransformer from '../generated-artifacts/LogMetadataTrans
import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransactionsFeature.json';
import * as MultiplexFeature from '../generated-artifacts/MultiplexFeature.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 OwnableFeature from '../generated-artifacts/OwnableFeature.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 SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
@@ -64,6 +58,7 @@ export const artifacts = {
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
BridgeAdapter: BridgeAdapter as ContractArtifact,
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
ILiquidityProviderFeature: ILiquidityProviderFeature as ContractArtifact,
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
@@ -77,11 +72,4 @@ export const artifacts = {
IMultiplexFeature: IMultiplexFeature as ContractArtifact,
OtcOrdersFeature: OtcOrdersFeature 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,
};

View File

@@ -35,11 +35,7 @@ export * from './bloom_filter_utils';
export { GREEDY_TOKENS } from './constants';
export {
AffiliateFeeTransformerContract,
AvalancheBridgeAdapterContract,
BSCBridgeAdapterContract,
CeloBridgeAdapterContract,
EthereumBridgeAdapterContract,
FantomBridgeAdapterContract,
BridgeAdapterContract,
FillQuoteTransformerContract,
IOwnableFeatureContract,
IOwnableFeatureEvents,
@@ -49,9 +45,7 @@ export {
IZeroExContract,
LogMetadataTransformerContract,
MultiplexFeatureContract,
OptimismBridgeAdapterContract,
PayTakerTransformerContract,
PolygonBridgeAdapterContract,
PositiveSlippageFeeTransformerContract,
TransformERC20FeatureContract,
WethTransformerContract,

View File

@@ -4,13 +4,9 @@
* -----------------------------------------------------------------------------
*/
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/celo_bridge_adapter';
export * from '../generated-wrappers/bridge_adapter';
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_controller';
export * from '../generated-wrappers/fill_quote_transformer';
@@ -32,11 +28,9 @@ export * from '../generated-wrappers/log_metadata_transformer';
export * from '../generated-wrappers/meta_transactions_feature';
export * from '../generated-wrappers/multiplex_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/ownable_feature';
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/simple_function_registry_feature';
export * from '../generated-wrappers/transform_erc20_feature';

View File

@@ -5,20 +5,15 @@
*/
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 AvalancheBridgeAdapter from '../test/generated-artifacts/AvalancheBridgeAdapter.json';
import * as BatchFillNativeOrdersFeature from '../test/generated-artifacts/BatchFillNativeOrdersFeature.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 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 ERC1155OrdersFeature from '../test/generated-artifacts/ERC1155OrdersFeature.json';
import * as ERC165Feature from '../test/generated-artifacts/ERC165Feature.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 FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
@@ -106,28 +101,26 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBalancerV2Batch from '../test/generated-artifacts/MixinBalancerV2Batch.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinBancorV3 from '../test/generated-artifacts/MixinBancorV3.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json';
import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
import * as MixinGMX from '../test/generated-artifacts/MixinGMX.json';
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
import * as MixinKyberDmm from '../test/generated-artifacts/MixinKyberDmm.json';
import * as MixinLido from '../test/generated-artifacts/MixinLido.json';
import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json';
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
import * as MixinNerve from '../test/generated-artifacts/MixinNerve.json';
import * as MixinPlatypus from '../test/generated-artifacts/MixinPlatypus.json';
import * as MixinOasis from '../test/generated-artifacts/MixinOasis.json';
import * as MixinShell from '../test/generated-artifacts/MixinShell.json';
import * as MixinUniswap from '../test/generated-artifacts/MixinUniswap.json';
import * as MixinUniswapV2 from '../test/generated-artifacts/MixinUniswapV2.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 MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json';
import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json';
@@ -143,13 +136,11 @@ import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.
import * as NativeOrdersProtocolFees from '../test/generated-artifacts/NativeOrdersProtocolFees.json';
import * as NativeOrdersSettlement from '../test/generated-artifacts/NativeOrdersSettlement.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 OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
import * as PancakeSwapFeature from '../test/generated-artifacts/PancakeSwapFeature.json';
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.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 SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
@@ -316,41 +307,32 @@ export const artifacts = {
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
Transformer: Transformer as ContractArtifact,
WethTransformer: WethTransformer as ContractArtifact,
AbstractBridgeAdapter: AbstractBridgeAdapter as ContractArtifact,
AvalancheBridgeAdapter: AvalancheBridgeAdapter as ContractArtifact,
BSCBridgeAdapter: BSCBridgeAdapter as ContractArtifact,
BridgeAdapter: BridgeAdapter as ContractArtifact,
BridgeProtocols: BridgeProtocols as ContractArtifact,
CeloBridgeAdapter: CeloBridgeAdapter as ContractArtifact,
EthereumBridgeAdapter: EthereumBridgeAdapter as ContractArtifact,
FantomBridgeAdapter: FantomBridgeAdapter as ContractArtifact,
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
OptimismBridgeAdapter: OptimismBridgeAdapter as ContractArtifact,
PolygonBridgeAdapter: PolygonBridgeAdapter as ContractArtifact,
MixinAaveV2: MixinAaveV2 as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBalancerV2Batch: MixinBalancerV2Batch as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinBancorV3: MixinBancorV3 as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCompound: MixinCompound as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,
MixinCurveV2: MixinCurveV2 as ContractArtifact,
MixinDodo: MixinDodo as ContractArtifact,
MixinDodoV2: MixinDodoV2 as ContractArtifact,
MixinGMX: MixinGMX as ContractArtifact,
MixinKyber: MixinKyber as ContractArtifact,
MixinKyberDmm: MixinKyberDmm as ContractArtifact,
MixinLido: MixinLido as ContractArtifact,
MixinMStable: MixinMStable as ContractArtifact,
MixinMakerPSM: MixinMakerPSM as ContractArtifact,
MixinMooniswap: MixinMooniswap as ContractArtifact,
MixinNerve: MixinNerve as ContractArtifact,
MixinPlatypus: MixinPlatypus as ContractArtifact,
MixinOasis: MixinOasis as ContractArtifact,
MixinShell: MixinShell as ContractArtifact,
MixinUniswap: MixinUniswap as ContractArtifact,
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
MixinVelodrome: MixinVelodrome as ContractArtifact,
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
IERC1155Token: IERC1155Token as ContractArtifact,
IERC721Token: IERC721Token as ContractArtifact,

View File

@@ -28,7 +28,7 @@ import { artifacts } from '../artifacts';
import { TestFillQuoteTransformerBridgeContract } from '../generated-wrappers/test_fill_quote_transformer_bridge';
import { getRandomLimitOrder, getRandomRfqOrder } from '../utils/orders';
import {
EthereumBridgeAdapterContract,
BridgeAdapterContract,
FillQuoteTransformerContract,
TestFillQuoteTransformerExchangeContract,
TestFillQuoteTransformerHostContract,
@@ -52,8 +52,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
let singleProtocolFee: BigNumber;
const GAS_PRICE = 1337;
// Left half is 0, corresponding to BridgeProtocol.Unknown
const TEST_BRIDGE_SOURCE = hexUtils.leftPad(hexUtils.random(16), 32);
const TEST_BRIDGE_SOURCE = hexUtils.random(32);
const HIGH_BIT = new BigNumber(2).pow(255);
const REVERT_AMOUNT = new BigNumber('0xdeadbeef');
@@ -65,8 +64,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
env.txDefaults,
artifacts,
);
const bridgeAdapter = await EthereumBridgeAdapterContract.deployFrom0xArtifactAsync(
artifacts.EthereumBridgeAdapter,
const bridgeAdapter = await BridgeAdapterContract.deployFrom0xArtifactAsync(
artifacts.BridgeAdapter,
env.provider,
env.txDefaults,
artifacts,

View File

@@ -3,20 +3,15 @@
* 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/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/bootstrap_feature';
export * from '../test/generated-wrappers/bridge_adapter';
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/erc1155_orders_feature';
export * from '../test/generated-wrappers/erc165_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_controller';
export * from '../test/generated-wrappers/fill_quote_transformer';
@@ -104,28 +99,26 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_aave_v2';
export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_balancer_v2_batch';
export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_bancor_v3';
export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_compound';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';
export * from '../test/generated-wrappers/mixin_curve_v2';
export * from '../test/generated-wrappers/mixin_dodo';
export * from '../test/generated-wrappers/mixin_dodo_v2';
export * from '../test/generated-wrappers/mixin_g_m_x';
export * from '../test/generated-wrappers/mixin_kyber';
export * from '../test/generated-wrappers/mixin_kyber_dmm';
export * from '../test/generated-wrappers/mixin_lido';
export * from '../test/generated-wrappers/mixin_m_stable';
export * from '../test/generated-wrappers/mixin_maker_p_s_m';
export * from '../test/generated-wrappers/mixin_mooniswap';
export * from '../test/generated-wrappers/mixin_nerve';
export * from '../test/generated-wrappers/mixin_platypus';
export * from '../test/generated-wrappers/mixin_oasis';
export * from '../test/generated-wrappers/mixin_shell';
export * from '../test/generated-wrappers/mixin_uniswap';
export * from '../test/generated-wrappers/mixin_uniswap_v2';
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/mooniswap_liquidity_provider';
export * from '../test/generated-wrappers/multiplex_feature';
@@ -141,13 +134,11 @@ export * from '../test/generated-wrappers/native_orders_feature';
export * from '../test/generated-wrappers/native_orders_info';
export * from '../test/generated-wrappers/native_orders_protocol_fees';
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/ownable_feature';
export * from '../test/generated-wrappers/pancake_swap_feature';
export * from '../test/generated-wrappers/pay_taker_transformer';
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/simple_function_registry_feature';
export * from '../test/generated-wrappers/test_bridge';

View File

@@ -4,13 +4,9 @@
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*", "./scripts/**/*"],
"files": [
"generated-artifacts/AffiliateFeeTransformer.json",
"generated-artifacts/AvalancheBridgeAdapter.json",
"generated-artifacts/BSCBridgeAdapter.json",
"generated-artifacts/BatchFillNativeOrdersFeature.json",
"generated-artifacts/CeloBridgeAdapter.json",
"generated-artifacts/BridgeAdapter.json",
"generated-artifacts/CurveLiquidityProvider.json",
"generated-artifacts/EthereumBridgeAdapter.json",
"generated-artifacts/FantomBridgeAdapter.json",
"generated-artifacts/FeeCollector.json",
"generated-artifacts/FeeCollectorController.json",
"generated-artifacts/FillQuoteTransformer.json",
@@ -32,30 +28,23 @@
"generated-artifacts/MetaTransactionsFeature.json",
"generated-artifacts/MultiplexFeature.json",
"generated-artifacts/NativeOrdersFeature.json",
"generated-artifacts/OptimismBridgeAdapter.json",
"generated-artifacts/OtcOrdersFeature.json",
"generated-artifacts/OwnableFeature.json",
"generated-artifacts/PayTakerTransformer.json",
"generated-artifacts/PolygonBridgeAdapter.json",
"generated-artifacts/PositiveSlippageFeeTransformer.json",
"generated-artifacts/SimpleFunctionRegistryFeature.json",
"generated-artifacts/TransformERC20Feature.json",
"generated-artifacts/WethTransformer.json",
"generated-artifacts/ZeroEx.json",
"test/generated-artifacts/AbstractBridgeAdapter.json",
"test/generated-artifacts/AffiliateFeeTransformer.json",
"test/generated-artifacts/AvalancheBridgeAdapter.json",
"test/generated-artifacts/BSCBridgeAdapter.json",
"test/generated-artifacts/BatchFillNativeOrdersFeature.json",
"test/generated-artifacts/BootstrapFeature.json",
"test/generated-artifacts/BridgeAdapter.json",
"test/generated-artifacts/BridgeProtocols.json",
"test/generated-artifacts/CeloBridgeAdapter.json",
"test/generated-artifacts/CurveLiquidityProvider.json",
"test/generated-artifacts/ERC1155OrdersFeature.json",
"test/generated-artifacts/ERC165Feature.json",
"test/generated-artifacts/ERC721OrdersFeature.json",
"test/generated-artifacts/EthereumBridgeAdapter.json",
"test/generated-artifacts/FantomBridgeAdapter.json",
"test/generated-artifacts/FeeCollector.json",
"test/generated-artifacts/FeeCollectorController.json",
"test/generated-artifacts/FillQuoteTransformer.json",
@@ -143,28 +132,26 @@
"test/generated-artifacts/MixinAaveV2.json",
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBalancerV2Batch.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinBancorV3.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCompound.json",
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",
"test/generated-artifacts/MixinCurveV2.json",
"test/generated-artifacts/MixinDodo.json",
"test/generated-artifacts/MixinDodoV2.json",
"test/generated-artifacts/MixinGMX.json",
"test/generated-artifacts/MixinKyber.json",
"test/generated-artifacts/MixinKyberDmm.json",
"test/generated-artifacts/MixinLido.json",
"test/generated-artifacts/MixinMStable.json",
"test/generated-artifacts/MixinMakerPSM.json",
"test/generated-artifacts/MixinMooniswap.json",
"test/generated-artifacts/MixinNerve.json",
"test/generated-artifacts/MixinPlatypus.json",
"test/generated-artifacts/MixinOasis.json",
"test/generated-artifacts/MixinShell.json",
"test/generated-artifacts/MixinUniswap.json",
"test/generated-artifacts/MixinUniswapV2.json",
"test/generated-artifacts/MixinUniswapV3.json",
"test/generated-artifacts/MixinVelodrome.json",
"test/generated-artifacts/MixinZeroExBridge.json",
"test/generated-artifacts/MooniswapLiquidityProvider.json",
"test/generated-artifacts/MultiplexFeature.json",
@@ -180,13 +167,11 @@
"test/generated-artifacts/NativeOrdersInfo.json",
"test/generated-artifacts/NativeOrdersProtocolFees.json",
"test/generated-artifacts/NativeOrdersSettlement.json",
"test/generated-artifacts/OptimismBridgeAdapter.json",
"test/generated-artifacts/OtcOrdersFeature.json",
"test/generated-artifacts/OwnableFeature.json",
"test/generated-artifacts/PancakeSwapFeature.json",
"test/generated-artifacts/PayTakerTransformer.json",
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
"test/generated-artifacts/PolygonBridgeAdapter.json",
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
"test/generated-artifacts/TestBridge.json",

View File

@@ -4,28 +4,23 @@ Audits
Below are links to our third-party audit reports.
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| **Release** | **Reports** |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| ERC721OrdersFeature | * `ABDK Consulting <https://s3.us-east-2.amazonaws.com/zeips.0x.org/audits/abdk-consulting/ABDK_0x_Solidity_v_1_0.pdf>`__ |
| | |
| | |
| ERC1155OrdersFeature | |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| Exchange V4 | * `Consensys Diligence (December 2020) <https://consensys.net/diligence/audits/2020/12/0x-exchange-v4/>`__ |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| Exchange V3 | * `Trail of Bits <http://zeips.0x.org.s3-website.us-east-2.amazonaws.com/audits/56/trail-of-bits/audit.pdf>`__ |
| | * `Consensys Diligence (Exchange) <https://diligence.consensys.net/audits/2019/09/0x-v3-exchange/>`__ |
| | * `Consensys Diligence (Staking) <https://diligence.consensys.net/audits/2019/10/0x-v3-staking/>`__ |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| Exchange V2.1 | * `First <https://docs.google.com/document/d/1jYv6V21MfCSwCS5fxD6ZyaLWGzkpRSUO0lZpST94XsA/edit>`_ |
| | * `Consensys Diligence <https://github.com/ConsenSys/0x_audit_report_2018-07-23>`_ |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| MultiAssetProxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2018-12>`__ |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| ERC1155Proxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2019-05>`__ |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| StaticCallProxy | * No third-party audit. |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
| ERC20BridgeProxy | * No third-party audit. |
+----------------------+---------------------------------------------------------------------------------------------------------------------------+
+------------------+---------------------------------------------------------------------------------------------------------------+
| **Release** | **Reports** |
+------------------+---------------------------------------------------------------------------------------------------------------+
| Exchange V4 | * `Consensys Diligence (December 2020) <https://consensys.net/diligence/audits/2020/12/0x-exchange-v4/>`__ |
+------------------+---------------------------------------------------------------------------------------------------------------+
| Exchange V3 | * `Trail of Bits <http://zeips.0x.org.s3-website.us-east-2.amazonaws.com/audits/56/trail-of-bits/audit.pdf>`_ |
| | * `Consensys Diligence (Exchange) <https://diligence.consensys.net/audits/2019/09/0x-v3-exchange/>`__ |
| | * `Consensys Diligence (Staking) <https://diligence.consensys.net/audits/2019/10/0x-v3-staking/>`__ |
+------------------+---------------------------------------------------------------------------------------------------------------+
| Exchange V2.1 | * `First <https://docs.google.com/document/d/1jYv6V21MfCSwCS5fxD6ZyaLWGzkpRSUO0lZpST94XsA/edit>`_ |
| | * `Consensys Diligence <https://github.com/ConsenSys/0x_audit_report_2018-07-23>`_ |
+------------------+---------------------------------------------------------------------------------------------------------------+
| MultiAssetProxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2018-12>`__ |
+------------------+---------------------------------------------------------------------------------------------------------------+
| ERC1155Proxy | * `Consensys Diligence <https://github.com/ConsenSys/0x-audit-report-2019-05>`__ |
+------------------+---------------------------------------------------------------------------------------------------------------+
| StaticCallProxy | * No third-party audit. |
+------------------+---------------------------------------------------------------------------------------------------------------+
| ERC20BridgeProxy | * No third-party audit. |
+------------------+---------------------------------------------------------------------------------------------------------------+

View File

@@ -52,30 +52,29 @@
},
"config": {
"contractsPackages": "@0x/contracts-erc20 @0x/contracts-test-utils @0x/contracts-utils @0x/contracts-zero-ex @0x/contracts-treasury",
"nonContractPackages": "@0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
"nonContractPackages": "@0x/migrations @0x/contract-wrappers @0x/contract-addresses @0x/contract-artifacts @0x/contract-wrappers-test @0x/asset-swapper",
"ignoreTestsForPackages": "",
"mnemonic": "concert load couple harbor equip island argue ramp clarify fence smart topic",
"packagesWithDocPages": "@0x/contract-wrappers",
"packagesWithDocPages": "@0x/contract-wrappers @0x/migrations",
"ignoreDependencyVersions": "@types/styled-components @types/node",
"ignoreDependencyVersionsForPackage": "contract-wrappers"
},
"devDependencies": {
"@0x/monorepo-scripts": "^3.2.1",
"@0x-lerna-fork/lerna": "3.16.10",
"@0x/monorepo-scripts": "^3.2.4",
"@0xproject/npm-cli-login": "^0.0.11",
"async-child-process": "^1.1.1",
"coveralls": "^3.0.0",
"ganache-cli": "6.12.2",
"ganache-cli": "6.8.0-istanbul.0",
"lcov-result-merger": "^3.0.0",
"lerna": "^3.0.0-beta.25",
"npm-run-all": "^4.1.2",
"prettier": "1.19.1",
"source-map-support": "^0.5.6",
"typescript": "4.6.3",
"typescript": "4.2.2",
"wsrun": "^5.2.4"
},
"resolutions": {
"merkle-patricia-tree": "3.0.0",
"**/bignumber.js": "^9.0.2"
"merkle-patricia-tree": "^2.3.2"
}
}

View File

@@ -1,317 +1,4 @@
[
{
"version": "16.63.1",
"changes": [
{
"note": "Add debridge curve pools",
"pr": 512
}
]
"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",
"changes": [
{
"note": "Add estimatedGas to ExtendedQuoteReport",
"pr": 463
}
],
"timestamp": 1650575781
},
{
"version": "16.55.0",
"changes": [
{
"note": "Fix fillRfqOrder VIP being used for swaps that need transformERC20",
"pr": 461
}
],
"timestamp": 1649347667
},
{
"version": "16.54.0",
"changes": [
{
"note": "Add true VIP support for eligible RFQt swaps",
"pr": 458
}
],
"timestamp": 1649215576
},
{
"version": "16.53.0",
"changes": [
{
"note": "Adds support for STG/USDC pool on Curve Mainnet",
"pr": 451
},
{
"note": "Use neon-router in asset-swapper tests",
"pr": 453
},
{
"note": "Add sampler blocknumber to quote report data",
"pr": 448
}
],
"timestamp": 1648739346
},
{
"version": "16.52.0",
"changes": [
{
"note": "Adds support for mobius money on celo",
"pr": 423
}
]
},
{
"version": "16.51.0",
"changes": [
{
"note": "Added `Curve` `YFI-ETH` pool",
"pr": 444
}
],
"timestamp": 1646888282
},
{
"version": "16.50.3",
"changes": [
{
"note": "Routing glue optimization",
"pr": 439
},
{
"note": "Move VIP source routing into neon-router & disable fallback orders for native/plp",
"pr": 440
}
],
"timestamp": 1646837959
},
{
"version": "16.50.2",
"changes": [
{
"note": "Update `Uniswap_V3` address on `Ropsten`",
"pr": 441
}
],
"timestamp": 1646617024
},
{
"version": "16.50.1",
"changes": [
{
"note": "Add BTRFLY/WETH Curve pool on mainnet",
"pr": 437
},
{
"note": "Lower Uniswap V3 Sampler gas allowance",
"pr": 438
}
],
"timestamp": 1646312638
},
{
"version": "16.50.0",
"changes": [
{
"note": "Adding support for Geist on `Fantom`",
"pr": 398
},
{
"note": "Improve Uniswap V3 gas schedule",
"pr": 424
}
],
"timestamp": 1646225739
},
{
"version": "16.49.9",
"changes": [
@@ -348,6 +35,7 @@
"note": "Fixed btrfly routing to include the ohmV2/dai, ohmV2/btfly, and ohmV2/weth pools",
"pr": 427
}
],
"timestamp": 1645113751
},

View File

@@ -5,118 +5,6 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## 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_
* Add estimatedGas to ExtendedQuoteReport (#463)
## v16.55.0 - _April 7, 2022_
* Fix fillRfqOrder VIP being used for swaps that need transformERC20 (#461)
## v16.54.0 - _April 6, 2022_
* Add true VIP support for eligible RFQt swaps (#458)
## v16.53.0 - _March 31, 2022_
* Adds support for STG/USDC pool on Curve Mainnet (#451)
* Use neon-router in asset-swapper tests (#453)
* Add sampler blocknumber to quote report data (#448)
## v16.52.0 - _Invalid date_
* Adds support for mobius money on celo (#423)
## v16.51.0 - _March 10, 2022_
* Added `Curve` `YFI-ETH` pool (#444)
## v16.50.3 - _March 9, 2022_
* Routing glue optimization (#439)
* Move VIP source routing into neon-router & disable fallback orders for native/plp (#440)
## v16.50.2 - _March 7, 2022_
* Update `Uniswap_V3` address on `Ropsten` (#441)
## v16.50.1 - _March 3, 2022_
* Add BTRFLY/WETH Curve pool on mainnet (#437)
* Lower Uniswap V3 Sampler gas allowance (#438)
## v16.50.0 - _March 2, 2022_
* Adding support for Geist on `Fantom` (#398)
* Improve Uniswap V3 gas schedule (#424)
## v16.49.9 - _February 24, 2022_
* Fix native order scaling & filter out 1 wei quotes (#430)

View File

@@ -6,7 +6,7 @@
"shouldSaveStandardInput": true,
"compilerSettings": {
"evmVersion": "istanbul",
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": false, "deduplicate": true } },
"optimizer": { "enabled": true, "runs": 200, "details": { "yul": true, "deduplicate": true } },
"outputSelection": {
"*": {
"*": [

View File

@@ -1,105 +0,0 @@
// 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;
}
}
}
}

View File

@@ -21,11 +21,44 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./SamplerUtils.sol";
import "./interfaces/IBalancerV2Vault.sol";
import "./BalancerV2Common.sol";
/// @dev Minimal Balancer V2 Vault interface
/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
interface IBalancerV2Vault {
enum SwapKind { GIVEN_IN, GIVEN_OUT }
contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] calldata swaps,
IAsset[] calldata assets,
FundManagement calldata funds
) external returns (int256[] memory assetDeltas);
}
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
contract BalancerV2Sampler is SamplerUtils {
struct BalancerV2PoolInfo {
bytes32 poolId;
address vault;
}
/// @dev Sample sell quotes from Balancer V2.
/// @param poolInfo Struct with pool related data
@@ -35,7 +68,7 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBalancerV2(
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
@@ -45,9 +78,9 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
{
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
address[] memory swapAssets = new address[](2);
swapAssets[0] = takerToken;
swapAssets[1] = makerToken;
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
@@ -64,7 +97,7 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
returns (int256[] memory amounts) {
// Outgoing balance is negative so we need to flip the sign
int256 amountOutFromPool = amounts[amounts.length - 1] * -1;
int256 amountOutFromPool = amounts[1] * -1;
if (amountOutFromPool <= 0) {
break;
}
@@ -84,7 +117,7 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBalancerV2(
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
@@ -94,9 +127,9 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
{
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
address[] memory swapAssets = new address[](2);
swapAssets[0] = takerToken;
swapAssets[1] = makerToken;
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
@@ -124,7 +157,7 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
}
function _createSwapSteps(
IBalancerV2Vault.BalancerV2PoolInfo memory poolInfo,
BalancerV2PoolInfo memory poolInfo,
uint256 amount
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
@@ -139,4 +172,18 @@ contract BalancerV2Sampler is SamplerUtils, BalancerV2Common {
return swapSteps;
}
function _createSwapFunds()
private
view
returns (IBalancerV2Vault.FundManagement memory)
{
return
IBalancerV2Vault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
}
}

View File

@@ -22,8 +22,9 @@ pragma experimental ABIEncoderV2;
import "./interfaces/IBancor.sol";
contract CompilerHack {}
contract BancorSampler {
contract BancorSampler is CompilerHack {
/// @dev Base gas limit for Bancor calls.
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k

View File

@@ -1,120 +0,0 @@
// 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;
}
}
}
}

View File

@@ -22,56 +22,52 @@ pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol";
import "./BalancerV2BatchSampler.sol";
import "./BancorSampler.sol";
import "./BancorV3Sampler.sol";
import "./CompoundSampler.sol";
import "./CurveSampler.sol";
import "./DODOSampler.sol";
import "./DODOV2Sampler.sol";
import "./GMXSampler.sol";
import "./KyberSampler.sol";
import "./KyberDmmSampler.sol";
import "./LidoSampler.sol";
import "./LiquidityProviderSampler.sol";
import "./MakerPSMSampler.sol";
import "./MultiBridgeSampler.sol";
import "./MStableSampler.sol";
import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./PlatypusSampler.sol";
import "./ShellSampler.sol";
import "./SmoothySampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
import "./UniswapV3Sampler.sol";
import "./VelodromeSampler.sol";
import "./UtilitySampler.sol";
contract ERC20BridgeSampler is
BalancerSampler,
BalancerV2Sampler,
BalancerV2BatchSampler,
BancorSampler,
BancorV3Sampler,
CompoundSampler,
CurveSampler,
DODOSampler,
DODOV2Sampler,
GMXSampler,
KyberSampler,
KyberDmmSampler,
LidoSampler,
LiquidityProviderSampler,
MakerPSMSampler,
MStableSampler,
MooniswapSampler,
MultiBridgeSampler,
NativeOrderSampler,
PlatypusSampler,
ShellSampler,
SmoothySampler,
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,
UniswapV3Sampler,
VelodromeSampler,
UtilitySampler
{
@@ -96,6 +92,4 @@ contract ERC20BridgeSampler is
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
}
}
receive() external payable {}
}

View File

@@ -1,96 +0,0 @@
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];
}
}

View File

@@ -0,0 +1,301 @@
// 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;
}
}

View File

@@ -22,18 +22,10 @@ pragma experimental ABIEncoderV2;
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 {
struct LidoInfo {
address stEthToken;
address wethToken;
address wstEthToken;
}
/// @dev Sample sell quotes from Lido
@@ -50,17 +42,20 @@ contract LidoSampler is SamplerUtils {
uint256[] memory takerTokenAmounts
)
public
view
pure
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.
return takerTokenAmounts;
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
// Return 0 values if not selling WETH for stETH
uint256 numSamples = takerTokenAmounts.length;
uint256[] memory makerTokenAmounts = new uint256[](numSamples);
return makerTokenAmounts;
}
return _sampleSellsForWrapped(lidoInfo, takerToken, makerToken, takerTokenAmounts);
// Minting stETH is always 1:1 therefore we can just return the same amounts back
return takerTokenAmounts;
}
/// @dev Sample buy quotes from Lido.
@@ -77,43 +72,20 @@ contract LidoSampler is SamplerUtils {
uint256[] memory makerTokenAmounts
)
public
view
pure
returns (uint256[] memory)
{
if (takerToken == lidoInfo.wethToken && makerToken == address(lidoInfo.stEthToken)) {
// Minting stETH is always 1:1 therefore we can just return the same amounts back.
return makerTokenAmounts;
_assertValidPair(makerToken, takerToken);
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
// Return 0 values if not buying stETH for WETH
uint256 numSamples = makerTokenAmounts.length;
uint256[] memory takerTokenAmounts = new uint256[](numSamples);
return takerTokenAmounts;
}
// Swap out `makerToken` and `takerToken` and re-use `_sampleSellsForWrapped`.
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.
// Minting stETH is always 1:1 therefore we can just return the same amounts back
return makerTokenAmounts;
}
}

View File

@@ -0,0 +1,82 @@
// 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;
}
}
}

View File

@@ -1,89 +0,0 @@
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];
}
}

View File

@@ -0,0 +1,156 @@
// 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];
}
}

View File

@@ -22,43 +22,17 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface IUniswapV3QuoterV2 {
interface IUniswapV3Quoter {
function factory()
external
view
returns (IUniswapV3Factory factory);
// @notice Returns the amount out received for a given exact input swap without executing the swap
// @param path The path of the swap, i.e. each token pair and the pool fee
// @param amountIn The amount of the first token to swap
// @return amountOut The amount of the last token that would be received
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
// @return gasEstimate The estimate of the gas that the swap consumes
function quoteExactInput(bytes memory path, uint256 amountIn)
external
returns (
uint256 amountOut,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksCrossedList,
uint256 gasEstimate
);
// @notice Returns the amount in required for a given exact output swap without executing the swap
// @param path The path of the swap, i.e. each token pair and the pool fee. Path must be provided in reverse order
// @param amountOut The amount of the last token to receive
// @return amountIn The amount of first token required to be paid
// @return sqrtPriceX96AfterList List of the sqrt price after the swap for each pool in the path
// @return initializedTicksCrossedList List of the initialized ticks that the swap crossed for each pool in the path
// @return gasEstimate The estimate of the gas that the swap consumes
returns (uint256 amountOut);
function quoteExactOutput(bytes memory path, uint256 amountOut)
external
returns (
uint256 amountIn,
uint160[] memory sqrtPriceX96AfterList,
uint32[] memory initializedTicksCrossedList,
uint256 gasEstimate
);
returns (uint256 amountIn);
}
interface IUniswapV3Factory {
@@ -77,25 +51,23 @@ interface IUniswapV3Pool {
contract UniswapV3Sampler
{
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
uint256 constant private QUOTE_GAS = 700e3;
uint256 constant private QUOTE_GAS = 600e3;
/// @dev Sample sell quotes from UniswapV3.
/// @param quoter UniswapV3 Quoter contract.
/// @param path Token route. Should be takerToken -> makerToken
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return uniswapPaths The encoded uniswap path for each sample.
/// @return uniswapGasUsed Estimated amount of gas used
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromUniswapV3(
IUniswapV3QuoterV2 quoter,
IUniswapV3Quoter quoter,
IERC20TokenV06[] memory path,
uint256[] memory takerTokenAmounts
)
public
returns (
bytes[] memory uniswapPaths,
uint256[] memory uniswapGasUsed,
uint256[] memory makerTokenAmounts
)
{
@@ -104,39 +76,31 @@ contract UniswapV3Sampler
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
uniswapPaths = new bytes[](takerTokenAmounts.length);
uniswapGasUsed = new uint256[](takerTokenAmounts.length);
for (uint256 i = 0; i < takerTokenAmounts.length; ++i) {
// Pick the best result from all the paths.
bytes memory topUniswapPath;
uint256 topBuyAmount = 0;
for (uint256 j = 0; j < poolPaths.length; ++j) {
bytes memory uniswapPath = _toUniswapPath(path, poolPaths[j]);
try quoter.quoteExactInput
{ gas: QUOTE_GAS }
(uniswapPath, takerTokenAmounts[i])
returns (
uint256 buyAmount,
uint160[] memory, /* sqrtPriceX96AfterList */
uint32[] memory, /* initializedTicksCrossedList */
uint256 gasUsed
)
try
quoter.quoteExactInput
{ gas: QUOTE_GAS }
(uniswapPath, takerTokenAmounts[i])
returns (uint256 buyAmount)
{
if (topBuyAmount <= buyAmount) {
topBuyAmount = buyAmount;
uniswapPaths[i] = uniswapPath;
uniswapGasUsed[i] = gasUsed;
topUniswapPath = uniswapPath;
}
} catch {}
} catch { }
}
// Break early if we can't complete the sells.
// Break early if we can't complete the buys.
if (topBuyAmount == 0) {
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
// then reset if no valid valid quote was found
uniswapPaths[i] = "";
uniswapGasUsed[i] = 0;
break;
}
makerTokenAmounts[i] = topBuyAmount;
uniswapPaths[i] = topUniswapPath;
}
}
@@ -145,18 +109,16 @@ contract UniswapV3Sampler
/// @param path Token route. Should be takerToken -> makerToken.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return uniswapPaths The encoded uniswap path for each sample.
/// @return uniswapGasUsed Estimated amount of gas used
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswapV3(
IUniswapV3QuoterV2 quoter,
IUniswapV3Quoter quoter,
IERC20TokenV06[] memory path,
uint256[] memory makerTokenAmounts
)
public
returns (
bytes[] memory uniswapPaths,
uint256[] memory uniswapGasUsed,
uint256[] memory takerTokenAmounts
)
{
@@ -166,10 +128,10 @@ contract UniswapV3Sampler
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
uniswapPaths = new bytes[](makerTokenAmounts.length);
uniswapGasUsed = new uint256[](makerTokenAmounts.length);
for (uint256 i = 0; i < makerTokenAmounts.length; ++i) {
// Pick the best result from all the paths.
bytes memory topUniswapPath;
uint256 topSellAmount = 0;
for (uint256 j = 0; j < poolPaths.length; ++j) {
// quoter requires path to be reversed for buys.
@@ -181,30 +143,21 @@ contract UniswapV3Sampler
quoter.quoteExactOutput
{ gas: QUOTE_GAS }
(uniswapPath, makerTokenAmounts[i])
returns (
uint256 sellAmount,
uint160[] memory, /* sqrtPriceX96AfterList */
uint32[] memory, /* initializedTicksCrossedList */
uint256 gasUsed
)
returns (uint256 sellAmount)
{
if (topSellAmount == 0 || topSellAmount >= sellAmount) {
topSellAmount = sellAmount;
// But the output path should still be encoded for sells.
uniswapPaths[i] = _toUniswapPath(path, poolPaths[j]);
uniswapGasUsed[i] = gasUsed;
topUniswapPath = _toUniswapPath(path, poolPaths[j]);
}
} catch {}
}
// Break early if we can't complete the buys.
if (topSellAmount == 0) {
// HACK(kimpers): To avoid too many local variables, paths and gas used is set directly in the loop
// then reset if no valid valid quote was found
uniswapPaths[i] = "";
uniswapGasUsed[i] = 0;
break;
}
takerTokenAmounts[i] = topSellAmount;
uniswapPaths[i] = topUniswapPath;
}
}
@@ -283,7 +236,6 @@ contract UniswapV3Sampler
function _reverseTokenPath(IERC20TokenV06[] memory tokenPath)
private
pure
returns (IERC20TokenV06[] memory reversed)
{
reversed = new IERC20TokenV06[](tokenPath.length);
@@ -294,7 +246,6 @@ contract UniswapV3Sampler
function _reversePoolPath(IUniswapV3Pool[] memory poolPath)
private
pure
returns (IUniswapV3Pool[] memory reversed)
{
reversed = new IUniswapV3Pool[](poolPath.length);

View File

@@ -1,134 +0,0 @@
// 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);
}
}

View File

@@ -1,54 +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;
/// @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);
}

View File

@@ -1,43 +0,0 @@
// 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);
}

View File

@@ -1,23 +0,0 @@
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);
}

View File

@@ -0,0 +1,96 @@
// 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);
}

View File

@@ -1,11 +0,0 @@
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);
}

View File

@@ -18,24 +18,28 @@
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./interfaces/IBalancerV2Vault.sol";
contract BalancerV2Common {
interface ISmoothy {
function _createSwapFunds()
internal
function getBalance (
uint256 tid
)
external
view
returns (IBalancerV2Vault.FundManagement memory)
{
return
IBalancerV2Vault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
}
returns (uint256 balance);
function _yBalances (
uint256 tid
)
external
view
returns (uint256 balance);
function getTokenStats (
uint256 tid
)
external
view
returns (uint256 softWeight, uint256 hardWeight, uint256 balance, uint256 decimals);
}

View File

@@ -0,0 +1,39 @@
// 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;
}
}

View File

@@ -0,0 +1,455 @@
// 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);
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.63.0",
"version": "16.49.9",
"engines": {
"node": ">=6.12"
},
@@ -34,13 +34,12 @@
"contracts:gen": "contracts-gen generate",
"contracts:copy": "contracts-gen copy",
"publish:private": "yarn build && gitpkg publish",
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))",
"list:deps": "yarn lerna list -l"
"sampler-size": "jq .compilerOutput.evm.deployedBytecode.object -- test/generated-artifacts/ERC20BridgeSampler.json | echo $(( $(wc -c) / 2 - 1 ))"
},
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|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|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler|VelodromeSampler).json",
"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",
"postpublish": {
"assets": []
}
@@ -59,22 +58,22 @@
"registry": "git@github.com:0xProject/gitpkg-registry.git"
},
"dependencies": {
"@0x/assert": "^3.0.34",
"@0x/base-contract": "^6.5.0",
"@0x/contract-addresses": "^6.16.0",
"@0x/contract-wrappers": "^13.20.4",
"@0x/contracts-erc20": "^3.3.32",
"@0x/contracts-zero-ex": "^0.35.0",
"@0x/dev-utils": "^4.2.14",
"@0x/json-schemas": "^6.4.4",
"@0x/neon-router": "^0.3.5",
"@0x/protocol-utils": "^11.15.0",
"@0x/assert": "^3.0.31",
"@0x/base-contract": "^6.4.5",
"@0x/contract-addresses": "^6.11.0",
"@0x/contract-wrappers": "^13.19.0",
"@0x/contracts-erc20": "^3.3.26",
"@0x/contracts-zero-ex": "^0.31.0",
"@0x/dev-utils": "^4.2.11",
"@0x/json-schemas": "^6.4.1",
"@0x/neon-router": "^0.3.3",
"@0x/protocol-utils": "^1.11.0",
"@0x/quote-server": "^6.0.6",
"@0x/types": "^3.3.6",
"@0x/typescript-typings": "^5.3.1",
"@0x/utils": "^6.5.3",
"@0x/web3-wrapper": "^7.6.5",
"@balancer-labs/sdk": "0.1.6",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
"@0x/utils": "^6.5.0",
"@0x/web3-wrapper": "^7.6.2",
"@balancer-labs/sor": "0.3.2",
"@bancor/sdk": "0.2.9",
"@ethersproject/abi": "^5.0.1",
"@ethersproject/address": "^5.0.1",
@@ -83,10 +82,9 @@
"@ethersproject/strings": "^5.0.10",
"axios": "^0.21.1",
"axios-mock-adapter": "^1.19.0",
"balancer-labs-sor-v1": "npm:@balancer-labs/sor@0.3.2",
"cream-sor": "^0.3.3",
"decimal.js": "^10.2.0",
"ethereum-types": "^3.7.0",
"ethereum-types": "^3.6.0",
"ethereumjs-util": "^7.0.10",
"fast-abi": "^0.0.4",
"graphql": "^15.4.0",
@@ -95,19 +93,20 @@
"lodash": "^4.17.11"
},
"devDependencies": {
"@0x/abi-gen": "^5.8.0",
"@0x/abi-gen": "^5.7.2",
"@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-test-utils": "^5.4.23",
"@0x/contracts-utils": "^4.8.13",
"@0x/contracts-gen": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.17",
"@0x/contracts-utils": "^4.8.7",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/sol-compiler": "^4.8.1",
"@0x/subproviders": "^6.6.5",
"@0x/migrations": "^8.1.15",
"@0x/sol-compiler": "^4.7.8",
"@0x/subproviders": "^6.6.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.6",
"@0x/types": "^3.3.4",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -124,7 +123,7 @@
"tslint": "5.11.0",
"typedoc": "~0.16.11",
"typemoq": "^2.1.0",
"typescript": "4.6.3"
"typescript": "4.2.2"
},
"publishConfig": {
"access": "public"

View File

@@ -4,7 +4,7 @@ export {
ContractTxFunctionObj,
SendTransactionOpts,
} from '@0x/base-contract';
export { ContractAddresses, ChainId, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
export { ContractAddresses } from '@0x/contract-addresses';
export {
V4RFQFirmQuote,
V4RFQIndicativeQuote,
@@ -116,15 +116,6 @@ export {
SamplerMetrics,
} from './types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export {
IRfqClient,
RfqClientV1Price,
RfqClientV1PriceRequest,
RfqClientV1PriceResponse,
RfqClientV1Quote,
RfqClientV1QuoteRequest,
RfqClientV1QuoteResponse,
} from './utils/irfq_client';
export {
DEFAULT_TOKEN_ADJACENCY_GRAPH_BY_CHAIN_ID,
DEFAULT_GAS_SCHEDULE,
@@ -132,7 +123,6 @@ export {
BUY_SOURCE_FILTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
ZERO_AMOUNT,
} from './utils/market_operation_utils/constants';
export {
Parameters,
@@ -142,6 +132,7 @@ export {
export {
BalancerFillData,
BancorFillData,
CollapsedFill,
CurveFillData,
CurveFunctionSelectors,
CurveInfo,
@@ -150,17 +141,17 @@ export {
ERC20BridgeSource,
ExchangeProxyOverhead,
FeeSchedule,
GasSchedule,
Fill,
FillAdjustor,
FillData,
GetMarketOrdersRfqOpts,
KyberFillData,
LiquidityProviderFillData,
LiquidityProviderRegistry,
MarketDepth,
MarketDepthSide,
MooniswapFillData,
MultiHopFillData,
NativeCollapsedFill,
NativeRfqOrderFillData,
NativeLimitOrderFillData,
NativeFillData,
@@ -169,7 +160,6 @@ export {
TokenAdjacencyGraph,
UniswapV2FillData,
} from './utils/market_operation_utils/types';
export { IdentityFillAdjustor } from './utils/market_operation_utils/identity_fill_adjustor';
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
export {
BridgeQuoteReportEntry,
@@ -193,5 +183,3 @@ export type Native = ERC20BridgeSource.Native;
export type MultiHop = ERC20BridgeSource.MultiHop;
export { rfqtMocker, RfqtQuoteEndpoint } from './utils/rfqt_mocker';
export { adjustOutput } from './utils/market_operation_utils/fills';

View File

@@ -1,57 +0,0 @@
import { BigNumber } from '@0x/utils';
import { ZERO_AMOUNT } from '../constants';
export interface GeistInfo {
lendingPool: string;
gToken: string;
underlyingToken: string;
}
// tslint:disable-next-line:no-unnecessary-class
export class GeistSampler {
public static sampleSellsFromGeist(
geistInfo: GeistInfo,
takerToken: string,
makerToken: string,
takerTokenAmounts: BigNumber[],
): BigNumber[] {
// Deposit/Withdrawal underlying <-> gToken is always 1:1
if (
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
) {
return takerTokenAmounts;
}
// Not matching the reserve return 0 results
const numSamples = takerTokenAmounts.length;
const makerTokenAmounts = new Array(numSamples);
makerTokenAmounts.fill(ZERO_AMOUNT);
return makerTokenAmounts;
}
public static sampleBuysFromGeist(
geistInfo: GeistInfo,
takerToken: string,
makerToken: string,
makerTokenAmounts: BigNumber[],
): BigNumber[] {
// Deposit/Withdrawal underlying <-> gToken is always 1:1
if (
(takerToken.toLowerCase() === geistInfo.gToken.toLowerCase() &&
makerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase()) ||
(takerToken.toLowerCase() === geistInfo.underlyingToken.toLowerCase() &&
makerToken.toLowerCase() === geistInfo.gToken.toLowerCase())
) {
return makerTokenAmounts;
}
// Not matching the reserve return 0 results
const numSamples = makerTokenAmounts.length;
const takerTokenAmounts = new Array(numSamples);
takerTokenAmounts.fill(ZERO_AMOUNT);
return takerTokenAmounts;
}
}

View File

@@ -42,7 +42,6 @@ import {
FinalUniswapV3FillData,
LiquidityProviderFillData,
MooniswapFillData,
NativeRfqOrderFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
UniswapV2FillData,
@@ -61,7 +60,6 @@ import {
isDirectSwapCompatible,
isMultiplexBatchFillCompatible,
isMultiplexMultiHopFillCompatible,
requiresTransformERC20,
} from './quote_consumer_utils';
// tslint:disable-next-line:custom-no-magic-numbers
@@ -75,7 +73,9 @@ const PANCAKE_SWAP_FORKS = [
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.ApeSwap,
ERC20BridgeSource.CafeSwap,
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
];
const FAKE_PROVIDER: any = {
sendAsync(): void {
@@ -220,7 +220,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.ApeSwap,
ERC20BridgeSource.CafeSwap,
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
])
) {
const source = slippedOrders[0].source;
@@ -276,13 +278,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve]) &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve]) &&
// Curve VIP cannot currently support WETH buy/sell as the functionality needs to WITHDRAW or DEPOSIT
// into WETH prior/post the trade.
// ETH buy/sell is supported
![sellToken, buyToken].includes(NATIVE_FEE_TOKEN_BY_CHAIN_ID[ChainId.Mainnet])
) {
const fillData = slippedOrders[0].fillData as CurveFillData;
const fillData = slippedOrders[0].fills[0].fillData as CurveFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -311,7 +313,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Mooniswap])
) {
const fillData = slippedOrders[0].fillData as MooniswapFillData;
const fillData = slippedOrders[0].fills[0].fillData as MooniswapFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
@@ -331,49 +333,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
};
}
// RFQT VIP
if (
[ChainId.Mainnet, ChainId.Polygon].includes(this.chainId) &&
!isToETH &&
!isFromETH &&
quote.orders.every(o => o.type === FillQuoteTransformerOrderType.Rfq) &&
!requiresTransformERC20(optsWithDefaults)
) {
const rfqOrdersData = quote.orders.map(o => o.fillData as NativeRfqOrderFillData);
const fillAmountPerOrder = (() => {
// Don't think order taker amounts are clipped to actual sell amount
// (the last one might be too large) so figure them out manually.
let remaining = sellAmount;
const fillAmounts = [];
for (const o of quote.orders) {
const fillAmount = BigNumber.min(o.takerAmount, remaining);
fillAmounts.push(fillAmount);
remaining = remaining.minus(fillAmount);
}
return fillAmounts;
})();
const callData =
quote.orders.length === 1
? this._exchangeProxy
.fillRfqOrder(rfqOrdersData[0].order, rfqOrdersData[0].signature, fillAmountPerOrder[0])
.getABIEncodedTransactionData()
: this._exchangeProxy
.batchFillRfqOrders(
rfqOrdersData.map(d => d.order),
rfqOrdersData.map(d => d.signature),
fillAmountPerOrder,
true,
)
.getABIEncodedTransactionData();
return {
calldataHexString: callData,
ethAmount: ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (this.chainId === ChainId.Mainnet && isMultiplexBatchFillCompatible(quote, optsWithDefaults)) {
return {
calldataHexString: this._encodeMultiplexBatchFillCalldata(

View File

@@ -25,7 +25,6 @@ import {
SwapQuoterRfqOpts,
} from './types';
import { assert } from './utils/assert';
import { IRfqClient } from './utils/irfq_client';
import { MarketOperationUtils } from './utils/market_operation_utils';
import { BancorService } from './utils/market_operation_utils/bancor_service';
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
@@ -33,8 +32,8 @@ import { DexOrderSampler } from './utils/market_operation_utils/sampler';
import { SourceFilters } from './utils/market_operation_utils/source_filters';
import {
ERC20BridgeSource,
FeeSchedule,
FillData,
GasSchedule,
GetMarketOrdersOpts,
MarketDepth,
MarketDepthSide,
@@ -328,7 +327,6 @@ export class SwapQuoter {
assetFillAmount: BigNumber,
marketOperation: MarketOperation,
options: Partial<SwapQuoteRequestOpts>,
rfqClient?: IRfqClient | undefined,
): Promise<SwapQuote> {
assert.isETHAddressHex('makerToken', makerToken);
assert.isETHAddressHex('takerToken', takerToken);
@@ -366,11 +364,9 @@ export class SwapQuoter {
const calcOpts: GetMarketOrdersOpts = {
...cloneOpts,
gasPrice,
feeSchedule: _.mapValues(opts.gasSchedule, gasCost => (fillData: FillData) => {
const gas = gasCost ? gasCost(fillData) : 0;
const fee = gasPrice.times(gas);
return { gas, fee };
}),
feeSchedule: _.mapValues(opts.feeSchedule, gasCost => (fillData: FillData) =>
gasCost === undefined ? 0 : gasPrice.times(gasCost(fillData)),
),
exchangeProxyOverhead: flags => gasPrice.times(opts.exchangeProxyOverhead(flags)),
};
// pass the QuoteRequestor on if rfqt enabled
@@ -385,7 +381,6 @@ export class SwapQuoter {
this.expiryBufferMs,
rfqtOptions?.metricsProxy,
);
calcOpts.rfqt.rfqClient = rfqClient;
}
const result: OptimizerResultWithReport = await this._marketOperationUtils.getOptimizerResultAsync(
@@ -504,7 +499,7 @@ function createSwapQuote(
operation: MarketOperation,
assetFillAmount: BigNumber,
gasPrice: BigNumber,
gasSchedule: GasSchedule,
gasSchedule: FeeSchedule,
slippage: number,
): SwapQuote {
const {
@@ -524,7 +519,7 @@ function createSwapQuote(
: calculateQuoteInfo(optimizedOrders, operation, assetFillAmount, gasPrice, gasSchedule, slippage);
// Put together the swap quote
const { makerTokenDecimals, takerTokenDecimals, blockNumber } = optimizerResult.marketSideLiquidity;
const { makerTokenDecimals, takerTokenDecimals } = optimizerResult.marketSideLiquidity;
const swapQuote = {
makerToken,
takerToken,
@@ -541,7 +536,6 @@ function createSwapQuote(
extendedQuoteReportSources,
isTwoHop,
priceComparisonsReport,
blockNumber,
};
if (operation === MarketOperation.Buy) {
@@ -564,7 +558,7 @@ function calculateQuoteInfo(
operation: MarketOperation,
assetFillAmount: BigNumber,
gasPrice: BigNumber,
gasSchedule: GasSchedule,
gasSchedule: FeeSchedule,
slippage: number,
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
const bestCaseFillResult = simulateBestCaseFill({
@@ -593,23 +587,25 @@ function calculateQuoteInfo(
function calculateTwoHopQuoteInfo(
optimizedOrders: OptimizedMarketOrder[],
operation: MarketOperation,
gasSchedule: GasSchedule,
gasSchedule: FeeSchedule,
slippage: number,
): { bestCaseQuoteInfo: SwapQuoteInfo; worstCaseQuoteInfo: SwapQuoteInfo; sourceBreakdown: SwapQuoteOrdersBreakdown } {
const [firstHopOrder, secondHopOrder] = optimizedOrders;
const [firstHopFill] = firstHopOrder.fills;
const [secondHopFill] = secondHopOrder.fills;
const gas = new BigNumber(
gasSchedule[ERC20BridgeSource.MultiHop]!({
firstHopSource: _.pick(firstHopOrder, 'source', 'fillData'),
secondHopSource: _.pick(secondHopOrder, 'source', 'fillData'),
firstHopSource: _.pick(firstHopFill, 'source', 'fillData'),
secondHopSource: _.pick(secondHopFill, 'source', 'fillData'),
}),
).toNumber();
const isSell = operation === MarketOperation.Sell;
return {
bestCaseQuoteInfo: {
makerAmount: isSell ? secondHopOrder.fill.output : secondHopOrder.fill.input,
takerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output,
totalTakerAmount: isSell ? firstHopOrder.fill.input : firstHopOrder.fill.output,
makerAmount: isSell ? secondHopFill.output : secondHopFill.input,
takerAmount: isSell ? firstHopFill.input : firstHopFill.output,
totalTakerAmount: isSell ? firstHopFill.input : firstHopFill.output,
feeTakerTokenAmount: constants.ZERO_AMOUNT,
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
gas,
@@ -635,7 +631,7 @@ function calculateTwoHopQuoteInfo(
[ERC20BridgeSource.MultiHop]: {
proportion: new BigNumber(1),
intermediateToken: secondHopOrder.takerToken,
hops: [firstHopOrder.source, secondHopOrder.source],
hops: [firstHopFill.source, secondHopFill.source],
},
},
};

View File

@@ -179,7 +179,6 @@ export interface SwapQuoteBase {
takerTokenDecimals: number;
takerAmountPerEth: BigNumber;
makerAmountPerEth: BigNumber;
blockNumber: number;
}
/**

View File

@@ -17,10 +17,7 @@ import {
const SUCCESS_CODE = 201;
/**
* Returns the AltOffering if it exists for a given pair
*/
export function getAltMarketInfo(
function getAltMarketInfo(
offerings: AltOffering[],
buyTokenAddress: string,
sellTokenAddress: string,

View File

@@ -1,59 +0,0 @@
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>;
}

View File

@@ -1,13 +1,14 @@
import { ChainId } from '@0x/contract-addresses';
import { BigNumber } from '@0x/utils';
import { BigNumber, NULL_BYTES } from '@0x/utils';
import {
ACRYPTOS_BSC_INFOS,
APESWAP_ROUTER_BY_CHAIN_ID,
BAKERYSWAP_ROUTER_BY_CHAIN_ID,
BELT_BSC_INFOS,
BISWAP_ROUTER_BY_CHAIN_ID,
CAFESWAP_ROUTER_BY_CHAIN_ID,
CHEESESWAP_ROUTER_BY_CHAIN_ID,
COMETHSWAP_ROUTER_BY_CHAIN_ID,
COMPONENT_POOLS_BY_CHAIN_ID,
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
CURVE_AVALANCHE_INFOS,
@@ -24,11 +25,12 @@ import {
FIREBIRDONESWAP_BSC_INFOS,
FIREBIRDONESWAP_POLYGON_INFOS,
IRONSWAP_POLYGON_INFOS,
KNIGHTSWAP_ROUTER_BY_CHAIN_ID,
JETSWAP_ROUTER_BY_CHAIN_ID,
JULSWAP_ROUTER_BY_CHAIN_ID,
KYBER_BANNED_RESERVES,
KYBER_BRIDGED_LIQUIDITY_PREFIX,
MAX_DODOV2_POOLS_QUERIED,
MDEX_ROUTER_BY_CHAIN_ID,
MESHSWAP_ROUTER_BY_CHAIN_ID,
MOBIUSMONEY_CELO_INFOS,
MAX_KYBER_RESERVES_QUERIED,
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
MSTABLE_POOLS_BY_CHAIN_ID,
NERVE_BSC_INFOS,
@@ -36,14 +38,18 @@ import {
PANCAKESWAP_ROUTER_BY_CHAIN_ID,
PANCAKESWAPV2_ROUTER_BY_CHAIN_ID,
PANGOLIN_ROUTER_BY_CHAIN_ID,
PLATYPUS_AVALANCHE_INFOS,
POLYDEX_ROUTER_BY_CHAIN_ID,
QUICKSWAP_ROUTER_BY_CHAIN_ID,
SADDLE_MAINNET_INFOS,
SHELL_POOLS_BY_CHAIN_ID,
SHIBASWAP_ROUTER_BY_CHAIN_ID,
SMOOTHY_BSC_INFOS,
SMOOTHY_MAINNET_INFOS,
SNOWSWAP_MAINNET_INFOS,
SPIRITSWAP_ROUTER_BY_CHAIN_ID,
SPOOKYSWAP_ROUTER_BY_CHAIN_ID,
SUSHISWAP_ROUTER_BY_CHAIN_ID,
SWERVE_MAINNET_INFOS,
SYNAPSE_AVALANCHE_INFOS,
SYNAPSE_BSC_INFOS,
SYNAPSE_FANTOM_INFOS,
@@ -55,15 +61,35 @@ import {
UNISWAPV2_ROUTER_BY_CHAIN_ID,
WAULTSWAP_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
YOSHI_ROUTER_BY_CHAIN_ID,
} from './constants';
import { CurveInfo, ERC20BridgeSource, PlatypusInfo } from './types';
import { CurveInfo, ERC20BridgeSource } 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
export function isValidAddress(address: string | String): address is string {
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
export function getDodoV2Offsets(): BigNumber[] {
return Array(MAX_DODOV2_POOLS_QUERIED)
@@ -198,6 +224,32 @@ export function getCurveV2InfosForPair(chainId: ChainId, takerToken: string, mak
}
}
export function getSwerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(SWERVE_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
}
export function getSnowSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(SNOWSWAP_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
}
export function getNerveInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.BSC) {
return [];
@@ -322,6 +374,30 @@ 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[] {
if (chainId !== ChainId.Mainnet) {
return [];
@@ -373,27 +449,6 @@ export function getAcryptosInfosForPair(chainId: ChainId, takerToken: string, ma
),
);
}
export function getMobiusMoneyInfoForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Celo) {
return [];
}
return Object.values(MOBIUSMONEY_CELO_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) && [makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
}
export function 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(
chainId: ChainId,
@@ -425,16 +480,18 @@ export function getCurveLikeInfosForPair(
source:
| ERC20BridgeSource.Curve
| ERC20BridgeSource.CurveV2
| ERC20BridgeSource.Swerve
| ERC20BridgeSource.SnowSwap
| ERC20BridgeSource.Nerve
| ERC20BridgeSource.Synapse
| ERC20BridgeSource.Belt
| ERC20BridgeSource.Ellipsis
| ERC20BridgeSource.Smoothy
| ERC20BridgeSource.Saddle
| ERC20BridgeSource.IronSwap
| ERC20BridgeSource.XSigma
| ERC20BridgeSource.FirebirdOneSwap
| ERC20BridgeSource.ACryptos
| ERC20BridgeSource.MobiusMoney,
| ERC20BridgeSource.ACryptos,
): CurveDetailedInfo[] {
let pools: CurveInfo[] = [];
switch (source) {
@@ -444,6 +501,12 @@ export function getCurveLikeInfosForPair(
case ERC20BridgeSource.CurveV2:
pools = getCurveV2InfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Swerve:
pools = getSwerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.SnowSwap:
pools = getSnowSwapInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Nerve:
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
break;
@@ -456,6 +519,9 @@ export function getCurveLikeInfosForPair(
case ERC20BridgeSource.Ellipsis:
pools = getEllipsisInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Smoothy:
pools = getSmoothyInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Saddle:
pools = getSaddleInfosForPair(chainId, takerToken, makerToken);
break;
@@ -471,9 +537,6 @@ export function getCurveLikeInfosForPair(
case ERC20BridgeSource.ACryptos:
pools = getAcryptosInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.MobiusMoney:
pools = getMobiusMoneyInfoForPair(chainId, takerToken, makerToken);
break;
default:
throw new Error(`Unknown Curve like source ${source}`);
}
@@ -494,22 +557,22 @@ export function uniswapV2LikeRouterAddress(
| ERC20BridgeSource.PancakeSwapV2
| ERC20BridgeSource.BakerySwap
| ERC20BridgeSource.ApeSwap
| ERC20BridgeSource.CafeSwap
| ERC20BridgeSource.CheeseSwap
| ERC20BridgeSource.JulSwap
| ERC20BridgeSource.QuickSwap
| ERC20BridgeSource.ComethSwap
| ERC20BridgeSource.Dfyn
| ERC20BridgeSource.WaultSwap
| ERC20BridgeSource.Polydex
| ERC20BridgeSource.ShibaSwap
| ERC20BridgeSource.JetSwap
| ERC20BridgeSource.TraderJoe
| ERC20BridgeSource.Pangolin
| ERC20BridgeSource.UbeSwap
| ERC20BridgeSource.MorpheusSwap
| ERC20BridgeSource.SpookySwap
| ERC20BridgeSource.SpiritSwap
| ERC20BridgeSource.BiSwap
| ERC20BridgeSource.Yoshi
| ERC20BridgeSource.MDex
| ERC20BridgeSource.KnightSwap
| ERC20BridgeSource.MeshSwap,
| ERC20BridgeSource.SpiritSwap,
): string {
switch (source) {
case ERC20BridgeSource.UniswapV2:
@@ -526,16 +589,26 @@ export function uniswapV2LikeRouterAddress(
return BAKERYSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.ApeSwap:
return APESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.CafeSwap:
return CAFESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.CheeseSwap:
return CHEESESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.JulSwap:
return JULSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.QuickSwap:
return QUICKSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.ComethSwap:
return COMETHSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.Dfyn:
return DFYN_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.WaultSwap:
return WAULTSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.Polydex:
return POLYDEX_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.ShibaSwap:
return SHIBASWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.JetSwap:
return JETSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.Pangolin:
return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.TraderJoe:
@@ -548,16 +621,6 @@ export function uniswapV2LikeRouterAddress(
return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.SpiritSwap:
return SPIRITSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.BiSwap:
return BISWAP_ROUTER_BY_CHAIN_ID[chainId];
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:
throw new Error(`Unknown UniswapV2 like source ${source}`);
}

View File

@@ -48,7 +48,7 @@ export function getComparisonPrices(
} else {
try {
const fillFeeInEth = new BigNumber(
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }).fee,
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }),
);
const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.RfqOrder));
feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);

View File

@@ -3,17 +3,74 @@ import { BigNumber, hexUtils } from '@0x/utils';
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
import { DEFAULT_FEE_ESTIMATE, POSITIVE_INF, SOURCE_FLAGS } from './constants';
import { POSITIVE_INF, SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill } from './types';
// tslint:disable: prefer-for-of no-bitwise completed-docs
/**
* 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.
* Create `Fill` objects from orders and dex quotes.
*/
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({
input,
output,
@@ -28,106 +85,122 @@ export function ethToOutputAmount({
ethAmount: BigNumber | number;
}): BigNumber {
return !outputAmountPerEth.isZero()
? outputAmountPerEth.times(ethAmount).integerValue()
? outputAmountPerEth.times(ethAmount)
: inputAmountPerEth.times(ethAmount).times(output.dividedToIntegerBy(input));
}
export function nativeOrderToFill(
export function nativeOrdersToFills(
side: MarketOperation,
order: NativeOrderWithFillableAmounts,
orders: NativeOrderWithFillableAmounts[],
targetInput: BigNumber = POSITIVE_INF,
outputAmountPerEth: BigNumber,
inputAmountPerEth: BigNumber,
fees: FeeSchedule,
filterNegativeAdjustedRateOrders: boolean = true,
): Fill | undefined {
): Fill[] {
const sourcePathId = hexUtils.random();
// Create a single path from all orders.
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = order;
const makerAmount = fillableMakerAmount;
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
const { fee, gas } =
fees[ERC20BridgeSource.Native] === undefined ? DEFAULT_FEE_ESTIMATE : fees[ERC20BridgeSource.Native]!(order);
const outputPenalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
// targetInput can be less than the order size
// whilst the penalty is constant, it affects the adjusted output
// only up until the target has been exhausted.
// A large order and an order at the exact target should be penalized
// the same.
const clippedInput = BigNumber.min(targetInput, input);
// scale the clipped output inline with the input
const clippedOutput = clippedInput.dividedBy(input).times(output);
const adjustedOutput =
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
const adjustedRate =
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
// Optionally skip orders with rates that are <= 0.
if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) {
return undefined;
let fills: Array<Fill & { adjustedRate: BigNumber }> = [];
for (const o of orders) {
const { fillableTakerAmount, fillableTakerFeeAmount, fillableMakerAmount, type } = o;
const makerAmount = fillableMakerAmount;
const takerAmount = fillableTakerAmount.plus(fillableTakerFeeAmount);
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
const outputPenalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
// targetInput can be less than the order size
// whilst the penalty is constant, it affects the adjusted output
// only up until the target has been exhausted.
// A large order and an order at the exact target should be penalized
// the same.
const clippedInput = BigNumber.min(targetInput, input);
// scale the clipped output inline with the input
const clippedOutput = clippedInput.dividedBy(input).times(output);
const adjustedOutput =
side === MarketOperation.Sell ? clippedOutput.minus(outputPenalty) : clippedOutput.plus(outputPenalty);
const adjustedRate =
side === MarketOperation.Sell ? adjustedOutput.div(clippedInput) : clippedInput.div(adjustedOutput);
// Optionally skip orders with rates that are <= 0.
if (filterNegativeAdjustedRateOrders && adjustedRate.lte(0)) {
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 },
});
}
return {
sourcePathId,
adjustedOutput,
input: clippedInput,
output: clippedOutput,
flags: SOURCE_FLAGS[type === FillQuoteTransformerOrderType.Rfq ? 'RfqOrder' : 'LimitOrder'],
source: ERC20BridgeSource.Native,
type,
fillData: { ...order },
gas,
};
// Sort by descending adjusted rate.
fills = fills.sort((a, b) => b.adjustedRate.comparedTo(a.adjustedRate));
// Re-index fills.
for (let i = 0; i < fills.length; ++i) {
fills[i].parent = i === 0 ? undefined : fills[i - 1];
fills[i].index = i;
}
return fills;
}
export function dexSampleToFill(
export function dexSamplesToFills(
side: MarketOperation,
sample: DexSample,
samples: DexSample[],
outputAmountPerEth: BigNumber,
inputAmountPerEth: BigNumber,
fees: FeeSchedule,
): Fill {
): Fill[] {
const sourcePathId = hexUtils.random();
const { source, fillData } = sample;
const input = sample.input;
const output = sample.output;
const { fee, gas } =
fees[source] === undefined ? DEFAULT_FEE_ESTIMATE : fees[source]!(sample.fillData) || DEFAULT_FEE_ESTIMATE;
const fills: Fill[] = [];
// Drop any non-zero entries. This can occur if the any fills on Kyber were UniswapReserves
// We need not worry about Kyber fills going to UniswapReserve as the input amount
// we fill is the same as we sampled. I.e we received [0,20,30] output from [1,2,3] input
// and we only fill [2,3] on Kyber (as 1 returns 0 output)
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);
const fee = fees[source] === undefined ? 0 : fees[source]!(sample.fillData) || 0;
let penalty = ZERO_AMOUNT;
if (i === 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);
const penalty = ethToOutputAmount({
input,
output,
inputAmountPerEth,
outputAmountPerEth,
ethAmount: fee,
});
return {
sourcePathId,
input,
output,
adjustedOutput: adjustOutput(side, output, penalty),
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);
fills.push({
sourcePathId,
input,
output,
adjustedOutput,
source,
fillData,
type: FillQuoteTransformerOrderType.Bridge,
index: i,
parent: i !== 0 ? fills[fills.length - 1] : undefined,
flags: SOURCE_FLAGS[source],
});
}
return fills;
}

View File

@@ -1,36 +0,0 @@
import { FANTOM_TOKENS, GEIST_FANTOM_POOLS } from './constants';
import { GeistInfo } from './types';
const gTokenToUnderlyingToken = new Map<string, string>([
[FANTOM_TOKENS.gFTM, FANTOM_TOKENS.WFTM],
[FANTOM_TOKENS.gfUSDT, FANTOM_TOKENS.fUSDT],
[FANTOM_TOKENS.gDAI, FANTOM_TOKENS.DAI],
[FANTOM_TOKENS.gUSDC, FANTOM_TOKENS.USDC],
[FANTOM_TOKENS.gETH, FANTOM_TOKENS.WETH],
[FANTOM_TOKENS.gWBTC, FANTOM_TOKENS.WBTC],
[FANTOM_TOKENS.gCRV, FANTOM_TOKENS.WCRV],
[FANTOM_TOKENS.gMIM, FANTOM_TOKENS.MIM],
]);
/**
* Returns GeistInfo for a certain pair if that pair exists on Geist
*/
export function getGeistInfoForPair(takerToken: string, makerToken: string): GeistInfo | undefined {
let gToken;
let underlyingToken;
if (gTokenToUnderlyingToken.get(takerToken) === makerToken) {
gToken = takerToken;
underlyingToken = makerToken;
} else if (gTokenToUnderlyingToken.get(makerToken) === takerToken) {
gToken = makerToken;
underlyingToken = takerToken;
} else {
return undefined;
}
return {
lendingPool: GEIST_FANTOM_POOLS.lendingPool,
gToken,
underlyingToken,
};
}

View File

@@ -1,13 +0,0 @@
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;
}
}

View File

@@ -4,15 +4,12 @@ import * as _ from 'lodash';
import { DEFAULT_INFO_LOGGER, INVALID_SIGNATURE } from '../../constants';
import {
AltRfqMakerAssetOfferings,
AssetSwapperContractAddresses,
MarketOperation,
NativeOrderWithFillableAmounts,
SignedNativeOrder,
} from '../../types';
import { getAltMarketInfo } from '../alt_mm_implementation_utils';
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../quote_requestor';
import { toSignedNativeOrder } from '../rfq_client_mappers';
import { QuoteRequestor } from '../quote_requestor';
import {
getNativeAdjustedFillableAmountsFromMakerAmount,
getNativeAdjustedFillableAmountsFromTakerAmount,
@@ -41,17 +38,19 @@ import {
SOURCE_FLAGS,
ZERO_AMOUNT,
} from './constants';
import { IdentityFillAdjustor } from './identity_fill_adjustor';
import { createFills } from './fills';
import { getBestTwoHopQuote } from './multihop_utils';
import { createOrdersFromTwoHopSample } from './orders';
import { Path, PathPenaltyOpts } from './path';
import { findOptimalPathFromSamples } from './path_optimizer';
import { fillsToSortedPaths, findOptimalPathJSAsync, findOptimalRustPathFromSamples } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler';
import { SourceFilters } from './source_filters';
import {
AggregationError,
CollapsedFill,
DexSample,
ERC20BridgeSource,
Fill,
GenerateOptimizedOrdersOpts,
GetMarketOrdersOpts,
MarketSideLiquidity,
@@ -60,6 +59,8 @@ import {
OrderDomain,
} from './types';
const SHOULD_USE_RUST_ROUTER = process.env.RUST_ROUTER === 'true';
// tslint:disable:boolean-naming
export class MarketOperationUtils {
@@ -163,20 +164,18 @@ export class MarketOperationUtils {
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
// Get ETH -> maker token price.
this._sampler.getBestNativeTokenSellRate(
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
makerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
_opts.feeSchedule,
),
// Get ETH -> taker token price.
this._sampler.getBestNativeTokenSellRate(
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
takerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
_opts.feeSchedule,
),
// Get sell quotes for taker -> maker.
this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
@@ -242,7 +241,6 @@ export class MarketOperationUtils {
dexQuotes,
},
isRfqSupported,
blockNumber: blockNumber.toNumber(),
};
}
@@ -271,25 +269,22 @@ export class MarketOperationUtils {
// Call the sampler contract.
const samplerPromise = this._sampler.executeAsync(
this._sampler.getBlockNumber(),
this._sampler.getTokenDecimals([makerToken, takerToken]),
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableMakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
// Get ETH -> makerToken token price.
this._sampler.getBestNativeTokenSellRate(
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
makerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
_opts.feeSchedule,
),
// Get ETH -> taker token price.
this._sampler.getBestNativeTokenSellRate(
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
takerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
_opts.feeSchedule,
),
// Get buy quotes for taker -> maker.
this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
@@ -307,7 +302,6 @@ export class MarketOperationUtils {
const [
[
blockNumber,
tokenDecimals,
orderFillableMakerAmounts,
ethToMakerAssetRate,
@@ -348,7 +342,6 @@ export class MarketOperationUtils {
dexQuotes,
},
isRfqSupported,
blockNumber: blockNumber.toNumber(),
};
}
@@ -379,17 +372,15 @@ export class MarketOperationUtils {
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
const ops = [
this._sampler.getBlockNumber(),
...batchNativeOrders.map(orders =>
this._sampler.getLimitOrderFillableMakerAmounts(orders, this.contractAddresses.exchangeProxy),
),
...batchNativeOrders.map(orders =>
this._sampler.getBestNativeTokenSellRate(
this._sampler.getMedianSellRate(
feeSourceFilters.sources,
orders[0].order.takerToken,
this._nativeFeeToken,
this._nativeFeeTokenAmount,
_opts.feeSchedule,
),
),
...batchNativeOrders.map((orders, i) =>
@@ -405,15 +396,13 @@ export class MarketOperationUtils {
),
];
const [blockNumberRaw, ...executeResults] = await this._sampler.executeBatchAsync(ops);
const executeResults = await this._sampler.executeBatchAsync(ops);
const batchOrderFillableMakerAmounts = executeResults.splice(0, batchNativeOrders.length) as BigNumber[][];
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
const inputAmountPerEth = ZERO_AMOUNT;
const blockNumber: number = (blockNumberRaw as BigNumber).toNumber();
return Promise.all(
batchNativeOrders.map(async (nativeOrders, i) => {
if (nativeOrders.length === 0) {
@@ -446,7 +435,6 @@ export class MarketOperationUtils {
twoHopQuotes: [],
},
isRfqSupported: false,
blockNumber,
},
{
bridgeSlippage: _opts.bridgeSlippage,
@@ -456,7 +444,6 @@ export class MarketOperationUtils {
allowFallback: _opts.allowFallback,
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
fillAdjustor: _opts.fillAdjustor,
},
);
return optimizerResult;
@@ -506,6 +493,18 @@ export class MarketOperationUtils {
} as NativeOrderWithFillableAmounts),
);
// Convert native orders and dex quotes into `Fill` objects.
const fills = createFills({
side,
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
dexQuotes,
targetInput: inputAmount,
outputAmountPerEth,
inputAmountPerEth,
excludedSources: opts.excludedSources,
feeSchedule: opts.feeSchedule,
});
// Find the optimal path.
const penaltyOpts: PathPenaltyOpts = {
outputAmountPerEth,
@@ -518,38 +517,51 @@ export class MarketOperationUtils {
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
// Find the unoptimized best rate to calculate savings from optimizer
const _unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, penaltyOpts)[0];
const unoptimizedPath = _unoptimizedPath ? _unoptimizedPath.collapse(orderOpts) : undefined;
// Find the optimal path using Rust router if enabled, otherwise fallback to JS Router
let optimalPath: Path | undefined;
optimalPath = findOptimalPathFromSamples(
side,
dexQuotes,
[...nativeOrders, ...augmentedRfqtIndicativeQuotes],
inputAmount,
penaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
opts.fillAdjustor,
opts.samplerMetrics,
);
if (SHOULD_USE_RUST_ROUTER) {
optimalPath = findOptimalRustPathFromSamples(
side,
dexQuotes,
[...nativeOrders, ...augmentedRfqtIndicativeQuotes],
inputAmount,
penaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
opts.samplerMetrics,
);
} else {
optimalPath = await findOptimalPathJSAsync(
side,
fills,
inputAmount,
opts.runLimit,
opts.samplerMetrics,
penaltyOpts,
);
}
const optimalPathAdjustedRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
const { adjustedRate: bestTwoHopAdjustedRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
const { adjustedRate: bestTwoHopRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
marketSideLiquidity,
opts.feeSchedule,
opts.exchangeProxyOverhead,
opts.fillAdjustor,
);
if (bestTwoHopQuote && bestTwoHopAdjustedRate.isGreaterThan(optimalPathAdjustedRate)) {
if (bestTwoHopQuote && bestTwoHopRate.isGreaterThan(optimalPathRate)) {
const twoHopOrders = createOrdersFromTwoHopSample(bestTwoHopQuote, orderOpts);
return {
optimizedOrders: twoHopOrders,
liquidityDelivered: bestTwoHopQuote,
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
marketSideLiquidity,
adjustedRate: bestTwoHopAdjustedRate,
adjustedRate: bestTwoHopRate,
unoptimizedPath,
takerAmountPerEth,
makerAmountPerEth,
};
@@ -560,14 +572,17 @@ export class MarketOperationUtils {
throw new Error(AggregationError.NoOptimalPath);
}
const finalizedPath = optimalPath.finalize(orderOpts);
// Generate a fallback path if required
await this._addOptionalFallbackAsync(side, inputAmount, optimalPath, dexQuotes, fills, opts, penaltyOpts);
const collapsedPath = optimalPath.collapse(orderOpts);
return {
optimizedOrders: finalizedPath.orders,
liquidityDelivered: finalizedPath.fills,
sourceFlags: finalizedPath.sourceFlags,
optimizedOrders: collapsedPath.orders,
liquidityDelivered: collapsedPath.collapsedFills as CollapsedFill[],
sourceFlags: collapsedPath.sourceFlags,
marketSideLiquidity,
adjustedRate: optimalPathAdjustedRate,
adjustedRate: optimalPathRate,
unoptimizedPath,
takerAmountPerEth,
makerAmountPerEth,
};
@@ -593,7 +608,6 @@ export class MarketOperationUtils {
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
samplerMetrics: _opts.samplerMetrics,
fillAdjustor: _opts.fillAdjustor,
};
if (nativeOrders.length === 0) {
@@ -606,15 +620,9 @@ export class MarketOperationUtils {
? this.getMarketSellLiquidityAsync.bind(this)
: this.getMarketBuyLiquidityAsync.bind(this);
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;
try {
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, {
...optimizerOpts,
fillAdjustor: new IdentityFillAdjustor(),
});
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
} catch (e) {
// 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
@@ -638,17 +646,6 @@ export class MarketOperationUtils {
}
// 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;
if (
marketSideLiquidity.isRfqSupported &&
@@ -659,49 +656,17 @@ export class MarketOperationUtils {
// Timing of RFQT lifecycle
const timeStart = new Date().getTime();
const { makerToken, takerToken } = nativeOrders[0].order;
// Filter Alt Rfq Maker Asset Offerings to the current pair
const filteredOfferings: AltRfqMakerAssetOfferings = {};
if (rfqt.altRfqAssetOfferings) {
const endpoints = Object.keys(rfqt.altRfqAssetOfferings);
for (const endpoint of endpoints) {
// Get the current pair if being offered
const offering = getAltMarketInfo(rfqt.altRfqAssetOfferings[endpoint], makerToken, takerToken);
if (offering) {
filteredOfferings[endpoint] = [offering];
}
}
}
if (rfqt.isIndicative) {
// An indicative quote is being requested, and indicative quotes price-aware enabled
// Make the RFQT request and then re-run the sampler if new orders come back.
const indicativeQuotes =
rfqt.rfqClient !== undefined
? ((
await rfqt.rfqClient.getV1PricesAsync({
altRfqAssetOfferings: filteredOfferings,
assetFillAmount: amount,
chainId: this._sampler.chainId,
comparisonPrice: wholeOrderPrice,
integratorId: rfqt.integrator.integratorId,
intentOnFilling: rfqt.intentOnFilling,
makerToken,
marketOperation: side,
takerAddress: rfqt.takerAddress,
takerToken,
txOrigin: rfqt.txOrigin,
})
).prices as V4RFQIndicativeQuoteMM[])
: await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
makerToken,
takerToken,
amount,
side,
wholeOrderPrice,
rfqt,
);
const indicativeQuotes = await rfqt.quoteRequestor.requestRfqtIndicativeQuotesAsync(
makerToken,
takerToken,
amount,
side,
wholeOrderPrice,
rfqt,
);
const deltaTime = new Date().getTime() - timeStart;
DEFAULT_INFO_LOGGER({
rfqQuoteType: 'indicative',
@@ -709,57 +674,20 @@ export class MarketOperationUtils {
});
// Re-run optimizer with the new indicative quote
if (indicativeQuotes.length > 0) {
// Attach the indicative quotes to the market side liquidity
marketSideLiquidity.quotes.rfqtIndicativeQuotes = indicativeQuotes;
// 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,
);
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
}
} else {
// A firm quote is being requested, and firm quotes price-aware enabled.
// Ensure that `intentOnFilling` is enabled and make the request.
const firmQuotes =
rfqt.rfqClient !== undefined
? (
await rfqt.rfqClient.getV1QuotesAsync({
altRfqAssetOfferings: filteredOfferings,
assetFillAmount: amount,
chainId: this._sampler.chainId,
comparisonPrice: wholeOrderPrice,
integratorId: rfqt.integrator.integratorId,
intentOnFilling: rfqt.intentOnFilling,
makerToken,
marketOperation: side,
takerAddress: rfqt.takerAddress,
takerToken,
txOrigin: rfqt.txOrigin,
})
).quotes.map(toSignedNativeOrder)
: await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
makerToken,
takerToken,
amount,
side,
wholeOrderPrice,
rfqt,
);
const firmQuotes = await rfqt.quoteRequestor.requestRfqtFirmQuotesAsync(
makerToken,
takerToken,
amount,
side,
wholeOrderPrice,
rfqt,
);
const deltaTime = new Date().getTime() - timeStart;
DEFAULT_INFO_LOGGER({
rfqQuoteType: 'firm',
@@ -788,8 +716,6 @@ export class MarketOperationUtils {
fillableTakerFeeAmount: ZERO_AMOUNT,
}),
);
// Attach the firm RFQt quotes to the market side liquidity
marketSideLiquidity.quotes.nativeOrders = [
...quotesWithOrderFillableAmounts,
...marketSideLiquidity.quotes.nativeOrders,
@@ -798,27 +724,7 @@ export class MarketOperationUtils {
// 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
// and we let it bubble up if it happens.
// 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,
);
optimizerResult = await this._generateOptimizedOrdersAsync(marketSideLiquidity, optimizerOpts);
}
}
}
@@ -864,13 +770,79 @@ export class MarketOperationUtils {
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
void Promise.all(
Object.values(this._sampler.poolsCaches).map(async cache => {
if (!cache || cache.isFresh(takerToken, makerToken)) {
if (cache.isFresh(takerToken, makerToken)) {
return Promise.resolve([]);
}
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
}),
);
}
// 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

View File

@@ -9,7 +9,6 @@ import {
DexSample,
ExchangeProxyOverhead,
FeeSchedule,
FillAdjustor,
MarketSideLiquidity,
MultiHopFillData,
TokenAdjacencyGraph,
@@ -39,7 +38,6 @@ export function getBestTwoHopQuote(
marketSideLiquidity: Omit<MarketSideLiquidity, 'makerTokenDecimals' | 'takerTokenDecimals'>,
feeSchedule?: FeeSchedule,
exchangeProxyOverhead?: ExchangeProxyOverhead,
fillAdjustor?: FillAdjustor,
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
const { twoHopQuotes } = quotes;
@@ -59,15 +57,7 @@ export function getBestTwoHopQuote(
}
const best = filteredQuotes
.map(quote =>
getTwoHopAdjustedRate(
side,
quote,
inputAmount,
outputAmountPerEth,
feeSchedule,
exchangeProxyOverhead,
fillAdjustor,
),
getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
)
.reduce(
(prev, curr, i) =>
@@ -80,7 +70,6 @@ export function getBestTwoHopQuote(
outputAmountPerEth,
feeSchedule,
exchangeProxyOverhead,
fillAdjustor,
),
quote: filteredQuotes[0],
},

View File

@@ -1,6 +1,5 @@
import { BridgeProtocol, encodeBridgeSourceId, FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { AbiEncoder, BigNumber } from '@0x/utils';
import _ = require('lodash');
import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
@@ -9,39 +8,34 @@ import {
AaveV2FillData,
AggregationError,
BalancerFillData,
BalancerV2BatchSwapFillData,
BalancerV2FillData,
BancorFillData,
CollapsedFill,
CompoundFillData,
CurveFillData,
DexSample,
DODOFillData,
ERC20BridgeSource,
Fill,
FillData,
FinalUniswapV3FillData,
GeistFillData,
GenericRouterFillData,
GMXFillData,
KyberDmmFillData,
KyberFillData,
LidoFillData,
LiquidityProviderFillData,
MakerPsmFillData,
MooniswapFillData,
MultiHopFillData,
NativeFillData,
NativeCollapsedFill,
NativeLimitOrderFillData,
NativeRfqOrderFillData,
OptimizedMarketBridgeOrder,
OptimizedMarketOrder,
OptimizedMarketOrderBase,
OrderDomain,
PlatypusFillData,
ShellFillData,
UniswapV2FillData,
UniswapV3FillData,
UniswapV3PathAmount,
VelodromeFillData,
} from './types';
// tslint:disable completed-docs
@@ -61,27 +55,23 @@ export function createOrdersFromTwoHopSample(
): OptimizedMarketOrder[] {
const [makerToken, takerToken] = getMakerTakerTokens(opts);
const { firstHopSource, secondHopSource, intermediateToken } = sample.fillData;
const firstHopFill: Fill = {
const firstHopFill: CollapsedFill = {
sourcePathId: '',
source: firstHopSource.source,
type: FillQuoteTransformerOrderType.Bridge,
input: opts.side === MarketOperation.Sell ? sample.input : ZERO_AMOUNT,
output: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
adjustedOutput: opts.side === MarketOperation.Sell ? ZERO_AMOUNT : sample.output,
subFills: [],
fillData: firstHopSource.fillData,
flags: BigInt(0),
gas: 1,
};
const secondHopFill: Fill = {
const secondHopFill: CollapsedFill = {
sourcePathId: '',
source: secondHopSource.source,
type: FillQuoteTransformerOrderType.Bridge,
input: opts.side === MarketOperation.Sell ? MAX_UINT256 : sample.input,
output: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
adjustedOutput: opts.side === MarketOperation.Sell ? sample.output : MAX_UINT256,
subFills: [],
fillData: secondHopSource.fillData,
flags: BigInt(0),
gas: 1,
};
return [
createBridgeOrder(firstHopFill, intermediateToken, takerToken, opts.side),
@@ -94,9 +84,11 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
case ERC20BridgeSource.Balancer:
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
case ERC20BridgeSource.BalancerV2:
return encodeBridgeSourceId(BridgeProtocol.BalancerV2Batch, 'BalancerV2');
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'BalancerV2');
case ERC20BridgeSource.Bancor:
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
// case ERC20BridgeSource.CoFiX:
// return encodeBridgeSourceId(BridgeProtocol.CoFiX, 'CoFiX');
case ERC20BridgeSource.Curve:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Curve');
case ERC20BridgeSource.Cream:
@@ -105,6 +97,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.CryptoCom, 'CryptoCom');
case ERC20BridgeSource.Dodo:
return encodeBridgeSourceId(BridgeProtocol.Dodo, 'Dodo');
case ERC20BridgeSource.Kyber:
return encodeBridgeSourceId(BridgeProtocol.Kyber, 'Kyber');
case ERC20BridgeSource.LiquidityProvider:
// "LiquidityProvider" is too long to encode (17 characters).
return encodeBridgeSourceId(BridgeProtocol.Unknown, 'LP');
@@ -114,16 +108,24 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Mooniswap, 'Mooniswap');
case ERC20BridgeSource.MStable:
return encodeBridgeSourceId(BridgeProtocol.MStable, 'MStable');
case ERC20BridgeSource.Eth2Dai:
return encodeBridgeSourceId(BridgeProtocol.Oasis, 'Eth2Dai');
case ERC20BridgeSource.Shell:
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Shell');
case ERC20BridgeSource.SnowSwap:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'SnowSwap');
case ERC20BridgeSource.SushiSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SushiSwap');
case ERC20BridgeSource.Swerve:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Swerve');
case ERC20BridgeSource.Uniswap:
return encodeBridgeSourceId(BridgeProtocol.Uniswap, 'Uniswap');
case ERC20BridgeSource.UniswapV2:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UniswapV2');
case ERC20BridgeSource.DodoV2:
return encodeBridgeSourceId(BridgeProtocol.DodoV2, 'DodoV2');
case ERC20BridgeSource.Linkswap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Linkswap');
case ERC20BridgeSource.PancakeSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'PancakeSwap');
case ERC20BridgeSource.PancakeSwapV2:
@@ -140,32 +142,44 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Ellipsis');
case ERC20BridgeSource.Component:
return encodeBridgeSourceId(BridgeProtocol.Shell, 'Component');
case ERC20BridgeSource.Smoothy:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Smoothy');
case ERC20BridgeSource.Saddle:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Saddle');
case ERC20BridgeSource.XSigma:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'xSigma');
case ERC20BridgeSource.ApeSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ApeSwap');
case ERC20BridgeSource.CafeSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CafeSwap');
case ERC20BridgeSource.CheeseSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CheeseSwap');
case ERC20BridgeSource.JulSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JulSwap');
case ERC20BridgeSource.UniswapV3:
return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3');
case ERC20BridgeSource.KyberDmm:
return encodeBridgeSourceId(BridgeProtocol.KyberDmm, 'KyberDmm');
case ERC20BridgeSource.QuickSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'QuickSwap');
case ERC20BridgeSource.ComethSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ComethSwap');
case ERC20BridgeSource.Dfyn:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Dfyn');
case ERC20BridgeSource.CurveV2:
return encodeBridgeSourceId(BridgeProtocol.CurveV2, 'CurveV2');
case ERC20BridgeSource.WaultSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'WaultSwap');
case ERC20BridgeSource.Polydex:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Polydex');
case ERC20BridgeSource.FirebirdOneSwap:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'FirebirdOneSwap');
case ERC20BridgeSource.Lido:
return encodeBridgeSourceId(BridgeProtocol.Lido, 'Lido');
case ERC20BridgeSource.ShibaSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'ShibaSwap');
case ERC20BridgeSource.JetSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JetSwap');
case ERC20BridgeSource.IronSwap:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'IronSwap');
case ERC20BridgeSource.ACryptos:
@@ -184,32 +198,10 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
case ERC20BridgeSource.MorpheusSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
case ERC20BridgeSource.Yoshi:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Yoshi');
case ERC20BridgeSource.AaveV2:
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
case ERC20BridgeSource.Compound:
return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound');
case ERC20BridgeSource.Geist:
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'Geist');
case ERC20BridgeSource.MobiusMoney:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'MobiusMoney');
case ERC20BridgeSource.BiSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BiSwap');
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');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -233,16 +225,18 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
switch (order.source) {
case ERC20BridgeSource.Curve:
case ERC20BridgeSource.CurveV2:
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Synapse:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Smoothy:
case ERC20BridgeSource.Saddle:
case ERC20BridgeSource.XSigma:
case ERC20BridgeSource.FirebirdOneSwap:
case ERC20BridgeSource.IronSwap:
case ERC20BridgeSource.ACryptos:
case ERC20BridgeSource.MobiusMoney:
const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
bridgeData = encoder.encode([
curveFillData.pool.poolAddress,
@@ -257,18 +251,9 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
bridgeData = encoder.encode([balancerFillData.poolAddress]);
break;
case ERC20BridgeSource.BalancerV2:
{
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2BatchSwapFillData>).fillData;
bridgeData = encoder.encode([
balancerV2FillData.vault,
balancerV2FillData.swapSteps,
balancerV2FillData.assets,
]);
}
break;
case ERC20BridgeSource.Beethovenx:
const beethovenFillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
const { vault, poolId } = beethovenFillData;
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
const { vault, poolId } = balancerV2FillData;
bridgeData = encoder.encode([vault, poolId]);
break;
case ERC20BridgeSource.Bancor:
@@ -278,29 +263,34 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.UniswapV2:
case ERC20BridgeSource.SushiSwap:
case ERC20BridgeSource.CryptoCom:
case ERC20BridgeSource.Linkswap:
case ERC20BridgeSource.PancakeSwap:
case ERC20BridgeSource.PancakeSwapV2:
case ERC20BridgeSource.BakerySwap:
case ERC20BridgeSource.ApeSwap:
case ERC20BridgeSource.CafeSwap:
case ERC20BridgeSource.CheeseSwap:
case ERC20BridgeSource.JulSwap:
case ERC20BridgeSource.QuickSwap:
case ERC20BridgeSource.ComethSwap:
case ERC20BridgeSource.Dfyn:
case ERC20BridgeSource.WaultSwap:
case ERC20BridgeSource.Polydex:
case ERC20BridgeSource.ShibaSwap:
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
case ERC20BridgeSource.UbeSwap:
case ERC20BridgeSource.SpiritSwap:
case ERC20BridgeSource.SpookySwap:
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;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
case ERC20BridgeSource.Kyber:
const kyberFillData = (order as OptimizedMarketBridgeOrder<KyberFillData>).fillData;
bridgeData = encoder.encode([kyberFillData.networkProxy, kyberFillData.hint]);
break;
case ERC20BridgeSource.Mooniswap:
const mooniswapFillData = (order as OptimizedMarketBridgeOrder<MooniswapFillData>).fillData;
bridgeData = encoder.encode([mooniswapFillData.poolAddress]);
@@ -330,6 +320,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const uniFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([uniFillData.router]);
break;
case ERC20BridgeSource.Eth2Dai:
const oasisFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([oasisFillData.router]);
break;
case ERC20BridgeSource.MStable:
const mStableFillData = (order as OptimizedMarketBridgeOrder<GenericRouterFillData>).fillData;
bridgeData = encoder.encode([mStableFillData.router]);
@@ -352,7 +346,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
break;
case ERC20BridgeSource.Lido:
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress, lidoFillData.wstEthTokenAddress]);
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
break;
case ERC20BridgeSource.AaveV2:
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
@@ -362,41 +356,69 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const compoundFillData = (order as OptimizedMarketBridgeOrder<CompoundFillData>).fillData;
bridgeData = encoder.encode([compoundFillData.cToken]);
break;
case ERC20BridgeSource.Geist:
const geistFillData = (order as OptimizedMarketBridgeOrder<GeistFillData>).fillData;
bridgeData = encoder.encode([geistFillData.lendingPool, geistFillData.gToken]);
break;
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;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
return bridgeData;
}
export function createBridgeOrder(
fill: CollapsedFill,
makerToken: string,
takerToken: string,
side: MarketOperation,
): OptimizedMarketBridgeOrder {
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
return {
makerToken,
takerToken,
makerAmount,
takerAmount,
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
source: fill.source,
sourcePathId: fill.sourcePathId,
type: FillQuoteTransformerOrderType.Bridge,
fills: [fill],
};
}
function createFinalBridgeOrderFillDataFromCollapsedFill(fill: CollapsedFill): FillData {
switch (fill.source) {
case ERC20BridgeSource.UniswapV3: {
const fd = fill.fillData as UniswapV3FillData;
return {
router: fd.router,
tokenAddressPath: fd.tokenAddressPath,
uniswapPath: getBestUniswapV3PathForInputAmount(fd, fill.input),
};
}
default:
break;
}
return fill.fillData;
}
function getBestUniswapV3PathForInputAmount(fillData: UniswapV3FillData, inputAmount: BigNumber): string {
if (fillData.pathAmounts.length === 0) {
throw new Error(`No Uniswap V3 paths`);
}
// Find the best path that can satisfy `inputAmount`.
// Assumes `fillData.pathAmounts` is sorted ascending.
for (const { inputAmount: pathInputAmount, uniswapPath } of fillData.pathAmounts) {
if (pathInputAmount.gte(inputAmount)) {
return uniswapPath;
}
}
return fillData.pathAmounts[fillData.pathAmounts.length - 1].uniswapPath;
}
export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] {
const makerToken = opts.side === MarketOperation.Sell ? opts.outputToken : opts.inputToken;
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
return [makerToken, takerToken];
}
export const poolEncoder = AbiEncoder.create([{ name: 'poolAddress', type: 'address' }]);
const curveEncoder = AbiEncoder.create([
{ name: 'curveAddress', type: 'address' },
@@ -414,8 +436,6 @@ const balancerV2Encoder = AbiEncoder.create([
]);
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<
@@ -427,6 +447,10 @@ export const BRIDGE_ENCODERS: {
{ 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' },
@@ -439,36 +463,30 @@ export const BRIDGE_ENCODERS: {
// Curve like
[ERC20BridgeSource.Curve]: curveEncoder,
[ERC20BridgeSource.CurveV2]: curveEncoder,
[ERC20BridgeSource.Swerve]: curveEncoder,
[ERC20BridgeSource.SnowSwap]: curveEncoder,
[ERC20BridgeSource.Nerve]: curveEncoder,
[ERC20BridgeSource.Synapse]: curveEncoder,
[ERC20BridgeSource.Belt]: curveEncoder,
[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.BancorV3]: routerAddressPathEncoder,
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
[ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
[ERC20BridgeSource.Linkswap]: routerAddressPathEncoder,
[ERC20BridgeSource.ShibaSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.Pangolin]: routerAddressPathEncoder,
[ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder,
[ERC20BridgeSource.SpiritSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.SpookySwap]: routerAddressPathEncoder,
[ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.BiSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.MDex]: routerAddressPathEncoder,
[ERC20BridgeSource.KnightSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.Yoshi]: routerAddressPathEncoder,
[ERC20BridgeSource.MeshSwap]: routerAddressPathEncoder,
// Avalanche
[ERC20BridgeSource.GMX]: gmxAddressPathEncoder,
[ERC20BridgeSource.Platypus]: platypusAddressPathEncoder,
// Celo
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
// BSC
@@ -476,50 +494,40 @@ export const BRIDGE_ENCODERS: {
[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]: 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.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,address)'),
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.Geist]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Velodrome]: AbiEncoder.create('(address,bool)'),
};
function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, BigNumber] {
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {
return [
// Maker asset amount.
side === MarketOperation.Sell ? fill.output.integerValue(BigNumber.ROUND_DOWN) : fill.input,
@@ -529,7 +537,7 @@ function getFillTokenAmounts(fill: Fill, side: MarketOperation): [BigNumber, Big
}
export function createNativeOptimizedOrder(
fill: Fill<NativeFillData>,
fill: NativeCollapsedFill,
side: MarketOperation,
): OptimizedMarketOrderBase<NativeLimitOrderFillData> | OptimizedMarketOrderBase<NativeRfqOrderFillData> {
const fillData = fill.fillData;
@@ -541,76 +549,10 @@ export function createNativeOptimizedOrder(
takerToken: fillData.order.takerToken,
makerAmount,
takerAmount,
fills: [fill],
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(
fill: Fill,
makerToken: string,
takerToken: string,
side: MarketOperation,
): OptimizedMarketBridgeOrder {
const [makerAmount, takerAmount] = getFillTokenAmounts(fill, side);
return {
type: FillQuoteTransformerOrderType.Bridge,
source: fill.source,
makerToken,
takerToken,
makerAmount,
takerAmount,
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
fill: cleanFillForExport(fill),
sourcePathId: fill.sourcePathId,
};
}
function cleanFillForExport(fill: Fill): Fill {
return _.omit(fill, ['flags', 'fillData', 'sourcePathId', 'source', 'type']) as Fill;
}
function createFinalBridgeOrderFillDataFromCollapsedFill(fill: Fill): FillData {
switch (fill.source) {
case ERC20BridgeSource.UniswapV3: {
const fd = fill.fillData as UniswapV3FillData;
const { uniswapPath, gasUsed } = getBestUniswapV3PathAmountForInputAmount(fd, fill.input);
const finalFillData: FinalUniswapV3FillData = {
router: fd.router,
tokenAddressPath: fd.tokenAddressPath,
uniswapPath,
gasUsed,
};
return finalFillData;
}
default:
break;
}
return fill.fillData;
}
function getBestUniswapV3PathAmountForInputAmount(
fillData: UniswapV3FillData,
inputAmount: BigNumber,
): UniswapV3PathAmount {
if (fillData.pathAmounts.length === 0) {
throw new Error(`No Uniswap V3 paths`);
}
// Find the best path that can satisfy `inputAmount`.
// Assumes `fillData.pathAmounts` is sorted ascending.
for (const pathAmount of fillData.pathAmounts) {
if (pathAmount.inputAmount.gte(inputAmount)) {
return pathAmount;
}
}
return fillData.pathAmounts[fillData.pathAmounts.length - 1];
}
export function getMakerTakerTokens(opts: CreateOrderFromPathOpts): [string, string] {
const makerToken = opts.side === MarketOperation.Sell ? opts.outputToken : opts.inputToken;
const takerToken = opts.side === MarketOperation.Sell ? opts.inputToken : opts.outputToken;
return [makerToken, takerToken];
}

View File

@@ -1,5 +1,4 @@
import { BigNumber } from '@0x/utils';
import _ = require('lodash');
import { MarketOperation } from '../../types';
@@ -7,7 +6,14 @@ import { POSITIVE_INF, ZERO_AMOUNT } from './constants';
import { ethToOutputAmount } from './fills';
import { createBridgeOrder, createNativeOptimizedOrder, CreateOrderFromPathOpts, getMakerTakerTokens } from './orders';
import { getCompleteRate, getRate } from './rate_utils';
import { ERC20BridgeSource, ExchangeProxyOverhead, Fill, NativeFillData, OptimizedMarketOrder } from './types';
import {
CollapsedFill,
ERC20BridgeSource,
ExchangeProxyOverhead,
Fill,
NativeCollapsedFill,
OptimizedMarketOrder,
} from './types';
// tslint:disable: prefer-for-of no-bitwise completed-docs
@@ -31,6 +37,7 @@ export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = {
};
export class Path {
public collapsedFills?: ReadonlyArray<CollapsedFill>;
public orders?: OptimizedMarketOrder[];
public sourceFlags: bigint = BigInt(0);
protected _size: PathSize = { input: ZERO_AMOUNT, output: ZERO_AMOUNT };
@@ -50,6 +57,16 @@ export class 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 readonly side: MarketOperation,
public fills: ReadonlyArray<Fill>,
@@ -57,33 +74,77 @@ export class Path {
public readonly pathPenaltyOpts: PathPenaltyOpts,
) {}
public append(fill: Fill): this {
(this.fills as Fill[]).push(fill);
this.sourceFlags |= fill.flags;
this._addFillSize(fill);
return this;
}
/**
* Finalizes this path, creating fillable orders with the information required
* for settlement
* Add a fallback path to the current path
* Fallback must contain exclusive fills that are
* not present in this path
*/
public finalize(opts: CreateOrderFromPathOpts): FinalizedPath {
public addFallback(fallback: Path): this {
// 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 collapsedFills = this.collapsedFills === undefined ? this._collapseFills() : this.collapsedFills;
this.orders = [];
for (const fill of this.fills) {
// internal BigInt flag field is not supported JSON and is tricky
// to remove upstream. Since it's not needed in a FinalizedPath we just drop it.
const normalizedFill = _.omit(fill, 'flags') as Fill;
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));
for (let i = 0; i < collapsedFills.length; ) {
if (collapsedFills[i].source === ERC20BridgeSource.Native) {
this.orders.push(createNativeOptimizedOrder(collapsedFills[i] as NativeCollapsedFill, opts.side));
++i;
continue;
}
// If there are contiguous bridge orders, we can batch them together.
// TODO jacob pretty sure this is from DFB and we can remove
const contiguousBridgeFills = [collapsedFills[i]];
for (let j = i + 1; j < collapsedFills.length; ++j) {
if (collapsedFills[j].source === ERC20BridgeSource.Native) {
break;
}
contiguousBridgeFills.push(collapsedFills[j]);
}
this.orders.push(createBridgeOrder(contiguousBridgeFills[0], makerToken, takerToken, opts.side));
i += 1;
}
return this as FinalizedPath;
return this as CollapsedPath;
}
public size(): PathSize {
return this._size;
}
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 { 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 pathPenalty = ethToOutputAmount({
input,
@@ -103,10 +164,6 @@ export class Path {
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 {
const { input, output } = this.adjustedSize();
return getRate(this.side, input, output);
@@ -123,11 +180,16 @@ export class Path {
return best;
}
/**
* Compares two paths returning if this adjusted path
* is better than the other adjusted path
*/
public isAdjustedBetterThan(other: Path): boolean {
public adjustedSlippage(maxRate: BigNumber): number {
if (maxRate.eq(0)) {
return 0;
}
const totalRate = this.adjustedRate();
const rateChange = maxRate.minus(totalRate);
return rateChange.div(maxRate).toNumber();
}
public isBetterThan(other: Path): boolean {
if (!this.targetInput.isEqualTo(other.targetInput)) {
throw new Error(`Target input mismatch: ${this.targetInput} !== ${other.targetInput}`);
}
@@ -139,6 +201,78 @@ export class Path {
} else {
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 {
@@ -160,6 +294,7 @@ export class Path {
}
}
export interface FinalizedPath extends Path {
export interface CollapsedPath extends Path {
readonly collapsedFills: ReadonlyArray<CollapsedFill>;
readonly orders: OptimizedMarketOrder[];
}

View File

@@ -1,7 +1,6 @@
import { assert } from '@0x/assert';
import { ChainId } from '@0x/contract-addresses';
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { performance } from 'perf_hooks';
@@ -10,12 +9,13 @@ import { DEFAULT_WARNING_LOGGER } from '../../constants';
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID, ZERO_AMOUNT } from './constants';
import { dexSampleToFill, ethToOutputAmount, nativeOrderToFill } from './fills';
import { Path, PathPenaltyOpts } from './path';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillAdjustor, FillData, SamplerMetrics } from './types';
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types';
// 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
const MIN_NUM_SAMPLE_INPUTS = 3;
@@ -45,7 +45,7 @@ function calculateOuputFee(
): BigNumber {
if (isDexSample(sampleOrNativeOrder)) {
const { input, output, source, fillData } = sampleOrNativeOrder;
const fee = fees[source]?.(fillData).fee || ZERO_AMOUNT;
const fee = fees[source]?.(fillData) || 0;
const outputFee = ethToOutputAmount({
input,
output,
@@ -56,7 +56,7 @@ function calculateOuputFee(
return outputFee;
} else {
const { input, output } = nativeOrderToNormalizedAmounts(side, sampleOrNativeOrder);
const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder).fee || ZERO_AMOUNT;
const fee = fees[ERC20BridgeSource.Native]?.(sampleOrNativeOrder) || 0;
const outputFee = ethToOutputAmount({
input,
output,
@@ -76,151 +76,22 @@ function findRoutesAndCreateOptimalPath(
opts: PathPenaltyOpts,
fees: FeeSchedule,
neonRouterNumSamples: number,
vipSourcesSet: Set<ERC20BridgeSource>,
fillAdjustor: FillAdjustor,
): { allSourcesPath: Path | undefined; vipSourcesPath: Path | undefined } | undefined {
): Path | undefined {
// Currently the rust router is unable to handle 1 base unit sized quotes and will error out
// To avoid flooding the logs with these errors we just return an insufficient liquidity error
// which is how the JS router handles these quotes today
if (input.isLessThanOrEqualTo(ONE_BASE_UNIT)) {
if (input.eq(ONE_BASE_UNIT)) {
return undefined;
}
// Create a `Fill` from a dex sample and adjust it with any passed in
// adjustor
const createFillFromDexSample = (sample: DexSample): Fill => {
const fill = dexSampleToFill(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
const adjustedFills = fillAdjustor.adjustFills(side, [fill], input);
return adjustedFills[0];
};
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(
optimalRouteInputs,
optimalRouteOutputs,
samplesAndNativeOrdersWithResults,
sampleSourcePathIds,
);
const adjustedFills: Fill[] = [];
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);
for (const [
routeInput,
outputAmount,
routeSamplesAndNativeOrders,
sourcePathId,
] of routesAndSamplesAndOutputs) {
if (!Number.isFinite(outputAmount)) {
DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`);
return undefined;
}
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
continue;
}
// TODO: [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
const routeInputCorrected = BigNumber.min(
precisionErrorScalar.multipliedBy(routeInput).integerValue(BigNumber.ROUND_CEIL),
input,
);
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)) {
const nativeFill = nativeOrderToFill(
side,
current,
routeInputCorrected,
opts.outputAmountPerEth,
opts.inputAmountPerEth,
fees,
false,
);
// Note: If the order has an adjusted rate of less than or equal to 0 it will be undefined
if (nativeFill) {
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
}
continue;
}
// NOTE: For DexSamples only
let fill = createFillFromDexSample(current);
if (!fill) {
continue;
}
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
// 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');
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) {
fill = createFillFromDexSample(routeSamples[0]) ?? fill;
}
if (routeInputCorrected.isGreaterThan(routeSamples[k].input)) {
const left = routeSamples[k];
const right = routeSamples[k + 1];
if (left && right) {
fill =
createFillFromDexSample({
...right, // default to the greater (for gas used)
input: routeInputCorrected,
output: new BigNumber(outputAmount).integerValue(),
}) ?? fill;
} else {
assert.assert(Boolean(left || right), 'No valid sample to use');
fill = createFillFromDexSample(left || right) ?? fill;
}
break;
}
}
// TODO: remove once we have solved the rounding/precision loss issues in the Rust router
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)
const scaleOutput = (output: BigNumber) => {
const capped = BigNumber.min(output.integerValue(), maxSampledOutput);
return BigNumber.max(capped, 1);
};
adjustedFills.push({
...fill,
input: routeInputCorrected,
output: scaleOutput(fill.output),
adjustedOutput: scaleOutput(fill.adjustedOutput),
sourcePathId: sourcePathId ?? hexUtils.random(),
});
}
if (adjustedFills.length === 0) {
const createFill = (sample: DexSample): Fill | undefined => {
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
if (fills.length === 0) {
return undefined;
}
const pathFromRustInputs = Path.create(side, adjustedFills, input, opts);
return pathFromRustInputs;
return fills[0];
};
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
@@ -231,11 +102,11 @@ function findRoutesAndCreateOptimalPath(
continue;
}
const sourcePathId = hexUtils.random();
const singleSourceSamplesWithOutput = [...singleSourceSamples];
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
const currentOutput = singleSourceSamples[i].output;
if (currentOutput.isZero() || !currentOutput.isFinite()) {
// Remove trailing 0/invalid output samples
if (singleSourceSamples[i].output.isZero()) {
// Remove trailing 0 output samples
singleSourceSamplesWithOutput.pop();
} else {
break;
@@ -246,23 +117,17 @@ function findRoutesAndCreateOptimalPath(
continue;
}
// TODO: Do we need to handle 0 entries, from eg Kyber?
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
(memo, sample, sampleIdx) => {
// Use the fill from createFillFromDexSample to apply
// any user supplied adjustments
const f = createFillFromDexSample(sample);
memo.ids.push(`${f.source}-${serializedPaths.length}-${sampleIdx}`);
memo.inputs.push(f.input.integerValue().toNumber());
memo.outputs.push(f.output.integerValue().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);
memo.ids.push(`${sample.source}-${serializedPaths.length}-${sampleIdx}`);
memo.inputs.push(sample.input.integerValue().toNumber());
memo.outputs.push(sample.output.integerValue().toNumber());
memo.outputFees.push(
calculateOuputFee(side, sample, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
.integerValue()
.toNumber(),
);
return memo;
},
@@ -271,14 +136,11 @@ function findRoutesAndCreateOptimalPath(
inputs: [],
outputs: [],
outputFees: [],
isVip: vipSourcesSet.has(singleSourceSamplesWithOutput[0]?.source),
},
);
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
serializedPaths.push(serializedPath);
const sourcePathId = hexUtils.random();
sampleSourcePathIds.push(sourcePathId);
}
@@ -320,22 +182,18 @@ function findRoutesAndCreateOptimalPath(
normalizedOrderOutput.times(scaleToInput).times(fraction),
normalizedOrderOutput,
);
const id = `${ERC20BridgeSource.Native}-${nativeOrder.type}-${serializedPaths.length}-${idx}-${i}`;
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
inputs.push(currentInput.integerValue().toNumber());
outputs.push(currentOutput.integerValue().toNumber());
outputFees.push(fee);
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 = {
ids,
inputs,
outputs,
outputFees,
isVip,
};
samplesAndNativeOrdersWithResults.push([nativeOrder]);
@@ -354,45 +212,132 @@ function findRoutesAndCreateOptimalPath(
};
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
const allSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
const vipSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
const vipSourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
route(
rustArgs,
allSourcesRustRoute,
allSourcesOutputAmounts,
vipSourcesRustRoute,
vipSourcesOutputAmounts,
neonRouterNumSamples,
);
const strategySourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
route(rustArgs, allSourcesRustRoute, strategySourcesOutputAmounts, neonRouterNumSamples);
assert.assert(
rustArgs.pathsIn.length === allSourcesRustRoute.length,
'different number of sources in the Router output than the input',
);
assert.assert(
rustArgs.pathsIn.length === allSourcesOutputAmounts.length,
'different number of sources in the Router output amounts results than the input',
);
assert.assert(
rustArgs.pathsIn.length === vipSourcesRustRoute.length,
'different number of sources in the Router output than the input',
);
assert.assert(
rustArgs.pathsIn.length === vipSourcesOutputAmounts.length,
rustArgs.pathsIn.length === strategySourcesOutputAmounts.length,
'different number of sources in the Router output amounts results than the input',
);
const allSourcesPath = createPathFromStrategy(allSourcesRustRoute, allSourcesOutputAmounts);
const vipSourcesPath = createPathFromStrategy(vipSourcesRustRoute, vipSourcesOutputAmounts);
const routesAndSamplesAndOutputs = _.zip(
allSourcesRustRoute,
samplesAndNativeOrdersWithResults,
strategySourcesOutputAmounts,
sampleSourcePathIds,
);
const adjustedFills: Fill[] = [];
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
return {
allSourcesPath,
vipSourcesPath,
};
const scale = input.dividedBy(totalRoutedAmount);
for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) {
if (!Number.isFinite(outputAmount)) {
DEFAULT_WARNING_LOGGER(rustArgs, `neon-router: invalid route outputAmount ${outputAmount}`);
return undefined;
}
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount) {
continue;
}
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
const rustInputAdjusted = BigNumber.min(
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
input,
);
const current = routeSamplesAndNativeOrders[routeSamplesAndNativeOrders.length - 1];
if (!isDexSample(current)) {
const nativeFill = nativeOrdersToFills(
side,
[current],
rustInputAdjusted,
opts.outputAmountPerEth,
opts.inputAmountPerEth,
fees,
false,
)[0] as Fill | undefined;
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
// and nativeFill will be `undefined`
if (nativeFill) {
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
}
continue;
}
// NOTE: For DexSamples only
let fill = createFill(current);
if (!fill) {
continue;
}
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
// Descend to approach a closer fill for fillData which may not be consistent
// throughout the path (UniswapV3) and for a closer guesstimate at
// gas used
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
for (let k = routeSamples.length - 1; k >= 0; k--) {
if (k === 0) {
fill = createFill(routeSamples[0]) ?? fill;
}
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
const left = routeSamples[k];
const right = routeSamples[k + 1];
if (left && right) {
fill =
createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: new BigNumber(outputAmount),
}) ?? fill;
} else {
assert.assert(Boolean(left || right), 'No valid sample to use');
fill = createFill(left || right) ?? fill;
}
break;
}
}
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
const maxSampledOutput = BigNumber.max(...routeSamples.map(s => s.output));
// Scale output by scale factor but never go above the largest sample (unknown liquidity) or below 1 base unit (unfillable)
const scaleOutput = (output: BigNumber) => {
// Don't try to scale 0 output as it will be clamped to 1
if (output.eq(ZERO_AMOUNT)) {
return output;
}
const scaled = output
.times(scale)
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
return BigNumber.max(BigNumber.min(scaled, maxSampledOutput), 1);
};
adjustedFills.push({
...fill,
input: rustInputAdjusted,
output: scaleOutput(fill.output),
adjustedOutput: scaleOutput(fill.adjustedOutput),
index: 0,
parent: undefined,
sourcePathId: sourcePathId ?? hexUtils.random(),
});
}
if (adjustedFills.length === 0) {
return undefined;
}
const pathFromRustInputs = Path.create(side, adjustedFills, input, opts);
return pathFromRustInputs;
}
export function findOptimalPathFromSamples(
export function findOptimalRustPathFromSamples(
side: MarketOperation,
samples: DexSample[][],
nativeOrders: NativeOrderWithFillableAmounts[],
@@ -401,21 +346,11 @@ export function findOptimalPathFromSamples(
fees: FeeSchedule,
chainId: ChainId,
neonRouterNumSamples: number,
fillAdjustor: FillAdjustor,
samplerMetrics?: SamplerMetrics,
): Path | undefined {
const beforeTimeMs = performance.now();
const sendMetrics = () => {
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'total',
timingMs: performance.now() - beforeTimeMs,
});
};
const vipSourcesSet = new Set(VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId]);
const paths = findRoutesAndCreateOptimalPath(
const beforeAllTimeMs = performance.now();
let beforeTimeMs = performance.now();
const allSourcesPath = findRoutesAndCreateOptimalPath(
side,
samples,
nativeOrders,
@@ -423,22 +358,197 @@ export function findOptimalPathFromSamples(
opts,
fees,
neonRouterNumSamples,
vipSourcesSet,
fillAdjustor,
);
if (!paths) {
sendMetrics();
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'all',
timingMs: performance.now() - beforeTimeMs,
});
if (!allSourcesPath) {
return undefined;
}
const { allSourcesPath, vipSourcesPath } = paths;
const vipSources = VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID[chainId];
if (!allSourcesPath || vipSourcesPath?.isAdjustedBetterThan(allSourcesPath)) {
sendMetrics();
return vipSourcesPath;
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
// we need to try to route them in isolation and compare with the results all sources
if (vipSources.length > 0) {
beforeTimeMs = performance.now();
const vipSourcesSet = new Set(vipSources);
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
if (vipSourcesSamples.length > 0) {
const vipSourcesPath = findRoutesAndCreateOptimalPath(
side,
vipSourcesSamples,
[],
input,
opts,
fees,
neonRouterNumSamples,
);
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'vip',
timingMs: performance.now() - beforeTimeMs,
});
if (vipSourcesPath?.isBetterThan(allSourcesPath)) {
return vipSourcesPath;
}
}
}
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'total',
timingMs: performance.now() - beforeAllTimeMs,
});
sendMetrics();
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()]));
}

View File

@@ -1,5 +1,5 @@
import { getPoolsWithTokens, parsePoolData } from 'balancer-labs-sor-v1';
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor';
import { Pool } from '@balancer-labs/sor/dist/types';
import { gql, request } from 'graphql-request';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants';

View File

@@ -1,7 +1,7 @@
import { ChainId } from '@0x/contract-addresses';
import { BigNumber } from '@0x/utils';
// import { parsePoolData } from '@balancer-labs'; // TODO - upgrade to v2
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { Pool } from '@balancer-labs/sor/dist/types';
import { gql, request } from 'graphql-request';
import { DEFAULT_WARNING_LOGGER } from '../../../constants';
@@ -51,7 +51,7 @@ export class BalancerV2PoolsCache extends PoolsCache {
constructor(
chainId: ChainId,
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId]!,
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL_BY_CHAIN[chainId],
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
private readonly _warningLogger: LogFunction = DEFAULT_WARNING_LOGGER,

View File

@@ -1,190 +0,0 @@
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;
}

View File

@@ -1,4 +1,4 @@
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { Pool } from '@balancer-labs/sor/dist/types';
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';

View File

@@ -1,91 +0,0 @@
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>;
}

View File

@@ -1,4 +1,4 @@
import { Pool } from 'balancer-labs-sor-v1/dist/types';
import { Pool } from '@balancer-labs/sor/dist/types';
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
export { Pool };

View File

@@ -1,114 +0,0 @@
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 [];
}
}
}

View File

@@ -1,20 +1,9 @@
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { MarketOperation } from '../../types';
import { SOURCE_FLAGS, ZERO_AMOUNT } from './constants';
import { adjustOutput } from './fills';
import { IdentityFillAdjustor } from './identity_fill_adjustor';
import {
DexSample,
ERC20BridgeSource,
ExchangeProxyOverhead,
FeeSchedule,
Fill,
FillAdjustor,
MultiHopFillData,
} from './types';
import { DexSample, ERC20BridgeSource, ExchangeProxyOverhead, FeeSchedule, MultiHopFillData } from './types';
// tslint:disable:no-bitwise
@@ -29,55 +18,20 @@ export function getTwoHopAdjustedRate(
outputAmountPerEth: BigNumber,
fees: FeeSchedule = {},
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
fillAdjustor: FillAdjustor = new IdentityFillAdjustor(),
): BigNumber {
const { output, input, fillData } = twoHopQuote;
if (input.isLessThan(targetInput) || output.isZero()) {
return ZERO_AMOUNT;
}
// Flags to indicate which sources are used
const flags =
SOURCE_FLAGS.MultiHop |
SOURCE_FLAGS[fillData.firstHopSource.source] |
SOURCE_FLAGS[fillData.secondHopSource.source];
// Penalty of going to those sources in terms of output
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);
const penalty = outputAmountPerEth.times(
exchangeProxyOverhead(
SOURCE_FLAGS.MultiHop |
SOURCE_FLAGS[fillData.firstHopSource.source] |
SOURCE_FLAGS[fillData.secondHopSource.source],
).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
);
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);
return side === MarketOperation.Sell ? adjustedOutput.div(input) : input.div(adjustedOutput);
}
/**
@@ -105,8 +59,6 @@ export function getCompleteRate(
/**
* 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 {
if (input.eq(0) || output.eq(0)) {

View File

@@ -5,8 +5,9 @@ import { SamplerOverrides } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { BancorService } from './bancor_service';
import { PoolsCacheMap, SamplerOperations } from './sampler_operations';
import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
import { PoolsCache } from './pools_cache';
import { SamplerOperations } from './sampler_operations';
import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
/**
* Generate sample amounts up to `maxFillAmount`.
@@ -36,7 +37,7 @@ export class DexOrderSampler extends SamplerOperations {
public readonly chainId: ChainId,
_samplerContract: ERC20BridgeSamplerContract,
private readonly _samplerOverrides?: SamplerOverrides,
poolsCaches?: PoolsCacheMap,
poolsCaches?: { [key in ERC20BridgeSource]: PoolsCache },
tokenAdjacencyGraph?: TokenAdjacencyGraph,
liquidityProviderRegistry?: LiquidityProviderRegistry,
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,

Some files were not shown because too many files have changed in this diff Show More