Compare commits

..

16 Commits

Author SHA1 Message Date
Github Actions
fb249f02fc Publish
- @0x/contracts-asset-proxy@3.7.11
 - @0x/contracts-broker@1.1.29
 - @0x/contracts-coordinator@3.1.30
 - @0x/contracts-dev-utils@1.3.28
 - @0x/contracts-erc1155@2.1.29
 - @0x/contracts-erc20@3.3.8
 - @0x/contracts-erc721@3.1.29
 - @0x/contracts-exchange-forwarder@4.2.30
 - @0x/contracts-exchange-libs@4.3.29
 - @0x/contracts-exchange@3.2.30
 - @0x/contracts-extensions@6.2.24
 - @0x/contracts-integrations@2.7.40
 - @0x/contracts-multisig@4.1.30
 - @0x/contracts-staking@2.0.37
 - @0x/contracts-test-utils@5.4.0
 - @0x/contracts-treasury@1.1.6
 - @0x/contracts-utils@4.7.8
 - @0x/contracts-zero-ex@0.23.0
 - @0x/asset-swapper@6.10.0
 - @0x/contract-addresses@6.1.0
 - @0x/contract-artifacts@3.14.2
 - @0x/contract-wrappers-test@12.2.45
 - @0x/contract-wrappers@13.16.1
 - @0x/migrations@8.0.6
 - @0x/order-utils@10.4.21
 - @0x/protocol-utils@1.6.0
2021-05-05 11:32:51 +00:00
Github Actions
fdf04ef275 Updated CHANGELOGS & MD docs 2021-05-05 11:32:46 +00:00
Jacob Evans
b0f5f634f2 bump test-publish resource_class 2021-05-05 21:02:31 +10:00
Jacob Evans
6ee0108565 fix: async pool cache (#226) 2021-05-05 21:01:51 +10:00
Jacob Evans
c73097e688 chore: Deploy FQT with UniswapV3 support (#227) 2021-05-05 21:01:39 +10:00
Lawrence Forman
a2d42b07b5 UniswapV3 [TKR-4] (#197)
* add balancer v2

* fetch balancer v2 pools from subgraph

* feat: initial stab at a Balancer V2 Sampler WIP

* feat: add sampling for buys and fix build issues WIP [untested]

* fix: BalancerV2Sampler implementation issues, works on Kovan

* chore: BalancerV2 sampling boilerplate

* fix: update Balancer V2 mainnet address

* fix: consolidate differences between the 2 working branches

* fix: use mainnet Balancer V2 subgraph

* fix: stack too deep by minimizing and inline Balancer V2 vault interface

* fix: address review comments and clean up

* fix: sampler vault interface and pools cache assuming a pool has swaps

* address more review comments

* fix: TS type issues and add a comment about deadline argument

* fix: pools_cache_tests incorrect token addresses, prettier incompat

* fix: make ERC20BridgeSampler support BalancerV2 non view sampler fns

* fix: use a struct for passing encoded bridge data for Balancer V2

* chore: add changelog entries

* fix: improve gas accuracy of gas schedule for Balancer V2 & Maker Psm

* fix: don't exclude sources with stale caches & wait for cache refresh

* rebase

* `@0x/asset-swapper`: Fix stack too deep errors in sampler

* `@0x/asset-swapper`: Add uniswap V3 sampler.

* `@0x/contracts-zero-ex`: Add UniswapV3 support to FQT

* `@0x/protocol-utils`: Update BridgeProtocols.
`@0x/asset-swapper`: Add sell integration for UniswapV3.

* `@0x/asset-swapper`: Remove unnecessary swap quote consumer constructor arg.
`@0x/asset-swapper`: Enable UniswapV3 on ropsten.

* `@0x/asset-swapper`: UniswapV3 fixes

* `@0x/asset-swapper`: Fix stuff based on latest deployed testnet contracts

* `@0x/asset-swapper`: Remove UniV3 from mainnet sources for now

* `@0x/asset-swapper`: Tweak univ3 gas schedule

* fix CI?

* `@0x/contracts-test-utils`: Set default ganache gas limit to 100e6

Co-authored-by: xianny <xianny@gmail.com>
Co-authored-by: Kim Persson <kimpersson88@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: Jacob Evans <jacob@dekz.net>
2021-05-05 19:44:17 +10:00
Xianny
f9a794af93 feat: Balancer V2 integration (#206)
* add balancer v2

* fetch balancer v2 pools from subgraph

* feat: initial stab at a Balancer V2 Sampler WIP

* feat: add sampling for buys and fix build issues WIP [untested]

* fix: BalancerV2Sampler implementation issues, works on Kovan

* chore: BalancerV2 sampling boilerplate

* fix: update Balancer V2 mainnet address

* fix: consolidate differences between the 2 working branches

* fix: use mainnet Balancer V2 subgraph

* fix: stack too deep by minimizing and inline Balancer V2 vault interface

* fix: address review comments and clean up

* fix: sampler vault interface and pools cache assuming a pool has swaps

* address more review comments

* fix: TS type issues and add a comment about deadline argument

* fix: pools_cache_tests incorrect token addresses, prettier incompat

* fix: make ERC20BridgeSampler support BalancerV2 non view sampler fns

* fix: use a struct for passing encoded bridge data for Balancer V2

* chore: add changelog entries

* fix: improve gas accuracy of gas schedule for Balancer V2 & Maker Psm

* fix: don't exclude sources with stale caches & wait for cache refresh

* rebase

* `@0x/asset-swapper`: Fix stack too deep errors in sampler

Co-authored-by: Kim Persson <kimpersson88@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-05-05 11:01:28 +02:00
Jacob Evans
a2643674ca Fix: PLP now generates a fallback (#223)
* fix: PLP requires a fallback

* update changelog

* Fix missed source exclusion
2021-05-05 08:22:19 +10:00
Jacob Evans
c00ce9daac feat: Support ETH based Curve pools (#220)
* feat: Support ETH based Curve pools

* Disable Curve VIP for WETH trades

* feat: Support for sETH and ankrETH (Curve)

* Disable SnowSwap ETH pools

* feat: add BUSD Curve 3pool

* fix changelog

Co-authored-by: Romain Butteaud <romain.butteaud@gmail.com>
2021-05-05 07:33:41 +10:00
mzhu25
c68b5d7844 Fix/staking epoch finalization (#221)
* Patch staking and recover state in constructor

* Add ganache mainnet fork test

* Add ganache mainnet fork test

* update changelog

* hardcode last pool ID

* Separate patch contract to unbreak tests
2021-05-04 11:29:16 -07:00
Jacob Evans
09ed106d4c feat: Deployed Balancer V2 and Curve ETH support (#225) 2021-05-04 16:52:36 +10:00
Jacob Evans
a6b92fc658 fix: Fix test forever with new sources being added 2021-05-04 16:49:16 +10:00
mzhu25
4be4a1a30b Fix exchangeProxyGasOverhead used to compute fallback orders (#215)
* Fix exchangeProxyGasOverhead used to compute fallback orders

* update changelog
2021-05-04 13:41:21 +10:00
Romain Butteaud
9bede5d331 fix: reactive PancakeSwap, BakerySwap VIP on BSC (#222) 2021-05-04 13:40:57 +10:00
Romain Butteaud
b50d4aee6d Chore: adding xSigma liquidity source [TKR-59] (#201)
* Chore: adding xSigma liquidity source

* fix: prettier
2021-05-03 19:50:26 -07:00
Jacob Evans
55bc367bd6 feat: Add LUSD Curve pool (#218) 2021-05-04 08:08:27 +10:00
125 changed files with 2711 additions and 1339 deletions

View File

@@ -1,25 +1,25 @@
version: 2
version: 2.1
jobs:
build:
resource_class: medium+
resource_class: large
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
environment:
CONTRACTS_COMMIT_HASH: '9ed05f5'
NODE_OPTIONS: '--max-old-space-size=6442'
working_directory: ~/repo
steps:
- checkout
- run: echo 'export PATH=$HOME/CIRCLE_PROJECT_REPONAME/node_modules/.bin:$PATH' >> $BASH_ENV
- run:
name: install-yarn
command: npm install --force --global yarn@1.17.0
command: npm install --force --global yarn@1.22.0
- run:
name: yarn
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
- setup_remote_docker
- run: yarn build:ci
- run: yarn build:ts
- run: yarn build:ci || yarn build:ci || yarn build:ci
- run: yarn build:ts || yarn build:ts || yarn build:ts
- save_cache:
key: repo-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -31,57 +31,59 @@ jobs:
test-exchange-ganache:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-exchange
- run: yarn wsrun -p @0x/contracts-exchange -m --serial -c test:circleci
test-integrations-ganache:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-integrations
- run: yarn wsrun -p @0x/contracts-integrations -m --serial -c test:circleci
test-contracts-staking-ganache:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-staking
- run: yarn wsrun -p @0x/contracts-staking -m --serial -c test:circleci
test-contracts-extra-ganache:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-exchange-forwarder @0x/contracts-coordinator
- run: yarn wsrun -p @0x/contracts-exchange-forwarder -p @0x/contracts-coordinator -m --serial -c test:circleci
test-contracts-rest-ganache:
resource_class: medium+
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-multisig @0x/contracts-utils @0x/contracts-exchange-libs @0x/contracts-erc20 @0x/contracts-erc721 @0x/contracts-erc1155 @0x/contracts-asset-proxy @0x/contracts-broker @0x/contracts-zero-ex
- run: yarn wsrun -p @0x/contracts-multisig -p @0x/contracts-utils -p @0x/contracts-exchange-libs -p @0x/contracts-erc20 -p @0x/contracts-erc721 -p @0x/contracts-erc1155 -p @0x/contracts-asset-proxy -p @0x/contracts-broker -p @0x/contracts-zero-ex -m --serial -c test:circleci
test-publish:
resource_class: medium+
resource_class: large
environment:
NODE_OPTIONS: '--max-old-space-size=6442'
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
- image: 0xorg/verdaccio
working_directory: ~/repo
steps:
@@ -95,7 +97,7 @@ jobs:
path: ~/.npm/_logs
test-doc-generation:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
@@ -106,18 +108,18 @@ jobs:
no_output_timeout: 1200
test-rest:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:
keys:
- repo-{{ .Environment.CIRCLE_SHA1 }}
- run: yarn wsrun test:circleci @0x/contracts-test-utils
- run: yarn wsrun test:circleci @0x/contract-artifacts
- run: yarn wsrun test:circleci @0x/contract-wrappers-test
- run: yarn wsrun test:circleci @0x/migrations
- run: yarn wsrun test:circleci @0x/order-utils
- run: yarn wsrun test:circleci @0x/asset-swapper
- 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:
key: coverage-contract-wrappers-test-{{ .Environment.CIRCLE_SHA1 }}
paths:
@@ -134,7 +136,7 @@ jobs:
resource_class: large
working_directory: ~/repo
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
steps:
- restore_cache:
keys:
@@ -145,7 +147,7 @@ jobs:
- run: yarn diff_md_docs:ci
submit-coverage:
docker:
- image: nikolaik/python-nodejs:python3.7-nodejs10
- image: node:12
working_directory: ~/repo
steps:
- restore_cache:

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "3.7.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "3.7.10",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.7.11 - _May 5, 2021_
* Dependencies updated
## v3.7.10 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.7.10",
"version": "3.7.11",
"engines": {
"node": ">=6.12"
},
@@ -51,13 +51,13 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contract-wrappers": "^13.16.0",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contract-wrappers": "^13.16.1",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/lodash": "4.14.104",
@@ -79,16 +79,16 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-erc1155": "^2.1.28",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/order-utils": "^10.4.20",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-erc1155": "^2.1.29",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/order-utils": "^10.4.21",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"lodash": "^4.17.11"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "1.1.29",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "1.1.28",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.29 - _May 5, 2021_
* Dependencies updated
## v1.1.28 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-broker",
"version": "1.1.28",
"version": "1.1.29",
"engines": {
"node": ">=6.12"
},
@@ -51,20 +51,20 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -84,10 +84,10 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/order-utils": "^10.4.20",
"@0x/base-contract": "^6.4.0",
"@0x/order-utils": "^10.4.21",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "3.1.30",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "3.1.29",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.30 - _May 5, 2021_
* Dependencies updated
## v3.1.29 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.1.29",
"version": "3.1.30",
"engines": {
"node": ">=6.12"
},
@@ -52,17 +52,17 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-gen": "^2.0.37",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-gen": "^2.0.38",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -82,16 +82,16 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/assert": "^3.0.26",
"@0x/base-contract": "^6.3.2",
"@0x/contract-addresses": "^6.0.0",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/json-schemas": "^6.1.2",
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.1.0",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/json-schemas": "^6.1.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0",
"http-status-codes": "^1.3.2"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "1.3.28",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "1.3.27",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.28 - _May 5, 2021_
* Dependencies updated
## v1.3.27 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.3.27",
"version": "1.3.28",
"engines": {
"node": ">=6.12"
},
@@ -41,17 +41,17 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/dev-utils",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/assert": "^3.0.26",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/assert": "^3.0.27",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0",
"ethers": "~4.0.4",
"npm-run-all": "^4.1.2",
@@ -63,7 +63,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/base-contract": "^6.4.0",
"@types/node": "12.12.54"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "2.1.29",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "2.1.28",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.29 - _May 5, 2021_
* Dependencies updated
## v2.1.28 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.1.28",
"version": "2.1.29",
"engines": {
"node": ">=6.12"
},
@@ -52,11 +52,11 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
@@ -80,10 +80,10 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "3.3.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "3.3.7",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.8 - _May 5, 2021_
* Dependencies updated
## v3.3.7 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.7",
"version": "3.3.8",
"engines": {
"node": ">=6.12"
},
@@ -51,18 +51,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -82,7 +82,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2"
"@0x/base-contract": "^6.4.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "3.1.29",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "3.1.28",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.29 - _May 5, 2021_
* Dependencies updated
## v3.1.28 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.1.28",
"version": "3.1.29",
"engines": {
"node": ">=6.12"
},
@@ -52,18 +52,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -84,7 +84,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2"
"@0x/base-contract": "^6.4.0"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "4.2.30",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "4.2.29",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.2.30 - _May 5, 2021_
* Dependencies updated
## v4.2.29 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.2.29",
"version": "4.2.30",
"engines": {
"node": ">=6.12"
},
@@ -52,25 +52,25 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-erc1155": "^2.1.28",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-erc1155": "^2.1.29",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -90,7 +90,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/base-contract": "^6.4.0",
"@0x/typescript-typings": "^5.2.0",
"ethereum-types": "^3.5.0"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "4.3.29",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "4.3.28",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.3.29 - _May 5, 2021_
* Dependencies updated
## v4.3.28 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.3.28",
"version": "4.3.29",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/libs",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-gen": "^2.0.37",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/subproviders": "^6.5.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/subproviders": "^6.5.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -80,13 +80,13 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/order-utils": "^10.4.20",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/order-utils": "^10.4.21",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "3.2.30",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "3.2.29",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.30 - _May 5, 2021_
* Dependencies updated
## v3.2.29 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.2.29",
"version": "3.2.30",
"engines": {
"node": ">=6.12"
},
@@ -52,21 +52,21 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-multisig": "^4.1.29",
"@0x/contracts-staking": "^2.0.36",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-multisig": "^4.1.30",
"@0x/contracts-staking": "^2.0.37",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -88,13 +88,13 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-erc1155": "^2.1.28",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/order-utils": "^10.4.20",
"@0x/utils": "^6.4.2",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-erc1155": "^2.1.29",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/order-utils": "^10.4.21",
"@0x/utils": "^6.4.3",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "6.2.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "6.2.23",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.2.24 - _May 5, 2021_
* Dependencies updated
## v6.2.23 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "6.2.23",
"version": "6.2.24",
"engines": {
"node": ">=6.12"
},
@@ -52,23 +52,23 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -90,8 +90,8 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/typescript-typings": "^5.2.0",
"ethereum-types": "^3.5.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.39",
"version": "2.7.40",
"private": true,
"engines": {
"node": ">=6.12"
@@ -52,25 +52,25 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contract-addresses": "^6.0.0",
"@0x/contract-wrappers": "^13.16.0",
"@0x/contracts-broker": "^1.1.28",
"@0x/contracts-coordinator": "^3.1.29",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-exchange-forwarder": "^4.2.29",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-extensions": "^6.2.23",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-utils": "^4.7.7",
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.1.0",
"@0x/contract-wrappers": "^13.16.1",
"@0x/contracts-broker": "^1.1.29",
"@0x/contracts-coordinator": "^3.1.30",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-exchange-forwarder": "^4.2.30",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-extensions": "^6.2.24",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.8",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.2.6",
"@0x/migrations": "^8.0.5",
"@0x/order-utils": "^10.4.20",
"@0x/protocol-utils": "^1.5.1",
"@0x/sol-compiler": "^4.7.2",
"@0x/dev-utils": "^4.2.7",
"@0x/migrations": "^8.0.6",
"@0x/order-utils": "^10.4.21",
"@0x/protocol-utils": "^1.6.0",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -93,21 +93,21 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/asset-swapper": "^6.9.1",
"@0x/base-contract": "^6.3.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-erc1155": "^2.1.28",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-erc721": "^3.1.28",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-multisig": "^4.1.29",
"@0x/contracts-staking": "^2.0.36",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-zero-ex": "^0.22.3",
"@0x/subproviders": "^6.5.2",
"@0x/asset-swapper": "^6.10.0",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-erc1155": "^2.1.29",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-erc721": "^3.1.29",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-multisig": "^4.1.30",
"@0x/contracts-staking": "^2.0.37",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-zero-ex": "^0.23.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0",
"ethereumjs-util": "^7.0.10",
"lodash": "^4.17.11"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "4.1.30",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "4.1.29",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.30 - _May 5, 2021_
* Dependencies updated
## v4.1.29 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "4.1.29",
"version": "4.1.30",
"engines": {
"node": ">=6.12"
},
@@ -49,18 +49,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -78,7 +78,7 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/base-contract": "^6.4.0",
"@0x/typescript-typings": "^5.2.0",
"ethereum-types": "^3.5.0"
},

View File

@@ -1,4 +1,14 @@
[
{
"version": "2.0.37",
"changes": [
{
"note": "Patch epoch finalization issue",
"pr": 221
}
],
"timestamp": 1620214333
},
{
"timestamp": 1619596077,
"version": "2.0.36",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.37 - _May 5, 2021_
* Patch epoch finalization issue (#221)
## v2.0.36 - _April 28, 2021_
* Dependencies updated

View File

@@ -0,0 +1,55 @@
/*
Copyright 2019 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.5.9;
pragma experimental ABIEncoderV2;
import "./interfaces/IStaking.sol";
import "./sys/MixinParams.sol";
import "./stake/MixinStake.sol";
import "./fees/MixinExchangeFees.sol";
contract StakingPatch is
IStaking,
MixinParams,
MixinStake,
MixinExchangeFees
{
/// @dev Initialize storage owned by this contract.
/// This function should not be called directly.
/// The StakingProxy contract will call it in `attachStakingContract()`.
function init()
public
onlyAuthorized
{
uint256 currentEpoch_ = currentEpoch;
uint256 prevEpoch = currentEpoch_.safeSub(1);
// Patch corrupted state
aggregatedStatsByEpoch[prevEpoch].numPoolsToFinalize = 0;
this.endEpoch();
uint256 lastPoolId_ = 57;
for (uint256 i = 1; i <= lastPoolId_; i++) {
this.finalizePool(bytes32(i));
}
// Ensure that current epoch's state is not corrupted
aggregatedStatsByEpoch[currentEpoch_].numPoolsToFinalize = 0;
}
}

View File

@@ -53,6 +53,10 @@ contract MixinExchangeFees is
{
_assertValidProtocolFee(protocolFee);
if (protocolFee == 0) {
return;
}
// Transfer the protocol fee to this address if it should be paid in
// WETH.
if (msg.value == 0) {

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-staking",
"version": "2.0.36",
"version": "2.0.37",
"engines": {
"node": ">=6.12"
},
@@ -41,7 +41,7 @@
"config": {
"publicInterfaceContracts": "IStaking,IStakingEvents,IStakingProxy,IZrxVault,LibStakingRichErrors,Staking,StakingProxy,ZrxVault,TestStaking",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinCumulativeRewards|TestMixinParams|TestMixinScheduler|TestMixinStake|TestMixinStakeBalances|TestMixinStakeStorage|TestMixinStakingPool|TestMixinStakingPoolRewards|TestProtocolFees|TestProxyDestination|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStakingProxyUnit|TestStorageLayoutAndConstants|ZrxVault).json"
"abis": "./test/generated-artifacts/@(IStaking|IStakingEvents|IStakingProxy|IStorage|IStorageInit|IStructs|IZrxVault|LibCobbDouglas|LibFixedMath|LibFixedMathRichErrors|LibSafeDowncast|LibStakingRichErrors|MixinAbstract|MixinConstants|MixinCumulativeRewards|MixinDeploymentConstants|MixinExchangeFees|MixinExchangeManager|MixinFinalizer|MixinParams|MixinScheduler|MixinStake|MixinStakeBalances|MixinStakeStorage|MixinStakingPool|MixinStakingPoolRewards|MixinStorage|Staking|StakingPatch|StakingProxy|TestAssertStorageParams|TestCobbDouglas|TestCumulativeRewardTracking|TestDelegatorRewards|TestExchangeManager|TestFinalizer|TestInitTarget|TestLibFixedMath|TestLibSafeDowncast|TestMixinCumulativeRewards|TestMixinParams|TestMixinScheduler|TestMixinStake|TestMixinStakeBalances|TestMixinStakeStorage|TestMixinStakingPool|TestMixinStakingPoolRewards|TestProtocolFees|TestProxyDestination|TestStaking|TestStakingNoWETH|TestStakingProxy|TestStakingProxyUnit|TestStorageLayoutAndConstants|ZrxVault).json"
},
"repository": {
"type": "git",
@@ -53,20 +53,20 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-dev-utils": "^1.3.27",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-utils": "^4.7.7",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-dev-utils": "^1.3.28",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.8",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/lodash": "4.14.104",
"@types/node": "12.12.54",
"chai": "^4.0.1",
@@ -87,10 +87,10 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0",
"ethereumjs-util": "^7.0.10"
},

View File

@@ -33,6 +33,7 @@ import * as MixinStakingPool from '../test/generated-artifacts/MixinStakingPool.
import * as MixinStakingPoolRewards from '../test/generated-artifacts/MixinStakingPoolRewards.json';
import * as MixinStorage from '../test/generated-artifacts/MixinStorage.json';
import * as Staking from '../test/generated-artifacts/Staking.json';
import * as StakingPatch from '../test/generated-artifacts/StakingPatch.json';
import * as StakingProxy from '../test/generated-artifacts/StakingProxy.json';
import * as TestAssertStorageParams from '../test/generated-artifacts/TestAssertStorageParams.json';
import * as TestCobbDouglas from '../test/generated-artifacts/TestCobbDouglas.json';
@@ -61,6 +62,7 @@ import * as TestStorageLayoutAndConstants from '../test/generated-artifacts/Test
import * as ZrxVault from '../test/generated-artifacts/ZrxVault.json';
export const artifacts = {
Staking: Staking as ContractArtifact,
StakingPatch: StakingPatch as ContractArtifact,
StakingProxy: StakingProxy as ContractArtifact,
ZrxVault: ZrxVault as ContractArtifact,
MixinExchangeFees: MixinExchangeFees as ContractArtifact,

View File

@@ -0,0 +1,66 @@
import { blockchainTests, constants, expect, filterLogsToArguments } from '@0x/contracts-test-utils';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { StakingEvents, StakingPatchContract, StakingProxyContract, StakingProxyEvents } from './wrappers';
const abis = _.mapValues(artifacts, v => v.compilerOutput.abi);
const STAKING_PROXY = '0xa26e80e7dea86279c6d778d702cc413e6cffa777';
const STAKING_OWNER = '0x7d3455421bbc5ed534a83c88fd80387dc8271392';
const EXCHANGE_PROXY = '0xdef1c0ded9bec7f1a1670819833240f027b25eff';
blockchainTests.configure({
fork: {
unlockedAccounts: [STAKING_OWNER, EXCHANGE_PROXY],
},
});
blockchainTests.fork('Staking patch mainnet fork tests', env => {
let stakingProxyContract: StakingProxyContract;
let patchedStakingPatchContract: StakingPatchContract;
before(async () => {
stakingProxyContract = new StakingProxyContract(STAKING_PROXY, env.provider, undefined, abis);
patchedStakingPatchContract = await StakingPatchContract.deployFrom0xArtifactAsync(
artifacts.Staking,
env.provider,
env.txDefaults,
artifacts,
);
});
it('Staking proxy successfully attaches to patched logic', async () => {
const tx = await stakingProxyContract
.attachStakingContract(patchedStakingPatchContract.address)
.awaitTransactionSuccessAsync({ from: STAKING_OWNER, gasPrice: 0 }, { shouldValidate: false });
expect(filterLogsToArguments(tx.logs, StakingProxyEvents.StakingContractAttachedToProxy)).to.deep.equal([
{
newStakingPatchContractAddress: patchedStakingPatchContract.address,
},
]);
expect(filterLogsToArguments(tx.logs, StakingEvents.EpochEnded).length).to.equal(1);
expect(filterLogsToArguments(tx.logs, StakingEvents.EpochFinalized).length).to.equal(1);
logUtils.log(`${tx.gasUsed} gas used`);
});
it('Patched staking handles 0 gas protocol fees', async () => {
const staking = new StakingPatchContract(STAKING_PROXY, env.provider, undefined, abis);
const maker = '0x7b1886e49ab5433bb46f7258548092dc8cdca28b';
const zeroFeeTx = await staking
.payProtocolFee(maker, constants.NULL_ADDRESS, constants.ZERO_AMOUNT)
.awaitTransactionSuccessAsync({ from: EXCHANGE_PROXY, gasPrice: 0 }, { shouldValidate: false });
// StakingPoolEarnedRewardsInEpoch should _not_ be emitted for a zero protocol fee.
// tslint:disable-next-line:no-unused-expression
expect(filterLogsToArguments(zeroFeeTx.logs, StakingEvents.StakingPoolEarnedRewardsInEpoch)).to.be.empty;
// Coincidentally there's some ETH in the ExchangeProxy
const nonZeroFeeTx = await staking
.payProtocolFee(maker, constants.NULL_ADDRESS, new BigNumber(1))
.awaitTransactionSuccessAsync({ from: EXCHANGE_PROXY, gasPrice: 0, value: 1 }, { shouldValidate: false });
// StakingPoolEarnedRewardsInEpoch _should_ be emitted for a non-zero protocol fee.
expect(
filterLogsToArguments(nonZeroFeeTx.logs, StakingEvents.StakingPoolEarnedRewardsInEpoch),
).to.have.lengthOf(1);
});
});
// tslint:enable:no-unnecessary-type-assertion

View File

@@ -31,6 +31,7 @@ export * from '../test/generated-wrappers/mixin_staking_pool';
export * from '../test/generated-wrappers/mixin_staking_pool_rewards';
export * from '../test/generated-wrappers/mixin_storage';
export * from '../test/generated-wrappers/staking';
export * from '../test/generated-wrappers/staking_patch';
export * from '../test/generated-wrappers/staking_proxy';
export * from '../test/generated-wrappers/test_assert_storage_params';
export * from '../test/generated-wrappers/test_cobb_douglas';

View File

@@ -40,6 +40,7 @@
"test/generated-artifacts/MixinStakingPoolRewards.json",
"test/generated-artifacts/MixinStorage.json",
"test/generated-artifacts/Staking.json",
"test/generated-artifacts/StakingPatch.json",
"test/generated-artifacts/StakingProxy.json",
"test/generated-artifacts/TestAssertStorageParams.json",
"test/generated-artifacts/TestCobbDouglas.json",

View File

@@ -1,4 +1,14 @@
[
{
"version": "5.4.0",
"changes": [
{
"note": "Set default ganache gas limit to 100e6",
"pr": 197
}
],
"timestamp": 1620214333
},
{
"timestamp": 1619596077,
"version": "5.3.25",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.0 - _May 5, 2021_
* Set default ganache gas limit to 100e6 (#197)
## v5.3.25 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.3.25",
"version": "5.4.0",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
"devDependencies": {
"@0x/sol-compiler": "^4.7.2",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -42,20 +42,20 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/assert": "^3.0.26",
"@0x/base-contract": "^6.3.2",
"@0x/contract-addresses": "^6.0.0",
"@0x/dev-utils": "^4.2.6",
"@0x/json-schemas": "^6.1.2",
"@0x/order-utils": "^10.4.20",
"@0x/sol-coverage": "^4.0.36",
"@0x/sol-profiler": "^4.1.26",
"@0x/sol-trace": "^3.0.36",
"@0x/subproviders": "^6.5.2",
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.1.0",
"@0x/dev-utils": "^4.2.7",
"@0x/json-schemas": "^6.1.3",
"@0x/order-utils": "^10.4.21",
"@0x/sol-coverage": "^4.0.37",
"@0x/sol-profiler": "^4.1.27",
"@0x/sol-trace": "^3.0.37",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",

View File

@@ -20,6 +20,7 @@ export let providerConfigs: Web3Config = {
shouldUseInProcessGanache: true,
shouldAllowUnlimitedContractSize: true,
hardfork: 'istanbul',
gasLimit: 100e6,
unlocked_accounts: [
'0x6cc5f688a315f3dc28a7781717a9a798a59fda7b',
'0x55dc8f21d20d4c6ed3c82916a438a413ca68e335',

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "1.1.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.1.5",
"changes": [

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.6 - _May 5, 2021_
* Dependencies updated
## v1.1.5 - _April 30, 2021_
* Patched votingPower logic (#214)

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.1.5",
"version": "1.1.6",
"engines": {
"node": ">=6.12"
},
@@ -46,14 +46,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contract-addresses": "^6.0.0",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-staking": "^2.0.36",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.1.0",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-staking": "^2.0.37",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -72,13 +72,13 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/protocol-utils": "^1.5.1",
"@0x/subproviders": "^6.5.2",
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.6.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"ethereumjs-util": "^7.0.10"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1620214333,
"version": "4.7.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1619596077,
"version": "4.7.7",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.7.8 - _May 5, 2021_
* Dependencies updated
## v4.7.7 - _April 28, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.7.7",
"version": "4.7.8",
"engines": {
"node": ">=6.12"
},
@@ -50,15 +50,15 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.5.2",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",
"@0x/web3-wrapper": "^7.5.2",
"@0x/web3-wrapper": "^7.5.3",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -79,9 +79,9 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/base-contract": "^6.4.0",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/utils": "^6.4.3",
"bn.js": "^4.11.8",
"ethereum-types": "^3.5.0"
},

View File

@@ -1,4 +1,18 @@
[
{
"version": "0.23.0",
"changes": [
{
"note": "Added ETH support to `MixinCurve`",
"pr": 220
},
{
"note": "Add Balancer V2 integration",
"pr": 206
}
],
"timestamp": 1620214333
},
{
"timestamp": 1619830995,
"version": "0.22.3",

View File

@@ -5,6 +5,11 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.23.0 - _May 5, 2021_
* Added ETH support to `MixinCurve` (#220)
* Add Balancer V2 integration (#206)
## v0.22.3 - _May 1, 2021_
* Dependencies updated

View File

@@ -23,6 +23,7 @@ pragma experimental ABIEncoderV2;
import "./IBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCurve.sol";
@@ -38,11 +39,13 @@ 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 BridgeAdapter is
IBridgeAdapter,
MixinBalancer,
MixinBalancerV2,
MixinBancor,
MixinCoFiX,
MixinCurve,
@@ -58,14 +61,16 @@ contract BridgeAdapter is
MixinShell,
MixinUniswap,
MixinUniswapV2,
MixinUniswapV3,
MixinZeroExBridge
{
constructor(IEtherTokenV06 weth)
public
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinCoFiX()
MixinCurve()
MixinCurve(weth)
MixinCryptoCom()
MixinDodo()
MixinDodoV2()
@@ -78,6 +83,7 @@ contract BridgeAdapter is
MixinShell()
MixinUniswap(weth)
MixinUniswapV2()
MixinUniswapV3()
MixinZeroExBridge()
{}
@@ -99,6 +105,12 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
boughtAmount = _tradeUniswapV3(
sellToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.UNISWAPV2) {
boughtAmount = _tradeUniswapV2(
buyToken,
@@ -119,6 +131,13 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.BALANCERV2) {
boughtAmount = _tradeBalancerV2(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.KYBER) {
boughtAmount = _tradeKyber(
sellToken,

View File

@@ -44,4 +44,6 @@ library BridgeProtocols {
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;
}

View File

@@ -0,0 +1,117 @@
// 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 IBalancerV2Vault {
enum SwapKind { GIVEN_IN, GIVEN_OUT }
/**
* @dev Performs a swap with a single Pool.
*
* If the swap is given in (the number of tokens to send to the Pool is known), returns the amount of tokens
* taken from the Pool, which must be greater than or equal to `limit`.
*
* If the swap is given out (the number of tokens to take from the Pool is known), returns the amount of
* tokens sent to the Pool, which must be less than or equal to `limit`.
*
* Internal Balance usage and the recipient are determined by the `funds` struct.
*
* Emits a `Swap` event.
* For full documentation see https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
*/
function swap(
SingleSwap calldata request,
FundManagement calldata funds,
uint256 limit,
uint256 deadline
) external payable returns (uint256);
struct SingleSwap {
bytes32 poolId;
SwapKind kind;
IERC20TokenV06 assetIn;
IERC20TokenV06 assetOut;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
}
contract MixinBalancerV2 {
using LibERC20TokenV06 for IERC20TokenV06;
struct BalancerV2BridgeData {
IBalancerV2Vault vault;
bytes32 poolId;
}
function _tradeBalancerV2(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data.
BalancerV2BridgeData memory data = abi.decode(bridgeData, (BalancerV2BridgeData));
// Grant an allowance to the exchange to spend `fromTokenAddress` token.
sellToken.approveIfBelow(address(data.vault), sellAmount);
// Sell the entire sellAmount
IBalancerV2Vault.SingleSwap memory request = IBalancerV2Vault.SingleSwap({
poolId: data.poolId,
kind: IBalancerV2Vault.SwapKind.GIVEN_IN,
assetIn: sellToken,
assetOut: buyToken,
amount: sellAmount, // amount in
userData: ""
});
IBalancerV2Vault.FundManagement memory funds = IBalancerV2Vault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
boughtAmount = data.vault.swap(
request,
funds,
1, // min amount out
block.timestamp // expires after this block
);
return boughtAmount;
}
}

View File

@@ -21,6 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
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";
@@ -31,6 +32,15 @@ contract MixinCurve {
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
struct CurveBridgeData {
address curveAddress;
@@ -50,10 +60,17 @@ contract MixinCurve {
{
// Decode the bridge data to get the Curve metadata.
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
sellToken.approveIfBelow(data.curveAddress, sellAmount);
uint256 payableAmount;
if (sellToken == WETH) {
payableAmount = sellAmount;
WETH.withdraw(sellAmount);
} else {
sellToken.approveIfBelow(data.curveAddress, sellAmount);
}
uint256 beforeBalance = buyToken.balanceOf(address(this));
(bool success, bytes memory resultData) =
data.curveAddress.call(abi.encodeWithSelector(
data.curveAddress.call{value: payableAmount}(abi.encodeWithSelector(
data.exchangeFunctionSelector,
data.fromCoinIdx,
data.toCoinIdx,
@@ -65,6 +82,12 @@ contract MixinCurve {
if (!success) {
resultData.rrevert();
}
if (buyToken == WETH) {
boughtAmount = address(this).balance;
WETH.deposit{ value: boughtAmount }();
}
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
}
}

View File

@@ -0,0 +1,70 @@
// 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.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 IUniswapV3Router {
struct ExactInputParams {
bytes path;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
}
function exactInput(ExactInputParams memory params)
external
payable
returns (uint256 amountOut);
}
contract MixinUniswapV3 {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeUniswapV3(
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(IUniswapV3Router router, bytes memory path) =
abi.decode(bridgeData, (IUniswapV3Router, bytes));
// Grant the Uniswap router an allowance to sell the sell token.
sellToken.approveIfBelow(address(router), sellAmount);
boughtAmount = router.exactInput(IUniswapV3Router.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp,
amountIn: sellAmount,
amountOutMinimum: 1
}));
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.22.3",
"version": "0.23.0",
"engines": {
"node": ">=6.12"
},
@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|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.5.2",
"@0x/contract-addresses": "^6.0.0",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/dev-utils": "^4.2.6",
"@0x/order-utils": "^10.4.20",
"@0x/sol-compiler": "^4.7.2",
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.1.0",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.21",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@types/isomorphic-fetch": "^0.0.35",
@@ -82,13 +82,13 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/base-contract": "^6.3.2",
"@0x/protocol-utils": "^1.5.1",
"@0x/subproviders": "^6.5.2",
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.6.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"ethereum-types": "^3.5.0",
"ethereumjs-util": "^7.0.10"
},

View File

@@ -76,6 +76,7 @@ import * as LiquidityProviderSandbox from '../test/generated-artifacts/Liquidity
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
@@ -91,6 +92,7 @@ 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 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';
@@ -235,6 +237,7 @@ export const artifacts = {
BridgeProtocols: BridgeProtocols as ContractArtifact,
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
@@ -250,6 +253,7 @@ export const artifacts = {
MixinShell: MixinShell as ContractArtifact,
MixinUniswap: MixinUniswap as ContractArtifact,
MixinUniswapV2: MixinUniswapV2 as ContractArtifact,
MixinUniswapV3: MixinUniswapV3 as ContractArtifact,
MixinZeroExBridge: MixinZeroExBridge as ContractArtifact,
ILiquidityProvider: ILiquidityProvider as ContractArtifact,
IMooniswapPool: IMooniswapPool as ContractArtifact,

View File

@@ -74,6 +74,7 @@ export * from '../test/generated-wrappers/liquidity_provider_sandbox';
export * from '../test/generated-wrappers/log_metadata_transformer';
export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_crypto_com';
@@ -89,6 +90,7 @@ 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_zero_ex_bridge';
export * from '../test/generated-wrappers/mooniswap_liquidity_provider';
export * from '../test/generated-wrappers/multiplex_feature';

View File

@@ -105,6 +105,7 @@
"test/generated-artifacts/LogMetadataTransformer.json",
"test/generated-artifacts/MetaTransactionsFeature.json",
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCryptoCom.json",
@@ -120,6 +121,7 @@
"test/generated-artifacts/MixinShell.json",
"test/generated-artifacts/MixinUniswap.json",
"test/generated-artifacts/MixinUniswapV2.json",
"test/generated-artifacts/MixinUniswapV3.json",
"test/generated-artifacts/MixinZeroExBridge.json",
"test/generated-artifacts/MooniswapLiquidityProvider.json",
"test/generated-artifacts/MultiplexFeature.json",

View File

@@ -10,7 +10,7 @@
],
"scripts": {
"deps_versions:ci": "node ./node_modules/@0x/monorepo-scripts/lib/deps_versions.js",
"fix": "wsrun fix $PKG --fast-exit --parallel --exclude-missing",
"fix": "wsrun --fast-exit --parallel --exclude-missing -c fix $PKG",
"ganache": "ganache-cli -p 8545 --gasLimit 10000000 --networkId 50 -m \"${npm_package_config_mnemonic}\"",
"prettier": "prettier --write '**/*.{ts,tsx,json,md}' --config .prettierrc",
"prettier:ci": "prettier --list-different '**/*.{ts,tsx,json,md}' --config .prettierrc",
@@ -27,31 +27,25 @@
"install:all": "yarn install",
"wsrun": "wsrun",
"lerna": "lerna",
"build": "lerna link && wsrun build $PKG -r --stages --fast-exit --exclude-missing",
"build:ci": "lerna link && wsrun build:ci $PKG --fast-exit -r --stages --exclude-missing",
"build:contracts": "lerna link && wsrun build -p ${npm_package_config_contractsPackages} -c --fast-exit -r --stages --exclude-missing",
"build": "lerna link && wsrun -r --stages --fast-exit --exclude-missing -c build $PKG",
"build:ci": "lerna link && wsrun --fast-exit -r --stages --exclude-missing -c build:ci $PKG",
"build:contracts": "lerna link && wsrun -p ${npm_package_config_contractsPackages} --fast-exit -r --stages --exclude-missing -c build",
"build:ts": "tsc -b",
"watch:ts": "tsc -b -w",
"clean": "wsrun clean $PKG --fast-exit -r --parallel --exclude-missing",
"clean:contracts": "wsrun clean -p ${npm_package_config_contractsPackages} -c --fast-exit -r --parallel --exclude-missing",
"contracts:gen": "wsrun contracts:gen $PKG --fast-exit -r --parallel --exclude-missing",
"contracts:compile": "wsrun compile -p ${npm_package_config_contractsPackages} --fast-exit -r --stages --exclude-missing",
"contracts:compile:truffle": "wsrun compile:truffle -p ${npm_package_config_contractsPackages} --fast-exit -r --stages --exclude-missing",
"contracts:watch": "wsrun watch $PKG --parallel --exclude-missing",
"clean": "wsrun --fast-exit -r --parallel --exclude-missing clean -c $PKG",
"contracts:watch": "wsrun --parallel --exclude-missing watch -c $PKG",
"remove_node_modules": "lerna clean --yes; rm -rf node_modules",
"rebuild": "run-s clean build",
"test": "wsrun test $(echo $(echo ${npm_package_config_contractsPackages} ${npm_package_config_nonContractPackages} | tr ' ' '\n' | sort | uniq) ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') --fast-exit --serial --exclude-missing",
"test:all": "wsrun test $PKG --fast-exit --serial --exclude-missing",
"test:contracts": "wsrun test --serial -p $(echo ${npm_package_config_contractsPackages} ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') -c --fast-exit --exclude-missing",
"test:contracts:all": "wsrun test --serial -p ${npm_package_config_contractsPackages} -c --fast-exit --exclude-missing",
"test": "wsrun --fast-exit --serial --exclude-missing -p $(echo $(echo ${npm_package_config_contractsPackages} ${npm_package_config_nonContractPackages} | tr ' ' '\n' | sort | uniq) ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') -c test",
"test:all": "wsrun --fast-exit --serial --exclude-missing -c test $PKG",
"test:contracts": "wsrun --serial -p $(echo ${npm_package_config_contractsPackages} ${npm_package_config_ignoreTestsForPackages} | tr ' ' '\n' | sort | uniq -u | tr '\n' ' ') --fast-exit --exclude-missing -c test",
"test:contracts:all": "wsrun --serial -p ${npm_package_config_contractsPackages} --fast-exit --exclude-missing -c test",
"generate_doc": "node ./node_modules/@0x/monorepo-scripts/lib/doc_generate.js --config ./doc-gen-config.json",
"upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun s3:sync_md_docs --exclude-missing",
"diff_md_docs:ci": "wsrun diff_docs --exclude-missing",
"upload_md_docs": "aws s3 rm --recursive s3://docs-markdown; wsrun --exclude-missing -c s3:sync_md_docs",
"diff_md_docs:ci": "wsrun --exclude-missing -c diff_docs",
"test:generate_docs:circleci": "for i in ${npm_package_config_packagesWithDocPages}; do yarn generate_doc --package $i || break -1; done;",
"bundlewatch": "bundlewatch",
"lint": "wsrun lint $PKG --fast-exit --parallel --exclude-missing",
"lint:stages": "wsrun lint $PKG --fast-exit --stages --exclude-missing",
"lint:contracts": "wsrun lint -p ${npm_package_config_contractsPackages} -c --fast-exit --stages --exclude-missing",
"lint": "wsrun --fast-exit --parallel --exclude-missing -c lint $PKG",
"upgrade_org_deps": "node node_modules/@0x/monorepo-scripts/lib/upgrade_deps.js -p '@0x|ethereum-types'",
"upgrade_deps": "node node_modules/@0x/monorepo-scripts/lib/upgrade_deps.js",
"verdaccio": "docker run --rm -i -p 4873:4873 0xorg/verdaccio"
@@ -66,7 +60,7 @@
"ignoreDependencyVersionsForPackage": "contract-wrappers"
},
"devDependencies": {
"@0x/monorepo-scripts": "^3.1.6",
"@0x/monorepo-scripts": "^3.1.7",
"@0x-lerna-fork/lerna": "3.16.10",
"@0xproject/npm-cli-login": "^0.0.11",
"async-child-process": "^1.1.1",
@@ -78,7 +72,7 @@
"prettier": "~1.16.3",
"source-map-support": "^0.5.6",
"typescript": "4.2.2",
"wsrun": "^2.2.0"
"wsrun": "^5.2.4"
},
"resolutions": {
"merkle-patricia-tree": "^2.3.2"

View File

@@ -1,4 +1,46 @@
[
{
"version": "6.10.0",
"changes": [
{
"note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC",
"pr": 222
},
{
"note": "Add LUSD Curve pool",
"pr": 218
},
{
"note": "Fix exchangeProxyGasOverhead for fallback path",
"pr": 215
},
{
"note": "Enable ETH based Curve pools",
"pr": 220
},
{
"note": "Reactivate PancakeSwapV2 and BakerySwap VIP on BSC",
"pr": 222
},
{
"note": "Disable WETH based SnowSwap pools",
"pr": 220
},
{
"note": "PLP now includes a fallback due to observed collisions",
"pr": 223
},
{
"note": "Add Balancer V2 integration",
"pr": 206
},
{
"note": "Re-work the PoolCache for Balancer et al",
"pr": 226
}
],
"timestamp": 1620214333
},
{
"version": "6.9.1",
"changes": [

View File

@@ -5,6 +5,18 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.10.0 - _May 5, 2021_
* Reactivate PancakeSwapV2 and BakerySwap VIP on BSC (#222)
* Add LUSD Curve pool (#218)
* Fix exchangeProxyGasOverhead for fallback path (#215)
* Enable ETH based Curve pools (#220)
* Reactivate PancakeSwapV2 and BakerySwap VIP on BSC (#222)
* Disable WETH based SnowSwap pools (#220)
* PLP now includes a fallback due to observed collisions (#223)
* Add Balancer V2 integration (#206)
* Re-work the PoolCache for Balancer et al (#226)
## v6.9.1 - _May 1, 2021_
* Temporarily remove PancakeV2 and BakerySwap from VIP

View File

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

View File

@@ -0,0 +1,189 @@
// 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 "./SamplerUtils.sol";
/// @dev Minimal Balancer V2 Vault interface
/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
interface IBalancerV2Vault {
enum SwapKind { GIVEN_IN, GIVEN_OUT }
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] calldata swaps,
IAsset[] calldata assets,
FundManagement calldata funds
) external returns (int256[] memory assetDeltas);
}
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
contract BalancerV2Sampler is SamplerUtils {
struct BalancerV2PoolInfo {
bytes32 poolId;
address vault;
}
/// @dev Sample sell quotes from Balancer V2.
/// @param poolInfo Struct with pool related data
/// @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 makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBalancerV2(
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
IBalancerV2Vault.FundManagement memory swapFunds =
_createSwapFunds();
for (uint256 i = 0; i < numSamples; i++) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
_createSwapSteps(poolInfo, 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
int256 amountOutFromPool = amounts[1] * -1;
if (amountOutFromPool <= 0) {
break;
}
makerTokenAmounts[i] = uint256(amountOutFromPool);
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from Balancer V2.
/// @param poolInfo Struct with pool related data
/// @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 takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBalancerV2(
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
IBalancerV2Vault.FundManagement memory swapFunds =
_createSwapFunds();
for (uint256 i = 0; i < numSamples; i++) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
_createSwapSteps(poolInfo, makerTokenAmounts[i]);
try
// For buys we specify the makerToken which is what taker will receive from the trade
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds)
returns (int256[] memory amounts) {
int256 amountIntoPool = amounts[0];
if (amountIntoPool <= 0) {
break;
}
takerTokenAmounts[i] = uint256(amountIntoPool);
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
function _createSwapSteps(
BalancerV2PoolInfo memory poolInfo,
uint256 amount
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
new IBalancerV2Vault.BatchSwapStep[](1);
swapSteps[0] = IBalancerV2Vault.BatchSwapStep({
poolId: poolInfo.poolId,
assetInIndex: 0,
assetOutIndex: 1,
amount: amount,
userData: ""
});
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,16 +22,15 @@ pragma experimental ABIEncoderV2;
import "./interfaces/IBancor.sol";
contract DeploymentConstants {}
contract CompilerHack {}
contract BancorSampler is DeploymentConstants
{
contract BancorSampler is CompilerHack {
/// @dev Base gas limit for Bancor calls.
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
struct BancorSamplerOpts {
address registry;
IBancorRegistry registry;
address[][] paths;
}
@@ -112,7 +111,7 @@ contract BancorSampler is DeploymentConstants
view
returns (address bancorNetwork, address[] memory path)
{
bancorNetwork = _getBancorNetwork(opts.registry);
bancorNetwork = opts.registry.getAddress(opts.registry.BANCOR_NETWORK());
if (opts.paths.length == 0) {
return (bancorNetwork, path);
}
@@ -140,13 +139,4 @@ contract BancorSampler is DeploymentConstants
}
}
}
function _getBancorNetwork(address registry)
private
view
returns (address)
{
IBancorRegistry registry = IBancorRegistry(registry);
return registry.getAddress(registry.BANCOR_NETWORK());
}
}

View File

@@ -21,6 +21,7 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol";
import "./BancorSampler.sol";
import "./CurveSampler.sol";
import "./DODOSampler.sol";
@@ -38,11 +39,13 @@ import "./SmoothySampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
import "./UniswapV3Sampler.sol";
import "./UtilitySampler.sol";
contract ERC20BridgeSampler is
BalancerSampler,
BalancerV2Sampler,
BancorSampler,
CurveSampler,
DODOSampler,
@@ -60,6 +63,7 @@ contract ERC20BridgeSampler is
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,
UniswapV3Sampler,
UtilitySampler
{
@@ -73,7 +77,6 @@ contract ERC20BridgeSampler is
/// @return callResults ABI-encoded results data for each call.
function batchCall(bytes[] calldata callDatas)
external
view
returns (CallResults[] memory callResults)
{
callResults = new CallResults[](callDatas.length);
@@ -82,7 +85,7 @@ contract ERC20BridgeSampler is
if (callDatas[i].length == 0) {
continue;
}
(callResults[i].success, callResults[i].data) = address(this).staticcall(callDatas[i]);
(callResults[i].success, callResults[i].data) = address(this).call(callDatas[i]);
}
}
}

View File

@@ -0,0 +1,313 @@
// 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 "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface IUniswapV3Quoter {
function factory()
external
view
returns (IUniswapV3Factory factory);
function quoteExactInput(bytes memory path, uint256 amountIn)
external
returns (uint256 amountOut);
function quoteExactOutput(bytes memory path, uint256 amountOut)
external
returns (uint256 amountIn);
}
interface IUniswapV3Factory {
function getPool(IERC20TokenV06 a, IERC20TokenV06 b, uint24 fee)
external
view
returns (IUniswapV3Pool pool);
}
interface IUniswapV3Pool {
function token0() external view returns (IERC20TokenV06);
function token1() external view returns (IERC20TokenV06);
function fee() external view returns (uint24);
}
contract UniswapV3Sampler
{
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
uint256 constant private QUOTE_GAS = 300e3;
/// @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 makerTokenAmounts Maker amounts bought at each taker token
/// amount.
/// @return uniswapPaths The encoded uniswap path for each sample.
function sampleSellsFromUniswapV3(
IUniswapV3Quoter quoter,
IERC20TokenV06[] memory path,
uint256[] memory takerTokenAmounts
)
public
returns (
uint256[] memory makerTokenAmounts,
bytes[] memory uniswapPaths
)
{
IUniswapV3Pool[][] memory poolPaths =
_getValidPoolPaths(quoter.factory(), path, 0);
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
uniswapPaths = new bytes[](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)
{
if (topBuyAmount <= buyAmount) {
topBuyAmount = buyAmount;
topUniswapPath = uniswapPath;
}
} catch { }
}
// Break early if we can't complete the buys.
if (topBuyAmount == 0) {
break;
}
makerTokenAmounts[i] = topBuyAmount;
uniswapPaths[i] = topUniswapPath;
}
}
/// @dev Sample buy quotes from UniswapV3.
/// @param quoter UniswapV3 Quoter contract.
/// @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.
/// @return uniswapPaths The encoded uniswap path for each sample.
function sampleBuysFromUniswapV3(
IUniswapV3Quoter quoter,
IERC20TokenV06[] memory path,
uint256[] memory makerTokenAmounts
)
public
returns (
uint256[] memory takerTokenAmounts,
bytes[] memory uniswapPaths
)
{
IUniswapV3Pool[][] memory poolPaths =
_getValidPoolPaths(quoter.factory(), path, 0);
IERC20TokenV06[] memory reversedPath = _reverseTokenPath(path);
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
uniswapPaths = new bytes[](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.
bytes memory uniswapPath = _toUniswapPath(
reversedPath,
_reversePoolPath(poolPaths[j])
);
try
quoter.quoteExactOutput
{ gas: QUOTE_GAS }
(uniswapPath, makerTokenAmounts[i])
returns (uint256 sellAmount)
{
if (topSellAmount == 0 || topSellAmount >= sellAmount) {
topSellAmount = sellAmount;
// But the output path should still be encoded for sells.
topUniswapPath = _toUniswapPath(path, poolPaths[j]);
}
} catch {}
}
// Break early if we can't complete the buys.
if (topSellAmount == 0) {
break;
}
takerTokenAmounts[i] = topSellAmount;
uniswapPaths[i] = topUniswapPath;
}
}
function _getValidPoolPaths(
IUniswapV3Factory factory,
IERC20TokenV06[] memory tokenPath,
uint256 startIndex
)
private
view
returns (IUniswapV3Pool[][] memory poolPaths)
{
require(
tokenPath.length - startIndex >= 2,
"UniswapV3Sampler/tokenPath too short"
);
uint24[3] memory validPoolFees = [
// The launch pool fees. Could get hairier if they add more.
uint24(0.0005e6),
uint24(0.003e6),
uint24(0.01e6)
];
IUniswapV3Pool[] memory validPools =
new IUniswapV3Pool[](validPoolFees.length);
uint256 numValidPools = 0;
{
IERC20TokenV06 inputToken = tokenPath[startIndex];
IERC20TokenV06 outputToken = tokenPath[startIndex + 1];
for (uint256 i = 0; i < validPoolFees.length; ++i) {
IUniswapV3Pool pool =
factory.getPool(inputToken, outputToken, validPoolFees[i]);
if (_isValidPool(pool)) {
validPools[numValidPools++] = pool;
}
}
}
if (numValidPools == 0) {
// No valid pools for this hop.
return poolPaths;
}
if (startIndex + 2 == tokenPath.length) {
// End of path.
poolPaths = new IUniswapV3Pool[][](numValidPools);
for (uint256 i = 0; i < numValidPools; ++i) {
poolPaths[i] = new IUniswapV3Pool[](1);
poolPaths[i][0] = validPools[i];
}
return poolPaths;
}
// Get paths for subsequent hops.
IUniswapV3Pool[][] memory subsequentPoolPaths =
_getValidPoolPaths(factory, tokenPath, startIndex + 1);
if (subsequentPoolPaths.length == 0) {
// Could not complete the path.
return poolPaths;
}
// Combine our pools with the next hop paths.
poolPaths = new IUniswapV3Pool[][](
numValidPools * subsequentPoolPaths.length
);
for (uint256 i = 0; i < numValidPools; ++i) {
for (uint256 j = 0; j < subsequentPoolPaths.length; ++j) {
uint256 o = i * subsequentPoolPaths.length + j;
// Prepend pool to the subsequent path.
poolPaths[o] =
new IUniswapV3Pool[](1 + subsequentPoolPaths[j].length);
poolPaths[o][0] = validPools[i];
for (uint256 k = 0; k < subsequentPoolPaths[j].length; ++k) {
poolPaths[o][1 + k] = subsequentPoolPaths[j][k];
}
}
}
return poolPaths;
}
function _reverseTokenPath(IERC20TokenV06[] memory tokenPath)
private
returns (IERC20TokenV06[] memory reversed)
{
reversed = new IERC20TokenV06[](tokenPath.length);
for (uint256 i = 0; i < tokenPath.length; ++i) {
reversed[i] = tokenPath[tokenPath.length - i - 1];
}
}
function _reversePoolPath(IUniswapV3Pool[] memory poolPath)
private
returns (IUniswapV3Pool[] memory reversed)
{
reversed = new IUniswapV3Pool[](poolPath.length);
for (uint256 i = 0; i < poolPath.length; ++i) {
reversed[i] = poolPath[poolPath.length - i - 1];
}
}
function _isValidPool(IUniswapV3Pool pool)
private
view
returns (bool isValid)
{
// Check if it has been deployed.
{
uint256 codeSize;
assembly {
codeSize := extcodesize(pool)
}
if (codeSize == 0) {
return false;
}
}
// Must have a balance of both tokens.
if (pool.token0().balanceOf(address(pool)) == 0) {
return false;
}
if (pool.token1().balanceOf(address(pool)) == 0) {
return false;
}
return true;
}
function _toUniswapPath(
IERC20TokenV06[] memory tokenPath,
IUniswapV3Pool[] memory poolPath
)
private
view
returns (bytes memory uniswapPath)
{
require(
tokenPath.length >= 2 && tokenPath.length == poolPath.length + 1,
"UniswapV3Sampler/invalid path lengths"
);
// Uniswap paths are tightly packed as:
// [token0, token0token1PairFee, token1, token1Token2PairFee, token2, ...]
uniswapPath = new bytes(tokenPath.length * 20 + poolPath.length * 3);
uint256 o;
assembly { o := add(uniswapPath, 32) }
for (uint256 i = 0; i < tokenPath.length; ++i) {
if (i > 0) {
uint24 poolFee = poolPath[i - 1].fee();
assembly {
mstore(o, shl(232, poolFee))
o := add(o, 3)
}
}
IERC20TokenV06 token = tokenPath[i];
assembly {
mstore(o, shl(96, token))
o := add(o, 20)
}
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "6.9.1",
"version": "6.10.0",
"engines": {
"node": ">=6.12"
},
@@ -38,7 +38,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}
@@ -57,20 +57,20 @@
"registry": "git@github.com:0xProject/gitpkg-registry.git"
},
"dependencies": {
"@0x/assert": "^3.0.26",
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.0.0",
"@0x/contract-wrappers": "^13.16.0",
"@0x/contracts-erc20": "^3.3.7",
"@0x/contracts-zero-ex": "^0.22.3",
"@0x/dev-utils": "^4.2.6",
"@0x/json-schemas": "^6.1.2",
"@0x/protocol-utils": "^1.5.1",
"@0x/contract-addresses": "^6.1.0",
"@0x/contract-wrappers": "^13.16.1",
"@0x/contracts-erc20": "^3.3.8",
"@0x/contracts-zero-ex": "^0.23.0",
"@0x/dev-utils": "^4.2.7",
"@0x/json-schemas": "^6.1.3",
"@0x/protocol-utils": "^1.6.0",
"@0x/quote-server": "^5.0.0",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.2",
"@0x/web3-wrapper": "^7.5.2",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"@balancer-labs/sor": "0.3.2",
"@bancor/sdk": "0.2.9",
"@ethersproject/abi": "^5.0.1",
@@ -85,21 +85,23 @@
"ethereum-types": "^3.5.0",
"ethereumjs-util": "^7.0.10",
"fast-abi": "^0.0.2",
"graphql": "^15.4.0",
"graphql-request": "^3.4.0",
"heartbeats": "^5.0.1",
"lodash": "^4.17.11"
},
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.10",
"@0x/contracts-exchange": "^3.2.29",
"@0x/contracts-exchange-libs": "^4.3.28",
"@0x/contracts-gen": "^2.0.37",
"@0x/contracts-test-utils": "^5.3.25",
"@0x/contracts-utils": "^4.7.7",
"@0x/contracts-asset-proxy": "^3.7.11",
"@0x/contracts-exchange": "^3.2.30",
"@0x/contracts-exchange-libs": "^4.3.29",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.0",
"@0x/contracts-utils": "^4.7.8",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.0.5",
"@0x/sol-compiler": "^4.7.2",
"@0x/subproviders": "^6.5.2",
"@0x/migrations": "^8.0.6",
"@0x/sol-compiler": "^4.7.3",
"@0x/subproviders": "^6.5.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",

View File

@@ -115,6 +115,7 @@ export {
SOURCE_FLAGS,
BUY_SOURCE_FILTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
NATIVE_FEE_TOKEN_BY_CHAIN_ID,
} from './utils/market_operation_utils/constants';
export {
Parameters,

View File

@@ -13,8 +13,7 @@ import {
FillQuoteTransformerSide,
findTransformerNonce,
} from '@0x/protocol-utils';
import { BigNumber, providerUtils } from '@0x/utils';
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../constants';
@@ -77,14 +76,14 @@ const PANCAKE_SWAP_FORKS = [
ERC20BridgeSource.CheeseSwap,
ERC20BridgeSource.JulSwap,
];
const DUMMY_WETH_CONTRACT = new WETH9Contract(NULL_ADDRESS, {
const FAKE_PROVIDER: any = {
sendAsync(): void {
return;
},
} as any);
};
const DUMMY_WETH_CONTRACT = new WETH9Contract(NULL_ADDRESS, FAKE_PROVIDER);
export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly provider: ZeroExProvider;
public readonly chainId: ChainId;
public readonly transformerNonces: {
wethTransformer: number;
@@ -97,19 +96,13 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
private readonly _exchangeProxy: IZeroExContract;
private readonly _multiplex: MultiplexFeatureContract;
constructor(
supportedProvider: SupportedProvider,
public readonly contractAddresses: ContractAddresses,
options: Partial<SwapQuoteConsumerOpts> = {},
) {
constructor(public readonly contractAddresses: ContractAddresses, options: Partial<SwapQuoteConsumerOpts> = {}) {
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
assert.isNumber('chainId', chainId);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
this.provider = provider;
this.chainId = chainId;
this.contractAddresses = contractAddresses;
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, supportedProvider);
this._multiplex = new MultiplexFeatureContract(contractAddresses.exchangeProxy, supportedProvider);
this._exchangeProxy = new IZeroExContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
this._multiplex = new MultiplexFeatureContract(contractAddresses.exchangeProxy, FAKE_PROVIDER);
this.transformerNonces = {
wethTransformer: findTransformerNonce(
contractAddresses.transformers.wethTransformer,
@@ -197,9 +190,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
this.chainId === ChainId.BSC &&
isDirectSwapCompatible(quote, optsWithDefaults, [
ERC20BridgeSource.PancakeSwap,
// Temporarily removed until latest PancakeSwap VIP has been deployed
// ERC20BridgeSource.PancakeSwapV2,
// ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.PancakeSwapV2,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.ApeSwap,
ERC20BridgeSource.CafeSwap,
@@ -260,7 +252,11 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (
this.chainId === ChainId.Mainnet &&
isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])
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].fills[0].fillData as CurveFillData;
return {

View File

@@ -1,6 +1,4 @@
import { ContractAddresses, getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
import { providerUtils } from '@0x/utils';
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { constants } from '../constants';
@@ -17,32 +15,22 @@ import { assert } from '../utils/assert';
import { ExchangeProxySwapQuoteConsumer } from './exchange_proxy_swap_quote_consumer';
export class SwapQuoteConsumer implements SwapQuoteConsumerBase {
public readonly provider: ZeroExProvider;
public readonly chainId: number;
private readonly _contractAddresses: ContractAddresses;
private readonly _exchangeProxyConsumer: ExchangeProxySwapQuoteConsumer;
public static getSwapQuoteConsumer(
supportedProvider: SupportedProvider,
options: Partial<SwapQuoteConsumerOpts> = {},
): SwapQuoteConsumer {
return new SwapQuoteConsumer(supportedProvider, options);
public static getSwapQuoteConsumer(options: Partial<SwapQuoteConsumerOpts> = {}): SwapQuoteConsumer {
return new SwapQuoteConsumer(options);
}
constructor(supportedProvider: SupportedProvider, options: Partial<SwapQuoteConsumerOpts> = {}) {
constructor(options: Partial<SwapQuoteConsumerOpts> = {}) {
const { chainId } = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
assert.isNumber('chainId', chainId);
const provider = providerUtils.standardizeOrThrow(supportedProvider);
this.provider = provider;
this.chainId = chainId;
this._contractAddresses = options.contractAddresses || getContractAddressesForChainOrThrow(chainId);
this._exchangeProxyConsumer = new ExchangeProxySwapQuoteConsumer(
supportedProvider,
this._contractAddresses,
options,
);
this._exchangeProxyConsumer = new ExchangeProxySwapQuoteConsumer(this._contractAddresses, options);
}
/**

View File

@@ -27,7 +27,7 @@ import {
import { assert } from './utils/assert';
import { MarketOperationUtils } from './utils/market_operation_utils';
import { BancorService } from './utils/market_operation_utils/bancor_service';
import { SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
import { SAMPLER_ADDRESS, SOURCE_FLAGS, ZERO_AMOUNT } from './utils/market_operation_utils/constants';
import { DexOrderSampler } from './utils/market_operation_utils/sampler';
import { SourceFilters } from './utils/market_operation_utils/source_filters';
import {
@@ -93,7 +93,7 @@ export class SwapQuoter {
rfqt,
tokenAdjacencyGraph,
liquidityProviderRegistry,
} = _.merge({}, constants.DEFAULT_SWAP_QUOTER_OPTS, options);
} = { ...constants.DEFAULT_SWAP_QUOTER_OPTS, ...options };
const provider = providerUtils.standardizeOrThrow(supportedProvider);
assert.isValidOrderbook('orderbook', orderbook);
assert.isNumber('chainId', chainId);
@@ -116,7 +116,7 @@ export class SwapQuoter {
const samplerBytecode = _.get(artifacts.ERC20BridgeSampler, 'compilerOutput.evm.deployedBytecode.object');
const defaultCodeOverrides = samplerBytecode
? {
[this._contractAddresses.erc20BridgeSampler]: { code: samplerBytecode },
[SAMPLER_ADDRESS]: { code: samplerBytecode },
}
: {};
const samplerOverrides = _.assign(
@@ -125,7 +125,7 @@ export class SwapQuoter {
);
const fastAbi = new FastABI(ERC20BridgeSamplerContract.ABI() as MethodAbi[]);
const samplerContract = new ERC20BridgeSamplerContract(
this._contractAddresses.erc20BridgeSampler,
SAMPLER_ADDRESS,
this.provider,
{
gas: samplerGasLimit,
@@ -143,8 +143,7 @@ export class SwapQuoter {
this.chainId,
samplerContract,
samplerOverrides,
undefined, // balancer pool cache
undefined, // cream pool cache
undefined, // pools caches for balancer and cream
tokenAdjacencyGraph,
liquidityProviderRegistry,
this.chainId === ChainId.Mainnet // Enable Bancor only on Mainnet

View File

@@ -29,6 +29,7 @@ import {
SUSHISWAP_ROUTER_BY_CHAIN_ID,
SWERVE_MAINNET_INFOS,
UNISWAPV2_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
} from './constants';
import { CurveInfo, ERC20BridgeSource } from './types';
@@ -203,6 +204,19 @@ export function getSaddleInfosForPair(chainId: ChainId, takerToken: string, make
);
}
export function getXSigmaInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId !== ChainId.Mainnet) {
return [];
}
return Object.values(XSIGMA_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaToken === undefined) ||
(c.tokens.includes(t) && c.metaToken !== undefined && [makerToken, takerToken].includes(c.metaToken)),
),
);
}
export function getShellLikeInfosForPair(
chainId: ChainId,
takerToken: string,
@@ -219,6 +233,11 @@ export function getShellLikeInfosForPair(
}
}
export interface CurveDetailedInfo extends CurveInfo {
makerTokenIdx: number;
takerTokenIdx: number;
}
export function getCurveLikeInfosForPair(
chainId: ChainId,
takerToken: string,
@@ -231,28 +250,46 @@ export function getCurveLikeInfosForPair(
| ERC20BridgeSource.Belt
| ERC20BridgeSource.Ellipsis
| ERC20BridgeSource.Smoothy
| ERC20BridgeSource.Saddle,
): CurveInfo[] {
| ERC20BridgeSource.Saddle
| ERC20BridgeSource.XSigma,
): CurveDetailedInfo[] {
let pools: CurveInfo[] = [];
switch (source) {
case ERC20BridgeSource.Curve:
return getCurveInfosForPair(chainId, takerToken, makerToken);
pools = getCurveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Swerve:
return getSwerveInfosForPair(chainId, takerToken, makerToken);
pools = getSwerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.SnowSwap:
return getSnowSwapInfosForPair(chainId, takerToken, makerToken);
pools = getSnowSwapInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Nerve:
return getNerveInfosForPair(chainId, takerToken, makerToken);
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Belt:
return getBeltInfosForPair(chainId, takerToken, makerToken);
pools = getBeltInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Ellipsis:
return getEllipsisInfosForPair(chainId, takerToken, makerToken);
pools = getEllipsisInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Smoothy:
return getSmoothyInfosForPair(chainId, takerToken, makerToken);
pools = getSmoothyInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Saddle:
return getSaddleInfosForPair(chainId, takerToken, makerToken);
pools = getSaddleInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.XSigma:
pools = getXSigmaInfosForPair(chainId, takerToken, makerToken);
break;
default:
throw new Error(`Unknown Curve like source ${source}`);
}
return pools.map(pool => ({
...pool,
makerTokenIdx: pool.tokens.indexOf(makerToken),
takerTokenIdx: pool.tokens.indexOf(takerToken),
}));
}
export function uniswapV2LikeRouterAddress(

View File

@@ -24,6 +24,7 @@ import {
PsmInfo,
TokenAdjacencyGraph,
UniswapV2FillData,
UniswapV3FillData,
} from './types';
// tslint:disable: custom-no-magic-numbers no-bitwise
@@ -39,6 +40,7 @@ export const ONE_HOUR_IN_SECONDS = 60 * 60;
export const ONE_SECOND_MS = 1000;
export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const SAMPLER_ADDRESS = '0x5555555555555555555555555555555555555555';
export const COMPARISON_PRICE_DECIMALS = 10;
function valueByChainId<T>(rest: Partial<{ [key in ChainId]: T }>, defaultValue: T): { [key in ChainId]: T } {
@@ -67,6 +69,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
ERC20BridgeSource.BalancerV2,
ERC20BridgeSource.Bancor,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
@@ -86,6 +89,8 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
ERC20BridgeSource.XSigma,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -93,6 +98,7 @@ export const SELL_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
@@ -133,6 +139,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
ERC20BridgeSource.BalancerV2,
// ERC20BridgeSource.Bancor, // FIXME: Bancor Buys not implemented in Sampler
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
@@ -152,6 +159,8 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
ERC20BridgeSource.XSigma,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Ropsten]: new SourceFilters([
ERC20BridgeSource.Kyber,
@@ -159,6 +168,7 @@ export const BUY_SOURCE_FILTER_BY_CHAIN_ID = valueByChainId<SourceFilters>(
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.Uniswap,
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.UniswapV3,
]),
[ChainId.Rinkeby]: new SourceFilters([ERC20BridgeSource.Native]),
[ChainId.Kovan]: new SourceFilters([ERC20BridgeSource.Native]),
@@ -293,6 +303,7 @@ export const MAINNET_TOKENS = {
STABLEx: '0xcd91538b91b4ba7797d39a2f66e63810b50a33d0',
alUSD: '0xbc6da0fe9ad5f3b0d58160288917aa56653660e9',
FRAX: '0x853d955acef822db058eb8505911ed77f175b99e',
LUSD: '0x5f98805a4e8be255a32880fdec7f6728c6568ba0',
};
export const BSC_TOKENS = {
@@ -329,11 +340,11 @@ export const CURVE_POOLS = {
oBTC: '0xd81da8d904b52208541bade1bd6595d8a251f8dd', // 21.obtc
UST: '0x890f4e345b1daed0367a877a1612f86a1f86985f', // 22.ust
eurs: '0x0ce6a5ff5217e38315f87032cf90686c96627caa', // 23.eurs
// seth: '0xc5424b857f758e906013f3555dad202e4bdb4567', // 24.seth
seth: '0xc5424b857f758e906013f3555dad202e4bdb4567', // 24.seth
aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave
// curve steth: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022' // 26.stETH
steth: '0xdc24316b9ae028f1497c275eb9192a3ea0f67022', // 26.stETH
saave: '0xeb16ae0052ed37f479f7fe63849198df1765a733', // saave
// ankreth: '0xa96a65c051bf88b4095ee1f2451c2a9d43f53ae2', // ankreth
ankreth: '0xa96a65c051bf88b4095ee1f2451c2a9d43f53ae2', // ankreth
USDP: '0x42d7025938bec20b69cbae5a77421082407f053a', // usdp
ib: '0x2dded6da1bf5dbdf597c45fcfaa3194e53ecfeaf', // iron bank
link: '0xf178c0b5bb7e7abf4e12a4838c7b7c5ba2c623c0', // link
@@ -342,6 +353,8 @@ export const CURVE_POOLS = {
STABLEx: '0x3252efd4ea2d6c78091a1f43982ee2c3659cc3d1',
alUSD: '0x43b4fdfd4ff969587185cdb6f0bd875c5fc83f8c',
FRAX: '0xd632f22692fac7611d2aa1c0d552930d43caed3b',
LUSD: '0xed279fdd11ca84beef15af5d39bb4d4bee23f0ca',
BUSD: '0x4807862aa8b2bf68830e4c8dc86d0e9a998e085a',
};
export const SWERVE_POOLS = {
@@ -351,7 +364,12 @@ export const SWERVE_POOLS = {
export const SNOWSWAP_POOLS = {
yUSD: '0xbf7ccd6c446acfcc5df023043f2167b62e81899b',
yVault: '0x4571753311e37ddb44faa8fb78a6df9a6e3c6c0b',
eth: '0x16bea2e63adade5984298d53a4d4d9c09e278192',
// POOL Disabled as it uses WETH over ETH
// There is a conflict with Curve and SnowSwap
// where Curve uses ETH and SnowSwap uses WETH
// To re-enable this we need to flag an WETH
// unwrap or not
// eth: '0x16bea2e63adade5984298d53a4d4d9c09e278192',
};
export const SMOOTHY_POOLS = {
@@ -375,6 +393,10 @@ export const ELLIPSIS_POOLS = {
threePool: '0x160caed03795365f3a589f10c379ffa7d75d4e76',
};
export const XSIGMA_POOLS = {
stable: '0x3333333ACdEdBbC9Ad7bda0876e60714195681c5',
};
export const DEFAULT_INTERMEDIATE_TOKENS_BY_CHAIN_ID = valueByChainId<string[]>(
{
[ChainId.Mainnet]: [
@@ -628,6 +650,34 @@ export const CURVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: CURVE_POOLS.FRAX,
gasSchedule: 387e3,
}),
[CURVE_POOLS.LUSD]: createCurveMetaTriPool({
token: MAINNET_TOKENS.LUSD,
pool: CURVE_POOLS.LUSD,
gasSchedule: 387e3,
}),
[CURVE_POOLS.BUSD]: createCurveMetaTriPool({
token: MAINNET_TOKENS.BUSD,
pool: CURVE_POOLS.BUSD,
gasSchedule: 387e3,
}),
[CURVE_POOLS.steth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.stETH],
pool: CURVE_POOLS.steth,
gasSchedule: 151e3,
}),
[CURVE_POOLS.seth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.sETH],
pool: CURVE_POOLS.seth,
gasSchedule: 187e3,
}),
[CURVE_POOLS.ankreth]: createCurveExchangePool({
// This pool uses ETH
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.ankrETH],
pool: CURVE_POOLS.ankreth,
gasSchedule: 125e3,
}),
};
export const SWERVE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
@@ -654,11 +704,12 @@ export const SNOWSWAP_MAINNET_INFOS: { [name: string]: CurveInfo } = {
pool: SNOWSWAP_POOLS.yVault,
gasSchedule: 1490e3,
}),
[SNOWSWAP_POOLS.eth]: createCurveExchangePool({
tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.vETH, MAINNET_TOKENS.ankrETH, MAINNET_TOKENS.crETH],
pool: SNOWSWAP_POOLS.eth,
gasSchedule: 990e3,
}),
// Unsupported due to collision with WETH and ETH with execution using MixinCurve
// [SNOWSWAP_POOLS.eth]: createCurveExchangePool({
// tokens: [MAINNET_TOKENS.WETH, MAINNET_TOKENS.vETH, MAINNET_TOKENS.ankrETH, MAINNET_TOKENS.crETH],
// pool: SNOWSWAP_POOLS.eth,
// gasSchedule: 990e3,
// }),
};
export const BELT_BSC_INFOS: { [name: string]: CurveInfo } = {
@@ -677,6 +728,14 @@ export const ELLIPSIS_BSC_INFOS: { [name: string]: CurveInfo } = {
}),
};
export const XSIGMA_MAINNET_INFOS: { [name: string]: CurveInfo } = {
[XSIGMA_POOLS.stable]: createCurveExchangePool({
tokens: [MAINNET_TOKENS.DAI, MAINNET_TOKENS.USDC, MAINNET_TOKENS.USDT],
pool: XSIGMA_POOLS.stable,
gasSchedule: 150e3,
}),
};
// Curve pools like using custom selectors
export const SADDLE_MAINNET_INFOS: { [name: string]: CurveInfo } = {
[SADDLE_POOLS.stables]: {
@@ -986,9 +1045,32 @@ export const COMPONENT_POOLS_BY_CHAIN_ID = valueByChainId(
},
);
export const BALANCER_V2_VAULT_ADDRESS_BY_CHAIN = valueByChainId<string>(
{
[ChainId.Mainnet]: '0xba12222222228d8ba445958a75a0704d566bf2c8',
},
NULL_ADDRESS,
);
export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer';
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
export const BALANCER_V2_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-v2';
export const UNISWAPV3_CONFIG_BY_CHAIN_ID = valueByChainId(
{
// Unconfirmed Mainnet contracts, please confirm
[ChainId.Mainnet]: {
quoter: '0xb27308f9f90d607463bb33ea1bebb41c27ce5ab6',
router: '0xe592427a0aece92de3edee1f18e0157c05861564',
},
[ChainId.Ropsten]: {
quoter: '0x2f9e608fd881861b8916257b76613cb22ee0652c',
router: '0x03782388516e94fcd4c18666303601a12aa729ea',
},
},
{ quoter: NULL_ADDRESS, router: NULL_ADDRESS },
);
//
// BSC
@@ -1073,6 +1155,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Ellipsis]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.Smoothy]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.Saddle]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.XSigma]: fillData => (fillData as CurveFillData).pool.gasSchedule,
[ERC20BridgeSource.MultiBridge]: () => 350e3,
[ERC20BridgeSource.UniswapV2]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
@@ -1111,17 +1194,12 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
return gas;
},
[ERC20BridgeSource.Balancer]: () => 120e3,
[ERC20BridgeSource.BalancerV2]: () => 100e3,
[ERC20BridgeSource.Cream]: () => 120e3,
[ERC20BridgeSource.MStable]: () => 700e3,
[ERC20BridgeSource.MakerPsm]: (fillData?: FillData) => {
const psmFillData = fillData as MakerPsmFillData;
// TODO(kimpers): update with more accurate numbers after allowances have been set
if (psmFillData.takerToken === psmFillData.gemTokenAddress) {
return psmFillData.isSellOperation ? 389e3 : 423e3;
} else {
return 444e3;
}
return psmFillData.takerToken === psmFillData.gemTokenAddress ? 210e3 : 290e3;
},
[ERC20BridgeSource.Mooniswap]: () => 130e3,
[ERC20BridgeSource.Shell]: () => 170e3,
@@ -1226,6 +1304,14 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
}
return gas;
},
[ERC20BridgeSource.UniswapV3]: (fillData?: FillData) => {
let gas = 160e3;
const path = (fillData as UniswapV3FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 117e3; // +117k for each hop.
}
return gas;
},
};
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };

View File

@@ -1,109 +0,0 @@
import { Pool } from '@balancer-labs/sor/dist/types';
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
import { BALANCER_MAX_POOLS_FETCHED } from './constants';
// tslint:disable:boolean-naming
interface CacheValue {
timestamp: number;
pools: Pool[];
}
// tslint:disable:custom-no-magic-numbers
const FIVE_SECONDS_MS = 5 * 1000;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const DEFAULT_TIMEOUT_MS = 1000;
// tslint:enable:custom-no-magic-numbers
export class CreamPoolsCache {
constructor(
private readonly _cache: { [key: string]: CacheValue } = {},
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {}
public async getPoolsForPairAsync(
takerToken: string,
makerToken: string,
timeoutMs: number = DEFAULT_TIMEOUT_MS,
): Promise<Pool[]> {
const timeout = new Promise<Pool[]>(resolve => setTimeout(resolve, timeoutMs, []));
return Promise.race([this._getPoolsForPairAsync(takerToken, makerToken), timeout]);
}
public getCachedPoolAddressesForPair(
takerToken: string,
makerToken: string,
cacheExpiryMs?: number,
): string[] | undefined {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (cacheExpiryMs === undefined) {
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
const minTimestamp = Date.now() - cacheExpiryMs;
if (value === undefined || value.timestamp < minTimestamp) {
return undefined;
} else {
return value.pools.map(pool => pool.id);
}
}
public howToSampleCream(
takerToken: string,
makerToken: string,
isAllowedSource: boolean,
): { onChain: boolean; offChain: boolean } {
// If CREAM is excluded as a source, do not sample.
if (!isAllowedSource) {
return { onChain: false, offChain: false };
}
const cachedCreamPools = this.getCachedPoolAddressesForPair(takerToken, makerToken, ONE_DAY_MS);
// Sample CREAM on-chain (i.e. via the ERC20BridgeSampler contract) if:
// - Cached values are not stale
// - There is at least one CREAM pool for this pair
const onChain = cachedCreamPools !== undefined && cachedCreamPools.length > 0;
// Sample CREAM off-chain (i.e. via GraphQL query + `computeCreamBuy/SellQuote`)
// if cached values are stale
const offChain = cachedCreamPools === undefined;
return { onChain, offChain };
}
protected async _getPoolsForPairAsync(
takerToken: string,
makerToken: string,
cacheExpiryMs: number = FIVE_SECONDS_MS,
): Promise<Pool[]> {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
const minTimestamp = Date.now() - cacheExpiryMs;
if (value === undefined || value.timestamp < minTimestamp) {
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
const timestamp = Date.now();
this._cache[key] = {
pools,
timestamp,
};
}
return this._cache[key].pools;
}
// tslint:disable-next-line: prefer-function-over-method
protected async _loadTopPoolsAsync(): Promise<void> {
// Do nothing
}
// tslint:disable-next-line:prefer-function-over-method
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
try {
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
// Sort by maker token balance (descending)
const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) =>
b.balanceOut.minus(a.balanceOut).toNumber(),
);
return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools;
} catch (err) {
return [];
}
}
}

View File

@@ -100,29 +100,6 @@ export class MarketOperationUtils {
const quoteSourceFilters = this._sellSources.merge(requestFilters);
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
const {
onChain: sampleBalancerOnChain,
offChain: sampleBalancerOffChain,
} = this._sampler.balancerPoolsCache.howToSampleBalancer(
takerToken,
makerToken,
quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer),
);
const {
onChain: sampleCreamOnChain,
offChain: sampleCreamOffChain,
} = this._sampler.creamPoolsCache.howToSampleCream(
takerToken,
makerToken,
quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream),
);
const offChainSources = [
...(!sampleCreamOnChain ? [ERC20BridgeSource.Cream] : []),
...(!sampleBalancerOnChain ? [ERC20BridgeSource.Balancer] : []),
];
// Used to determine whether the tx origin is an EOA or a contract
const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
@@ -146,12 +123,7 @@ export class MarketOperationUtils {
this._nativeFeeTokenAmount,
),
// Get sell quotes for taker -> maker.
this._sampler.getSellQuotes(
quoteSourceFilters.exclude(offChainSources).sources,
makerToken,
takerToken,
sampleAmounts,
),
this._sampler.getSellQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
this._sampler.getTwoHopSellQuotes(
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
makerToken,
@@ -161,13 +133,8 @@ export class MarketOperationUtils {
this._sampler.isAddressContract(txOrigin),
);
const offChainBalancerPromise = sampleBalancerOffChain
? this._sampler.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
: Promise.resolve([]);
const offChainCreamPromise = sampleCreamOffChain
? this._sampler.getCreamSellQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
: Promise.resolve([]);
// Refresh the cached pools asynchronously if required
void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
const [
[
@@ -179,9 +146,7 @@ export class MarketOperationUtils {
rawTwoHopQuotes,
isTxOriginContract,
],
offChainBalancerQuotes,
offChainCreamQuotes,
] = await Promise.all([samplerPromise, offChainBalancerPromise, offChainCreamPromise]);
] = await Promise.all([samplerPromise]);
// Filter out any invalid two hop quotes where we couldn't find a route
const twoHopQuotes = rawTwoHopQuotes.filter(
@@ -210,7 +175,7 @@ export class MarketOperationUtils {
nativeOrders: limitOrdersWithFillableAmounts,
rfqtIndicativeQuotes: [],
twoHopQuotes,
dexQuotes: dexQuotes.concat([...offChainBalancerQuotes, ...offChainCreamQuotes]),
dexQuotes,
},
isRfqSupported,
};
@@ -236,29 +201,6 @@ export class MarketOperationUtils {
const quoteSourceFilters = this._buySources.merge(requestFilters);
const feeSourceFilters = this._feeSources.exclude(_opts.excludedFeeSources);
const {
onChain: sampleBalancerOnChain,
offChain: sampleBalancerOffChain,
} = this._sampler.balancerPoolsCache.howToSampleBalancer(
takerToken,
makerToken,
quoteSourceFilters.isAllowed(ERC20BridgeSource.Balancer),
);
const {
onChain: sampleCreamOnChain,
offChain: sampleCreamOffChain,
} = this._sampler.creamPoolsCache.howToSampleCream(
takerToken,
makerToken,
quoteSourceFilters.isAllowed(ERC20BridgeSource.Cream),
);
const offChainSources = [
...(!sampleCreamOnChain ? [ERC20BridgeSource.Cream] : []),
...(!sampleBalancerOnChain ? [ERC20BridgeSource.Balancer] : []),
];
// Used to determine whether the tx origin is an EOA or a contract
const txOrigin = (_opts.rfqt && _opts.rfqt.txOrigin) || NULL_ADDRESS;
@@ -282,12 +224,7 @@ export class MarketOperationUtils {
this._nativeFeeTokenAmount,
),
// Get buy quotes for taker -> maker.
this._sampler.getBuyQuotes(
quoteSourceFilters.exclude(offChainSources).sources,
makerToken,
takerToken,
sampleAmounts,
),
this._sampler.getBuyQuotes(quoteSourceFilters.sources, makerToken, takerToken, sampleAmounts),
this._sampler.getTwoHopBuyQuotes(
quoteSourceFilters.isAllowed(ERC20BridgeSource.MultiHop) ? quoteSourceFilters.sources : [],
makerToken,
@@ -297,13 +234,8 @@ export class MarketOperationUtils {
this._sampler.isAddressContract(txOrigin),
);
const offChainBalancerPromise = sampleBalancerOffChain
? this._sampler.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
: Promise.resolve([]);
const offChainCreamPromise = sampleCreamOffChain
? this._sampler.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, sampleAmounts)
: Promise.resolve([]);
// Refresh the cached pools asynchronously if required
void this._refreshPoolCacheIfRequiredAsync(takerToken, makerToken);
const [
[
@@ -315,9 +247,7 @@ export class MarketOperationUtils {
rawTwoHopQuotes,
isTxOriginContract,
],
offChainBalancerQuotes,
offChainCreamQuotes,
] = await Promise.all([samplerPromise, offChainBalancerPromise, offChainCreamPromise]);
] = await Promise.all([samplerPromise]);
// Filter out any invalid two hop quotes where we couldn't find a route
const twoHopQuotes = rawTwoHopQuotes.filter(
@@ -346,7 +276,7 @@ export class MarketOperationUtils {
nativeOrders: limitOrdersWithFillableAmounts,
rfqtIndicativeQuotes: [],
twoHopQuotes,
dexQuotes: dexQuotes.concat(offChainBalancerQuotes, offChainCreamQuotes),
dexQuotes,
},
isRfqSupported,
};
@@ -554,20 +484,29 @@ export class MarketOperationUtils {
throw new Error(AggregationError.NoOptimalPath);
}
// Generate a fallback path if native orders are in the optimal path.
const nativeFills = optimalPath.fills.filter(f => f.source === ERC20BridgeSource.Native);
if (opts.allowFallback && nativeFills.length !== 0) {
// 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 nonNativeFills = fills.filter(p => p.length > 0 && p[0].source !== ERC20BridgeSource.Native);
const nonNativeOptimalPath = await findOptimalPathAsync(side, nonNativeFills, inputAmount, opts.runLimit);
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
const sturdyOptimalPath = await findOptimalPathAsync(side, sturdyFills, inputAmount, opts.runLimit, {
...penaltyOpts,
exchangeProxyOverhead: (sourceFlags: number) =>
// tslint:disable-next-line: no-bitwise
penaltyOpts.exchangeProxyOverhead(sourceFlags | optimalPath.sourceFlags),
});
// 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 (
nonNativeOptimalPath !== undefined &&
(nativeFills.length === optimalPath.fills.length ||
nonNativeOptimalPath.adjustedSlippage(optimalPathRate) <= maxFallbackSlippage)
sturdyOptimalPath !== undefined &&
(fragileFills.length === optimalPath.fills.length ||
sturdyOptimalPath.adjustedSlippage(optimalPathRate) <= maxFallbackSlippage)
) {
optimalPath.addFallback(nonNativeOptimalPath);
optimalPath.addFallback(sturdyOptimalPath);
}
}
const collapsedPath = optimalPath.collapse(orderOpts);
@@ -740,6 +679,17 @@ export class MarketOperationUtils {
}
return { ...optimizerResult, quoteReport };
}
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
void Promise.all(
Object.values(this._sampler.poolsCaches).map(async cache => {
if (cache.isFresh(takerToken, makerToken)) {
return Promise.resolve([]);
}
return cache.getFreshPoolsForPairAsync(takerToken, makerToken);
}),
);
}
}
// tslint:disable: max-file-line-count

View File

@@ -7,12 +7,14 @@ import { MAX_UINT256, ZERO_AMOUNT } from './constants';
import {
AggregationError,
BalancerFillData,
BalancerV2FillData,
BancorFillData,
CollapsedFill,
CurveFillData,
DexSample,
DODOFillData,
ERC20BridgeSource,
FillData,
GenericRouterFillData,
KyberFillData,
LiquidityProviderFillData,
@@ -28,6 +30,7 @@ import {
OrderDomain,
ShellFillData,
UniswapV2FillData,
UniswapV3FillData,
} from './types';
// tslint:disable completed-docs
@@ -41,6 +44,11 @@ export interface CreateOrderFromPathOpts {
bridgeSlippage: number;
}
interface FinalUniswapV3FillData extends Omit<UniswapV3FillData, 'uniswapPaths'> {
// The uniswap-encoded path that can fll the maximum input amount.
uniswapPath: string;
}
export function createOrdersFromTwoHopSample(
sample: DexSample<MultiHopFillData>,
opts: CreateOrderFromPathOpts,
@@ -75,6 +83,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
switch (source) {
case ERC20BridgeSource.Balancer:
return encodeBridgeSourceId(BridgeProtocol.Balancer, 'Balancer');
case ERC20BridgeSource.BalancerV2:
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'BalancerV2');
case ERC20BridgeSource.Bancor:
return encodeBridgeSourceId(BridgeProtocol.Bancor, 'Bancor');
// case ERC20BridgeSource.CoFiX:
@@ -136,6 +146,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
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:
@@ -144,6 +156,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'CheeseSwap');
case ERC20BridgeSource.JulSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'JulSwap');
case ERC20BridgeSource.UniswapV3:
return encodeBridgeSourceId(BridgeProtocol.UniswapV3, 'UniswapV3');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -173,6 +187,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Smoothy:
case ERC20BridgeSource.Saddle:
case ERC20BridgeSource.XSigma:
const curveFillData = (order as OptimizedMarketBridgeOrder<CurveFillData>).fillData;
bridgeData = encoder.encode([
curveFillData.pool.poolAddress,
@@ -186,6 +201,11 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const balancerFillData = (order as OptimizedMarketBridgeOrder<BalancerFillData>).fillData;
bridgeData = encoder.encode([balancerFillData.poolAddress]);
break;
case ERC20BridgeSource.BalancerV2:
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
const { vault, poolId } = balancerV2FillData;
bridgeData = encoder.encode([vault, poolId]);
break;
case ERC20BridgeSource.Bancor:
const bancorFillData = (order as OptimizedMarketBridgeOrder<BancorFillData>).fillData;
bridgeData = encoder.encode([bancorFillData.networkAddress, bancorFillData.path]);
@@ -250,6 +270,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const psmFillData = (order as OptimizedMarketBridgeOrder<MakerPsmFillData>).fillData;
bridgeData = encoder.encode([psmFillData.psmAddress, psmFillData.gemTokenAddress]);
break;
case ERC20BridgeSource.UniswapV3:
const uniswapV3FillData = (order as OptimizedMarketBridgeOrder<FinalUniswapV3FillData>).fillData;
bridgeData = encoder.encode([uniswapV3FillData.router, uniswapV3FillData.uniswapPath]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -268,7 +292,7 @@ export function createBridgeOrder(
takerToken,
makerAmount,
takerAmount,
fillData: fill.fillData,
fillData: createFinalBridgeOrderFillDataFromCollapsedFill(fill),
source: fill.source,
sourcePathId: fill.sourcePathId,
type: FillQuoteTransformerOrderType.Bridge,
@@ -276,6 +300,36 @@ export function createBridgeOrder(
};
}
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;
@@ -293,6 +347,7 @@ const makerPsmEncoder = AbiEncoder.create([
{ name: 'psmAddress', type: 'address' },
{ name: 'gemTokenAddress', type: 'address' },
]);
const balancerV2Encoder = AbiEncoder.create([{ name: 'vault', type: 'address' }, { name: 'poolId', type: 'bytes32' }]);
const routerAddressPathEncoder = AbiEncoder.create('(address,address[])');
const tokenAddressEncoder = AbiEncoder.create([{ name: 'tokenAddress', type: 'address' }]);
@@ -328,6 +383,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Ellipsis]: curveEncoder,
[ERC20BridgeSource.Smoothy]: curveEncoder,
[ERC20BridgeSource.Saddle]: curveEncoder,
[ERC20BridgeSource.XSigma]: curveEncoder,
// UniswapV2 like, (router, address[])
[ERC20BridgeSource.Bancor]: routerAddressPathEncoder,
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
@@ -354,6 +410,11 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Uniswap]: poolEncoder,
// Custom integrations
[ERC20BridgeSource.MakerPsm]: makerPsmEncoder,
[ERC20BridgeSource.BalancerV2]: balancerV2Encoder,
[ERC20BridgeSource.UniswapV3]: AbiEncoder.create([
{ name: 'router', type: 'address' },
{ name: 'path', type: 'bytes' },
]),
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -1,19 +1,12 @@
import { getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor';
import { Pool } from '@balancer-labs/sor/dist/types';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from './constants';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_SUBGRAPH_URL, BALANCER_TOP_POOLS_FETCHED } from '../constants';
// tslint:disable:boolean-naming
interface CacheValue {
timestamp: number;
pools: Pool[];
}
import { CacheValue, PoolsCache } from './pools_cache';
// tslint:disable:custom-no-magic-numbers
const FIVE_SECONDS_MS = 5 * 1000;
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
const DEFAULT_TIMEOUT_MS = 1000;
// tslint:enable:custom-no-magic-numbers
interface BalancerPoolResponse {
@@ -24,88 +17,19 @@ interface BalancerPoolResponse {
totalWeight: string;
}
export class BalancerPoolsCache {
export class BalancerPoolsCache extends PoolsCache {
constructor(
private readonly _cache: { [key: string]: CacheValue } = {},
private readonly _subgraphUrl: string = BALANCER_SUBGRAPH_URL,
cache: { [key: string]: CacheValue } = {},
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
private readonly subgraphUrl: string = BALANCER_SUBGRAPH_URL,
private readonly topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
private readonly _topPoolsFetched: number = BALANCER_TOP_POOLS_FETCHED,
) {
super(cache);
void this._loadTopPoolsAsync();
// Reload the top pools every 12 hours
setInterval(async () => void this._loadTopPoolsAsync(), ONE_DAY_MS / 2);
}
public async getPoolsForPairAsync(
takerToken: string,
makerToken: string,
timeoutMs: number = DEFAULT_TIMEOUT_MS,
): Promise<Pool[]> {
const timeout = new Promise<Pool[]>(resolve => setTimeout(resolve, timeoutMs, []));
return Promise.race([this._getPoolsForPairAsync(takerToken, makerToken), timeout]);
}
public getCachedPoolAddressesForPair(
takerToken: string,
makerToken: string,
cacheExpiryMs?: number,
): string[] | undefined {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (cacheExpiryMs === undefined) {
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
const minTimestamp = Date.now() - cacheExpiryMs;
if (value === undefined || value.timestamp < minTimestamp) {
return undefined;
} else {
return value.pools.map(pool => pool.id);
}
}
public howToSampleBalancer(
takerToken: string,
makerToken: string,
isAllowedSource: boolean,
): { onChain: boolean; offChain: boolean } {
// If Balancer is excluded as a source, do not sample.
if (!isAllowedSource) {
return { onChain: false, offChain: false };
}
const cachedBalancerPools = this.getCachedPoolAddressesForPair(takerToken, makerToken, ONE_DAY_MS);
// Sample Balancer on-chain (i.e. via the ERC20BridgeSampler contract) if:
// - Cached values are not stale
// - There is at least one Balancer pool for this pair
const onChain = cachedBalancerPools !== undefined && cachedBalancerPools.length > 0;
// Sample Balancer off-chain (i.e. via GraphQL query + `computeBalancerBuy/SellQuote`)
// if cached values are stale
const offChain = cachedBalancerPools === undefined;
return { onChain, offChain };
}
protected async _getPoolsForPairAsync(
takerToken: string,
makerToken: string,
cacheExpiryMs: number = FIVE_SECONDS_MS,
): Promise<Pool[]> {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
const minTimestamp = Date.now() - cacheExpiryMs;
if (value === undefined || value.timestamp < minTimestamp) {
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
this._cachePoolsForPair(takerToken, makerToken, pools);
}
return this._cache[key].pools;
}
protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[]): void {
const key = JSON.stringify([takerToken, makerToken]);
this._cache[key] = {
pools,
timestamp: Date.now(),
};
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
try {
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
@@ -140,7 +64,8 @@ export class BalancerPoolsCache {
const poolData = parsePoolData([pool], from, to);
fromToPools[from][to].push(poolData[0]);
// Cache this as we progress through
this._cachePoolsForPair(from, to, fromToPools[from][to]);
const expiresAt = Date.now() + this._cacheTimeMs;
this._cachePoolsForPair(from, to, fromToPools[from][to], expiresAt);
} catch {
// soldier on
}
@@ -153,7 +78,7 @@ export class BalancerPoolsCache {
const query = `
query {
pools (first: ${
this.topPoolsFetched
this._topPoolsFetched
}, where: {publicSwap: true, liquidity_gt: 0}, orderBy: swapsCount, orderDirection: desc) {
id
publicSwap
@@ -172,7 +97,7 @@ export class BalancerPoolsCache {
}
`;
try {
const response = await fetch(this.subgraphUrl, {
const response = await fetch(this._subgraphUrl, {
method: 'POST',
headers: {
Accept: 'application/json',

View File

@@ -0,0 +1,86 @@
// import { getPoolsWithTokens, parsePoolData } from '@balancer-labs/sor'; // TODO - upgrade to v2
import { BigNumber } from '@0x/utils';
import { Pool } from '@balancer-labs/sor/dist/types';
import { request } from 'graphql-request';
import { BALANCER_MAX_POOLS_FETCHED, BALANCER_V2_SUBGRAPH_URL } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
export class BalancerV2PoolsCache extends PoolsCache {
constructor(
private readonly subgraphUrl: string = BALANCER_V2_SUBGRAPH_URL,
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
cache: { [key: string]: CacheValue } = {},
) {
super(cache);
}
// protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
// try {
// const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
// // Sort by maker token balance (descending)
// const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) =>
// b.balanceOut.minus(a.balanceOut).toNumber(),
// );
// return pools.length > this.maxPoolsFetched ? pools.slice(0, this.maxPoolsFetched) : pools;
// } catch (err) {
// return [];
// }
// }
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
const query = `
query getPools {
pools(
first: ${this.maxPoolsFetched},
where: {
tokensList_contains: ["${takerToken}", "${makerToken}"]
}
) {
id
tokens {
address
balance
weight
}
swapFee
swaps(
orderBy: timestamp, orderDirection: desc, first: 1,
where:{
tokenIn: "${takerToken}",
tokenOut: "${makerToken}"
}
) {
tokenAmountIn
tokenAmountOut
}
}
}
`;
try {
const { pools } = await request(this.subgraphUrl, query);
return pools.map((pool: any) => {
const tToken = pool.tokens.find((t: any) => t.address === takerToken);
const mToken = pool.tokens.find((t: any) => t.address === makerToken);
const swap = pool.swaps[0];
const tokenAmountOut = swap ? swap.tokenAmountOut : undefined;
const tokenAmountIn = swap ? swap.tokenAmountIn : undefined;
const spotPrice =
tokenAmountOut && tokenAmountIn ? new BigNumber(tokenAmountOut).div(tokenAmountIn) : undefined; // TODO: xianny check
return {
id: pool.id,
balanceIn: new BigNumber(tToken.balance),
balanceOut: new BigNumber(mToken.balance),
weightIn: new BigNumber(tToken.weight),
weightOut: new BigNumber(mToken.weight),
swapFee: new BigNumber(pool.swapFee),
spotPrice,
};
});
} catch (e) {
return [];
}
}
}

View File

@@ -0,0 +1,28 @@
import { Pool } from '@balancer-labs/sor/dist/types';
import { getPoolsWithTokens, parsePoolData } from 'cream-sor';
import { BALANCER_MAX_POOLS_FETCHED } from '../constants';
import { CacheValue, PoolsCache } from './pools_cache';
export class CreamPoolsCache extends PoolsCache {
constructor(
_cache: { [key: string]: CacheValue } = {},
private readonly maxPoolsFetched: number = BALANCER_MAX_POOLS_FETCHED,
) {
super(_cache);
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
try {
const poolData = (await getPoolsWithTokens(takerToken, makerToken)).pools;
// Sort by maker token balance (descending)
const pools = parsePoolData(poolData, takerToken, makerToken).sort((a, b) =>
b.balanceOut.minus(a.balanceOut).toNumber(),
);
return pools.slice(0, this.maxPoolsFetched);
} catch (err) {
return [];
}
}
}

View File

@@ -0,0 +1,4 @@
export { BalancerPoolsCache } from './balancer_utils';
export { BalancerV2PoolsCache } from './balancer_v2_utils';
export { CreamPoolsCache } from './cream_utils';
export { PoolsCache } from './pools_cache';

View File

@@ -0,0 +1,78 @@
import { Pool } from '@balancer-labs/sor/dist/types';
import { ONE_HOUR_IN_SECONDS, ONE_SECOND_MS } from '../constants';
export { Pool };
export interface CacheValue {
expiresAt: number;
pools: Pool[];
}
// 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 = 1000;
// tslint:enable:custom-no-magic-numbers
export abstract class PoolsCache {
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<Pool[]> {
const timeout = new Promise<Pool[]>(resolve => setTimeout(resolve, timeoutMs, []));
return Promise.race([this._getAndSaveFreshPoolsForPairAsync(takerToken, makerToken), timeout]);
}
public getCachedPoolAddressesForPair(
takerToken: string,
makerToken: string,
ignoreExpired: boolean = true,
): string[] | undefined {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (ignoreExpired) {
return value === undefined ? [] : value.pools.map(pool => pool.id);
}
if (!value) {
return undefined;
}
if (PoolsCache._isExpired(value)) {
return undefined;
}
return (value || []).pools.map(pool => pool.id);
}
public isFresh(takerToken: string, makerToken: string): boolean {
const cached = this.getCachedPoolAddressesForPair(takerToken, makerToken, false);
return cached !== undefined;
}
protected async _getAndSaveFreshPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
const key = JSON.stringify([takerToken, makerToken]);
const value = this._cache[key];
if (value === undefined || value.expiresAt >= Date.now()) {
const pools = await this._fetchPoolsForPairAsync(takerToken, makerToken);
const expiresAt = Date.now() + this._cacheTimeMs;
this._cachePoolsForPair(takerToken, makerToken, pools, expiresAt);
}
return this._cache[key].pools;
}
protected _cachePoolsForPair(takerToken: string, makerToken: string, pools: Pool[], expiresAt: number): void {
const key = JSON.stringify([takerToken, makerToken]);
this._cache[key] = {
pools,
expiresAt,
};
}
protected abstract _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]>;
}

View File

@@ -4,11 +4,10 @@ import { BigNumber, NULL_BYTES } from '@0x/utils';
import { SamplerOverrides } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { BalancerPoolsCache } from './balancer_utils';
import { BancorService } from './bancor_service';
import { CreamPoolsCache } from './cream_utils';
import { PoolsCache } from './pools_cache';
import { SamplerOperations } from './sampler_operations';
import { BatchedOperation, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAdjacencyGraph } from './types';
/**
* Generate sample amounts up to `maxFillAmount`.
@@ -37,21 +36,12 @@ export class DexOrderSampler extends SamplerOperations {
public readonly chainId: ChainId,
_samplerContract: ERC20BridgeSamplerContract,
private readonly _samplerOverrides?: SamplerOverrides,
balancerPoolsCache?: BalancerPoolsCache,
creamPoolsCache?: CreamPoolsCache,
poolsCaches?: { [key in ERC20BridgeSource]: PoolsCache },
tokenAdjacencyGraph?: TokenAdjacencyGraph,
liquidityProviderRegistry?: LiquidityProviderRegistry,
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
) {
super(
chainId,
_samplerContract,
balancerPoolsCache,
creamPoolsCache,
tokenAdjacencyGraph,
liquidityProviderRegistry,
bancorServiceFn,
);
super(chainId, _samplerContract, poolsCaches, tokenAdjacencyGraph, liquidityProviderRegistry, bancorServiceFn);
}
/* Type overloads for `executeAsync()`. Could skip this if we would upgrade TS. */

View File

@@ -6,7 +6,6 @@ import * as _ from 'lodash';
import { SamplerCallResult, SignedNativeOrder } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { BalancerPoolsCache } from './balancer_utils';
import { BancorService } from './bancor_service';
import {
getCurveLikeInfosForPair,
@@ -19,6 +18,7 @@ import {
uniswapV2LikeRouterAddress,
} from './bridge_source_utils';
import {
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
DODO_CONFIG_BY_CHAIN_ID,
DODOV2_FACTORIES_BY_CHAIN_ID,
@@ -36,15 +36,18 @@ import {
OASIS_ROUTER_BY_CHAIN_ID,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
UNISWAPV1_ROUTER_BY_CHAIN_ID,
UNISWAPV3_CONFIG_BY_CHAIN_ID,
ZERO_AMOUNT,
} from './constants';
import { CreamPoolsCache } from './cream_utils';
import { getLiquidityProvidersForPair } from './liquidity_provider_utils';
import { getIntermediateTokens } from './multihop_utils';
import { BalancerPoolsCache, BalancerV2PoolsCache, CreamPoolsCache, PoolsCache } from './pools_cache';
import { SamplerContractOperation } from './sampler_contract_operation';
import { SourceFilters } from './source_filters';
import {
BalancerFillData,
BalancerV2FillData,
BalancerV2PoolInfo,
BancorFillData,
BatchedOperation,
CurveFillData,
@@ -64,8 +67,10 @@ import {
PsmInfo,
ShellFillData,
SourceQuoteOperation,
SourcesWithPoolsCache,
TokenAdjacencyGraph,
UniswapV2FillData,
UniswapV3FillData,
} from './types';
/**
@@ -88,6 +93,7 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
*/
export class SamplerOperations {
public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
public readonly poolsCaches: { [key in SourcesWithPoolsCache]: PoolsCache };
protected _bancorService?: BancorService;
public static constant<T>(result: T): BatchedOperation<T> {
return {
@@ -100,8 +106,7 @@ export class SamplerOperations {
constructor(
public readonly chainId: ChainId,
protected readonly _samplerContract: ERC20BridgeSamplerContract,
public readonly balancerPoolsCache: BalancerPoolsCache = new BalancerPoolsCache(),
public readonly creamPoolsCache: CreamPoolsCache = new CreamPoolsCache(),
poolsCaches?: { [key in SourcesWithPoolsCache]: PoolsCache },
protected readonly tokenAdjacencyGraph: TokenAdjacencyGraph = { default: [] },
liquidityProviderRegistry: LiquidityProviderRegistry = {},
bancorServiceFn: () => Promise<BancorService | undefined> = async () => undefined,
@@ -110,6 +115,13 @@ export class SamplerOperations {
...LIQUIDITY_PROVIDER_REGISTRY_BY_CHAIN_ID[chainId],
...liquidityProviderRegistry,
};
this.poolsCaches = poolsCaches
? poolsCaches
: {
[ERC20BridgeSource.BalancerV2]: new BalancerV2PoolsCache(),
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
};
// Initialize the Bancor service, fetching paths in the background
bancorServiceFn()
.then(service => (this._bancorService = service))
@@ -473,6 +485,38 @@ export class SamplerOperations {
});
}
public getBalancerV2SellQuotes(
poolInfo: BalancerV2PoolInfo,
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
source: ERC20BridgeSource,
): SourceQuoteOperation<BalancerV2FillData> {
return new SamplerContractOperation({
source,
fillData: poolInfo,
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromBalancerV2,
params: [poolInfo, takerToken, makerToken, takerFillAmounts],
});
}
public getBalancerV2BuyQuotes(
poolInfo: BalancerV2PoolInfo,
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
source: ERC20BridgeSource,
): SourceQuoteOperation<BalancerV2FillData> {
return new SamplerContractOperation({
source,
fillData: poolInfo,
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromBalancerV2,
params: [poolInfo, takerToken, makerToken, makerFillAmounts],
});
}
public getBalancerSellQuotes(
poolAddress: string,
makerToken: string,
@@ -505,79 +549,6 @@ export class SamplerOperations {
});
}
public async getBalancerSellQuotesOffChainAsync(
makerToken: string,
takerToken: string,
_takerFillAmounts: BigNumber[],
): Promise<Array<Array<DexSample<BalancerFillData>>>> {
// Prime the cache but do not sample off chain
await this.balancerPoolsCache.getPoolsForPairAsync(takerToken, makerToken);
return [];
// return pools.map(pool =>
// takerFillAmounts.map(amount => ({
// source: ERC20BridgeSource.Balancer,
// output: computeBalancerSellQuote(pool, amount),
// input: amount,
// fillData: { poolAddress: pool.id },
// })),
// );
}
public async getBalancerBuyQuotesOffChainAsync(
makerToken: string,
takerToken: string,
_makerFillAmounts: BigNumber[],
): Promise<Array<Array<DexSample<BalancerFillData>>>> {
// Prime the pools but do not sample off chain
// Prime the cache but do not sample off chain
await this.balancerPoolsCache.getPoolsForPairAsync(takerToken, makerToken);
return [];
// return pools.map(pool =>
// makerFillAmounts.map(amount => ({
// source: ERC20BridgeSource.Balancer,
// output: computeBalancerBuyQuote(pool, amount),
// input: amount,
// fillData: { poolAddress: pool.id },
// })),
// );
}
public async getCreamSellQuotesOffChainAsync(
makerToken: string,
takerToken: string,
_takerFillAmounts: BigNumber[],
): Promise<Array<Array<DexSample<BalancerFillData>>>> {
// Prime the cache but do not sample off chain
await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken);
return [];
// return pools.map(pool =>
// takerFillAmounts.map(amount => ({
// source: ERC20BridgeSource.Cream,
// output: computeBalancerSellQuote(pool, amount),
// input: amount,
// fillData: { poolAddress: pool.id },
// })),
// );
}
public async getCreamBuyQuotesOffChainAsync(
makerToken: string,
takerToken: string,
_makerFillAmounts: BigNumber[],
): Promise<Array<Array<DexSample<BalancerFillData>>>> {
// Prime the cache but do not sample off chain
await this.creamPoolsCache.getPoolsForPairAsync(takerToken, makerToken);
return [];
// return pools.map(pool =>
// makerFillAmounts.map(amount => ({
// source: ERC20BridgeSource.Cream,
// output: computeBalancerBuyQuote(pool, amount),
// input: amount,
// fillData: { poolAddress: pool.id },
// })),
// );
}
public getMStableSellQuotes(
router: string,
makerToken: string,
@@ -708,6 +679,62 @@ export class SamplerOperations {
});
}
public getUniswapV3SellQuotes(
router: string,
quoter: string,
tokenAddressPath: string[],
takerFillAmounts: BigNumber[],
source: ERC20BridgeSource = ERC20BridgeSource.UniswapV3,
): SourceQuoteOperation<UniswapV3FillData> {
return new SamplerContractOperation({
source,
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromUniswapV3,
params: [quoter, tokenAddressPath, takerFillAmounts],
callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => {
const [samples, paths] = this._samplerContract.getABIDecodedReturnData<[BigNumber[], string[]]>(
'sampleSellsFromUniswapV3',
callResults,
);
fillData.router = router;
fillData.tokenAddressPath = tokenAddressPath;
fillData.pathAmounts = paths.map((uniswapPath, i) => ({
uniswapPath,
inputAmount: takerFillAmounts[i],
}));
return samples;
},
});
}
public getUniswapV3BuyQuotes(
router: string,
quoter: string,
tokenAddressPath: string[],
makerFillAmounts: BigNumber[],
source: ERC20BridgeSource = ERC20BridgeSource.UniswapV3,
): SourceQuoteOperation<UniswapV3FillData> {
return new SamplerContractOperation({
source,
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromUniswapV3,
params: [quoter, tokenAddressPath, makerFillAmounts],
callback: (callResults: string, fillData: UniswapV3FillData): BigNumber[] => {
const [samples, paths] = this._samplerContract.getABIDecodedReturnData<[BigNumber[], string[]]>(
'sampleBuysFromUniswapV3',
callResults,
);
fillData.router = router;
fillData.tokenAddressPath = tokenAddressPath;
fillData.pathAmounts = paths.map((uniswapPath, i) => ({
uniswapPath,
inputAmount: makerFillAmounts[i],
}));
return samples;
},
});
}
public getTwoHopSellQuotes(
sources: ERC20BridgeSource[],
makerToken: string,
@@ -1132,11 +1159,12 @@ export class SamplerOperations {
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
case ERC20BridgeSource.XSigma:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveSellQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
pool.takerTokenIdx,
pool.makerTokenIdx,
takerFillAmounts,
source,
),
@@ -1187,29 +1215,56 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return this.balancerPoolsCache
.getCachedPoolAddressesForPair(takerToken, makerToken)!
.map(poolAddress =>
this.getBalancerSellQuotes(
poolAddress,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerSellQuotes(
poolAddress,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
case ERC20BridgeSource.BalancerV2:
const poolIds =
this.poolsCaches[ERC20BridgeSource.BalancerV2].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || [];
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
return poolIds.map(poolId =>
this.getBalancerV2SellQuotes(
{ poolId, vault },
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.BalancerV2,
),
);
case ERC20BridgeSource.Cream:
return this.creamPoolsCache
.getCachedPoolAddressesForPair(takerToken, makerToken)!
.map(poolAddress =>
this.getBalancerSellQuotes(
poolAddress,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerSellQuotes(
poolAddress,
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1269,6 +1324,16 @@ export class SamplerOperations {
return [];
}
return this.getMakerPsmSellQuotes(psmInfo, makerToken, takerToken, takerFillAmounts);
case ERC20BridgeSource.UniswapV3: {
const { quoter, router } = UNISWAPV3_CONFIG_BY_CHAIN_ID[this.chainId];
if (!isValidAddress(router) || !isValidAddress(quoter)) {
return [];
}
return [
[takerToken, makerToken],
...intermediateTokens.map(t => [takerToken, t, makerToken]),
].map(path => this.getUniswapV3SellQuotes(router, quoter, path, takerFillAmounts));
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
@@ -1346,11 +1411,12 @@ export class SamplerOperations {
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
case ERC20BridgeSource.XSigma:
return getCurveLikeInfosForPair(this.chainId, takerToken, makerToken, source).map(pool =>
this.getCurveBuyQuotes(
pool,
pool.tokens.indexOf(takerToken),
pool.tokens.indexOf(makerToken),
pool.takerTokenIdx,
pool.makerTokenIdx,
makerFillAmounts,
source,
),
@@ -1401,29 +1467,55 @@ export class SamplerOperations {
),
];
case ERC20BridgeSource.Balancer:
return this.balancerPoolsCache
.getCachedPoolAddressesForPair(takerToken, makerToken)!
.map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
return (
this.poolsCaches[ERC20BridgeSource.Balancer].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Balancer,
),
);
case ERC20BridgeSource.BalancerV2:
const poolIds =
this.poolsCaches[ERC20BridgeSource.BalancerV2].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || [];
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
return poolIds.map(poolId =>
this.getBalancerV2BuyQuotes(
{ poolId, vault },
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.BalancerV2,
),
);
case ERC20BridgeSource.Cream:
return this.creamPoolsCache
.getCachedPoolAddressesForPair(takerToken, makerToken)!
.map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || []
).map(poolAddress =>
this.getBalancerBuyQuotes(
poolAddress,
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.Cream,
),
);
case ERC20BridgeSource.Dodo:
if (!isValidAddress(DODO_CONFIG_BY_CHAIN_ID[this.chainId].registry)) {
return [];
@@ -1478,6 +1570,16 @@ export class SamplerOperations {
return [];
}
return this.getMakerPsmBuyQuotes(psmInfo, makerToken, takerToken, makerFillAmounts);
case ERC20BridgeSource.UniswapV3: {
const { quoter, router } = UNISWAPV3_CONFIG_BY_CHAIN_ID[this.chainId];
if (!isValidAddress(router) || !isValidAddress(quoter)) {
return [];
}
return [
[takerToken, makerToken],
...intermediateTokens.map(t => [takerToken, t, makerToken]),
].map(path => this.getUniswapV3BuyQuotes(router, quoter, path, makerFillAmounts));
}
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -45,6 +45,7 @@ export enum ERC20BridgeSource {
LiquidityProvider = 'LiquidityProvider',
MultiBridge = 'MultiBridge',
Balancer = 'Balancer',
BalancerV2 = 'Balancer_V2',
Cream = 'CREAM',
Bancor = 'Bancor',
MakerPsm = 'MakerPsm',
@@ -63,6 +64,8 @@ export enum ERC20BridgeSource {
Smoothy = 'Smoothy',
Component = 'Component',
Saddle = 'Saddle',
XSigma = 'xSigma',
UniswapV3 = 'Uniswap_V3',
// BSC only
PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2',
@@ -75,6 +78,7 @@ export enum ERC20BridgeSource {
CheeseSwap = 'CheeseSwap',
JulSwap = 'JulSwap',
}
export type SourcesWithPoolsCache = ERC20BridgeSource.Balancer | ERC20BridgeSource.BalancerV2 | ERC20BridgeSource.Cream;
// tslint:disable: enum-naming
/**
@@ -119,6 +123,14 @@ export interface PsmInfo {
gemTokenAddress: string;
}
/**
* Configuration info for a Balancer V2 pool.
*/
export interface BalancerV2PoolInfo {
poolId: string;
vault: string;
}
// Internal `fillData` field for `Fill` objects.
export interface FillData {}
@@ -144,6 +156,11 @@ export interface BalancerFillData extends FillData {
poolAddress: string;
}
export interface BalancerV2FillData extends FillData {
vault: string;
poolId: string;
}
export interface UniswapV2FillData extends FillData {
tokenAddressPath: string[];
router: string;
@@ -201,6 +218,12 @@ export interface HopInfo {
returnData: string;
}
export interface UniswapV3FillData extends FillData {
tokenAddressPath: string[];
router: string;
pathAmounts: Array<{ uniswapPath: string; inputAmount: BigNumber }>;
}
/**
* Represents a node on a fill path.
*/

View File

@@ -8,6 +8,7 @@ import { ContractArtifact } from 'ethereum-types';
import * as ApproximateBuys from '../test/generated-artifacts/ApproximateBuys.json';
import * as BalanceChecker from '../test/generated-artifacts/BalanceChecker.json';
import * as BalancerSampler from '../test/generated-artifacts/BalancerSampler.json';
import * as BalancerV2Sampler from '../test/generated-artifacts/BalancerV2Sampler.json';
import * as BancorSampler from '../test/generated-artifacts/BancorSampler.json';
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
@@ -43,11 +44,13 @@ import * as TestNativeOrderSampler from '../test/generated-artifacts/TestNativeO
import * as TwoHopSampler from '../test/generated-artifacts/TwoHopSampler.json';
import * as UniswapSampler from '../test/generated-artifacts/UniswapSampler.json';
import * as UniswapV2Sampler from '../test/generated-artifacts/UniswapV2Sampler.json';
import * as UniswapV3Sampler from '../test/generated-artifacts/UniswapV3Sampler.json';
import * as UtilitySampler from '../test/generated-artifacts/UtilitySampler.json';
export const artifacts = {
ApproximateBuys: ApproximateBuys as ContractArtifact,
BalanceChecker: BalanceChecker as ContractArtifact,
BalancerSampler: BalancerSampler as ContractArtifact,
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
BancorSampler: BancorSampler as ContractArtifact,
CurveSampler: CurveSampler as ContractArtifact,
DODOSampler: DODOSampler as ContractArtifact,
@@ -68,6 +71,7 @@ export const artifacts = {
TwoHopSampler: TwoHopSampler as ContractArtifact,
UniswapSampler: UniswapSampler as ContractArtifact,
UniswapV2Sampler: UniswapV2Sampler as ContractArtifact,
UniswapV3Sampler: UniswapV3Sampler as ContractArtifact,
UtilitySampler: UtilitySampler as ContractArtifact,
IBalancer: IBalancer as ContractArtifact,
IBancor: IBancor as ContractArtifact,

View File

@@ -18,7 +18,6 @@ import { DummyLiquidityProviderContract, TestERC20BridgeSamplerContract } from '
// tslint:disable: custom-no-magic-numbers
const { NULL_ADDRESS } = constants;
blockchainTests('erc20-bridge-sampler', env => {
let testContract: TestERC20BridgeSamplerContract;
const RATE_DENOMINATOR = constants.ONE_ETHER;
@@ -45,7 +44,7 @@ blockchainTests('erc20-bridge-sampler', env => {
testContract = await TestERC20BridgeSamplerContract.deployFrom0xArtifactAsync(
artifacts.TestERC20BridgeSampler,
env.provider,
env.txDefaults,
{ ...env.txDefaults, gas: 100e6 },
{},
);
UNISWAP_V2_ROUTER = await testContract.uniswapV2Router().callAsync();

View File

@@ -9,14 +9,12 @@ import {
} from '@0x/contracts-test-utils';
import { FillQuoteTransformerOrderType, LimitOrderFields, SignatureType } from '@0x/protocol-utils';
import { BigNumber, hexUtils, NULL_ADDRESS } from '@0x/utils';
import { Pool } from '@balancer-labs/sor/dist/types';
import * as _ from 'lodash';
import { SignedOrder } from '../src/types';
import { DexOrderSampler, getSampleAmounts } from '../src/utils/market_operation_utils/sampler';
import { ERC20BridgeSource, TokenAdjacencyGraph } from '../src/utils/market_operation_utils/types';
import { MockBalancerPoolsCache } from './utils/mock_balancer_pools_cache';
import { MockSamplerContract } from './utils/mock_sampler_contract';
import { generatePseudoRandomSalt } from './utils/utils';
@@ -112,7 +110,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -137,7 +134,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -166,7 +162,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -200,7 +195,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
{
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
},
@@ -245,7 +239,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
{
[poolAddress]: { tokens: [expectedMakerToken, expectedTakerToken], gasCost },
},
@@ -291,7 +284,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -325,7 +317,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -358,7 +349,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -391,7 +381,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -425,7 +414,6 @@ describe('DexSampler tests', () => {
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableAmounts] = await dexOrderSampler.executeAsync(
@@ -490,7 +478,6 @@ describe('DexSampler tests', () => {
sampler,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
undefined,
async () => undefined,
@@ -542,38 +529,6 @@ describe('DexSampler tests', () => {
expect(quotes).to.have.lengthOf(sources.length + additionalSourceCount);
expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes));
});
it('getSellQuotes() fetches pools but not samples from Balancer', async () => {
// HACK
// We disabled the off-chain sampling due to incorrect data observed between
// on-chain and off-chain sampling
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const expectedTakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
const pools: Pool[] = [generateBalancerPool(), generateBalancerPool()];
const balancerPoolsCache = new MockBalancerPoolsCache({
getPoolsForPairAsync: async (takerToken: string, makerToken: string) => {
expect(takerToken).equal(expectedTakerToken);
expect(makerToken).equal(expectedMakerToken);
return Promise.resolve(pools);
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
undefined,
undefined,
undefined,
async () => undefined,
);
const quotes = await dexOrderSampler.getBalancerSellQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedTakerFillAmounts,
);
expect(quotes).to.have.lengthOf(0);
});
it('getBuyQuotes()', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
@@ -620,7 +575,6 @@ describe('DexSampler tests', () => {
sampler,
undefined,
undefined,
undefined,
tokenAdjacencyGraph,
undefined,
async () => undefined,
@@ -665,80 +619,39 @@ describe('DexSampler tests', () => {
expect(quotes).to.have.lengthOf(sources.length + 1);
expect(quotes).to.deep.eq(expectedQuotes.concat(uniswapV2ETHQuotes));
});
it('getBuyQuotes() uses samples from Balancer', async () => {
const expectedTakerToken = randomAddress();
const expectedMakerToken = randomAddress();
const expectedMakerFillAmounts = getSampleAmounts(new BigNumber(100e18), 3);
const pools: Pool[] = [generateBalancerPool(), generateBalancerPool()];
const balancerPoolsCache = new MockBalancerPoolsCache({
getPoolsForPairAsync: async (takerToken: string, makerToken: string) => {
expect(takerToken).equal(expectedTakerToken);
expect(makerToken).equal(expectedMakerToken);
return Promise.resolve(pools);
},
describe('batched operations', () => {
it('getLimitOrderFillableMakerAssetAmounts(), getLimitOrderFillableTakerAssetAmounts()', async () => {
const expectedFillableTakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const expectedFillableMakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const sampler = new MockSamplerContract({
getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableMakerAmounts;
},
getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableTakerAmounts;
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress),
dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress),
);
expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts);
expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts);
});
const dexOrderSampler = new DexOrderSampler(
chainId,
new MockSamplerContract({}),
undefined,
balancerPoolsCache,
undefined,
undefined,
undefined,
async () => undefined,
);
const quotes = await dexOrderSampler.getBalancerBuyQuotesOffChainAsync(
expectedMakerToken,
expectedTakerToken,
expectedMakerFillAmounts,
);
expect(quotes).to.have.lengthOf(0);
});
});
describe('batched operations', () => {
it('getLimitOrderFillableMakerAssetAmounts(), getLimitOrderFillableTakerAssetAmounts()', async () => {
const expectedFillableTakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const expectedFillableMakerAmounts = ORDERS.map(() => getRandomInteger(0, 100e18));
const sampler = new MockSamplerContract({
getLimitOrderFillableMakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableMakerAmounts;
},
getLimitOrderFillableTakerAssetAmounts: (orders, signatures) => {
expect(orders).to.deep.eq(SIMPLE_ORDERS);
expect(signatures).to.deep.eq(ORDERS.map(o => o.signature));
return expectedFillableTakerAmounts;
},
});
const dexOrderSampler = new DexOrderSampler(
chainId,
sampler,
undefined,
undefined,
undefined,
undefined,
undefined,
async () => undefined,
);
const [fillableMakerAmounts, fillableTakerAmounts] = await dexOrderSampler.executeAsync(
dexOrderSampler.getLimitOrderFillableMakerAmounts(ORDERS, exchangeProxyAddress),
dexOrderSampler.getLimitOrderFillableTakerAmounts(ORDERS, exchangeProxyAddress),
);
expect(fillableMakerAmounts).to.deep.eq(expectedFillableMakerAmounts);
expect(fillableTakerAmounts).to.deep.eq(expectedFillableTakerAmounts);
});
});
});
function generateBalancerPool(): Pool {
return {
id: randomAddress(),
balanceIn: getRandomInteger(1, 1e18),
balanceOut: getRandomInteger(1, 1e18),
weightIn: getRandomInteger(0, 1e5),
weightOut: getRandomInteger(0, 1e5),
swapFee: getRandomInteger(0, 1e5),
};
}
// tslint:disable-next-line: max-file-line-count

View File

@@ -60,12 +60,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
let consumer: ExchangeProxySwapQuoteConsumer;
before(async () => {
const fakeProvider = {
async sendAsync(): Promise<void> {
/* noop */
},
};
consumer = new ExchangeProxySwapQuoteConsumer(fakeProvider, contractAddresses, { chainId: CHAIN_ID });
consumer = new ExchangeProxySwapQuoteConsumer(contractAddresses, { chainId: CHAIN_ID });
});
function getRandomOrder(orderFields?: Partial<LimitOrderFields>): LimitOrderFields {

View File

@@ -11,21 +11,21 @@ import {
import { FillQuoteTransformerOrderType, LimitOrder, RfqOrder, SignatureType } from '@0x/protocol-utils';
import { BigNumber, hexUtils, NULL_BYTES } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import { Pool } from '@balancer-labs/sor/dist/types';
import * as _ from 'lodash';
import * as TypeMoq from 'typemoq';
import { MarketOperation, QuoteRequestor, RfqRequestOpts, SignedNativeOrder } from '../src';
import { NativeOrderWithFillableAmounts } from '../src/types';
import { MarketOperationUtils } from '../src/utils/market_operation_utils/';
import { BalancerPoolsCache } from '../src/utils/market_operation_utils/balancer_utils';
import {
BUY_SOURCE_FILTER_BY_CHAIN_ID,
POSITIVE_INF,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SOURCE_FLAGS,
} from '../src/utils/market_operation_utils/constants';
import { CreamPoolsCache } from '../src/utils/market_operation_utils/cream_utils';
import { createFills } from '../src/utils/market_operation_utils/fills';
import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache';
import { DexOrderSampler } from '../src/utils/market_operation_utils/sampler';
import { BATCH_SOURCE_FILTERS } from '../src/utils/market_operation_utils/sampler_operations';
import { SourceFilters } from '../src/utils/market_operation_utils/source_filters';
@@ -47,33 +47,16 @@ import {
const MAKER_TOKEN = randomAddress();
const TAKER_TOKEN = randomAddress();
const DEFAULT_EXCLUDED = [
ERC20BridgeSource.UniswapV2,
ERC20BridgeSource.Curve,
ERC20BridgeSource.Balancer,
ERC20BridgeSource.MStable,
ERC20BridgeSource.Mooniswap,
ERC20BridgeSource.Bancor,
ERC20BridgeSource.Swerve,
ERC20BridgeSource.SnowSwap,
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Shell,
ERC20BridgeSource.Cream,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
ERC20BridgeSource.PancakeSwap,
ERC20BridgeSource.BakerySwap,
ERC20BridgeSource.MakerPsm,
ERC20BridgeSource.KyberDmm,
ERC20BridgeSource.Smoothy,
ERC20BridgeSource.Component,
ERC20BridgeSource.Saddle,
ERC20BridgeSource.PancakeSwapV2,
const DEFAULT_INCLUDED = [
ERC20BridgeSource.Eth2Dai,
ERC20BridgeSource.Kyber,
ERC20BridgeSource.Native,
ERC20BridgeSource.Uniswap,
];
const DEFAULT_EXCLUDED = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources.filter(
s => !DEFAULT_INCLUDED.includes(s),
);
const BUY_SOURCES = BUY_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const SELL_SOURCES = SELL_SOURCE_FILTER_BY_CHAIN_ID[ChainId.Mainnet].sources;
const TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = { default: [] };
@@ -114,6 +97,32 @@ async function getMarketBuyOrdersAsync(
return utils.getOptimizerResultAsync(nativeOrders, makerAmount, MarketOperation.Buy, opts);
}
class MockPoolsCache extends PoolsCache {
constructor(private readonly _handler: (takerToken: string, makerToken: string) => Pool[]) {
super({});
}
protected async _fetchPoolsForPairAsync(takerToken: string, makerToken: string): Promise<Pool[]> {
return this._handler(takerToken, makerToken);
}
}
// Return some pool so that sampling functions are called for Balancer, BalancerV2, and Cream
// tslint:disable:custom-no-magic-numbers
const mockPoolsCache = new MockPoolsCache((_takerToken: string, _makerToken: string) => {
return [
{
id: '0xe4b2554b622cc342ac7d6dc19b594553577941df000200000000000000000003',
balanceIn: new BigNumber('13655.491506618973154788'),
balanceOut: new BigNumber('8217005.926472'),
weightIn: new BigNumber('0.5'),
weightOut: new BigNumber('0.5'),
swapFee: new BigNumber('0.008'),
spotPrice: new BigNumber(596.92685),
},
];
});
// tslint:enable:custom-no-magic-numbers
// tslint:disable: custom-no-magic-numbers promise-function-async
describe('MarketOperationUtils tests', () => {
const CHAIN_ID = ChainId.Mainnet;
@@ -309,6 +318,11 @@ describe('MarketOperationUtils tests', () => {
const DEFAULT_FILL_DATA: FillDataBySource = {
[ERC20BridgeSource.UniswapV2]: { tokenAddressPath: [] },
[ERC20BridgeSource.Balancer]: { poolAddress: randomAddress() },
[ERC20BridgeSource.BalancerV2]: {
vault: randomAddress(),
poolId: randomAddress(),
deadline: Math.floor(Date.now() / 1000) + 300,
},
[ERC20BridgeSource.Bancor]: { path: [], networkAddress: randomAddress() },
[ERC20BridgeSource.Kyber]: { hint: '0x', reserveId: '0x', networkAddress: randomAddress() },
[ERC20BridgeSource.Curve]: {
@@ -398,53 +412,6 @@ describe('MarketOperationUtils tests', () => {
getSellQuotes: createGetMultipleSellQuotesOperationFromRates(DEFAULT_RATES),
getBuyQuotes: createGetMultipleBuyQuotesOperationFromRates(DEFAULT_RATES),
getMedianSellRate: createGetMedianSellRate(1),
getBalancerSellQuotesOffChainAsync: (
_makerToken: string,
_takerToken: string,
takerFillAmounts: BigNumber[],
) => [
createSamplesFromRates(
ERC20BridgeSource.Balancer,
takerFillAmounts,
createDecreasingRates(takerFillAmounts.length),
DEFAULT_FILL_DATA[ERC20BridgeSource.Balancer],
),
],
getBalancerBuyQuotesOffChainAsync: (
_makerToken: string,
_takerToken: string,
makerFillAmounts: BigNumber[],
) => [
createSamplesFromRates(
ERC20BridgeSource.Balancer,
makerFillAmounts,
createDecreasingRates(makerFillAmounts.length).map(r => new BigNumber(1).div(r)),
DEFAULT_FILL_DATA[ERC20BridgeSource.Balancer],
),
],
getCreamSellQuotesOffChainAsync: (_makerToken: string, _takerToken: string, takerFillAmounts: BigNumber[]) => [
createSamplesFromRates(
ERC20BridgeSource.Cream,
takerFillAmounts,
createDecreasingRates(takerFillAmounts.length),
DEFAULT_FILL_DATA[ERC20BridgeSource.Cream],
),
],
getCreamBuyQuotesOffChainAsync: (_makerToken: string, _takerToken: string, makerFillAmounts: BigNumber[]) => [
createSamplesFromRates(
ERC20BridgeSource.Cream,
makerFillAmounts,
createDecreasingRates(makerFillAmounts.length).map(r => new BigNumber(1).div(r)),
DEFAULT_FILL_DATA[ERC20BridgeSource.Cream],
),
],
getBancorSellQuotesOffChainAsync: (_makerToken: string, _takerToken: string, takerFillAmounts: BigNumber[]) =>
createSamplesFromRates(
ERC20BridgeSource.Bancor,
takerFillAmounts,
createDecreasingRates(takerFillAmounts.length),
DEFAULT_FILL_DATA[ERC20BridgeSource.Bancor],
),
getTwoHopSellQuotes: (..._params: any[]) => [],
getTwoHopBuyQuotes: (..._params: any[]) => [],
isAddressContract: (..._params: any[]) => false,
@@ -457,8 +424,11 @@ describe('MarketOperationUtils tests', () => {
async executeBatchAsync(ops: any[]): Promise<any[]> {
return ops;
},
balancerPoolsCache: new BalancerPoolsCache(),
creamPoolsCache: new CreamPoolsCache(),
poolsCaches: {
[ERC20BridgeSource.BalancerV2]: mockPoolsCache,
[ERC20BridgeSource.Balancer]: mockPoolsCache,
[ERC20BridgeSource.Cream]: mockPoolsCache,
},
liquidityProviderRegistry: {},
chainId: CHAIN_ID,
} as any) as DexOrderSampler;
@@ -537,22 +507,6 @@ describe('MarketOperationUtils tests', () => {
sourcesPolled.push(ERC20BridgeSource.MultiHop);
return DEFAULT_OPS.getTwoHopSellQuotes(...args);
},
getBalancerSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
getCreamSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
});
await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
@@ -583,22 +537,6 @@ describe('MarketOperationUtils tests', () => {
}
return DEFAULT_OPS.getTwoHopSellQuotes(...args);
},
getBalancerSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
getCreamSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
});
await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
@@ -629,22 +567,6 @@ describe('MarketOperationUtils tests', () => {
}
return DEFAULT_OPS.getTwoHopSellQuotes(sources, ...args);
},
getBalancerSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
getCreamSellQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamSellQuotesOffChainAsync(makerToken, takerToken, takerFillAmounts);
},
});
await getMarketSellOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
@@ -1446,22 +1368,6 @@ describe('MarketOperationUtils tests', () => {
}
return DEFAULT_OPS.getTwoHopBuyQuotes(..._args);
},
getBalancerBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
getCreamBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
});
await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
@@ -1492,22 +1398,6 @@ describe('MarketOperationUtils tests', () => {
}
return DEFAULT_OPS.getTwoHopBuyQuotes(..._args);
},
getBalancerBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
getCreamBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
});
await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,
@@ -1538,22 +1428,6 @@ describe('MarketOperationUtils tests', () => {
}
return DEFAULT_OPS.getTwoHopBuyQuotes(..._args);
},
getBalancerBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Balancer);
return DEFAULT_OPS.getBalancerBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
getCreamBuyQuotesOffChainAsync: (
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
) => {
sourcesPolled = sourcesPolled.concat(ERC20BridgeSource.Cream);
return DEFAULT_OPS.getCreamBuyQuotesOffChainAsync(makerToken, takerToken, makerFillAmounts);
},
});
await getMarketBuyOrdersAsync(marketOperationUtils, ORDERS, FILL_AMOUNT, {
...DEFAULT_OPTS,

View File

@@ -0,0 +1,68 @@
import * as chai from 'chai';
import 'mocha';
import {
BalancerPoolsCache,
BalancerV2PoolsCache,
CreamPoolsCache,
PoolsCache,
} from '../src/utils/market_operation_utils/pools_cache';
import { chaiSetup } from './utils/chai_setup';
chaiSetup.configure();
const expect = chai.expect;
const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';
const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f';
const wethAddress = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const wbtcAddress = '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599';
const balAddress = '0xba100000625a3754423978a60c9317c58a424e3d';
const creamAddress = '0x2ba592f78db6436527729929aaf6c908497cb200';
const timeoutMs = 5000;
const poolKeys: string[] = ['id', 'balanceIn', 'balanceOut', 'weightIn', 'weightOut', 'swapFee'];
describe('Pools Caches for Balancer-based sampling', () => {
async function fetchAndAssertPoolsAsync(cache: PoolsCache, takerToken: string, makerToken: string): Promise<void> {
const pools = await cache.getFreshPoolsForPairAsync(takerToken, makerToken, timeoutMs);
expect(pools.length).greaterThan(0, `Failed to find any pools for ${takerToken} and ${makerToken}`);
expect(pools[0]).not.undefined();
expect(Object.keys(pools[0])).to.include.members(poolKeys);
const cachedPoolIds = cache.getCachedPoolAddressesForPair(takerToken, makerToken);
expect(cachedPoolIds).to.deep.equal(pools.map(p => p.id));
}
describe('BalancerPoolsCache', () => {
const cache = new BalancerPoolsCache();
it('fetches pools', async () => {
const pairs = [[usdcAddress, daiAddress], [usdcAddress, wethAddress], [daiAddress, wethAddress]];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
describe('BalancerV2PoolsCache', () => {
const cache = new BalancerV2PoolsCache();
it('fetches pools', async () => {
const pairs = [[wethAddress, wbtcAddress], [wethAddress, balAddress]];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
describe('CreamPoolsCache', () => {
const cache = new CreamPoolsCache();
it('fetches pools', async () => {
const pairs = [[usdcAddress, creamAddress], [creamAddress, wethAddress]];
await Promise.all(
// tslint:disable-next-line:promise-function-async
pairs.map(([takerToken, makerToken]) => fetchAndAssertPoolsAsync(cache, takerToken, makerToken)),
);
});
});
});

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