Compare commits

...

97 Commits

Author SHA1 Message Date
Noah Khamliche
2825114d20 arbitrum pls work 2022-02-08 17:49:36 -05:00
Noah Khamliche
e7191f021c rebased dev->arbitrum 2022-02-08 17:48:53 -05:00
Noah Khamliche
e925373700 arbitrum setup 2022-02-08 17:48:32 -05:00
Noah Khamliche
c5d73dfe1b arbitrum asset swapper 2022-02-08 17:47:25 -05:00
Jacob Evans
5d3c7f9831 fix: slippage inconsistency when recalculated in exchange proxy quote consumer (#412)
* fix: Pass slippage down rather than recalculate due to accuracy

* CHANGELOG
2022-02-08 17:46:07 -05:00
Github Actions
adbab7d9ae Publish
- @0x/asset-swapper@16.49.2
2022-02-08 17:46:07 -05:00
Github Actions
c1e241a7b7 Updated CHANGELOGS & MD docs 2022-02-08 17:46:07 -05:00
Lawrence Forman
7e1538e9c5 Fix ABI encoding error with two hop buys due to applying slippage to uint(-1) values (#410)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2022-02-08 17:46:06 -05:00
Github Actions
182aa02ea3 Publish
- @0x/asset-swapper@16.49.1
2022-02-08 17:46:06 -05:00
Github Actions
9530a62252 Updated CHANGELOGS & MD docs 2022-02-08 17:46:06 -05:00
Jacob Evans
5c01979d3a fix: worstCaseQuoteInfo calculating buys incorrectly [TKR-296] (#402)
* fix: WorstCaseQuoteInfo buy encoding

* CHANGELOG
2022-02-08 17:46:06 -05:00
Github Actions
547facc8c7 Publish
- @0x/asset-swapper@16.49.0
2022-02-08 17:46:06 -05:00
Github Actions
b0de0e0aac Updated CHANGELOGS & MD docs 2022-02-08 17:46:06 -05:00
Shawn
ec22db3444 feat: add curve pools (#409)
* feat: Additional Curve pools

* more pools

* fix the format

* changlog

* lowercase address

Co-authored-by: Jacob Evans <jacob@dekz.net>
2022-02-08 17:46:03 -05:00
Github Actions
66c854b0a0 Publish
- @0x/asset-swapper@16.48.0
2022-02-08 17:43:05 -05:00
Github Actions
587f13dffd Updated CHANGELOGS & MD docs 2022-02-08 17:43:05 -05:00
Jacob Evans
8ed6dd6178 chore: Use MIM on Fantom as an intermediary asset (#405) 2022-02-08 17:43:05 -05:00
Github Actions
ed667954c6 Publish
- @0x/asset-swapper@16.47.0
2022-02-08 17:43:05 -05:00
Github Actions
a0b4fabc78 Updated CHANGELOGS & MD docs 2022-02-08 17:43:05 -05:00
Noah Khamliche
bab1100790 Feat/synapse (#400)
* added synapse support
2022-02-08 17:43:03 -05:00
Github Actions
1569a6b7f9 Publish
- @0x/asset-swapper@16.46.0
2022-02-08 17:41:48 -05:00
Github Actions
3556122e33 Updated CHANGELOGS & MD docs 2022-02-08 17:41:48 -05:00
Jacob Evans
35c2d829cd chore: Enable Curve ETH/CVX (#394)
* chore: Enable Curve ETH/CVX

* pr number
2022-02-08 17:41:48 -05:00
Github Actions
cb35ad0826 Publish
- @0x/asset-swapper@16.45.2
2022-02-08 17:41:48 -05:00
Github Actions
73b6124d72 Updated CHANGELOGS & MD docs 2022-02-08 17:41:48 -05:00
Kim Persson
1693a457c7 fix: don't create fills for 0 output samples and negative adjusted rate orders (#387)
* fix: don't try to create fills for 0 output samples

* fix: negative adjusted output native orders causing undefined fills

* fix: make sure to use the same sourcePathId for fills from same source

* fix: should be same sourcePathId within the same DexSample[]

* fix: split native orders into 13 samples to align with interpolation

* chore: add changelog entry for asset-swapper
2022-02-08 17:41:48 -05:00
Github Actions
5ef78cc3d0 Publish
- @0x/asset-swapper@16.45.1
2022-02-08 17:41:48 -05:00
Github Actions
3ed639e828 Updated CHANGELOGS & MD docs 2022-02-08 17:41:48 -05:00
Jacob Evans
23227ebc5b chore: Celo Update certain tokens since Optics v2 (#390)
* chore: Celo Update certain tokens since Optics v2

* Changelog
2022-02-08 17:41:45 -05:00
Oskar Paolini
cda2fc8daa fixes axios object dumping in logs (#345) 2022-02-08 17:41:04 -05:00
Github Actions
b3152bcdd4 Publish
- @0x/asset-swapper@16.45.0
2022-02-08 17:41:04 -05:00
Github Actions
464fdab147 Updated CHANGELOGS & MD docs 2022-02-08 17:41:04 -05:00
Jacob Evans
0fbe7e355e feat: Capture Routing timing metrics (#388) 2022-02-08 17:41:04 -05:00
Github Actions
9229f10675 Publish
- @0x/asset-swapper@16.44.0
2022-02-08 17:41:03 -05:00
Github Actions
8fa6184ca3 Updated CHANGELOGS & MD docs 2022-02-08 17:41:03 -05:00
Kim Persson
0e85a100d4 feat: Udate neon-router and use router estimated output amount (#354)
* feat: use Rust router estimated output amount when possible

* fix: use strings for sample ids, and increase samples in the rust router

* fix: remove unnecessary interpolation of out of range values

* fix: don't recalculate sampled dist sum in a loop

* fix: use 14 samples for rust router to work around interpolation issues

* fix: unintentional logic change

* fix: remove local dev plotting param from route fn call

* feat: make neon-router number of samples configurable

* chore: bump to newly published neon-router version

* fix: handle insufficient liquidity at all requested sources

* chore: update asset-swapper changelog
2022-02-08 17:41:03 -05:00
Github Actions
702fe8e693 Publish
- @0x/contracts-erc20@3.3.25
 - @0x/contracts-test-utils@5.4.16
 - @0x/contracts-treasury@1.4.8
 - @0x/contracts-utils@4.8.6
 - @0x/contracts-zero-ex@0.30.1
 - @0x/asset-swapper@16.43.0
 - @0x/contract-addresses@6.11.0
 - @0x/contract-wrappers@13.18.5
 - @0x/migrations@8.1.14
 - @0x/protocol-utils@1.10.1
2022-02-08 17:41:03 -05:00
Github Actions
a0228e2890 Updated CHANGELOGS & MD docs 2022-02-08 17:41:03 -05:00
Shawn
b94d2bc74a feat/optimism [TKR-280] (#385)
* rebase and remove WIP Clipper new weth router test

* remove clipper test

* restore from clipper test

* add uniswapV3 on optimism

* modify token addresses and add EP-related addresses on optimism

* prettier

* modify CHANGLOG.json

* convert addresses to lowercase

* remove testnet addresses

Co-authored-by: Romain Butteaud <romain.butteaud@gmail.com>
2022-02-08 17:41:00 -05:00
Github Actions
26298a8c68 Publish
- @0x/asset-swapper@16.42.0
2022-02-08 17:38:46 -05:00
Github Actions
a5b12c1da8 Updated CHANGELOGS & MD docs 2022-02-08 17:38:46 -05:00
Jacob Evans
2f4e1b0ca3 fix: Beethovenx graphql endpoint (#383) 2022-02-08 17:38:46 -05:00
Jacob Evans
5af9386dd4 fix: PR number in CHANGELOG 2022-02-08 17:38:46 -05:00
Jacob Evans
2c5e8d315a feat: UniswapV3 on Polygon (#382) 2022-02-08 17:38:28 -05:00
Github Actions
9a884e4f1d Publish
- @0x/asset-swapper@16.41.0
2022-02-08 17:38:21 -05:00
Github Actions
f4745ca8a2 Updated CHANGELOGS & MD docs 2022-02-08 17:38:21 -05:00
Noah Khamliche
1fca663c97 Sushi router celo fix (#376)
* Updated Sushiswap router on CELO

Sushi address in the docs was incorrect, this is the correct address `0x1421bDe4B10e8dd459b3BCb598810B1337D56842`.

* address to lowercase

* Update CHANGELOG.json

* fixed sushi router address, replaced celo ref with wcelo, fixed mcusd address

* celo->wcelo

* Update packages/asset-swapper/CHANGELOG.json

Co-authored-by: Jacob Evans <jacob@dekz.net>

* changelog

* version bump

Co-authored-by: Jacob Evans <jacob@dekz.net>
2022-02-08 17:38:18 -05:00
Github Actions
5dbad1f247 Publish
- @0x/contracts-erc20@3.3.24
 - @0x/contracts-test-utils@5.4.15
 - @0x/contracts-treasury@1.4.7
 - @0x/contracts-utils@4.8.5
 - @0x/contracts-zero-ex@0.30.0
 - @0x/asset-swapper@16.40.0
 - @0x/contract-addresses@6.10.0
 - @0x/contract-wrappers@13.18.4
 - @0x/migrations@8.1.13
 - @0x/protocol-utils@1.10.0
2022-02-08 17:37:47 -05:00
Github Actions
83a3993f19 Updated CHANGELOGS & MD docs 2022-02-08 17:37:46 -05:00
Kim Persson
52f035c4e6 feat: deploy interest tokens (#321)
* feat: Aave aToken deposit/withdrawal [TKR-111] (#293)

* feat: AaveV2 deposit/withdrawal integration WIP

* feat: add basic Aave Reserves cache with data from subgraphs WIP

* feat: hook up Aave Reserves integration

* fix: set allowance before trade & use ERC20 token interface

* refactor: pass aToken to mixin to avoid lookup

* fix: migrate from swap/revert to normal sampling

* fix: Aave gas estimate & refactor to clean up code

* feat: Create a sampler no operation type and make AaveV2Sampler a no-op

* fix: Clipper merge conflict resolution issues

* fix: don't fetch unnecessary Aave pool data & clean up code

* chore: Add changelog entries

* feat: cToken deposit/withdrawal [TKR-222] (#294)

* feat: first stab at a CompoundSampler implementation

* feat: MixinCompound implementation WIP

* feat: Compound integration with cache WIP

* fix: decimals scaling in CompoundSampler

* feat: handle minting and redeeming of cETH

* fix: adjust Compound gas schedule

* refactor: clean up code and add comments in cToken cache

* fix: MixinCompound check allowance on WETH withdrawal & fix indentation

* fix: address review comments and clean up code

* chore: add changelog entries

* feat: enable AaveV2 on Avalanche

* chore: add freshly deployed FQT on Polygon, Avalanche

* fix: temporarily disable on Ethereum mainnet until we redeploy EP

* fix: address PR comments and update changelogs

* fix: correct contract-addresses changelog note
2022-02-08 17:37:46 -05:00
Jacob Evans
789f16bf5b chore: Curve added ETH/CRV pool (#378) 2022-02-08 17:37:46 -05:00
Jacob Evans
e844bb8e38 Revert "chore: Curve added ETH/CRV pool"
This reverts commit 90b441330b.
2022-02-08 17:37:46 -05:00
Jacob Evans
9baaf2c43f chore: Curve added ETH/CRV pool 2022-02-08 17:37:46 -05:00
Github Actions
8138f4d2dd Publish
- @0x/asset-swapper@16.38.0
2022-02-08 17:37:46 -05:00
Github Actions
59dde95034 Updated CHANGELOGS & MD docs 2022-02-08 17:37:46 -05:00
Jacob Evans
5d7368fb17 chore: Add ability to capture sampler metrics (#374)
* chore: Add ability to capture sampler metrics

* Added block number metrics
2022-02-08 17:37:46 -05:00
Github Actions
c4ab263aa1 Publish
- @0x/asset-swapper@16.37.0
2022-02-08 17:37:46 -05:00
Github Actions
820f850a19 Updated CHANGELOGS & MD docs 2022-02-08 17:37:45 -05:00
Noah Khamliche
09571c73af removed comma 2022-02-08 17:37:45 -05:00
Noah Khamliche
e3caedc68a Update CHANGELOG.json 2022-02-08 17:37:45 -05:00
Noah Khamliche
6a9fe6bf5e address to lowercase 2022-02-08 17:37:43 -05:00
Noah Khamliche
077c98740b Updated Sushiswap router on CELO
Sushi address in the docs was incorrect, this is the correct address `0x1421bDe4B10e8dd459b3BCb598810B1337D56842`.
2022-02-08 17:37:09 -05:00
Github Actions
3752f86115 Publish
- @0x/asset-swapper@16.36.0
2022-02-08 17:36:23 -05:00
Github Actions
f654d8a612 Updated CHANGELOGS & MD docs 2022-02-08 17:36:23 -05:00
Jacob Evans
3b463c12d7 feat: Specify FEI/TRIBE liquid pool (#371)
* feat: Specify FEI/TRIBE liquid pool

* CHANGELOG

* FXS/FRAX and OHM/FRAX
2022-02-08 17:36:23 -05:00
Github Actions
6995b7ac9a Publish
- @0x/asset-swapper@16.35.0
2022-02-08 17:36:23 -05:00
Github Actions
e4edd25987 Updated CHANGELOGS & MD docs 2022-02-08 17:36:23 -05:00
Shawn
53dfa6acb3 feat: Add Beethoven X, MorpheusSwap and JetSwap on Fantom (#370)
* Rebase to development branch

* add support for MorpheusSwap and JetSwap on Fantom

* fix lint

* modify changelog.json for asset-swapper

* fix the comments

* prettier

* Use BalancerV2PoolsCache for BeethovenX

Co-authored-by: Romain Butteaud <romain.butteaud@gmail.com>
2022-02-08 17:36:23 -05:00
Github Actions
b7d2575b8b Publish
- @0x/contracts-erc20@3.3.23
 - @0x/contracts-test-utils@5.4.14
 - @0x/contracts-treasury@1.4.6
 - @0x/contracts-utils@4.8.4
 - @0x/contracts-zero-ex@0.29.5
 - @0x/asset-swapper@16.34.0
 - @0x/contract-addresses@6.9.0
 - @0x/contract-wrappers@13.18.3
 - @0x/migrations@8.1.12
 - @0x/protocol-utils@1.9.5
2022-02-08 17:36:22 -05:00
Github Actions
6855e4dc60 Updated CHANGELOGS & MD docs 2022-02-08 17:36:22 -05:00
Noah Khamliche
eae9ca717a Update packages/contract-addresses/CHANGELOG.json
Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
2022-02-08 17:36:22 -05:00
Noah Khamliche
70d9b1ffda removed duplicate celo code 2022-02-08 17:36:22 -05:00
Noah Khamliche
939421a573 upped versions in changelog 2022-02-08 17:36:22 -05:00
Lawrence Forman
1e160e8a1c fix celo rebase 2022-02-08 17:36:19 -05:00
Github Actions
c1854031c0 Publish
- @0x/contracts-zero-ex@0.29.4
 - @0x/asset-swapper@16.33.0
 - @0x/migrations@8.1.11
2022-02-08 17:31:40 -05:00
Github Actions
92d99c859a Updated CHANGELOGS & MD docs 2022-02-08 17:31:40 -05:00
Jacob Evans
76c331dcbf feat: Uniswap V3 1bps pools [TKR-263] (#366)
* feat: Uniswap V3 1bps pools

* Update CHANGELOG
2022-02-08 17:31:39 -05:00
Lawrence Forman
0b719e48f2 @0x/contracts-zero-ex: Prevent EP ETH balance from reducing when executin mtxs (#365)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2022-02-08 17:31:39 -05:00
Github Actions
d958aeba21 Publish
- @0x/asset-swapper@16.32.0
2022-02-08 17:31:39 -05:00
Github Actions
699ed52e56 Updated CHANGELOGS & MD docs 2022-02-08 17:31:39 -05:00
Jorge Pérez
2da1abd5b5 fix: Extended Quote Report Changelog typo 2022-02-08 17:31:39 -05:00
Jorge Pérez
cbaa54e5bb feat: Extended Quote Report
* Extended Quote report for indicative quote

* feat: Only save 'full' quotes on quote report

* Unify extended quote report
2022-02-08 17:31:39 -05:00
Github Actions
e5926e8c2c Publish
- @0x/asset-swapper@16.31.0
2022-02-08 17:31:39 -05:00
Github Actions
24b6267d8a Updated CHANGELOGS & MD docs 2022-02-08 17:31:39 -05:00
Jacob Evans
1883a0c9f8 feat: [Avalanche] Add Curve, CurveV2 and KyberDMM (#363)
* feat: [Avalanche] Add Curve, CurveV2 and KyberDMM

* CHANGELOG

* fix missing file

* lint
2022-02-08 17:31:35 -05:00
Github Actions
e885f07531 Publish
- @0x/contracts-erc20@3.3.22
 - @0x/contracts-test-utils@5.4.13
 - @0x/contracts-treasury@1.4.5
 - @0x/contracts-utils@4.8.3
 - @0x/contracts-zero-ex@0.29.3
 - @0x/asset-swapper@16.30.1
 - @0x/contract-addresses@6.8.1
 - @0x/contract-wrappers@13.18.2
 - @0x/migrations@8.1.10
 - @0x/protocol-utils@1.9.4
2022-02-08 17:28:21 -05:00
Github Actions
288274476e Updated CHANGELOGS & MD docs 2022-02-08 17:28:21 -05:00
phil-ociraptor
4d78d0c9e8 chore: add OtcOrder feature to fullMigrateAsync (#350) 2022-02-08 17:28:21 -05:00
shawnxin89
5c9b655d8c further cleanup 2021-11-01 15:54:18 -07:00
shawnxin89
7fc98eca22 cleanup 2021-11-01 15:49:42 -07:00
shawnxin89
c0cb26892f add liquidity sources on Arbitrum 2021-11-01 15:06:21 -07:00
shawnxin89
04d3c50576 Merge branch 'development' of github.com:0xProject/protocol into development 2021-10-26 22:23:37 -07:00
shawnxin89
23f5ecac45 Merge branch 'development' of github.com:0xProject/protocol into development 2021-10-19 23:01:50 -07:00
shawnxin89
834a96fe47 Merge branch 'development' of github.com:0xProject/protocol into development 2021-10-18 10:17:04 -07:00
shawnxin89
dbf899b20d Merge branch 'development' of github.com:0xProject/protocol into development 2021-10-15 10:15:00 -07:00
shawnxin89
c6a138a158 Merge branch 'development' of github.com:0xProject/protocol into development 2021-10-08 11:15:29 -07:00
Romain Butteaud
f3b3ab05ea WIP Clipper new weth router test 2021-09-23 11:41:49 -07:00
80 changed files with 3618 additions and 329 deletions

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1640364306,
"version": "3.3.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "3.3.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "3.3.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "3.3.22",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "3.3.21",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.25 - _December 24, 2021_
* Dependencies updated
## v3.3.24 - _December 1, 2021_
* Dependencies updated
## v3.3.23 - _November 16, 2021_
* Dependencies updated
## v3.3.22 - _November 3, 2021_
* Dependencies updated
## v3.3.21 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.21",
"version": "3.3.25",
"engines": {
"node": ">=6.12"
},
@@ -53,8 +53,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.12",
"@0x/contracts-utils": "^4.8.2",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/contracts-utils": "^4.8.6",
"@0x/dev-utils": "^4.2.9",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1640364306,
"version": "5.4.16",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "5.4.15",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "5.4.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "5.4.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "5.4.12",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.16 - _December 24, 2021_
* Dependencies updated
## v5.4.15 - _December 1, 2021_
* Dependencies updated
## v5.4.14 - _November 16, 2021_
* Dependencies updated
## v5.4.13 - _November 3, 2021_
* Dependencies updated
## v5.4.12 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.12",
"version": "5.4.16",
"engines": {
"node": ">=6.12"
},
@@ -44,7 +44,7 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/order-utils": "^10.4.28",

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1640364306,
"version": "1.4.8",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "1.4.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "1.4.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "1.4.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "1.4.4",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.4.8 - _December 24, 2021_
* Dependencies updated
## v1.4.7 - _December 1, 2021_
* Dependencies updated
## v1.4.6 - _November 16, 2021_
* Dependencies updated
## v1.4.5 - _November 3, 2021_
* Dependencies updated
## v1.4.4 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.4.4",
"version": "1.4.8",
"engines": {
"node": ">=6.12"
},
@@ -47,12 +47,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.21",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-test-utils": "^5.4.12",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/sol-compiler": "^4.7.5",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.2",
"@0x/protocol-utils": "^1.9.3",
"@0x/protocol-utils": "^1.10.1",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1640364306,
"version": "4.8.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "4.8.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "4.8.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "4.8.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "4.8.2",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.8.6 - _December 24, 2021_
* Dependencies updated
## v4.8.5 - _December 1, 2021_
* Dependencies updated
## v4.8.4 - _November 16, 2021_
* Dependencies updated
## v4.8.3 - _November 3, 2021_
* Dependencies updated
## v4.8.2 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.8.2",
"version": "4.8.6",
"engines": {
"node": ">=6.12"
},
@@ -52,7 +52,7 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.12",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/dev-utils": "^4.2.9",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.7.5",

View File

@@ -1,12 +1,55 @@
[
{
"timestamp": 1640364306,
"version": "0.30.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.30.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"timestamp": 1637102971,
"version": "0.29.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "0.29.4",
"changes": [
{
"note": "Prevent EP ETH balance from reducing when executin mtxs",
"pr": 365
}
],
"timestamp": 1637065617
},
{
"version": "0.29.3",
"changes": [
{
"note": "Register transformERC20() and remove transformERC20Staging()",
"pr": 355
},
{
"note": "Add OtcOrders to FullMigration",
"pr": 350
}
]
],
"timestamp": 1635903615
},
{
"timestamp": 1634668033,

View File

@@ -5,6 +5,27 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.30.1 - _December 24, 2021_
* Dependencies updated
## v0.30.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v0.29.5 - _November 16, 2021_
* Dependencies updated
## v0.29.4 - _November 16, 2021_
* Prevent EP ETH balance from reducing when executin mtxs (#365)
## v0.29.3 - _November 3, 2021_
* Register transformERC20() and remove transformERC20Staging() (#355)
* Add OtcOrders to FullMigration (#350)
## v0.29.2 - _October 19, 2021_
* Dependencies updated

View File

@@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "MetaTransactions";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 1);
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
"MetaTransactionData("
@@ -105,6 +105,17 @@ contract MetaTransactionsFeature is
}
}
/// @dev Ensures that the ETH balance of `this` does not go below the
/// initial ETH balance before the call (excluding ETH attached to the call).
modifier doesNotReduceEthBalance() {
uint256 initialBalance = address(this).balance - msg.value;
_;
require(
initialBalance <= address(this).balance,
"MetaTransactionsFeature/ETH_LEAK"
);
}
constructor(address zeroExAddress)
public
FixinCommon()
@@ -140,6 +151,7 @@ contract MetaTransactionsFeature is
payable
override
nonReentrant(REENTRANCY_MTX)
doesNotReduceEthBalance
refundsAttachedEth
returns (bytes memory returnResult)
{
@@ -164,6 +176,7 @@ contract MetaTransactionsFeature is
payable
override
nonReentrant(REENTRANCY_MTX)
doesNotReduceEthBalance
refundsAttachedEth
returns (bytes[] memory returnResults)
{

View File

@@ -25,6 +25,7 @@ import "../features/interfaces/IOwnableFeature.sol";
import "../features/TransformERC20Feature.sol";
import "../features/MetaTransactionsFeature.sol";
import "../features/NativeOrdersFeature.sol";
import "../features/OtcOrdersFeature.sol";
import "./InitialMigration.sol";
@@ -40,6 +41,7 @@ contract FullMigration {
TransformERC20Feature transformERC20;
MetaTransactionsFeature metaTransactions;
NativeOrdersFeature nativeOrders;
OtcOrdersFeature otcOrders;
}
/// @dev Parameters needed to initialize features.
@@ -173,5 +175,16 @@ contract FullMigration {
address(this)
);
}
// OtcOrdersFeature
{
// Register the feature.
ownable.migrate(
address(features.otcOrders),
abi.encodeWithSelector(
OtcOrdersFeature.migrate.selector
),
address(this)
);
}
}
}

View File

@@ -22,10 +22,12 @@ pragma experimental ABIEncoderV2;
import "./IBridgeAdapter.sol";
import "./BridgeProtocols.sol";
import "./mixins/MixinAaveV2.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCompound.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
import "./mixins/MixinCryptoCom.sol";
@@ -47,10 +49,12 @@ import "./mixins/MixinZeroExBridge.sol";
contract BridgeAdapter is
IBridgeAdapter,
MixinAaveV2,
MixinBalancer,
MixinBalancerV2,
MixinBancor,
MixinCoFiX,
MixinCompound,
MixinCurve,
MixinCurveV2,
MixinCryptoCom,
@@ -72,10 +76,12 @@ contract BridgeAdapter is
{
constructor(IEtherTokenV06 weth)
public
MixinAaveV2()
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinCoFiX()
MixinCompound(weth)
MixinCurve(weth)
MixinCurveV2()
MixinCryptoCom()
@@ -245,6 +251,20 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.AAVEV2) {
boughtAmount = _tradeAaveV2(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.COMPOUND) {
boughtAmount = _tradeCompound(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,

View File

@@ -50,4 +50,6 @@ library BridgeProtocols {
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant CLIPPER = 22; // Not used: Clipper is now using PLP interface
uint128 internal constant AAVEV2 = 23;
uint128 internal constant COMPOUND = 24;
}

View File

@@ -0,0 +1,93 @@
// 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";
// Minimal Aave V2 LendingPool interface
interface ILendingPool {
/**
* @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens.
* - E.g. User deposits 100 USDC and gets in return 100 aUSDC
* @param asset The address of the underlying asset to deposit
* @param amount The amount to be deposited
* @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user
* wants to receive them on his own wallet, or a different address if the beneficiary of aTokens
* is a different wallet
* @param referralCode Code used to register the integrator originating the operation, for potential rewards.
* 0 if the action is executed directly by the user, without any middle-man
**/
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external;
/**
* @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned
* E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC
* @param asset The address of the underlying asset to withdraw
* @param amount The underlying amount to be withdrawn
* - Send the value type(uint256).max in order to withdraw the whole aToken balance
* @param to Address that will receive the underlying, same as msg.sender if the user
* wants to receive it on his own wallet, or a different address if the beneficiary is a
* different wallet
* @return The final amount withdrawn
**/
function withdraw(
address asset,
uint256 amount,
address to
) external returns (uint256);
}
contract MixinAaveV2 {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeAaveV2(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256)
{
(ILendingPool lendingPool, address aToken) = abi.decode(bridgeData, (ILendingPool, address));
sellToken.approveIfBelow(
address(lendingPool),
sellAmount
);
if (address(buyToken) == aToken) {
lendingPool.deposit(address(sellToken), sellAmount, address(this), 0);
// 1:1 mapping token -> aToken and have the same number of decimals as the underlying token
return sellAmount;
} else if (address(sellToken) == aToken) {
return lendingPool.withdraw(address(buyToken), sellAmount, address(this));
}
revert("MixinAaveV2/UNSUPPORTED_TOKEN_PAIR");
}
}

View File

@@ -0,0 +1,110 @@
// 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 "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
/// @dev Minimal CToken interface
interface ICToken {
/// @dev deposits specified amount underlying tokens and mints cToken for the sender
/// @param mintAmountInUnderlying amount of underlying tokens to deposit to mint cTokens
/// @return status code of whether the mint was successful or not
function mint(uint256 mintAmountInUnderlying) external returns (uint256);
/// @dev redeems specified amount of cTokens and returns the underlying token to the sender
/// @param redeemTokensInCtokens amount of cTokens to redeem for underlying collateral
/// @return status code of whether the redemption was successful or not
function redeem(uint256 redeemTokensInCtokens) external returns (uint256);
}
/// @dev Minimal CEther interface
interface ICEther {
/// @dev deposits the amount of Ether sent as value and return mints cEther for the sender
function mint() payable external;
/// @dev redeems specified amount of cETH and returns the underlying ether to the sender
/// @dev redeemTokensInCEther amount of cETH to redeem for underlying ether
/// @return status code of whether the redemption was successful or not
function redeem(uint256 redeemTokensInCEther) external returns (uint256);
}
contract MixinCompound {
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
uint256 constant private COMPOUND_SUCCESS_CODE = 0;
function _tradeCompound(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256)
{
(address cTokenAddress) = abi.decode(bridgeData, (address));
uint256 beforeBalance = buyToken.balanceOf(address(this));
if (address(buyToken) == cTokenAddress) {
if (address(sellToken) == address(WETH)) {
// ETH/WETH -> cETH
ICEther cETH = ICEther(cTokenAddress);
// Compound expects ETH to be sent with mint call
WETH.withdraw(sellAmount);
// NOTE: cETH mint will revert on failure instead of returning a status code
cETH.mint{value: sellAmount}();
} else {
sellToken.approveIfBelow(
cTokenAddress,
sellAmount
);
// Token -> cToken
ICToken cToken = ICToken(cTokenAddress);
require(cToken.mint(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_MINT_CTOKEN");
}
} else if (address(sellToken) == cTokenAddress) {
if (address(buyToken) == address(WETH)) {
// cETH -> ETH/WETH
uint256 etherBalanceBefore = address(this).balance;
ICEther cETH = ICEther(cTokenAddress);
require(cETH.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CETHER");
uint256 etherBalanceAfter = address(this).balance;
uint256 receivedEtherBalance = etherBalanceAfter.safeSub(etherBalanceBefore);
WETH.deposit{value: receivedEtherBalance}();
} else {
ICToken cToken = ICToken(cTokenAddress);
require(cToken.redeem(sellAmount) == COMPOUND_SUCCESS_CODE, "MixinCompound/FAILED_TO_REDEEM_CTOKEN");
}
}
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
}
}

View File

@@ -46,6 +46,10 @@ contract TestMetaTransactionsTransformERC20Feature is
payable
returns (uint256 outputTokenAmount)
{
if (msg.value == 555) {
tx.origin.transfer(1);
}
if (msg.value == 666) {
revert('FAIL');
}

Binary file not shown.

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.29.2",
"version": "0.30.1",
"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,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|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|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|FundRecoveryFeature|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|IFundRecoveryFeature|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinAaveV2|MixinBalancer|MixinBalancerV2|MixinBancor|MixinCoFiX|MixinCompound|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|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|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@@ -56,10 +56,10 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.6.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contracts-erc20": "^3.3.21",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.12",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/dev-utils": "^4.2.9",
"@0x/order-utils": "^10.4.28",
"@0x/sol-compiler": "^4.7.5",
@@ -83,7 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.2",
"@0x/protocol-utils": "^1.9.3",
"@0x/protocol-utils": "^1.10.1",
"@0x/subproviders": "^6.6.0",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",

View File

@@ -12,6 +12,7 @@ import {
IZeroExContract,
MetaTransactionsFeatureContract,
NativeOrdersFeatureContract,
OtcOrdersFeatureContract,
OwnableFeatureContract,
SimpleFunctionRegistryFeatureContract,
TransformERC20FeatureContract,
@@ -113,6 +114,7 @@ export interface FullFeatures extends BootstrapFeatures {
transformERC20: string;
metaTransactions: string;
nativeOrders: string;
otcOrders: string;
}
/**
@@ -123,6 +125,7 @@ export interface FullFeatureArtifacts extends BootstrapFeatureArtifacts {
metaTransactions: SimpleContractArtifact;
nativeOrders: SimpleContractArtifact;
feeCollectorController: SimpleContractArtifact;
otcOrders: SimpleContractArtifact;
}
/**
@@ -155,6 +158,7 @@ const DEFAULT_FULL_FEATURES_ARTIFACTS = {
metaTransactions: artifacts.MetaTransactionsFeature,
nativeOrders: artifacts.NativeOrdersFeature,
feeCollectorController: artifacts.FeeCollectorController,
otcOrders: artifacts.OtcOrdersFeature,
};
/**
@@ -222,6 +226,18 @@ export async function deployFullFeaturesAsync(
_config.protocolFeeMultiplier,
)
).address,
otcOrders:
features.otcOrders ||
(
await OtcOrdersFeatureContract.deployFrom0xArtifactAsync(
_featureArtifacts.otcOrders,
provider,
txDefaults,
artifacts,
_config.zeroExAddress,
_config.wethAddress,
)
).address,
};
}

View File

@@ -81,10 +81,12 @@ import * as LiquidityProviderFeature from '../test/generated-artifacts/Liquidity
import * as LiquidityProviderSandbox from '../test/generated-artifacts/LiquidityProviderSandbox.json';
import * as LogMetadataTransformer from '../test/generated-artifacts/LogMetadataTransformer.json';
import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransactionsFeature.json';
import * as MixinAaveV2 from '../test/generated-artifacts/MixinAaveV2.json';
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCompound from '../test/generated-artifacts/MixinCompound.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
@@ -272,10 +274,12 @@ export const artifacts = {
BridgeAdapter: BridgeAdapter as ContractArtifact,
BridgeProtocols: BridgeProtocols as ContractArtifact,
IBridgeAdapter: IBridgeAdapter as ContractArtifact,
MixinAaveV2: MixinAaveV2 as ContractArtifact,
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCompound: MixinCompound as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,
MixinCurveV2: MixinCurveV2 as ContractArtifact,

View File

@@ -38,6 +38,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
let nativeOrdersFeature: TestMetaTransactionsNativeOrdersFeatureContract;
const MAX_FEE_AMOUNT = new BigNumber('1e18');
const TRANSFORM_ERC20_ONE_WEI_VALUE = new BigNumber(555);
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
@@ -597,7 +598,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
);
});
it('cannot reenter `executeMetaTransaction()`', async () => {
it('cannot reduce initial ETH balance', async () => {
const args = getRandomTransformERC20Args();
const mtx = getRandomMetaTransaction({
callData: transformERC20Feature
@@ -609,58 +610,23 @@ blockchainTests.resets('MetaTransactions feature', env => {
args.transformations,
)
.getABIEncodedTransactionData(),
value: TRANSFORM_ERC20_REENTER_VALUE,
value: TRANSFORM_ERC20_ONE_WEI_VALUE,
});
const mtxHash = mtx.getHash();
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
const callOpts = {
gasPrice: mtx.maxGasPrice,
value: mtx.value,
};
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
return expect(tx).to.revertWith(
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
mtxHash,
undefined,
new ZeroExRevertErrors.Common.IllegalReentrancyError(
feature.getSelector('executeMetaTransaction'),
REENTRANCY_FLAG_MTX,
).encode(),
),
// Send pre-existing ETH to the EP.
await env.web3Wrapper.awaitTransactionSuccessAsync(
await env.web3Wrapper.sendTransactionAsync({
from: owner,
to: zeroEx.address,
value: new BigNumber(1),
}),
);
});
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
const args = getRandomTransformERC20Args();
const mtx = getRandomMetaTransaction({
callData: transformERC20Feature
.transformERC20(
args.inputToken,
args.outputToken,
args.inputTokenAmount,
args.minOutputTokenAmount,
args.transformations,
)
.getABIEncodedTransactionData(),
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
});
const mtxHash = mtx.getHash();
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
const callOpts = {
gasPrice: mtx.maxGasPrice,
value: mtx.value,
};
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
return expect(tx).to.revertWith(
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
mtxHash,
undefined,
new ZeroExRevertErrors.Common.IllegalReentrancyError(
feature.getSelector('batchExecuteMetaTransactions'),
REENTRANCY_FLAG_MTX,
).encode(),
),
);
return expect(tx).to.revertWith('MetaTransactionsFeature/ETH_LEAK');
});
});
@@ -817,6 +783,37 @@ blockchainTests.resets('MetaTransactions feature', env => {
),
);
});
it('cannot reduce initial ETH balance', async () => {
const args = getRandomTransformERC20Args();
const mtx = getRandomMetaTransaction({
callData: transformERC20Feature
.transformERC20(
args.inputToken,
args.outputToken,
args.inputTokenAmount,
args.minOutputTokenAmount,
args.transformations,
)
.getABIEncodedTransactionData(),
value: TRANSFORM_ERC20_ONE_WEI_VALUE,
});
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
const callOpts = {
gasPrice: mtx.maxGasPrice,
value: mtx.value,
};
// Send pre-existing ETH to the EP.
await env.web3Wrapper.awaitTransactionSuccessAsync(
await env.web3Wrapper.sendTransactionAsync({
from: owner,
to: zeroEx.address,
value: new BigNumber(1),
}),
);
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
return expect(tx).to.revertWith('MetaTransactionsFeature/ETH_LEAK');
});
});
describe('getMetaTransactionExecutedBlock()', () => {

View File

@@ -79,10 +79,12 @@ export * from '../test/generated-wrappers/liquidity_provider_feature';
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_aave_v2';
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_compound';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';
export * from '../test/generated-wrappers/mixin_curve_v2';

View File

@@ -112,10 +112,12 @@
"test/generated-artifacts/LiquidityProviderSandbox.json",
"test/generated-artifacts/LogMetadataTransformer.json",
"test/generated-artifacts/MetaTransactionsFeature.json",
"test/generated-artifacts/MixinAaveV2.json",
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCompound.json",
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",
"test/generated-artifacts/MixinCurveV2.json",

View File

@@ -1,4 +1,263 @@
[
{
"version": "16.50.0",
"changes": [
{
"note": "Add support for Arbitrum one mainnet"
}
]
},
{
"version": "16.49.3",
"changes": [
{
"note": "Fix `slippage` inconsistency when recalculated in exchange proxy quote consumer",
"pr": 412
}
]
},
{
"version": "16.49.2",
"changes": [
{
"note": "Fix ABI encoding error with two hop buys due to applying slippage to uint(-1) values",
"pr": 410
}
],
"timestamp": 1643653482
},
{
"version": "16.49.1",
"changes": [
{
"note": "Fix WorstCaseQuoteInfo encoding bug",
"pr": 402
}
],
"timestamp": 1643613597
},
{
"version": "16.49.0",
"changes": [
{
"note": "Add more curve pools",
"pr": 409
}
],
"timestamp": 1643407900
},
{
"version": "16.48.0",
"changes": [
{
"note": "Use `MIM` as an intermediate asset on `Fantom`",
"pr": 405
}
],
"timestamp": 1643148019
},
{
"version": "16.47.0",
"changes": [
{
"note": "Adding support for Synapse on all networks",
"pr": 400
}
],
"timestamp": 1643136662
},
{
"version": "16.46.0",
"changes": [
{
"note": "Enable `Curve` ETH/CVX pool",
"pr": 394
}
],
"timestamp": 1641863395
},
{
"version": "16.45.2",
"changes": [
{
"note": "Handle 0 output samples and negative adjusted rate native orders in routing",
"pr": 387
}
],
"timestamp": 1641827361
},
{
"version": "16.45.1",
"changes": [
{
"note": "Update `Celo` intermediate tokens",
"pr": 390
}
],
"timestamp": 1641359319
},
{
"version": "16.45.0",
"changes": [
{
"note": "Capture router timings",
"pr": 388
}
],
"timestamp": 1641308410
},
{
"version": "16.44.0",
"changes": [
{
"note": "Update neon-router and use router estimated output amount",
"pr": 354
}
],
"timestamp": 1640778328
},
{
"version": "16.43.0",
"changes": [
{
"note": "`UniswapV3` support for `Optimism`",
"pr": 385
}
],
"timestamp": 1640364306
},
{
"version": "16.42.0",
"changes": [
{
"note": "`UniswapV3` support for `Polygon`",
"pr": 382
},
{
"note": "Update `Beethoven` Graphql url",
"pr": 383
}
],
"timestamp": 1640124159
},
{
"version": "16.41.0",
"changes": [
{
"note": "Update mcusd contract address, and made celo native asset",
"pr": 376
}
],
"timestamp": 1638827302
},
{
"version": "16.40.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"version": "16.39.0",
"changes": [
{
"note": "Curve ETH/CRV pool",
"pr": 378
}
]
},
{
"version": "16.38.0",
"changes": [
{
"note": "Capture sampler metrics",
"pr": 374
}
],
"timestamp": 1638228231
},
{
"version": "16.37.0",
"changes": [
{
"note": "Changed Sushiswap router address",
"pr": 373
}
],
"timestamp": 1637349338
},
{
"version": "16.36.0",
"changes": [
{
"note": "Specify liquid routes for FEI/TRIBE FXS/FRAX and OHM/FRAX",
"pr": 371
}
],
"timestamp": 1637290768
},
{
"version": "16.35.0",
"changes": [
{
"note": "Add Beethoven X, MorpheusSwap and JetSwap to Fantom",
"pr": 370
}
],
"timestamp": 1637206290
},
{
"version": "16.34.0",
"changes": [
{
"note": "Add support Celo",
"pr": 367
}
],
"timestamp": 1637102971
},
{
"version": "16.33.0",
"changes": [
{
"note": "Add support for Uniswap V3 1 bps pools",
"pr": 366
}
],
"timestamp": 1637065617
},
{
"version": "16.32.0",
"changes": [
{
"note": "Extended Quote Report",
"pr": 361
}
],
"timestamp": 1636480845
},
{
"timestamp": 1635903615,
"version": "16.31.0",
"changes": [
{
"note": "Added `Curve`, `Curve_V2` and `KyberDmm` to Avalanche",
"pr": 363
}
]
},
{
"timestamp": 1635903615,
"version": "16.30.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "16.30.0",
"changes": [

View File

@@ -5,6 +5,103 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v16.49.2 - _January 31, 2022_
* Fix ABI encoding error with two hop buys due to applying slippage to uint(-1) values (#410)
## v16.49.1 - _January 31, 2022_
* Fix WorstCaseQuoteInfo encoding bug (#402)
## v16.49.0 - _January 28, 2022_
* Add more curve pools (#409)
## v16.48.0 - _January 25, 2022_
* Use `MIM` as an intermediate asset on `Fantom` (#405)
## v16.47.0 - _January 25, 2022_
* Adding support for Synapse on all networks (#400)
## v16.46.0 - _January 11, 2022_
* Enable `Curve` ETH/CVX pool (#394)
## v16.45.2 - _January 10, 2022_
* Handle 0 output samples and negative adjusted rate native orders in routing (#387)
## v16.45.1 - _January 5, 2022_
* Update `Celo` intermediate tokens (#390)
## v16.45.0 - _January 4, 2022_
* Capture router timings (#388)
## v16.44.0 - _December 29, 2021_
* Update neon-router and use router estimated output amount (#354)
## v16.43.0 - _December 24, 2021_
* `UniswapV3` support for `Optimism` (#385)
## v16.42.0 - _December 21, 2021_
* `UniswapV3` support for `Polygon` (#382)
* Update `Beethoven` Graphql url (#383)
## v16.41.0 - _December 6, 2021_
* Update mcusd contract address, and made celo native asset (#376)
## v16.40.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v16.39.0 - _Invalid date_
* Curve ETH/CRV pool (#378)
## v16.38.0 - _November 29, 2021_
* Capture sampler metrics (#374)
## v16.37.0 - _November 19, 2021_
* Changed Sushiswap router address (#373)
## v16.36.0 - _November 19, 2021_
* Specify liquid routes for FEI/TRIBE FXS/FRAX and OHM/FRAX (#371)
## v16.35.0 - _November 18, 2021_
* Add Beethoven X, MorpheusSwap and JetSwap to Fantom (#370)
## v16.34.0 - _November 16, 2021_
* Add support Celo (#367)
## v16.33.0 - _November 16, 2021_
* Add support for Uniswap V3 1 bps pools (#366)
## v16.32.0 - _November 9, 2021_
* Extended Quote Report (#361)
## v16.31.0 - _November 3, 2021_
* Added `Curve`, `Curve_V2` and `KyberDmm` to Avalanche (#363)
## v16.30.1 - _November 3, 2021_
* Dependencies updated
## v16.30.0 - _October 19, 2021_
* Fantom deployment (#347)

View File

@@ -0,0 +1,96 @@
// 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";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
// Minimal CToken interface
interface ICToken {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function exchangeRateStored() external view returns (uint);
function decimals() external view returns (uint8);
}
contract CompoundSampler is SamplerUtils {
uint256 constant private EXCHANGE_RATE_SCALE = 1e10;
function sampleSellsFromCompound(
ICToken cToken,
IERC20TokenV06 takerToken,
IERC20TokenV06 makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
uint256 exchangeRate = cToken.exchangeRateStored();
uint256 cTokenDecimals = uint256(cToken.decimals());
if (address(makerToken) == address(cToken)) {
// mint
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = (takerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals) / exchangeRate;
}
} else if (address(takerToken) == address(cToken)) {
// redeem
for (uint256 i = 0; i < numSamples; i++) {
makerTokenAmounts[i] = (takerTokenAmounts[i] * exchangeRate) / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
}
}
}
function sampleBuysFromCompound(
ICToken cToken,
IERC20TokenV06 takerToken,
IERC20TokenV06 makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
// Exchange rate is scaled by 1 * 10^(18 - 8 + Underlying Token Decimals
uint256 exchangeRate = cToken.exchangeRateStored();
uint256 cTokenDecimals = uint256(cToken.decimals());
if (address(makerToken) == address(cToken)) {
// mint
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = makerTokenAmounts[i] * exchangeRate / (EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals);
}
} else if (address(takerToken) == address(cToken)) {
// redeem
for (uint256 i = 0; i < numSamples; i++) {
takerTokenAmounts[i] = (makerTokenAmounts[i] * EXCHANGE_RATE_SCALE * 10 ** cTokenDecimals)/exchangeRate;
}
}
}
}

View File

@@ -23,6 +23,7 @@ pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol";
import "./BancorSampler.sol";
import "./CompoundSampler.sol";
import "./CurveSampler.sol";
import "./DODOSampler.sol";
import "./DODOV2Sampler.sol";
@@ -48,6 +49,7 @@ contract ERC20BridgeSampler is
BalancerSampler,
BalancerV2Sampler,
BancorSampler,
CompoundSampler,
CurveSampler,
DODOSampler,
DODOV2Sampler,

View File

@@ -159,8 +159,11 @@ contract KyberDmmSampler
(path[i], path[i + 1])
returns (address[] memory allPools)
{
if (allPools.length == 0) {
return new address[](0);
}
uint256 maxSupply = 0;
require(allPools.length >= 1, "KyberDMMSampler/NO_POOLS_FOUND");
for (uint256 j = 0; j < allPools.length; j++) {
uint256 totalSupply = IKyberDmmPool(allPools[j]).totalSupply();
if (totalSupply > maxSupply) {

View File

@@ -51,7 +51,7 @@ interface IUniswapV3Pool {
contract UniswapV3Sampler
{
/// @dev Gas limit for UniswapV3 calls. This is 100% a guess.
uint256 constant private QUOTE_GAS = 300e3;
uint256 constant private QUOTE_GAS = 600e3;
/// @dev Sample sell quotes from UniswapV3.
/// @param quoter UniswapV3 Quoter contract.
@@ -174,8 +174,9 @@ contract UniswapV3Sampler
tokenPath.length - startIndex >= 2,
"UniswapV3Sampler/tokenPath too short"
);
uint24[3] memory validPoolFees = [
uint24[4] memory validPoolFees = [
// The launch pool fees. Could get hairier if they add more.
uint24(0.0001e6),
uint24(0.0005e6),
uint24(0.003e6),
uint24(0.01e6)

View File

@@ -77,4 +77,19 @@ contract UtilitySampler {
assembly { size := extcodesize(account) }
return size > 0;
}
function getGasLeft()
public
returns (uint256)
{
return gasleft();
}
function getBlockNumber()
public
view
returns (uint256)
{
return block.number;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "16.30.0",
"version": "16.49.2",
"engines": {
"node": ">=6.12"
},
@@ -39,7 +39,7 @@
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BalancerV2Sampler|BancorSampler|CompoundSampler|CurveSampler|DODOSampler|DODOV2Sampler|DummyLiquidityProvider|ERC20BridgeSampler|FakeTaker|IBalancer|IBancor|ICurve|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|ISmoothy|IUniswapExchangeQuotes|IUniswapV2Router01|KyberDmmSampler|KyberSampler|LidoSampler|LiquidityProviderSampler|MStableSampler|MakerPSMSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SmoothySampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UniswapV3Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}
@@ -60,14 +60,15 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-wrappers": "^13.18.1",
"@0x/contracts-erc20": "^3.3.21",
"@0x/contracts-zero-ex": "^0.29.2",
"@0x/contract-addresses": "^6.10.0",
"@0x/contract-wrappers": "^13.18.4",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-erc20": "^3.3.24",
"@0x/contracts-zero-ex": "^0.30.0",
"@0x/dev-utils": "^4.2.9",
"@0x/json-schemas": "^6.3.0",
"@0x/neon-router": "^0.2.1",
"@0x/protocol-utils": "^1.9.3",
"@0x/neon-router": "^0.3.1",
"@0x/protocol-utils": "^1.10.1",
"@0x/quote-server": "^6.0.6",
"@0x/types": "^3.3.4",
"@0x/typescript-typings": "^5.2.1",
@@ -98,10 +99,10 @@
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-libs": "^4.3.37",
"@0x/contracts-gen": "^2.0.40",
"@0x/contracts-test-utils": "^5.4.12",
"@0x/contracts-utils": "^4.8.2",
"@0x/contracts-test-utils": "^5.4.16",
"@0x/contracts-utils": "^4.8.6",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^8.1.9",
"@0x/migrations": "^8.1.14",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -28,7 +28,6 @@ const ONE_SECOND_MS = 1000;
const ONE_MINUTE_SECS = 60;
const ONE_MINUTE_MS = ONE_SECOND_MS * ONE_MINUTE_SECS;
const DEFAULT_PER_PAGE = 1000;
const ZERO_AMOUNT = new BigNumber(0);
const ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS = 180;
const DEFAULT_ORDER_PRUNER_OPTS: OrderPrunerOpts = {
@@ -43,6 +42,7 @@ const PROTOCOL_FEE_MULTIPLIER = new BigNumber(0);
// default 50% buffer for selecting native orders to be aggregated with other sources
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
export const ZERO_AMOUNT = new BigNumber(0);
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
chainId: ChainId.Mainnet,
orderRefreshIntervalMs: 10000, // 10 seconds

View File

@@ -113,6 +113,7 @@ export {
SwapQuoterError,
SwapQuoterOpts,
SwapQuoterRfqOpts,
SamplerMetrics,
} from './types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export {
@@ -162,14 +163,20 @@ export {
export { ProtocolFeeUtils } from './utils/protocol_fee_utils';
export {
BridgeQuoteReportEntry,
jsonifyFillData,
MultiHopQuoteReportEntry,
NativeLimitOrderQuoteReportEntry,
NativeRfqOrderQuoteReportEntry,
QuoteReport,
QuoteReportEntry,
ExtendedQuoteReport,
ExtendedQuoteReportSources,
ExtendedQuoteReportEntry,
ExtendedQuoteReportIndexedEntry,
ExtendedQuoteReportIndexedEntryOutbound,
PriceComparisonsReport,
} from './utils/quote_report_generator';
export { QuoteRequestor } from './utils/quote_requestor';
export { QuoteRequestor, V4RFQIndicativeQuoteMM } from './utils/quote_requestor';
export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers';
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
export type Native = ERC20BridgeSource.Native;

View File

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

View File

@@ -360,8 +360,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
// Build up the transforms.
const transforms = [];
if (isFromETH) {
// Create a WETH wrapper if coming from ETH.
// Create a WETH wrapper if coming from ETH.
// Dont add the wethTransformer to CELO. There is no wrap/unwrap logic for CELO.
if (isFromETH && this.chainId !== ChainId.Celo) {
transforms.push({
deploymentNonce: this.transformerNonces.wethTransformer,
data: encodeWethTransformerData({
@@ -413,9 +414,9 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
}),
});
}
if (isToETH) {
// Create a WETH unwrapper if going to ETH.
// Create a WETH unwrapper if going to ETH.
// Dont add the wethTransformer on CELO. There is no wrap/unwrap logic for CELO.
if (isToETH && this.chainId !== ChainId.Celo) {
transforms.push({
deploymentNonce: this.transformerNonces.wethTransformer,
data: encodeWethTransformerData({
@@ -492,10 +493,11 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
amounts: [],
}),
});
const TO_ETH_ADDRESS = this.chainId === ChainId.Celo ? this.contractAddresses.etherToken : ETH_TOKEN_ADDRESS;
const calldataHexString = this._exchangeProxy
.transformERC20(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
isToETH ? TO_ETH_ADDRESS : buyToken,
shouldSellEntireBalance ? MAX_UINT256 : sellAmount,
minBuyAmount,
transforms,
@@ -689,7 +691,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): OptimizedMarketOrder[] {
const slippage = getMaxQuoteSlippageRate(quote);
if (!slippage) {
if (slippage === 0) {
return quote.orders;
}
return quote.orders.map(o => {
@@ -699,25 +701,20 @@ function slipNonNativeOrders(quote: MarketSellSwapQuote | MarketBuySwapQuote): O
return {
...o,
...(quote.type === MarketOperation.Sell
? { makerAmount: o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN) }
: { takerAmount: o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP) }),
? {
makerAmount: o.makerAmount.eq(MAX_UINT256)
? MAX_UINT256
: o.makerAmount.times(1 - slippage).integerValue(BigNumber.ROUND_DOWN),
}
: {
takerAmount: o.takerAmount.eq(MAX_UINT256)
? MAX_UINT256
: o.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
}),
};
});
}
function getMaxQuoteSlippageRate(quote: MarketBuySwapQuote | MarketSellSwapQuote): number {
if (quote.type === MarketOperation.Buy) {
// (worstCaseTaker - bestCaseTaker) / bestCaseTaker
// where worstCaseTaker >= bestCaseTaker
return quote.worstCaseQuoteInfo.takerAmount
.minus(quote.bestCaseQuoteInfo.takerAmount)
.div(quote.bestCaseQuoteInfo.takerAmount)
.toNumber();
}
// (bestCaseMaker - worstCaseMaker) / bestCaseMaker
// where bestCaseMaker >= worstCaseMaker
return quote.bestCaseQuoteInfo.makerAmount
.minus(quote.worstCaseQuoteInfo.makerAmount)
.div(quote.bestCaseQuoteInfo.makerAmount)
.toNumber();
return quote.worstCaseQuoteInfo.slippage;
}

View File

@@ -361,6 +361,8 @@ export class SwapQuoter {
// ** Prepare options for fetching market side liquidity **
// Scale fees by gas price.
const cloneOpts = _.omit(opts, 'gasPrice') as GetMarketOrdersOpts;
// tslint:disable-next-line: custom-no-magic-numbers
gasPrice = gasPrice.times(10);
const calcOpts: GetMarketOrdersOpts = {
...cloneOpts,
gasPrice,
@@ -505,6 +507,7 @@ function createSwapQuote(
const {
optimizedOrders,
quoteReport,
extendedQuoteReportSources,
sourceFlags,
takerAmountPerEth,
makerAmountPerEth,
@@ -532,6 +535,7 @@ function createSwapQuote(
takerAmountPerEth,
makerAmountPerEth,
quoteReport,
extendedQuoteReportSources,
isTwoHop,
priceComparisonsReport,
};
@@ -576,8 +580,8 @@ function calculateQuoteInfo(
});
return {
bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult),
worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult),
bestCaseQuoteInfo: fillResultsToQuoteInfo(bestCaseFillResult, 0),
worstCaseQuoteInfo: fillResultsToQuoteInfo(worstCaseFillResult, slippage),
sourceBreakdown: getSwapQuoteOrdersBreakdown(bestCaseFillResult.fillAmountBySource),
};
}
@@ -597,29 +601,33 @@ function calculateTwoHopQuoteInfo(
secondHopSource: _.pick(secondHopFill, 'source', 'fillData'),
}),
).toNumber();
const isSell = operation === MarketOperation.Sell;
return {
bestCaseQuoteInfo: {
makerAmount: operation === MarketOperation.Sell ? secondHopFill.output : secondHopFill.input,
takerAmount: operation === MarketOperation.Sell ? firstHopFill.input : firstHopFill.output,
totalTakerAmount: operation === MarketOperation.Sell ? firstHopFill.input : firstHopFill.output,
makerAmount: isSell ? secondHopFill.output : secondHopFill.input,
takerAmount: isSell ? firstHopFill.input : firstHopFill.output,
totalTakerAmount: isSell ? firstHopFill.input : firstHopFill.output,
feeTakerTokenAmount: constants.ZERO_AMOUNT,
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
gas,
slippage: 0,
},
// TODO jacob consolidate this with quote simulation worstCase
worstCaseQuoteInfo: {
makerAmount: MarketOperation.Sell
makerAmount: isSell
? secondHopOrder.makerAmount.times(1 - slippage).integerValue()
: secondHopOrder.makerAmount,
takerAmount: MarketOperation.Sell
takerAmount: isSell
? firstHopOrder.takerAmount
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
totalTakerAmount: MarketOperation.Sell
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
totalTakerAmount: isSell
? firstHopOrder.takerAmount
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(),
: firstHopOrder.takerAmount.times(1 + slippage).integerValue(BigNumber.ROUND_UP),
feeTakerTokenAmount: constants.ZERO_AMOUNT,
protocolFeeInWeiAmount: constants.ZERO_AMOUNT,
gas,
slippage,
},
sourceBreakdown: {
[ERC20BridgeSource.MultiHop]: {
@@ -645,7 +653,7 @@ function getSwapQuoteOrdersBreakdown(fillAmountBySource: { [source: string]: Big
return breakdown;
}
function fillResultsToQuoteInfo(fr: QuoteFillResult): SwapQuoteInfo {
function fillResultsToQuoteInfo(fr: QuoteFillResult, slippage: number): SwapQuoteInfo {
return {
makerAmount: fr.totalMakerAssetAmount,
takerAmount: fr.takerAssetAmount,
@@ -653,6 +661,7 @@ function fillResultsToQuoteInfo(fr: QuoteFillResult): SwapQuoteInfo {
feeTakerTokenAmount: fr.takerFeeTakerAssetAmount,
protocolFeeInWeiAmount: fr.protocolFeeAmount,
gas: fr.gas,
slippage,
};
}

View File

@@ -19,7 +19,8 @@ import {
OptimizedMarketOrder,
TokenAdjacencyGraph,
} from './utils/market_operation_utils/types';
import { PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
export { SamplerMetrics } from './utils/market_operation_utils/types';
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from './utils/quote_report_generator';
import { MetricsProxy } from './utils/quote_requestor';
/**
@@ -171,6 +172,7 @@ export interface SwapQuoteBase {
worstCaseQuoteInfo: SwapQuoteInfo;
sourceBreakdown: SwapQuoteOrdersBreakdown;
quoteReport?: QuoteReport;
extendedQuoteReportSources?: ExtendedQuoteReportSources;
priceComparisonsReport?: PriceComparisonsReport;
isTwoHop: boolean;
makerTokenDecimals: number;
@@ -206,6 +208,7 @@ export type SwapQuote = MarketBuySwapQuote | MarketSellSwapQuote;
* makerTokenAmount: The amount of makerAsset that will be acquired through the swap.
* protocolFeeInWeiAmount: The amount of ETH to pay (in WEI) as protocol fee to perform the swap for desired asset.
* gas: Amount of estimated gas needed to fill the quote.
* slippage: Amount of slippage to allow for.
*/
export interface SwapQuoteInfo {
feeTakerTokenAmount: BigNumber;
@@ -214,6 +217,7 @@ export interface SwapQuoteInfo {
makerAmount: BigNumber;
protocolFeeInWeiAmount: BigNumber;
gas: number;
slippage: number;
}
/**

View File

@@ -223,7 +223,17 @@ export async function returnQuoteFromAltMMAsync<ResponseT>(
cancelToken,
})
.catch(err => {
warningLogger(err, `Alt RFQ MM request failed`);
if (err.response) {
// request was made and market maker responded
warningLogger(
{ data: err.response.data, status: err.response.status, headers: err.response.headers },
`Alt RFQ MM request failed`,
);
} else if (err.request) {
warningLogger({}, 'Alt RFQ MM no response received');
} else {
warningLogger({ err: err.message }, 'Failed to construct Alt RFQ MM request');
}
throw new Error(`Alt RFQ MM request failed`);
});

View File

@@ -0,0 +1,106 @@
import { logUtils } from '@0x/utils';
import { gql, request } from 'graphql-request';
import { constants } from '../../constants';
const RESERVES_GQL_QUERY = gql`
{
reserves(
first: 300
where: { isActive: true, isFrozen: false }
orderBy: totalLiquidity
orderDirection: desc
) {
id
underlyingAsset
aToken {
id
}
pool {
id
lendingPool
}
}
}
`;
export interface AaveReserve {
id: string;
underlyingAsset: string;
aToken: {
id: string;
};
pool: {
id: string;
lendingPool: string;
};
}
interface Cache {
[key: string]: AaveReserve[];
}
// tslint:disable-next-line:custom-no-magic-numbers
const RESERVES_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**
* Fetches Aave V2 reserve information from the official subgraph(s).
* The reserve information is updated every 30 minutes and cached
* so that it can be accessed with the underlying token's address
*/
export class AaveV2ReservesCache {
private _cache: Cache = {};
constructor(private readonly _subgraphUrl: string) {
const resfreshReserves = async () => this.fetchAndUpdateReservesAsync();
// tslint:disable-next-line:no-floating-promises
resfreshReserves();
setInterval(resfreshReserves, RESERVES_REFRESH_INTERVAL_MS);
}
/**
* Fetches Aave V2 reserves from the subgraph and updates the cache
*/
public async fetchAndUpdateReservesAsync(): Promise<void> {
try {
const { reserves } = await request<{ reserves: AaveReserve[] }>(this._subgraphUrl, RESERVES_GQL_QUERY);
const newCache = reserves.reduce<Cache>((memo, reserve) => {
const underlyingAsset = reserve.underlyingAsset.toLowerCase();
if (!memo[underlyingAsset]) {
memo[underlyingAsset] = [];
}
memo[underlyingAsset].push(reserve);
return memo;
}, {});
this._cache = newCache;
} catch (err) {
logUtils.warn(`Failed to update Aave V2 reserves cache: ${err.message}`);
// Empty cache just to be safe
this._cache = {};
}
}
public get(takerToken: string, makerToken: string): AaveReserve | undefined {
// Deposit takerToken into reserve
if (this._cache[takerToken.toLowerCase()]) {
const matchingReserve = this._cache[takerToken.toLowerCase()].find(
r => r.aToken.id === makerToken.toLowerCase(),
);
if (matchingReserve) {
return matchingReserve;
}
}
// Withdraw makerToken from reserve
if (this._cache[makerToken.toLowerCase()]) {
const matchingReserve = this._cache[makerToken.toLowerCase()].find(
r => r.aToken.id === takerToken.toLowerCase(),
);
if (matchingReserve) {
return matchingReserve;
}
}
// No match
return undefined;
}
}

View File

@@ -11,9 +11,13 @@ import {
COMETHSWAP_ROUTER_BY_CHAIN_ID,
COMPONENT_POOLS_BY_CHAIN_ID,
CRYPTO_COM_ROUTER_BY_CHAIN_ID,
CURVE_AVALANCHE_INFOS,
CURVE_FANTOM_INFOS,
CURVE_ARBITRUM_INFOS,
CURVE_MAINNET_INFOS,
CURVE_OPTIMISM_INFOS,
CURVE_POLYGON_INFOS,
CURVE_V2_AVALANCHE_INFOS,
CURVE_V2_FANTOM_INFOS,
CURVE_V2_MAINNET_INFOS,
CURVE_V2_POLYGON_INFOS,
@@ -28,6 +32,7 @@ import {
KYBER_BRIDGED_LIQUIDITY_PREFIX,
MAX_DODOV2_POOLS_QUERIED,
MAX_KYBER_RESERVES_QUERIED,
MORPHEUSSWAP_ROUTER_BY_CHAIN_ID,
MSTABLE_POOLS_BY_CHAIN_ID,
NERVE_BSC_INFOS,
NULL_ADDRESS,
@@ -46,10 +51,18 @@ import {
SPOOKYSWAP_ROUTER_BY_CHAIN_ID,
SUSHISWAP_ROUTER_BY_CHAIN_ID,
SWERVE_MAINNET_INFOS,
SYNAPSE_AVALANCHE_INFOS,
SYNAPSE_BSC_INFOS,
SYNAPSE_FANTOM_INFOS,
SYNAPSE_MAINNET_INFOS,
SYNAPSE_OPTIMISM_INFOS,
SYNAPSE_POLYGON_INFOS,
TRADER_JOE_ROUTER_BY_CHAIN_ID,
UBESWAP_ROUTER_BY_CHAIN_ID,
UNISWAPV2_ROUTER_BY_CHAIN_ID,
WAULTSWAP_ROUTER_BY_CHAIN_ID,
XSIGMA_MAINNET_INFOS,
SYNAPSE_ARBITRUM_INFOS,
} from './constants';
import { CurveInfo, ERC20BridgeSource } from './types';
@@ -146,6 +159,33 @@ export function getCurveInfosForPair(chainId: ChainId, takerToken: string, maker
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Avalanche:
return Object.values(CURVE_AVALANCHE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Optimism:
return Object.values(CURVE_OPTIMISM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Arbitrum:
return Object.values(CURVE_OPTIMISM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
default:
return [];
}
@@ -181,6 +221,24 @@ export function getCurveV2InfosForPair(chainId: ChainId, takerToken: string, mak
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Avalanche:
return Object.values(CURVE_V2_AVALANCHE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Arbitrum:
return Object.values(CURVE_ARBITRUM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
default:
return [];
}
@@ -225,6 +283,76 @@ export function getNerveInfosForPair(chainId: ChainId, takerToken: string, maker
);
}
export function getSynapseInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
switch (chainId) {
case ChainId.Mainnet:
return Object.values(SYNAPSE_MAINNET_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Optimism:
return Object.values(SYNAPSE_OPTIMISM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.BSC:
return Object.values(SYNAPSE_BSC_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Polygon:
return Object.values(SYNAPSE_POLYGON_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Fantom:
return Object.values(SYNAPSE_FANTOM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Avalanche:
return Object.values(SYNAPSE_AVALANCHE_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
case ChainId.Arbitrum:
return Object.values(SYNAPSE_ARBITRUM_INFOS).filter(c =>
[makerToken, takerToken].every(
t =>
(c.tokens.includes(t) && c.metaTokens === undefined) ||
(c.tokens.includes(t) &&
[makerToken, takerToken].filter(v => c.metaTokens?.includes(v)).length > 0),
),
);
default:
return [];
}
}
export function getFirebirdOneSwapInfosForPair(chainId: ChainId, takerToken: string, makerToken: string): CurveInfo[] {
if (chainId === ChainId.BSC) {
return Object.values(FIREBIRDONESWAP_BSC_INFOS).filter(c =>
@@ -384,6 +512,7 @@ export function getCurveLikeInfosForPair(
| ERC20BridgeSource.Swerve
| ERC20BridgeSource.SnowSwap
| ERC20BridgeSource.Nerve
| ERC20BridgeSource.Synapse
| ERC20BridgeSource.Belt
| ERC20BridgeSource.Ellipsis
| ERC20BridgeSource.Smoothy
@@ -410,6 +539,9 @@ export function getCurveLikeInfosForPair(
case ERC20BridgeSource.Nerve:
pools = getNerveInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Synapse:
pools = getSynapseInfosForPair(chainId, takerToken, makerToken);
break;
case ERC20BridgeSource.Belt:
pools = getBeltInfosForPair(chainId, takerToken, makerToken);
break;
@@ -466,6 +598,8 @@ export function uniswapV2LikeRouterAddress(
| ERC20BridgeSource.JetSwap
| ERC20BridgeSource.TraderJoe
| ERC20BridgeSource.Pangolin
| ERC20BridgeSource.UbeSwap
| ERC20BridgeSource.MorpheusSwap
| ERC20BridgeSource.SpookySwap
| ERC20BridgeSource.SpiritSwap,
): string {
@@ -508,6 +642,10 @@ export function uniswapV2LikeRouterAddress(
return PANGOLIN_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.TraderJoe:
return TRADER_JOE_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.UbeSwap:
return UBESWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.MorpheusSwap:
return MORPHEUSSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.SpookySwap:
return SPOOKYSWAP_ROUTER_BY_CHAIN_ID[chainId];
case ERC20BridgeSource.SpiritSwap:

View File

@@ -0,0 +1,78 @@
import { logUtils } from '@0x/utils';
import axios from 'axios';
import { constants } from '../../constants';
export interface CToken {
tokenAddress: string;
underlyingAddress: string;
}
interface CTokenApiResponse {
cToken: Array<{
token_address: string;
underlying_address: string;
}>;
}
interface Cache {
[key: string]: CToken;
}
// tslint:disable-next-line:custom-no-magic-numbers
const CTOKEN_REFRESH_INTERVAL_MS = 30 * constants.ONE_MINUTE_MS;
/**
* Fetches a list of CTokens from Compound's official API.
* The token information is updated every 30 minutes and cached
* so that it can be accessed with the underlying token's address.
*/
export class CompoundCTokenCache {
private _cache: Cache = {};
constructor(private readonly _apiUrl: string, private readonly _wethAddress: string) {
const refreshCTokenCache = async () => this.fetchAndUpdateCTokensAsync();
// tslint:disable-next-line:no-floating-promises
refreshCTokenCache();
setInterval(refreshCTokenCache, CTOKEN_REFRESH_INTERVAL_MS);
}
public async fetchAndUpdateCTokensAsync(): Promise<void> {
try {
const { data } = await axios.get<CTokenApiResponse>(`${this._apiUrl}/ctoken`);
const newCache = data?.cToken.reduce<Cache>((memo, cToken) => {
// NOTE: Re-map cETH with null underlying token address to WETH address (we only handle WETH internally)
const underlyingAddressClean = cToken.underlying_address
? cToken.underlying_address.toLowerCase()
: this._wethAddress;
const tokenData: CToken = {
tokenAddress: cToken.token_address.toLowerCase(),
underlyingAddress: underlyingAddressClean,
};
memo[underlyingAddressClean] = tokenData;
return memo;
}, {});
this._cache = newCache;
} catch (err) {
logUtils.warn(`Failed to update Compound cToken cache: ${err.message}`);
// NOTE: Safe to keep already cached data as tokens should only be added to the list
}
}
public get(takerToken: string, makerToken: string): CToken | undefined {
// mint cToken
let cToken = this._cache[takerToken.toLowerCase()];
if (cToken && makerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
return cToken;
}
// redeem cToken
cToken = this._cache[makerToken.toLowerCase()];
if (cToken && takerToken.toLowerCase() === cToken.tokenAddress.toLowerCase()) {
return cToken;
}
// No match
return undefined;
}
}

View File

@@ -18,12 +18,15 @@ import {
import {
dexSampleToReportSource,
ExtendedQuoteReportSources,
generateExtendedQuoteReportSources,
generateQuoteReport,
multiHopSampleToReportSource,
nativeOrderToReportEntry,
PriceComparisonsReport,
QuoteReport,
} from './../quote_report_generator';
import { getComparisonPrices } from './comparison_price';
import {
BUY_SOURCE_FILTER_BY_CHAIN_ID,
@@ -78,6 +81,25 @@ export class MarketOperationUtils {
return generateQuoteReport(side, quotes.nativeOrders, liquidityDelivered, comparisonPrice, quoteRequestor);
}
private static _computeExtendedQuoteReportSources(
quoteRequestor: QuoteRequestor | undefined,
marketSideLiquidity: MarketSideLiquidity,
amount: BigNumber,
optimizerResult: OptimizerResult,
comparisonPrice?: BigNumber | undefined,
): ExtendedQuoteReportSources {
const { side, quotes } = marketSideLiquidity;
const { liquidityDelivered } = optimizerResult;
return generateExtendedQuoteReportSources(
side,
quotes,
liquidityDelivered,
amount,
comparisonPrice,
quoteRequestor,
);
}
private static _computePriceComparisonsReport(
quoteRequestor: QuoteRequestor | undefined,
marketSideLiquidity: MarketSideLiquidity,
@@ -136,6 +158,8 @@ export class MarketOperationUtils {
// Call the sampler contract.
const samplerPromise = this._sampler.executeAsync(
this._sampler.getBlockNumber(),
this._sampler.getGasLeft(),
this._sampler.getTokenDecimals([makerToken, takerToken]),
// Get native order fillable amounts.
this._sampler.getLimitOrderFillableTakerAmounts(nativeOrders, this.contractAddresses.exchangeProxy),
@@ -162,6 +186,7 @@ export class MarketOperationUtils {
takerAmount,
),
this._sampler.isAddressContract(txOrigin),
this._sampler.getGasLeft(),
);
// Refresh the cached pools asynchronously if required
@@ -169,6 +194,8 @@ export class MarketOperationUtils {
const [
[
blockNumber,
gasBefore,
tokenDecimals,
orderFillableTakerAmounts,
outputAmountPerEth,
@@ -176,9 +203,14 @@ export class MarketOperationUtils {
dexQuotes,
rawTwoHopQuotes,
isTxOriginContract,
gasAfter,
],
] = await Promise.all([samplerPromise]);
// Log the gas metrics
_opts.samplerMetrics?.logGasDetails({ gasBefore, gasAfter });
_opts.samplerMetrics?.logBlockNumber(blockNumber);
// Filter out any invalid two hop quotes where we couldn't find a route
const twoHopQuotes = rawTwoHopQuotes.filter(
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
@@ -411,6 +443,7 @@ export class MarketOperationUtils {
feeSchedule: _opts.feeSchedule,
allowFallback: _opts.allowFallback,
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
},
);
return optimizerResult;
@@ -499,9 +532,18 @@ export class MarketOperationUtils {
penaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
opts.samplerMetrics,
);
} else {
optimalPath = await findOptimalPathJSAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
optimalPath = await findOptimalPathJSAsync(
side,
fills,
inputAmount,
opts.runLimit,
opts.samplerMetrics,
penaltyOpts,
);
}
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
@@ -564,6 +606,8 @@ export class MarketOperationUtils {
allowFallback: _opts.allowFallback,
exchangeProxyOverhead: _opts.exchangeProxyOverhead,
gasPrice: _opts.gasPrice,
neonRouterNumSamples: _opts.neonRouterNumSamples,
samplerMetrics: _opts.samplerMetrics,
};
if (nativeOrders.length === 0) {
@@ -702,6 +746,16 @@ export class MarketOperationUtils {
);
}
// Always compute the Extended Quote Report
let extendedQuoteReportSources: ExtendedQuoteReportSources | undefined;
extendedQuoteReportSources = MarketOperationUtils._computeExtendedQuoteReportSources(
_opts.rfqt ? _opts.rfqt.quoteRequestor : undefined,
marketSideLiquidity,
amount,
optimizerResult,
wholeOrderPrice,
);
let priceComparisonsReport: PriceComparisonsReport | undefined;
if (_opts.shouldIncludePriceComparisonsReport) {
priceComparisonsReport = MarketOperationUtils._computePriceComparisonsReport(
@@ -710,7 +764,7 @@ export class MarketOperationUtils {
wholeOrderPrice,
);
}
return { ...optimizerResult, quoteReport, priceComparisonsReport };
return { ...optimizerResult, quoteReport, extendedQuoteReportSources, priceComparisonsReport };
}
private async _refreshPoolCacheIfRequiredAsync(takerToken: string, makerToken: string): Promise<void> {
@@ -764,6 +818,8 @@ export class MarketOperationUtils {
sturdyPenaltyOpts,
opts.feeSchedule,
this._sampler.chainId,
opts.neonRouterNumSamples,
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
);
} else {
const sturdyFills = fills.filter(p => p.length > 0 && !fragileSources.includes(p[0].source));
@@ -772,6 +828,7 @@ export class MarketOperationUtils {
sturdyFills,
inputAmount,
opts.runLimit,
undefined, // hack: set sampler metrics to undefined to avoid fallback timings
sturdyPenaltyOpts,
);
}

View File

@@ -5,11 +5,13 @@ import { AssetSwapperContractAddresses, MarketOperation } from '../../types';
import { MAX_UINT256, ZERO_AMOUNT } from './constants';
import {
AaveV2FillData,
AggregationError,
BalancerFillData,
BalancerV2FillData,
BancorFillData,
CollapsedFill,
CompoundFillData,
CurveFillData,
DexSample,
DODOFillData,
@@ -132,6 +134,8 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'BakerySwap');
case ERC20BridgeSource.Nerve:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Nerve');
case ERC20BridgeSource.Synapse:
return encodeBridgeSourceId(BridgeProtocol.Nerve, 'Synapse');
case ERC20BridgeSource.Belt:
return encodeBridgeSourceId(BridgeProtocol.Curve, 'Belt');
case ERC20BridgeSource.Ellipsis:
@@ -184,10 +188,20 @@ export function getErc20BridgeSourceToBridgeSource(source: ERC20BridgeSource): s
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'Pangolin');
case ERC20BridgeSource.TraderJoe:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'TraderJoe');
case ERC20BridgeSource.UbeSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'UbeSwap');
case ERC20BridgeSource.Beethovenx:
return encodeBridgeSourceId(BridgeProtocol.BalancerV2, 'Beethovenx');
case ERC20BridgeSource.SpiritSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpiritSwap');
case ERC20BridgeSource.SpookySwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'SpookySwap');
case ERC20BridgeSource.MorpheusSwap:
return encodeBridgeSourceId(BridgeProtocol.UniswapV2, 'MorpheusSwap');
case ERC20BridgeSource.AaveV2:
return encodeBridgeSourceId(BridgeProtocol.AaveV2, 'AaveV2');
case ERC20BridgeSource.Compound:
return encodeBridgeSourceId(BridgeProtocol.Compound, 'Compound');
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -214,6 +228,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Synapse:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Smoothy:
@@ -236,6 +251,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
bridgeData = encoder.encode([balancerFillData.poolAddress]);
break;
case ERC20BridgeSource.BalancerV2:
case ERC20BridgeSource.Beethovenx:
const balancerV2FillData = (order as OptimizedMarketBridgeOrder<BalancerV2FillData>).fillData;
const { vault, poolId } = balancerV2FillData;
bridgeData = encoder.encode([vault, poolId]);
@@ -264,8 +280,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
case ERC20BridgeSource.UbeSwap:
case ERC20BridgeSource.SpiritSwap:
case ERC20BridgeSource.SpookySwap:
case ERC20BridgeSource.MorpheusSwap:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData>).fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
break;
@@ -330,6 +348,15 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const lidoFillData = (order as OptimizedMarketBridgeOrder<LidoFillData>).fillData;
bridgeData = encoder.encode([lidoFillData.stEthTokenAddress]);
break;
case ERC20BridgeSource.AaveV2:
const aaveFillData = (order as OptimizedMarketBridgeOrder<AaveV2FillData>).fillData;
bridgeData = encoder.encode([aaveFillData.lendingPool, aaveFillData.aToken]);
break;
case ERC20BridgeSource.Compound:
const compoundFillData = (order as OptimizedMarketBridgeOrder<CompoundFillData>).fillData;
bridgeData = encoder.encode([compoundFillData.cToken]);
break;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -439,6 +466,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.Swerve]: curveEncoder,
[ERC20BridgeSource.SnowSwap]: curveEncoder,
[ERC20BridgeSource.Nerve]: curveEncoder,
[ERC20BridgeSource.Synapse]: curveEncoder,
[ERC20BridgeSource.Belt]: curveEncoder,
[ERC20BridgeSource.Ellipsis]: curveEncoder,
[ERC20BridgeSource.Smoothy]: curveEncoder,
@@ -458,6 +486,9 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.TraderJoe]: routerAddressPathEncoder,
[ERC20BridgeSource.SpiritSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.SpookySwap]: routerAddressPathEncoder,
[ERC20BridgeSource.MorpheusSwap]: routerAddressPathEncoder,
// Celo
[ERC20BridgeSource.UbeSwap]: routerAddressPathEncoder,
// BSC
[ERC20BridgeSource.PancakeSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.PancakeSwapV2]: routerAddressPathEncoder,
@@ -485,12 +516,15 @@ export const BRIDGE_ENCODERS: {
// Custom integrations
[ERC20BridgeSource.MakerPsm]: makerPsmEncoder,
[ERC20BridgeSource.BalancerV2]: balancerV2Encoder,
[ERC20BridgeSource.Beethovenx]: balancerV2Encoder,
[ERC20BridgeSource.UniswapV3]: AbiEncoder.create([
{ name: 'router', type: 'address' },
{ name: 'path', type: 'bytes' },
]),
[ERC20BridgeSource.KyberDmm]: AbiEncoder.create('(address,address[],address[])'),
[ERC20BridgeSource.Lido]: AbiEncoder.create('(address)'),
[ERC20BridgeSource.AaveV2]: AbiEncoder.create('(address,address)'),
[ERC20BridgeSource.Compound]: AbiEncoder.create('(address)'),
};
function getFillTokenAmounts(fill: CollapsedFill, side: MarketOperation): [BigNumber, BigNumber] {

View File

@@ -1,23 +1,21 @@
import { assert } from '@0x/assert';
import { ChainId } from '@0x/contract-addresses';
import { OptimizerCapture, route, SerializedPath } from '@0x/neon-router';
import { BigNumber } from '@0x/utils';
import { BigNumber, hexUtils } from '@0x/utils';
import * as _ from 'lodash';
import { performance } from 'perf_hooks';
import { DEFAULT_INFO_LOGGER } from '../../constants';
import { MarketOperation, NativeOrderWithFillableAmounts } from '../../types';
import { VIP_ERC20_BRIDGE_SOURCES_BY_CHAIN_ID } from '../market_operation_utils/constants';
import { dexSamplesToFills, ethToOutputAmount, nativeOrdersToFills } from './fills';
import { DEFAULT_PATH_PENALTY_OPTS, Path, PathPenaltyOpts } from './path';
import { getRate } from './rate_utils';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData } from './types';
import { DexSample, ERC20BridgeSource, FeeSchedule, Fill, FillData, SamplerMetrics } from './types';
// tslint:disable: prefer-for-of custom-no-magic-numbers completed-docs no-bitwise
const RUN_LIMIT_DECAY_FACTOR = 0.5;
const RUST_ROUTER_NUM_SAMPLES = 200;
const FILL_QUOTE_TRANSFORMER_GAS_OVERHEAD = new BigNumber(150e3);
// NOTE: The Rust router will panic with less than 3 samples
const MIN_NUM_SAMPLE_INPUTS = 3;
@@ -69,21 +67,6 @@ function calculateOuputFee(
}
}
// Use linear interpolation to approximate the output
// at a certain input somewhere between the two samples
// See https://en.wikipedia.org/wiki/Linear_interpolation
const interpolateOutputFromSamples = (
left: { input: BigNumber; output: BigNumber },
right: { input: BigNumber; output: BigNumber },
targetInput: BigNumber,
): BigNumber =>
left.output.plus(
right.output
.minus(left.output)
.dividedBy(right.input.minus(left.input))
.times(targetInput.minus(left.input)),
);
function findRoutesAndCreateOptimalPath(
side: MarketOperation,
samples: DexSample[][],
@@ -91,29 +74,27 @@ function findRoutesAndCreateOptimalPath(
input: BigNumber,
opts: PathPenaltyOpts,
fees: FeeSchedule,
neonRouterNumSamples: number,
): Path | undefined {
const createFill = (sample: DexSample) =>
dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees)[0];
// Track sample id's to integers (required by rust router)
const sampleIdLookup: { [key: string]: number } = {};
let sampleIdCounter = 0;
const sampleToId = (source: ERC20BridgeSource, index: number): number => {
const key = `${source}-${index}`;
if (sampleIdLookup[key]) {
return sampleIdLookup[key];
} else {
sampleIdLookup[key] = ++sampleIdCounter;
return sampleIdLookup[key];
const createFill = (sample: DexSample): Fill | undefined => {
const fills = dexSamplesToFills(side, [sample], opts.outputAmountPerEth, opts.inputAmountPerEth, fees);
// NOTE: If the sample has 0 output dexSamplesToFills will return [] because no fill can be created
if (fills.length === 0) {
return undefined;
}
return fills[0];
};
const samplesAndNativeOrdersWithResults: Array<DexSample[] | NativeOrderWithFillableAmounts[]> = [];
const serializedPaths: SerializedPath[] = [];
const sampleSourcePathIds: string[] = [];
for (const singleSourceSamples of samples) {
if (singleSourceSamples.length === 0) {
continue;
}
const sourcePathId = hexUtils.random();
const singleSourceSamplesWithOutput = [...singleSourceSamples];
for (let i = singleSourceSamples.length - 1; i >= 0; i--) {
if (singleSourceSamples[i].output.isZero()) {
@@ -131,7 +112,7 @@ function findRoutesAndCreateOptimalPath(
// TODO(kimpers): Do we need to handle 0 entries, from eg Kyber?
const serializedPath = singleSourceSamplesWithOutput.reduce<SerializedPath>(
(memo, sample, sampleIdx) => {
memo.ids.push(sampleToId(sample.source, sampleIdx));
memo.ids.push(`${sample.source}-${serializedPaths.length}-${sampleIdx}`);
memo.inputs.push(sample.input.integerValue().toNumber());
memo.outputs.push(sample.output.integerValue().toNumber());
memo.outputFees.push(
@@ -152,8 +133,10 @@ function findRoutesAndCreateOptimalPath(
samplesAndNativeOrdersWithResults.push(singleSourceSamplesWithOutput);
serializedPaths.push(serializedPath);
sampleSourcePathIds.push(sourcePathId);
}
const nativeOrdersourcePathId = hexUtils.random();
for (const [idx, nativeOrder] of nativeOrders.entries()) {
const { input: normalizedOrderInput, output: normalizedOrderOutput } = nativeOrderToNormalizedAmounts(
side,
@@ -164,32 +147,25 @@ function findRoutesAndCreateOptimalPath(
if (normalizedOrderInput.isLessThanOrEqualTo(0) || normalizedOrderOutput.isLessThanOrEqualTo(0)) {
continue;
}
// HACK: the router requires at minimum 3 samples as a basis for interpolation
const inputs = [
0,
normalizedOrderInput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderInput.integerValue().toNumber(),
];
const outputs = [
0,
normalizedOrderOutput
.dividedBy(2)
.integerValue()
.toNumber(),
normalizedOrderOutput.integerValue().toNumber(),
];
// NOTE: same fee no matter if full or partial fill
const fee = calculateOuputFee(side, nativeOrder, opts.outputAmountPerEth, opts.inputAmountPerEth, fees)
.integerValue()
.toNumber();
const outputFees = [fee, fee, fee];
// NOTE: ids can be the same for all fake samples
const id = sampleToId(ERC20BridgeSource.Native, idx);
const ids = [id, id, id];
// HACK: due to an issue with the Rust router interpolation we need to create exactly 13 samples from the native order
const ids = [];
const inputs = [];
const outputs = [];
const outputFees = [];
for (let i = 1; i <= 13; i++) {
const fraction = i / 13;
const currentInput = BigNumber.min(normalizedOrderInput.times(fraction), normalizedOrderInput);
const currentOutput = BigNumber.min(normalizedOrderOutput.times(fraction), normalizedOrderOutput);
const id = `${ERC20BridgeSource.Native}-${serializedPaths.length}-${idx}-${i}`;
inputs.push(currentInput.integerValue().toNumber());
outputs.push(currentOutput.integerValue().toNumber());
outputFees.push(fee);
ids.push(id);
}
const serializedPath: SerializedPath = {
ids,
@@ -200,6 +176,7 @@ function findRoutesAndCreateOptimalPath(
samplesAndNativeOrdersWithResults.push([nativeOrder]);
serializedPaths.push(serializedPath);
sampleSourcePathIds.push(nativeOrdersourcePathId);
}
if (serializedPaths.length === 0) {
@@ -212,30 +189,33 @@ function findRoutesAndCreateOptimalPath(
pathsIn: serializedPaths,
};
const before = performance.now();
const allSourcesRustRoute = new Float64Array(rustArgs.pathsIn.length);
route(rustArgs, allSourcesRustRoute, RUST_ROUTER_NUM_SAMPLES);
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'real' },
'Rust router real routing performance',
);
const strategySourcesOutputAmounts = new Float64Array(rustArgs.pathsIn.length);
route(rustArgs, allSourcesRustRoute, strategySourcesOutputAmounts, neonRouterNumSamples);
assert.assert(
rustArgs.pathsIn.length === allSourcesRustRoute.length,
'different number of sources in the Router output than the input',
);
assert.assert(
rustArgs.pathsIn.length === strategySourcesOutputAmounts.length,
'different number of sources in the Router output amounts results than the input',
);
const routesAndSamples = _.zip(allSourcesRustRoute, samplesAndNativeOrdersWithResults);
const routesAndSamplesAndOutputs = _.zip(
allSourcesRustRoute,
samplesAndNativeOrdersWithResults,
strategySourcesOutputAmounts,
sampleSourcePathIds,
);
const adjustedFills: Fill[] = [];
const totalRoutedAmount = BigNumber.sum(...allSourcesRustRoute);
const scale = input.dividedBy(totalRoutedAmount);
for (const [routeInput, routeSamplesAndNativeOrders] of routesAndSamples) {
if (!routeInput || !routeSamplesAndNativeOrders) {
for (const [routeInput, routeSamplesAndNativeOrders, outputAmount, sourcePathId] of routesAndSamplesAndOutputs) {
if (!routeInput || !routeSamplesAndNativeOrders || !outputAmount || !Number.isFinite(outputAmount)) {
continue;
}
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precisions loss for number/f64
// TODO(kimpers): [TKR-241] amounts are sometimes clipped in the router due to precision loss for number/f64
// we can work around it by scaling it and rounding up. However now we end up with a total amount of a couple base units too much
const rustInputAdjusted = BigNumber.min(
new BigNumber(routeInput).multipliedBy(scale).integerValue(BigNumber.ROUND_CEIL),
@@ -251,14 +231,21 @@ function findRoutesAndCreateOptimalPath(
opts.outputAmountPerEth,
opts.inputAmountPerEth,
fees,
)[0];
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push(nativeFill);
)[0] as Fill | undefined;
// Note: If the order has an adjusted rate of less than or equal to 0 it will be skipped
// and nativeFill will be `undefined`
if (nativeFill) {
// NOTE: For Limit/RFQ orders we are done here. No need to scale output
adjustedFills.push({ ...nativeFill, sourcePathId: sourcePathId ?? hexUtils.random() });
}
continue;
}
// NOTE: For DexSamples only
let fill = createFill(current);
if (!fill) {
continue;
}
const routeSamples = routeSamplesAndNativeOrders as Array<DexSample<FillData>>;
// Descend to approach a closer fill for fillData which may not be consistent
// throughout the path (UniswapV3) and for a closer guesstimate at
@@ -267,49 +254,47 @@ function findRoutesAndCreateOptimalPath(
assert.assert(routeSamples.length >= 1, 'Found no sample to use for source');
for (let k = routeSamples.length - 1; k >= 0; k--) {
if (k === 0) {
fill = createFill(routeSamples[0]);
fill = createFill(routeSamples[0]) ?? fill;
}
if (rustInputAdjusted.isGreaterThan(routeSamples[k].input)) {
// Between here and the previous fill
// HACK: Use the midpoint between the two
const left = routeSamples[k];
const right = routeSamples[k + 1];
if (left && right) {
// Approximate how much output we get for the input with the surrounding samples
const interpolatedOutput = interpolateOutputFromSamples(
left,
right,
rustInputAdjusted,
).decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
fill = createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: interpolatedOutput,
});
fill =
createFill({
...right, // default to the greater (for gas used)
input: rustInputAdjusted,
output: new BigNumber(outputAmount),
}) ?? fill;
} else {
assert.assert(Boolean(left || right), 'No valid sample to use');
fill = createFill(left || right);
fill = createFill(left || right) ?? fill;
}
break;
}
}
const scaleOutput = (output: BigNumber) =>
// TODO(kimpers): remove once we have solved the rounding/precision loss issues in the Rust router
const scaleOutput = (fillInput: BigNumber, output: BigNumber) =>
output
.dividedBy(fill.input)
.dividedBy(fillInput)
.times(rustInputAdjusted)
.decimalPlaces(0, side === MarketOperation.Sell ? BigNumber.ROUND_FLOOR : BigNumber.ROUND_CEIL);
adjustedFills.push({
...fill,
input: rustInputAdjusted,
output: scaleOutput(fill.output),
adjustedOutput: scaleOutput(fill.adjustedOutput),
output: scaleOutput(fill.input, fill.output),
adjustedOutput: scaleOutput(fill.input, fill.adjustedOutput),
index: 0,
parent: undefined,
sourcePathId: sourcePathId ?? hexUtils.random(),
});
}
if (adjustedFills.length === 0) {
return undefined;
}
const pathFromRustInputs = Path.create(side, adjustedFills, input);
return pathFromRustInputs;
@@ -323,15 +308,27 @@ export function findOptimalRustPathFromSamples(
opts: PathPenaltyOpts,
fees: FeeSchedule,
chainId: ChainId,
neonRouterNumSamples: number,
samplerMetrics?: SamplerMetrics,
): Path | undefined {
const before = performance.now();
const logPerformance = () =>
DEFAULT_INFO_LOGGER(
{ router: 'neon-router', performanceMs: performance.now() - before, type: 'total' },
'Rust router total routing performance',
);
const allSourcesPath = findRoutesAndCreateOptimalPath(side, samples, nativeOrders, input, opts, fees);
const beforeAllTimeMs = performance.now();
let beforeTimeMs = performance.now();
const allSourcesPath = findRoutesAndCreateOptimalPath(
side,
samples,
nativeOrders,
input,
opts,
fees,
neonRouterNumSamples,
);
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'all',
timingMs: performance.now() - beforeTimeMs,
});
if (!allSourcesPath) {
return undefined;
}
@@ -341,11 +338,27 @@ export function findOptimalRustPathFromSamples(
// HACK(kimpers): The Rust router currently doesn't account for VIP sources correctly
// we need to try to route them in isolation and compare with the results all sources
if (vipSources.length > 0) {
beforeTimeMs = performance.now();
const vipSourcesSet = new Set(vipSources);
const vipSourcesSamples = samples.filter(s => s[0] && vipSourcesSet.has(s[0].source));
if (vipSourcesSamples.length > 0) {
const vipSourcesPath = findRoutesAndCreateOptimalPath(side, vipSourcesSamples, [], input, opts, fees);
const vipSourcesPath = findRoutesAndCreateOptimalPath(
side,
vipSourcesSamples,
[],
input,
opts,
fees,
neonRouterNumSamples,
);
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'vip',
timingMs: performance.now() - beforeTimeMs,
});
const { input: allSourcesInput, output: allSourcesOutput } = allSourcesPath.adjustedSize();
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
@@ -358,13 +371,18 @@ export function findOptimalRustPathFromSamples(
const allSourcesAdjustedRateWithFqtOverhead = getRate(side, allSourcesInput, outputWithFqtOverhead);
if (vipSourcesPath?.adjustedRate().isGreaterThan(allSourcesAdjustedRateWithFqtOverhead)) {
logPerformance();
return vipSourcesPath;
}
}
}
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'neon-router',
type: 'total',
timingMs: performance.now() - beforeAllTimeMs,
});
logPerformance();
return allSourcesPath;
}
@@ -377,8 +395,10 @@ export async function findOptimalPathJSAsync(
fills: Fill[][],
targetInput: BigNumber,
runLimit: number = 2 ** 8,
samplerMetrics?: SamplerMetrics,
opts: PathPenaltyOpts = DEFAULT_PATH_PENALTY_OPTS,
): Promise<Path | undefined> {
const beforeTimeMs = performance.now();
// Sort fill arrays by descending adjusted completed rate.
// Remove any paths which cannot impact the optimal path
const sortedPaths = reducePaths(fillsToSortedPaths(fills, side, targetInput, opts), side);
@@ -392,7 +412,15 @@ export async function findOptimalPathJSAsync(
// Yield to event loop.
await Promise.resolve();
}
return optimalPath.isComplete() ? optimalPath : undefined;
const finalPath = optimalPath.isComplete() ? optimalPath : undefined;
// tslint:disable-next-line: no-unused-expression
samplerMetrics &&
samplerMetrics.logRouterDetails({
router: 'js',
type: 'total',
timingMs: performance.now() - beforeTimeMs,
});
return finalPath;
}
// Sort fill arrays by descending adjusted completed rate.

View File

@@ -14,7 +14,8 @@ import { BatchedOperation, ERC20BridgeSource, LiquidityProviderRegistry, TokenAd
*/
export function getSampleAmounts(maxFillAmount: BigNumber, numSamples: number, expBase: number = 1): BigNumber[] {
const distribution = [...Array<BigNumber>(numSamples)].map((_v, i) => new BigNumber(expBase).pow(i));
const stepSizes = distribution.map(d => d.div(BigNumber.sum(...distribution)));
const distributionSum = BigNumber.sum(...distribution);
const stepSizes = distribution.map(d => d.div(distributionSum));
const amounts = stepSizes.map((_s, i) => {
if (i === numSamples - 1) {
return maxFillAmount;
@@ -130,6 +131,37 @@ export class DexOrderSampler extends SamplerOperations {
BatchedOperationResult<T8>
]>;
// prettier-ignore
public async executeAsync<
T1, T2, T3, T4, T5, T6, T7, T8, T9
>(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9]): Promise<[
BatchedOperationResult<T1>,
BatchedOperationResult<T2>,
BatchedOperationResult<T3>,
BatchedOperationResult<T4>,
BatchedOperationResult<T5>,
BatchedOperationResult<T6>,
BatchedOperationResult<T7>,
BatchedOperationResult<T8>,
BatchedOperationResult<T9>
]>;
// prettier-ignore
public async executeAsync<
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10
>(...ops: [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10]): Promise<[
BatchedOperationResult<T1>,
BatchedOperationResult<T2>,
BatchedOperationResult<T3>,
BatchedOperationResult<T4>,
BatchedOperationResult<T5>,
BatchedOperationResult<T6>,
BatchedOperationResult<T7>,
BatchedOperationResult<T8>,
BatchedOperationResult<T9>,
BatchedOperationResult<T10>,
]>;
/**
* Run a series of operations from `DexOrderSampler.ops` in a single transaction.
*/

View File

@@ -0,0 +1,36 @@
import { BigNumber, logUtils, NULL_BYTES } from '@0x/utils';
import { ERC20BridgeSource, FillData, SourceQuoteOperation } from './types';
interface SamplerNoOperationCall {
callback: () => BigNumber[];
}
/**
* SamplerNoOperation can be used for sources where we already have all the necessary information
* required to perform the sample operations, without needing access to any on-chain data. Using a noop sample
* you can skip the eth_call, and just calculate the results directly in typescript land.
*/
export class SamplerNoOperation<TFillData extends FillData = FillData> implements SourceQuoteOperation<TFillData> {
public readonly source: ERC20BridgeSource;
public fillData: TFillData;
private readonly _callback: () => BigNumber[];
constructor(opts: { source: ERC20BridgeSource; fillData?: TFillData } & SamplerNoOperationCall) {
this.source = opts.source;
this.fillData = opts.fillData || ({} as TFillData); // tslint:disable-line:no-object-literal-type-assertion
this._callback = opts.callback;
}
// tslint:disable-next-line:prefer-function-over-method
public encodeCall(): string {
return NULL_BYTES;
}
public handleCallResults(_callResults: string): BigNumber[] {
return this._callback();
}
public handleRevert(_callResults: string): BigNumber[] {
logUtils.warn(`SamplerNoOperation: ${this.source} reverted`);
return [];
}
}

View File

@@ -3,9 +3,11 @@ import { LimitOrderFields } from '@0x/protocol-utils';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { AaveV2Sampler } from '../../noop_samplers/AaveV2Sampler';
import { SamplerCallResult, SignedNativeOrder } from '../../types';
import { ERC20BridgeSamplerContract } from '../../wrappers';
import { AaveV2ReservesCache } from './aave_reserves_cache';
import { BancorService } from './bancor_service';
import {
getCurveLikeInfosForPair,
@@ -17,9 +19,14 @@ import {
isValidAddress,
uniswapV2LikeRouterAddress,
} from './bridge_source_utils';
import { CompoundCTokenCache } from './compound_ctoken_cache';
import {
AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID,
BALANCER_V2_VAULT_ADDRESS_BY_CHAIN,
BANCOR_REGISTRY_BY_CHAIN_ID,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN,
BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN,
COMPOUND_API_URL_BY_CHAIN_ID,
DODOV1_CONFIG_BY_CHAIN_ID,
DODOV2_FACTORIES_BY_CHAIN_ID,
KYBER_CONFIG_BY_CHAIN_ID,
@@ -43,13 +50,17 @@ 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 { SamplerNoOperation } from './sampler_no_operation';
import { SourceFilters } from './source_filters';
import {
AaveV2FillData,
AaveV2Info,
BalancerFillData,
BalancerV2FillData,
BalancerV2PoolInfo,
BancorFillData,
BatchedOperation,
CompoundFillData,
CurveFillData,
CurveInfo,
DexSample,
@@ -97,6 +108,8 @@ export const BATCH_SOURCE_FILTERS = SourceFilters.all().exclude([ERC20BridgeSour
export class SamplerOperations {
public readonly liquidityProviderRegistry: LiquidityProviderRegistry;
public readonly poolsCaches: { [key in SourcesWithPoolsCache]: PoolsCache };
public readonly aaveReservesCache: AaveV2ReservesCache | undefined;
public readonly compoundCTokenCache: CompoundCTokenCache | undefined;
protected _bancorService?: BancorService;
public static constant<T>(result: T): BatchedOperation<T> {
return {
@@ -122,9 +135,26 @@ export class SamplerOperations {
? poolsCaches
: {
[ERC20BridgeSource.BalancerV2]: new BalancerV2PoolsCache(chainId),
[ERC20BridgeSource.Beethovenx]: new BalancerV2PoolsCache(
chainId,
BEETHOVEN_X_SUBGRAPH_URL_BY_CHAIN[chainId],
),
[ERC20BridgeSource.Balancer]: new BalancerPoolsCache(),
[ERC20BridgeSource.Cream]: new CreamPoolsCache(),
};
const aaveSubgraphUrl = AAVE_V2_SUBGRAPH_URL_BY_CHAIN_ID[chainId];
if (aaveSubgraphUrl) {
this.aaveReservesCache = new AaveV2ReservesCache(aaveSubgraphUrl);
}
const compoundApiUrl = COMPOUND_API_URL_BY_CHAIN_ID[chainId];
if (compoundApiUrl) {
this.compoundCTokenCache = new CompoundCTokenCache(
compoundApiUrl,
NATIVE_FEE_TOKEN_BY_CHAIN_ID[this.chainId],
);
}
// Initialize the Bancor service, fetching paths in the background
bancorServiceFn()
.then(service => (this._bancorService = service))
@@ -152,6 +182,30 @@ export class SamplerOperations {
};
}
public getGasLeft(): BatchedOperation<BigNumber> {
return {
encodeCall: () => this._samplerContract.getGasLeft().getABIEncodedTransactionData(),
handleCallResults: (callResults: string) =>
this._samplerContract.getABIDecodedReturnData<BigNumber>('getGasLeft', callResults),
handleRevert: () => {
/* should never happen */
throw new Error('Invalid result for getGasLeft');
},
};
}
public getBlockNumber(): BatchedOperation<BigNumber> {
return {
encodeCall: () => this._samplerContract.getBlockNumber().getABIEncodedTransactionData(),
handleCallResults: (callResults: string) =>
this._samplerContract.getABIDecodedReturnData<BigNumber>('getBlockNumber', callResults),
handleRevert: () => {
/* should never happen */
throw new Error('Invalid result for getBlockNumber');
},
};
}
public getLimitOrderFillableTakerAmounts(
orders: SignedNativeOrder[],
exchangeAddress: string,
@@ -1069,6 +1123,64 @@ export class SamplerOperations {
});
}
// tslint:disable-next-line:prefer-function-over-method
public getAaveV2SellQuotes(
aaveInfo: AaveV2Info,
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<AaveV2FillData> {
return new SamplerNoOperation({
source: ERC20BridgeSource.AaveV2,
fillData: { ...aaveInfo, takerToken },
callback: () => AaveV2Sampler.sampleSellsFromAaveV2(aaveInfo, takerToken, makerToken, takerFillAmounts),
});
}
// tslint:disable-next-line:prefer-function-over-method
public getAaveV2BuyQuotes(
aaveInfo: AaveV2Info,
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<AaveV2FillData> {
return new SamplerNoOperation({
source: ERC20BridgeSource.AaveV2,
fillData: { ...aaveInfo, takerToken },
callback: () => AaveV2Sampler.sampleBuysFromAaveV2(aaveInfo, takerToken, makerToken, makerFillAmounts),
});
}
public getCompoundSellQuotes(
cToken: string,
makerToken: string,
takerToken: string,
takerFillAmounts: BigNumber[],
): SourceQuoteOperation<CompoundFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Compound,
fillData: { cToken, takerToken, makerToken },
contract: this._samplerContract,
function: this._samplerContract.sampleSellsFromCompound,
params: [cToken, takerToken, makerToken, takerFillAmounts],
});
}
public getCompoundBuyQuotes(
cToken: string,
makerToken: string,
takerToken: string,
makerFillAmounts: BigNumber[],
): SourceQuoteOperation<CompoundFillData> {
return new SamplerContractOperation({
source: ERC20BridgeSource.Compound,
fillData: { cToken, takerToken, makerToken },
contract: this._samplerContract,
function: this._samplerContract.sampleBuysFromCompound,
params: [cToken, takerToken, makerToken, makerFillAmounts],
});
}
public getMedianSellRate(
sources: ERC20BridgeSource[],
makerToken: string,
@@ -1197,8 +1309,10 @@ export class SamplerOperations {
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
case ERC20BridgeSource.UbeSwap:
case ERC20BridgeSource.SpiritSwap:
case ERC20BridgeSource.SpookySwap:
case ERC20BridgeSource.MorpheusSwap:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1228,6 +1342,7 @@ export class SamplerOperations {
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Synapse:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
@@ -1300,13 +1415,14 @@ export class SamplerOperations {
),
);
case ERC20BridgeSource.BalancerV2:
case ERC20BridgeSource.Beethovenx:
const poolIds =
this.poolsCaches[ERC20BridgeSource.BalancerV2].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || [];
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
const vault =
source === ERC20BridgeSource.BalancerV2
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
@@ -1316,10 +1432,9 @@ export class SamplerOperations {
makerToken,
takerToken,
takerFillAmounts,
ERC20BridgeSource.BalancerV2,
source,
),
);
case ERC20BridgeSource.Cream:
return (
this.poolsCaches[ERC20BridgeSource.Cream].getCachedPoolAddressesForPair(
@@ -1417,6 +1532,38 @@ export class SamplerOperations {
return this.getLidoSellQuotes(lidoInfo, makerToken, takerToken, takerFillAmounts);
}
case ERC20BridgeSource.AaveV2: {
if (!this.aaveReservesCache) {
return [];
}
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
if (!reserve) {
return [];
}
const info: AaveV2Info = {
lendingPool: reserve.pool.lendingPool,
aToken: reserve.aToken.id,
underlyingToken: reserve.underlyingAsset,
};
return this.getAaveV2SellQuotes(info, makerToken, takerToken, takerFillAmounts);
}
case ERC20BridgeSource.Compound: {
if (!this.compoundCTokenCache) {
return [];
}
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
if (!cToken) {
return [];
}
return this.getCompoundSellQuotes(
cToken.tokenAddress,
makerToken,
takerToken,
takerFillAmounts,
);
}
default:
throw new Error(`Unsupported sell sample source: ${source}`);
}
@@ -1468,8 +1615,10 @@ export class SamplerOperations {
case ERC20BridgeSource.JetSwap:
case ERC20BridgeSource.Pangolin:
case ERC20BridgeSource.TraderJoe:
case ERC20BridgeSource.UbeSwap:
case ERC20BridgeSource.SpiritSwap:
case ERC20BridgeSource.SpookySwap:
case ERC20BridgeSource.MorpheusSwap:
const uniLikeRouter = uniswapV2LikeRouterAddress(this.chainId, source);
if (!isValidAddress(uniLikeRouter)) {
return [];
@@ -1499,6 +1648,7 @@ export class SamplerOperations {
case ERC20BridgeSource.Swerve:
case ERC20BridgeSource.SnowSwap:
case ERC20BridgeSource.Nerve:
case ERC20BridgeSource.Synapse:
case ERC20BridgeSource.Belt:
case ERC20BridgeSource.Ellipsis:
case ERC20BridgeSource.Saddle:
@@ -1571,13 +1721,14 @@ export class SamplerOperations {
),
);
case ERC20BridgeSource.BalancerV2:
case ERC20BridgeSource.Beethovenx:
const poolIds =
this.poolsCaches[ERC20BridgeSource.BalancerV2].getCachedPoolAddressesForPair(
takerToken,
makerToken,
) || [];
this.poolsCaches[source].getCachedPoolAddressesForPair(takerToken, makerToken) || [];
const vault = BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId];
const vault =
source === ERC20BridgeSource.BalancerV2
? BALANCER_V2_VAULT_ADDRESS_BY_CHAIN[this.chainId]
: BEETHOVEN_X_VAULT_ADDRESS_BY_CHAIN[this.chainId];
if (vault === NULL_ADDRESS) {
return [];
}
@@ -1587,7 +1738,7 @@ export class SamplerOperations {
makerToken,
takerToken,
makerFillAmounts,
ERC20BridgeSource.BalancerV2,
source,
),
);
case ERC20BridgeSource.Cream:
@@ -1683,6 +1834,32 @@ export class SamplerOperations {
return this.getLidoBuyQuotes(lidoInfo, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.AaveV2: {
if (!this.aaveReservesCache) {
return [];
}
const reserve = this.aaveReservesCache.get(takerToken, makerToken);
if (!reserve) {
return [];
}
const info: AaveV2Info = {
lendingPool: reserve.pool.lendingPool,
aToken: reserve.aToken.id,
underlyingToken: reserve.underlyingAsset,
};
return this.getAaveV2BuyQuotes(info, makerToken, takerToken, makerFillAmounts);
}
case ERC20BridgeSource.Compound: {
if (!this.compoundCTokenCache) {
return [];
}
const cToken = this.compoundCTokenCache.get(takerToken, makerToken);
if (!cToken) {
return [];
}
return this.getCompoundBuyQuotes(cToken.tokenAddress, makerToken, takerToken, makerFillAmounts);
}
default:
throw new Error(`Unsupported buy sample source: ${source}`);
}

View File

@@ -3,13 +3,12 @@ import {
FillQuoteTransformerOrderType,
FillQuoteTransformerRfqOrderInfo,
} from '@0x/protocol-utils';
import { V4RFQIndicativeQuote } from '@0x/quote-server';
import { MarketOperation } from '@0x/types';
import { BigNumber } from '@0x/utils';
import { NativeOrderWithFillableAmounts, RfqFirmQuoteValidator, RfqRequestOpts } from '../../types';
import { QuoteRequestor } from '../../utils/quote_requestor';
import { PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from '../../utils/quote_requestor';
import { ExtendedQuoteReportSources, PriceComparisonsReport, QuoteReport } from '../quote_report_generator';
import { CollapsedPath } from './path';
import { SourceFilters } from './source_filters';
@@ -69,6 +68,9 @@ export enum ERC20BridgeSource {
CurveV2 = 'Curve_V2',
Lido = 'Lido',
ShibaSwap = 'ShibaSwap',
AaveV2 = 'Aave_V2',
Compound = 'Compound',
Synapse = 'Synapse',
// BSC only
PancakeSwap = 'PancakeSwap',
PancakeSwapV2 = 'PancakeSwap_V2',
@@ -93,11 +95,19 @@ export enum ERC20BridgeSource {
// Avalanche
Pangolin = 'Pangolin',
TraderJoe = 'TraderJoe',
// Celo only
UbeSwap = 'UbeSwap',
// Fantom
SpiritSwap = 'SpiritSwap',
SpookySwap = 'SpookySwap',
Beethovenx = 'Beethovenx',
MorpheusSwap = 'MorpheusSwap',
}
export type SourcesWithPoolsCache = ERC20BridgeSource.Balancer | ERC20BridgeSource.BalancerV2 | ERC20BridgeSource.Cream;
export type SourcesWithPoolsCache =
| ERC20BridgeSource.Balancer
| ERC20BridgeSource.BalancerV2
| ERC20BridgeSource.Beethovenx
| ERC20BridgeSource.Cream;
// tslint:disable: enum-naming
/**
@@ -106,11 +116,13 @@ export type SourcesWithPoolsCache = ERC20BridgeSource.Balancer | ERC20BridgeSour
export enum CurveFunctionSelectors {
None = '0x00000000',
exchange = '0x3df02124',
exchange_underlying = '0xa6417ed6',
exchange_underlying = '0xa6417ed6', // exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy)
get_dy_underlying = '0x07211ef7',
get_dx_underlying = '0x0e71d1b9',
get_dy = '0x5e0d443f',
get_dy = '0x5e0d443f', // get_dy(int128,int128,uint256)
get_dx = '0x67df02ca',
get_dy_uint256 = '0x556d6e9f', // get_dy(uint256,uint256,uint256)
exchange_underlying_uint256 = '0x65b2489b', // exchange_underlying(uint256,uint256,uint256,uint256)
// Curve V2
exchange_v2 = '0x5b41b908',
exchange_underlying_v2 = '0x65b2489b',
@@ -119,7 +131,7 @@ export enum CurveFunctionSelectors {
// Smoothy
swap_uint256 = '0x5673b02d', // swap(uint256,uint256,uint256,uint256)
get_swap_amount = '0x45cf2ef6', // getSwapAmount(uint256,uint256,uint256)
// Nerve BSC, Saddle Mainnet
// Nerve BSC, Saddle Mainnet, Synapse
swap = '0x91695586', // swap(uint8,uint8,uint256,uint256,uint256)
calculateSwap = '0xa95b089f', // calculateSwap(uint8,uint8,uint256)
}
@@ -163,6 +175,12 @@ export interface BalancerV2PoolInfo {
vault: string;
}
export interface AaveV2Info {
lendingPool: string;
aToken: string;
underlyingToken: string;
}
// Internal `fillData` field for `Fill` objects.
export interface FillData {}
@@ -270,6 +288,19 @@ export interface LidoFillData extends FillData {
takerToken: string;
}
export interface AaveV2FillData extends FillData {
lendingPool: string;
aToken: string;
underlyingToken: string;
takerToken: string;
}
export interface CompoundFillData extends FillData {
cToken: string;
takerToken: string;
makerToken: string;
}
/**
* Represents a node on a fill path.
*/
@@ -425,6 +456,10 @@ export interface GetMarketOrdersOpts {
* Default: 1.25.
*/
sampleDistributionBase: number;
/**
* Number of samples to use when creating fill curves with neon-router
*/
neonRouterNumSamples: number;
/**
* Fees for each liquidity source, expressed in gas.
*/
@@ -462,6 +497,37 @@ export interface GetMarketOrdersOpts {
* Gas price to use for quote
*/
gasPrice: BigNumber;
/**
* Sampler metrics for recording data on the sampler service and operations
*/
samplerMetrics?: SamplerMetrics;
}
export interface SamplerMetrics {
/**
* Logs the gas information performed during a sampler call.
*
* @param data.gasBefore The gas remaining measured before any operations have been performed
* @param data.gasAfter The gas remaining measured after all operations have been performed
*/
logGasDetails(data: { gasBefore: BigNumber; gasAfter: BigNumber }): void;
/**
* Logs the block number
*
* @param blockNumber block number of the sampler call
*/
logBlockNumber(blockNumber: BigNumber): void;
/**
* Logs the routing timings
*
* @param data.router The router type (neon-router or js)
* @param data.type The type of timing being recorded (e.g total timing, all sources timing or vip timing)
* @param data.timingMs The timing in milliseconds
*/
logRouterDetails(data: { router: 'neon-router' | 'js'; type: 'all' | 'vip' | 'total'; timingMs: number }): void;
}
/**
@@ -491,6 +557,7 @@ export interface OptimizerResult {
export interface OptimizerResultWithReport extends OptimizerResult {
quoteReport?: QuoteReport;
extendedQuoteReportSources?: ExtendedQuoteReportSources;
priceComparisonsReport?: PriceComparisonsReport;
}
@@ -519,7 +586,7 @@ export interface MarketSideLiquidity {
export interface RawQuotes {
nativeOrders: NativeOrderWithFillableAmounts[];
rfqtIndicativeQuotes: V4RFQIndicativeQuote[];
rfqtIndicativeQuotes: V4RFQIndicativeQuoteMM[];
twoHopQuotes: Array<DexSample<MultiHopFillData>>;
dexQuotes: Array<Array<DexSample<FillData>>>;
}
@@ -546,6 +613,8 @@ export interface GenerateOptimizedOrdersOpts {
allowFallback?: boolean;
shouldBatchBridgeOrders?: boolean;
gasPrice: BigNumber;
neonRouterNumSamples: number;
samplerMetrics?: SamplerMetrics;
}
export interface ComparisonPrice {

View File

@@ -14,8 +14,9 @@ import {
NativeFillData,
NativeLimitOrderFillData,
NativeRfqOrderFillData,
RawQuotes,
} from './market_operation_utils/types';
import { QuoteRequestor } from './quote_requestor';
import { QuoteRequestor, V4RFQIndicativeQuoteMM } from './quote_requestor';
export interface QuoteReportEntryBase {
liquiditySource: ERC20BridgeSource;
@@ -36,30 +37,77 @@ export interface NativeLimitOrderQuoteReportEntry extends QuoteReportEntryBase {
liquiditySource: ERC20BridgeSource.Native;
fillData: NativeFillData;
fillableTakerAmount: BigNumber;
isRfqt: false;
isRFQ: false;
}
export interface NativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase {
liquiditySource: ERC20BridgeSource.Native;
fillData: NativeFillData;
fillableTakerAmount: BigNumber;
isRfqt: true;
isRFQ: true;
nativeOrder: RfqOrderFields;
makerUri: string;
comparisonPrice?: number;
}
export interface IndicativeRfqOrderQuoteReportEntry extends QuoteReportEntryBase {
liquiditySource: ERC20BridgeSource.Native;
fillableTakerAmount: BigNumber;
isRFQ: true;
makerUri?: string;
comparisonPrice?: number;
}
export type QuoteReportEntry =
| BridgeQuoteReportEntry
| MultiHopQuoteReportEntry
| NativeLimitOrderQuoteReportEntry
| NativeRfqOrderQuoteReportEntry;
export type ExtendedQuoteReportEntry =
| BridgeQuoteReportEntry
| MultiHopQuoteReportEntry
| NativeLimitOrderQuoteReportEntry
| NativeRfqOrderQuoteReportEntry
| IndicativeRfqOrderQuoteReportEntry;
export type ExtendedQuoteReportIndexedEntry = ExtendedQuoteReportEntry & {
quoteEntryIndex: number;
isDelivered: boolean;
};
export type ExtendedQuoteReportIndexedEntryOutbound = Omit<ExtendedQuoteReportIndexedEntry, 'fillData'> & {
fillData?: string;
};
export interface QuoteReport {
sourcesConsidered: QuoteReportEntry[];
sourcesDelivered: QuoteReportEntry[];
}
export interface ExtendedQuoteReportSources {
sourcesConsidered: ExtendedQuoteReportIndexedEntry[];
sourcesDelivered: ExtendedQuoteReportIndexedEntry[] | undefined;
}
export interface ExtendedQuoteReport {
quoteId?: string;
taker?: string;
timestamp: number;
firmQuoteReport: boolean;
submissionBy: 'taker' | 'metaTxn' | 'rfqm';
buyAmount?: string;
sellAmount?: string;
buyTokenAddress: string;
sellTokenAddress: string;
integratorId?: string;
slippageBips?: number;
zeroExTransactionHash?: string;
decodedUniqueId?: string;
sourcesConsidered: ExtendedQuoteReportIndexedEntryOutbound[];
sourcesDelivered: ExtendedQuoteReportIndexedEntryOutbound[] | undefined;
}
export interface PriceComparisonsReport {
dexSources: BridgeQuoteReportEntry[];
multiHopSources: MultiHopQuoteReportEntry[];
@@ -80,7 +128,7 @@ export function generateQuoteReport(
const nativeOrderSourcesConsidered = nativeOrders.map(order =>
nativeOrderToReportEntry(order.type, order as any, order.fillableTakerAmount, comparisonPrice, quoteRequestor),
);
const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRfqt)];
const sourcesConsidered = [...nativeOrderSourcesConsidered.filter(order => order.isRFQ)];
let sourcesDelivered;
if (Array.isArray(liquidityDelivered)) {
@@ -116,6 +164,105 @@ export function generateQuoteReport(
};
}
/**
* Generates a report of sources considered while computing the optimized
* swap quote, the sources ultimately included in the computed quote. This
* extende version incudes all considered quotes, not only native liquidity.
*/
export function generateExtendedQuoteReportSources(
marketOperation: MarketOperation,
quotes: RawQuotes,
liquidityDelivered: ReadonlyArray<CollapsedFill> | DexSample<MultiHopFillData>,
amount: BigNumber,
comparisonPrice?: BigNumber | undefined,
quoteRequestor?: QuoteRequestor,
): ExtendedQuoteReportSources {
const sourcesConsidered: ExtendedQuoteReportEntry[] = [];
// NativeOrders
sourcesConsidered.push(
...quotes.nativeOrders.map(order =>
nativeOrderToReportEntry(
order.type,
order as any,
order.fillableTakerAmount,
comparisonPrice,
quoteRequestor,
),
),
);
// IndicativeQuotes
sourcesConsidered.push(
...quotes.rfqtIndicativeQuotes.map(order => indicativeQuoteToReportEntry(order, comparisonPrice)),
);
// MultiHop
sourcesConsidered.push(...quotes.twoHopQuotes.map(quote => multiHopSampleToReportSource(quote, marketOperation)));
// Dex Quotes
sourcesConsidered.push(
..._.flatten(
quotes.dexQuotes.map(dex =>
dex
.filter(quote => isDexSampleForTotalAmount(quote, marketOperation, amount))
.map(quote => dexSampleToReportSource(quote, marketOperation)),
),
),
);
const sourcesConsideredIndexed = sourcesConsidered.map(
(quote, index): ExtendedQuoteReportIndexedEntry => {
return {
...quote,
quoteEntryIndex: index,
isDelivered: false,
};
},
);
let sourcesDelivered;
if (Array.isArray(liquidityDelivered)) {
// create easy way to look up fillable amounts
const nativeOrderSignaturesToFillableAmounts = _.fromPairs(
quotes.nativeOrders.map(o => {
return [_nativeDataToId(o), o.fillableTakerAmount];
}),
);
// map sources delivered
sourcesDelivered = liquidityDelivered.map(collapsedFill => {
if (_isNativeOrderFromCollapsedFill(collapsedFill)) {
return nativeOrderToReportEntry(
collapsedFill.type,
collapsedFill.fillData,
nativeOrderSignaturesToFillableAmounts[_nativeDataToId(collapsedFill.fillData)],
comparisonPrice,
quoteRequestor,
);
} else {
return dexSampleToReportSource(collapsedFill, marketOperation);
}
});
} else {
sourcesDelivered = [
// tslint:disable-next-line: no-unnecessary-type-assertion
multiHopSampleToReportSource(liquidityDelivered as DexSample<MultiHopFillData>, marketOperation),
];
}
const sourcesDeliveredIndexed = sourcesDelivered.map(
(quote, index): ExtendedQuoteReportIndexedEntry => {
return {
...quote,
quoteEntryIndex: index,
isDelivered: false,
};
},
);
return {
sourcesConsidered: sourcesConsideredIndexed,
sourcesDelivered: sourcesDeliveredIndexed,
};
}
function _nativeDataToId(data: { signature: Signature }): string {
const { v, r, s } = data.signature;
return `${v}${r}${s}`;
@@ -153,6 +300,22 @@ export function dexSampleToReportSource(ds: DexSample, marketOperation: MarketOp
}
}
/**
* Checks if a DEX sample is the one that represents the whole amount requested by taker
* NOTE: this is used for the QuoteReport to filter samples
*/
function isDexSampleForTotalAmount(ds: DexSample, marketOperation: MarketOperation, amount: BigNumber): boolean {
// input and output map to different values
// based on the market operation
if (marketOperation === MarketOperation.Buy) {
return ds.input === amount;
} else if (marketOperation === MarketOperation.Sell) {
return ds.output === amount;
} else {
throw new Error(`Unexpected marketOperation ${marketOperation}`);
}
}
/**
* Generates a report sample for a MultiHop source
* NOTE: this is used for the QuoteReport and quote price comparison data
@@ -208,17 +371,17 @@ export function nativeOrderToReportEntry(
};
// if we find this is an rfqt order, label it as such and associate makerUri
const isRfqt = type === FillQuoteTransformerOrderType.Rfq;
const isRFQ = type === FillQuoteTransformerOrderType.Rfq;
const rfqtMakerUri =
isRfqt && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined;
isRFQ && quoteRequestor ? quoteRequestor.getMakerUriForSignature(fillData.signature) : undefined;
if (isRfqt) {
if (isRFQ) {
const nativeOrder = fillData.order as RfqOrderFields;
// tslint:disable-next-line: no-object-literal-type-assertion
return {
liquiditySource: ERC20BridgeSource.Native,
...nativeOrderBase,
isRfqt: true,
isRFQ: true,
makerUri: rfqtMakerUri || '',
...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}),
nativeOrder,
@@ -229,8 +392,49 @@ export function nativeOrderToReportEntry(
return {
liquiditySource: ERC20BridgeSource.Native,
...nativeOrderBase,
isRfqt: false,
isRFQ: false,
fillData,
};
}
}
/**
* Generates a report entry for an indicative RFQ Quote
* NOTE: this is used for the QuoteReport and quote price comparison data
*/
export function indicativeQuoteToReportEntry(
order: V4RFQIndicativeQuoteMM,
comparisonPrice?: BigNumber | undefined,
): IndicativeRfqOrderQuoteReportEntry {
const nativeOrderBase = {
makerAmount: order.makerAmount,
takerAmount: order.takerAmount,
fillableTakerAmount: order.takerAmount,
};
// tslint:disable-next-line: no-object-literal-type-assertion
return {
liquiditySource: ERC20BridgeSource.Native,
...nativeOrderBase,
isRFQ: true,
makerUri: order.makerUri,
fillData: {},
...(comparisonPrice ? { comparisonPrice: comparisonPrice.toNumber() } : {}),
};
}
/**
* For the extended quote report, we output the filldata as JSON
*/
export function jsonifyFillData(source: ExtendedQuoteReportIndexedEntry): ExtendedQuoteReportIndexedEntryOutbound {
return {
...source,
fillData: JSON.stringify(source.fillData, (key: string, value: any) => {
if (key === '_samplerContract') {
return {};
} else {
return value;
}
}),
};
}

View File

@@ -39,6 +39,10 @@ interface RfqQuote<T> {
makerUri: string;
}
export interface V4RFQIndicativeQuoteMM extends V4RFQIndicativeQuote {
makerUri: string;
}
export interface MetricsProxy {
/**
* Increments a counter that is tracking valid Firm Quotes that are dropped due to low expiration.
@@ -343,7 +347,7 @@ export class QuoteRequestor {
marketOperation: MarketOperation,
comparisonPrice: BigNumber | undefined,
options: RfqmRequestOptions,
): Promise<V4RFQIndicativeQuote[]> {
): Promise<V4RFQIndicativeQuoteMM[]> {
const _opts: RfqRequestOpts = {
...constants.DEFAULT_RFQT_REQUEST_OPTS,
...options,
@@ -367,7 +371,7 @@ export class QuoteRequestor {
marketOperation: MarketOperation,
comparisonPrice: BigNumber | undefined,
options: RfqRequestOpts,
): Promise<V4RFQIndicativeQuote[]> {
): Promise<V4RFQIndicativeQuoteMM[]> {
const _opts: RfqRequestOpts = { ...constants.DEFAULT_RFQT_REQUEST_OPTS, ...options };
// Originally a takerAddress was required for indicative quotes, but
// now we've eliminated that requirement. @0x/quote-server, however,
@@ -398,8 +402,8 @@ export class QuoteRequestor {
return this._orderSignatureToMakerUri[nativeDataToId({ signature })];
}
private _isValidRfqtIndicativeQuoteResponse(response: V4RFQIndicativeQuote): boolean {
const requiredKeys: Array<keyof V4RFQIndicativeQuote> = [
private _isValidRfqtIndicativeQuoteResponse(response: V4RFQIndicativeQuoteMM): boolean {
const requiredKeys: Array<keyof V4RFQIndicativeQuoteMM> = [
'makerAmount',
'takerAmount',
'makerToken',
@@ -545,7 +549,10 @@ export class QuoteRequestor {
},
});
rfqMakerBlacklist.logTimeoutOrLackThereof(typedMakerUrl.url, latencyMs >= timeoutMs);
return { response: response.data, makerUri: typedMakerUrl.url };
return {
response: { ...response.data, makerUri: typedMakerUrl.url },
makerUri: typedMakerUrl.url,
};
} else {
if (this._altRfqCreds === undefined) {
throw new Error(`don't have credentials for alt MM`);
@@ -694,7 +701,6 @@ export class QuoteRequestor {
} else {
const secondsRemaining = msRemainingUntilExpiration.div(ONE_SECOND_MS);
this._metrics?.measureExpirationForValidOrder(isLastLook, order.maker, secondsRemaining);
const takerAmount = new BigNumber(order.takerAmount);
const fillRatio = takerAmount.div(assetFillAmount);
if (fillRatio.lt(1) && fillRatio.gte(FILL_RATIO_WARNING_LEVEL)) {
@@ -744,9 +750,9 @@ export class QuoteRequestor {
comparisonPrice: BigNumber | undefined,
options: RfqRequestOpts,
assetOfferings: RfqMakerAssetOfferings,
): Promise<V4RFQIndicativeQuote[]> {
): Promise<V4RFQIndicativeQuoteMM[]> {
// fetch quotes
const rawQuotes = await this._getQuotesAsync<V4RFQIndicativeQuote>(
const rawQuotes = await this._getQuotesAsync<V4RFQIndicativeQuoteMM>(
makerToken,
takerToken,
assetFillAmount,
@@ -758,7 +764,7 @@ export class QuoteRequestor {
);
// validate
const validationFunction = (o: V4RFQIndicativeQuote) => this._isValidRfqtIndicativeQuoteResponse(o);
const validationFunction = (o: V4RFQIndicativeQuoteMM) => this._isValidRfqtIndicativeQuoteResponse(o);
const validQuotes = rawQuotes.filter(result => {
const order = result.response;
if (!validationFunction(order)) {

View File

@@ -10,6 +10,7 @@ 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 CompoundSampler from '../test/generated-artifacts/CompoundSampler.json';
import * as CurveSampler from '../test/generated-artifacts/CurveSampler.json';
import * as DODOSampler from '../test/generated-artifacts/DODOSampler.json';
import * as DODOV2Sampler from '../test/generated-artifacts/DODOV2Sampler.json';
@@ -52,6 +53,7 @@ export const artifacts = {
BalancerSampler: BalancerSampler as ContractArtifact,
BalancerV2Sampler: BalancerV2Sampler as ContractArtifact,
BancorSampler: BancorSampler as ContractArtifact,
CompoundSampler: CompoundSampler as ContractArtifact,
CurveSampler: CurveSampler as ContractArtifact,
DODOSampler: DODOSampler as ContractArtifact,
DODOV2Sampler: DODOV2Sampler as ContractArtifact,

View File

@@ -125,6 +125,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
gas: Math.floor(Math.random() * 8e6),
protocolFeeInWeiAmount: getRandomAmount(),
feeTakerTokenAmount: getRandomAmount(),
slippage: 0,
},
worstCaseQuoteInfo: {
makerAmount: makerTokenFillAmount,
@@ -133,6 +134,7 @@ describe('ExchangeProxySwapQuoteConsumer', () => {
gas: Math.floor(Math.random() * 8e6),
protocolFeeInWeiAmount: getRandomAmount(),
feeTakerTokenAmount: getRandomAmount(),
slippage: 0,
},
makerAmountPerEth: getRandomInteger(1, 1e9),
takerAmountPerEth: getRandomInteger(1, 1e9),

View File

@@ -23,6 +23,7 @@ import {
POSITIVE_INF,
SELL_SOURCE_FILTER_BY_CHAIN_ID,
SOURCE_FLAGS,
ZERO_AMOUNT,
} from '../src/utils/market_operation_utils/constants';
import { createFills } from '../src/utils/market_operation_utils/fills';
import { PoolsCache } from '../src/utils/market_operation_utils/pools_cache';
@@ -159,7 +160,11 @@ describe('MarketOperationUtils tests', () => {
} else {
requestor
.setup(r => r.requestRfqtIndicativeQuotesAsync(...args))
.returns(async () => results.map(r => r.order))
.returns(async () =>
results.map(r => {
return { ...r.order, makerUri: 'https://foo.bar/' };
}),
)
.verifiable(verifiable);
}
return requestor;
@@ -423,6 +428,8 @@ describe('MarketOperationUtils tests', () => {
getTwoHopSellQuotes: (..._params: any[]) => [],
getTwoHopBuyQuotes: (..._params: any[]) => [],
isAddressContract: (..._params: any[]) => false,
getGasLeft: () => ZERO_AMOUNT,
getBlockNumber: () => ZERO_AMOUNT,
};
const MOCK_SAMPLER = ({

View File

@@ -155,7 +155,7 @@ describe('generateQuoteReport', async () => {
makerAmount: rfqtOrder1.order.makerAmount,
takerAmount: rfqtOrder1.order.takerAmount,
fillableTakerAmount: rfqtOrder1.fillableTakerAmount,
isRfqt: true,
isRFQ: true,
makerUri: 'https://rfqt1.provider.club',
nativeOrder: rfqtOrder1.order,
fillData: {
@@ -167,7 +167,7 @@ describe('generateQuoteReport', async () => {
makerAmount: rfqtOrder2.order.makerAmount,
takerAmount: rfqtOrder2.order.takerAmount,
fillableTakerAmount: rfqtOrder2.fillableTakerAmount,
isRfqt: true,
isRFQ: true,
makerUri: 'https://rfqt2.provider.club',
nativeOrder: rfqtOrder2.order,
fillData: {
@@ -179,7 +179,7 @@ describe('generateQuoteReport', async () => {
makerAmount: orderbookOrder2.order.makerAmount,
takerAmount: orderbookOrder2.order.takerAmount,
fillableTakerAmount: orderbookOrder2.fillableTakerAmount,
isRfqt: false,
isRFQ: false,
fillData: {
order: orderbookOrder2.order,
} as NativeLimitOrderFillData,
@@ -263,7 +263,7 @@ describe('generateQuoteReport', async () => {
makerAmount: orderbookOrder1.order.makerAmount,
takerAmount: orderbookOrder1.order.takerAmount,
fillableTakerAmount: orderbookOrder1.fillableTakerAmount,
isRfqt: false,
isRFQ: false,
fillData: {
order: orderbookOrder1.order,
} as NativeLimitOrderFillData,

View File

@@ -494,15 +494,18 @@ describe('QuoteRequestor', async () => {
expiry: makeThreeMinuteExpiry(),
};
const goodMMUri1 = 'https://1337.0.0.1';
const goodMMUri2 = 'https://37.0.0.1';
mockedRequests.push({
...mockedDefaults,
endpoint: 'https://1337.0.0.1',
endpoint: goodMMUri1,
responseData: successfulQuote1,
});
// [GOOD] Another Successful response
mockedRequests.push({
...mockedDefaults,
endpoint: 'https://37.0.0.1',
endpoint: goodMMUri2,
responseData: successfulQuote1,
});
@@ -532,6 +535,16 @@ describe('QuoteRequestor', async () => {
responseData: { ...successfulQuote1, takerToken: otherToken1 },
});
const assetOfferings: { [k: string]: [[string, string]] } = {
'https://420.0.0.1': [[makerToken, takerToken]],
'https://421.0.0.1': [[makerToken, takerToken]],
'https://422.0.0.1': [[makerToken, takerToken]],
'https://423.0.0.1': [[makerToken, takerToken]],
'https://424.0.0.1': [[makerToken, takerToken]],
};
assetOfferings[goodMMUri1] = [[makerToken, takerToken]];
assetOfferings[goodMMUri2] = [[makerToken, takerToken]];
return testHelpers.withMockedRfqQuotes(
mockedRequests,
[],
@@ -539,15 +552,7 @@ describe('QuoteRequestor', async () => {
async () => {
const qr = new QuoteRequestor(
{}, // No RFQ-T asset offerings
{
'https://1337.0.0.1': [[makerToken, takerToken]],
'https://37.0.0.1': [[makerToken, takerToken]],
'https://420.0.0.1': [[makerToken, takerToken]],
'https://421.0.0.1': [[makerToken, takerToken]],
'https://422.0.0.1': [[makerToken, takerToken]],
'https://423.0.0.1': [[makerToken, takerToken]],
'https://424.0.0.1': [[makerToken, takerToken]],
},
assetOfferings,
quoteRequestorHttpClient,
);
const resp = await qr.requestRfqmIndicativeQuotesAsync(
@@ -572,7 +577,12 @@ describe('QuoteRequestor', async () => {
},
},
);
expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort());
expect(resp.sort()).to.eql(
[
{ ...successfulQuote1, makerUri: goodMMUri1 },
{ ...successfulQuote1, makerUri: goodMMUri2 },
].sort(),
);
},
quoteRequestorHttpClient,
);
@@ -622,9 +632,12 @@ describe('QuoteRequestor', async () => {
expiry: makeThreeMinuteExpiry(),
};
const goodMMUri1 = 'https://1337.0.0.1';
const goodMMUri2 = 'https://37.0.0.1';
mockedRequests.push({
...mockedDefaults,
endpoint: 'https://1337.0.0.1',
endpoint: goodMMUri1,
responseData: successfulQuote1,
});
// Test out a bad response code, ensure it doesnt cause throw
@@ -655,28 +668,26 @@ describe('QuoteRequestor', async () => {
// Another Successful response
mockedRequests.push({
...mockedDefaults,
endpoint: 'https://37.0.0.1',
endpoint: goodMMUri2,
responseData: successfulQuote1,
});
const assetOfferings: { [k: string]: [[string, string]] } = {
'https://420.0.0.1': [[makerToken, takerToken]],
'https://421.0.0.1': [[makerToken, takerToken]],
'https://422.0.0.1': [[makerToken, takerToken]],
'https://423.0.0.1': [[makerToken, takerToken]],
'https://424.0.0.1': [[makerToken, takerToken]],
};
assetOfferings[goodMMUri1] = [[makerToken, takerToken]];
assetOfferings[goodMMUri2] = [[makerToken, takerToken]];
return testHelpers.withMockedRfqQuotes(
mockedRequests,
[],
RfqQuoteEndpoint.Indicative,
async () => {
const qr = new QuoteRequestor(
{
'https://1337.0.0.1': [[makerToken, takerToken]],
'https://420.0.0.1': [[makerToken, takerToken]],
'https://421.0.0.1': [[makerToken, takerToken]],
'https://422.0.0.1': [[makerToken, takerToken]],
'https://423.0.0.1': [[makerToken, takerToken]],
'https://424.0.0.1': [[makerToken, takerToken]],
'https://37.0.0.1': [[makerToken, takerToken]],
},
{},
quoteRequestorHttpClient,
);
const qr = new QuoteRequestor(assetOfferings, {}, quoteRequestorHttpClient);
const resp = await qr.requestRfqtIndicativeQuotesAsync(
makerToken,
takerToken,
@@ -693,7 +704,12 @@ describe('QuoteRequestor', async () => {
intentOnFilling: true,
},
);
expect(resp.sort()).to.eql([successfulQuote1, successfulQuote1].sort());
expect(resp.sort()).to.eql(
[
{ ...successfulQuote1, makerUri: goodMMUri1 },
{ ...successfulQuote1, makerUri: goodMMUri2 },
].sort(),
);
},
quoteRequestorHttpClient,
);
@@ -784,7 +800,7 @@ describe('QuoteRequestor', async () => {
makerEndpointMaxResponseTimeMs: maxTimeoutMs,
},
);
expect(resp.sort()).to.eql([successfulQuote1].sort()); // notice only one result, despite two requests made
expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort()); // notice only one result, despite two requests made
},
quoteRequestorHttpClient,
);
@@ -847,7 +863,7 @@ describe('QuoteRequestor', async () => {
intentOnFilling: true,
},
);
expect(resp.sort()).to.eql([successfulQuote1].sort());
expect(resp.sort()).to.eql([{ ...successfulQuote1, makerUri: 'https://1337.0.0.1' }].sort());
},
quoteRequestorHttpClient,
);

View File

@@ -24,6 +24,7 @@ export async function getFullyFillableSwapQuoteWithNoFeesAsync(
totalTakerAmount: takerAmount,
protocolFeeInWeiAmount: protocolFeePerOrder.times(orders.length),
gas: 200e3,
slippage: 0,
};
const breakdown = {

View File

@@ -8,6 +8,7 @@ export * from '../test/generated-wrappers/balance_checker';
export * from '../test/generated-wrappers/balancer_sampler';
export * from '../test/generated-wrappers/balancer_v2_sampler';
export * from '../test/generated-wrappers/bancor_sampler';
export * from '../test/generated-wrappers/compound_sampler';
export * from '../test/generated-wrappers/curve_sampler';
export * from '../test/generated-wrappers/d_o_d_o_sampler';
export * from '../test/generated-wrappers/d_o_d_o_v2_sampler';

View File

@@ -11,6 +11,7 @@
"test/generated-artifacts/BalancerSampler.json",
"test/generated-artifacts/BalancerV2Sampler.json",
"test/generated-artifacts/BancorSampler.json",
"test/generated-artifacts/CompoundSampler.json",
"test/generated-artifacts/CurveSampler.json",
"test/generated-artifacts/DODOSampler.json",
"test/generated-artifacts/DODOV2Sampler.json",

View File

@@ -1,4 +1,51 @@
[
{
"version": "6.12.0",
"changes": [
{
"note": "Add Support for Arbitrum One Mainnet"
}
]
},
{
"version": "6.11.0",
"changes": [
{
"note": "Add Optimism addresses",
"pr": 385
}
],
"timestamp": 1640364306
},
{
"version": "6.10.0",
"changes": [
{
"note": "Add Aave supported FQT addresses for Polygon, Avalanche",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"version": "6.9.0",
"changes": [
{
"note": "Add Celo addresses",
"pr": 368
}
],
"timestamp": 1637102971
},
{
"timestamp": 1635903615,
"version": "6.8.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "6.8.0",
"changes": [

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.11.0 - _December 24, 2021_
* Add Optimism addresses (#385)
## v6.10.0 - _December 1, 2021_
* Add Aave supported FQT addresses for Polygon, Avalanche (#321)
## v6.9.0 - _November 16, 2021_
* Add Celo addresses (#368)
## v6.8.1 - _November 3, 2021_
* Dependencies updated
## v6.8.0 - _October 19, 2021_
* Fantom deployment (#347)

View File

@@ -244,11 +244,11 @@
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5",
"payTakerTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
"affiliateFeeTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a",
"fillQuoteTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db",
"positiveSlippageFeeTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace"
"wethTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
"payTakerTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a",
"affiliateFeeTransformer": "0x99356167edba8fbdc36959e3f5d0c43d1ba9c6db",
"fillQuoteTransformer": "0x45b3a72221e571017c0f0ec42189e11d149d0ace",
"positiveSlippageFeeTransformer": "0xdd66c23e07b4d6925b6089b5fe6fc9e62941afe8"
}
},
"137": {
@@ -289,7 +289,7 @@
"wethTransformer": "0xe309d011cc6f189a3e8dcba85922715a019fed38",
"payTakerTransformer": "0x5ba7b9be86cda01cfbf56e0fb97184783be9dda1",
"affiliateFeeTransformer": "0xbed27284b42e5684e987169cf1da09c5d6c49fa8",
"fillQuoteTransformer": "0xf708d512b8a82e2862543a630403327174410baf",
"fillQuoteTransformer": "0xd3afdf4a8ea9183e76c9c2306cda03ea4afffea5",
"positiveSlippageFeeTransformer": "0x4cd8f1c0df4d40fcc1e073845d5f6f4ed5cc8dab"
}
},
@@ -373,7 +373,7 @@
"wethTransformer": "0x9b8b52391071d71cd4ad1e61d7f273268fa34c6c",
"payTakerTransformer": "0x898c6fde239d646c73f0a57e3570b6f86a3d62a3",
"affiliateFeeTransformer": "0x34617b855411e52fbc05899435f44cbd0503022c",
"fillQuoteTransformer": "0x8a5417dd7ffde61ec61e11b45797e16686e1d6b9",
"fillQuoteTransformer": "0xd421f50b3ae27f223aa35a04944236d257235412",
"positiveSlippageFeeTransformer": "0x470ba89da18a6db6e8a0567b3c9214b960861857"
}
},
@@ -418,5 +418,173 @@
"fillQuoteTransformer": "0x71de60a1b160094a3f6c7e1b883ff9337d639131",
"positiveSlippageFeeTransformer": "0xe87d69b285005cc82b53b844322652c49ed64600"
}
},
"42161": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0x21be370d5312f44cb42ce377bc9b8a0cef1a4c83",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0xf760c5b88d970d6f97e64e264dac5a3767dafd74",
"exchangeProxy": "0xdef189deaef76e379df891899eb5a00a94cbc250",
"exchangeProxyTransformerDeployer": "0x47f01db18a38261e4cb153bae6db7d3743acb33c",
"exchangeProxyFlashWallet": "0xb4d961671cadfed687e040b076eee29840c142e5",
"exchangeProxyLiquidityProviderSandbox": "0xca64d4225804f2ae069760cb5ff2f1d8bac1c2f9",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x9b6aa8f26a92108e7d1f66373d757bb955112703",
"payTakerTransformer": "0x32df54951d33d7460e15fa59b1fcc262183ce4c2",
"affiliateFeeTransformer": "0x67efa679a4b56c38713d478e649c88247f4f8e88",
"fillQuoteTransformer": "0x71de60a1b160094a3f6c7e1b883ff9337d639131",
"positiveSlippageFeeTransformer": "0xe87d69b285005cc82b53b844322652c49ed64600"
}
},
"42220": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0x471ece3750da237f93b8e339c536989b8978a438",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x92115010fd9b170d4918b102efc86b1b7bebdc7f",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyTransformerDeployer": "0x1fe80d5ad9464dba2d60b88e449305f184823f8a",
"exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x948e03e708b4c62c63f89157a3aa76b986c110ed",
"payTakerTransformer": "0x90fb6c638ece8f3e4bfda1c6d6425626b53148b0",
"affiliateFeeTransformer": "0xc93913692ed073cb0cb37d4a760afd7916e9cb01",
"fillQuoteTransformer": "0xa825d4d3c4d2820c52da69fcccf269b4081871f2",
"positiveSlippageFeeTransformer": "0x9ffc7a79133ed5242777e40764777a6d5aab282c"
}
},
"10": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0x4200000000000000000000000000000000000006",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x6d506b2847df0c6f04d2628da1adaf4d8fb2e81b",
"exchangeProxy": "0xdef1abe32c034e558cdd535791643c58a13acc10",
"exchangeProxyTransformerDeployer": "0x3a539ed6bd42de8fbaf3899fb490c792e153d647",
"exchangeProxyFlashWallet": "0xa3128d9b7cca7d5af29780a56abeec12b05a6740",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x02ce7af6520e2862f961f5d7eda746642865179c",
"payTakerTransformer": "0x085d10a34f14f6a631ea8ff7d016782ee3ffaa11",
"affiliateFeeTransformer": "0x55cf1d7535250db75bf0190493f55781ee583553",
"fillQuoteTransformer": "0x3543ef833d28b7e983c293856561f21a7f089f1d",
"positiveSlippageFeeTransformer": "0xb11e14565dfbeb702dea9bc0cb47f1a8b32f4783"
}
},
"42161": {
"erc20Proxy": "0x0000000000000000000000000000000000000000",
"erc721Proxy": "0x0000000000000000000000000000000000000000",
"zrxToken": "0x0000000000000000000000000000000000000000",
"etherToken": "0x82af49447d8a07e3bd95bd0d56f35241523fbab1",
"exchangeV2": "0x0000000000000000000000000000000000000000",
"exchange": "0x0000000000000000000000000000000000000000",
"assetProxyOwner": "0x0000000000000000000000000000000000000000",
"zeroExGovernor": "0x0000000000000000000000000000000000000000",
"forwarder": "0x0000000000000000000000000000000000000000",
"coordinatorRegistry": "0x0000000000000000000000000000000000000000",
"coordinator": "0x0000000000000000000000000000000000000000",
"multiAssetProxy": "0x0000000000000000000000000000000000000000",
"staticCallProxy": "0x0000000000000000000000000000000000000000",
"erc1155Proxy": "0x0000000000000000000000000000000000000000",
"devUtils": "0x0000000000000000000000000000000000000000",
"zrxVault": "0x0000000000000000000000000000000000000000",
"staking": "0x0000000000000000000000000000000000000000",
"stakingProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeProxy": "0x0000000000000000000000000000000000000000",
"erc20BridgeSampler": "0x0000000000000000000000000000000000000000",
"chaiBridge": "0x0000000000000000000000000000000000000000",
"dydxBridge": "0x0000000000000000000000000000000000000000",
"godsUnchainedValidator": "0x0000000000000000000000000000000000000000",
"broker": "0x0000000000000000000000000000000000000000",
"chainlinkStopLimit": "0x0000000000000000000000000000000000000000",
"maximumGasPrice": "0x0000000000000000000000000000000000000000",
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
"exchangeProxyGovernor": "0x1fe80d5ad9464dba2d60b88e449305f184823f8a",
"exchangeProxy": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"exchangeProxyTransformerDeployer": "0x29f80c1f685e19ae1807063eda432f431ac623d0",
"exchangeProxyFlashWallet": "0xdb6f1920a889355780af7570773609bd8cb1f498",
"exchangeProxyLiquidityProviderSandbox": "0x0000000000000000000000000000000000000000",
"zrxTreasury": "0x0000000000000000000000000000000000000000",
"transformers": {
"wethTransformer": "0x10e968968f49dd66a5efeebbb2edcb9c49c4fc49",
"payTakerTransformer": "0xae3e8cf7bf340d7084f312dfae2aa8b01c885b02",
"affiliateFeeTransformer": "0x05a24978471869327904ea13da3c4322128e2aaa",
"fillQuoteTransformer": "0x5a653323c2a47a8e1b69fb3d0f15858bfc7fe205",
"positiveSlippageFeeTransformer": "0xD56B9C014b45ED95e2a048A0C28121Db30265F13"
}
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-addresses",
"version": "6.8.0",
"version": "6.11.0",
"engines": {
"node": ">=6.12"
},

View File

@@ -54,6 +54,10 @@ export enum ChainId {
PolygonMumbai = 80001,
Avalanche = 43114,
Fantom = 250,
Arbitrum = 42161,
Celo = 42220,
Optimism = 10,
Arbitrum = 42161,
}
/**

View File

@@ -1,4 +1,40 @@
[
{
"timestamp": 1640364306,
"version": "13.18.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "13.18.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "13.18.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "13.18.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "13.18.1",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v13.18.5 - _December 24, 2021_
* Dependencies updated
## v13.18.4 - _December 1, 2021_
* Dependencies updated
## v13.18.3 - _November 16, 2021_
* Dependencies updated
## v13.18.2 - _November 3, 2021_
* Dependencies updated
## v13.18.1 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contract-wrappers",
"version": "13.18.1",
"version": "13.18.5",
"engines": {
"node": ">=6.12"
},
@@ -57,7 +57,7 @@
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/json-schemas": "^6.3.0",
"@0x/types": "^3.3.4",
"@0x/utils": "^6.4.4",

View File

@@ -1,4 +1,50 @@
[
{
"timestamp": 1640364306,
"version": "8.1.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1638390144,
"version": "8.1.13",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637102971,
"version": "8.1.12",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1637065617,
"version": "8.1.11",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "8.1.10",
"changes": [
{
"note": "Add OtcOrders to fullMigrateAsync",
"pr": 350
}
],
"timestamp": 1635903615
},
{
"timestamp": 1634668033,
"version": "8.1.9",

View File

@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v8.1.14 - _December 24, 2021_
* Dependencies updated
## v8.1.13 - _December 1, 2021_
* Dependencies updated
## v8.1.12 - _November 16, 2021_
* Dependencies updated
## v8.1.11 - _November 16, 2021_
* Dependencies updated
## v8.1.10 - _November 3, 2021_
* Add OtcOrders to fullMigrateAsync (#350)
## v8.1.9 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/migrations",
"version": "8.1.9",
"version": "8.1.14",
"engines": {
"node": ">=6.12"
},
@@ -68,20 +68,20 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.2",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-addresses": "^6.11.0",
"@0x/contracts-asset-proxy": "^3.7.19",
"@0x/contracts-coordinator": "^3.1.38",
"@0x/contracts-dev-utils": "^1.3.36",
"@0x/contracts-erc1155": "^2.1.37",
"@0x/contracts-erc20": "^3.3.21",
"@0x/contracts-erc20": "^3.3.25",
"@0x/contracts-erc721": "^3.1.37",
"@0x/contracts-exchange": "^3.2.38",
"@0x/contracts-exchange-forwarder": "^4.2.38",
"@0x/contracts-extensions": "^6.2.32",
"@0x/contracts-multisig": "^4.1.38",
"@0x/contracts-staking": "^2.0.45",
"@0x/contracts-utils": "^4.8.2",
"@0x/contracts-zero-ex": "^0.29.2",
"@0x/contracts-utils": "^4.8.6",
"@0x/contracts-zero-ex": "^0.30.1",
"@0x/sol-compiler": "^4.7.5",
"@0x/subproviders": "^6.6.0",
"@0x/typescript-typings": "^5.2.1",

View File

@@ -1,4 +1,41 @@
[
{
"timestamp": 1640364306,
"version": "1.10.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.10.0",
"changes": [
{
"note": "Add `AaveV2` and `Compound` deposit/withdrawal liquidity source",
"pr": 321
}
],
"timestamp": 1638390144
},
{
"timestamp": 1637102971,
"version": "1.9.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1635903615,
"version": "1.9.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1634668033,
"version": "1.9.3",

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.10.1 - _December 24, 2021_
* Dependencies updated
## v1.10.0 - _December 1, 2021_
* Add `AaveV2` and `Compound` deposit/withdrawal liquidity source (#321)
## v1.9.5 - _November 16, 2021_
* Dependencies updated
## v1.9.4 - _November 3, 2021_
* Dependencies updated
## v1.9.3 - _October 19, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/protocol-utils",
"version": "1.9.3",
"version": "1.10.1",
"engines": {
"node": ">=6.12"
},
@@ -63,8 +63,8 @@
},
"dependencies": {
"@0x/assert": "^3.0.29",
"@0x/contract-addresses": "^6.8.0",
"@0x/contract-wrappers": "^13.18.1",
"@0x/contract-addresses": "^6.11.0",
"@0x/contract-wrappers": "^13.18.5",
"@0x/json-schemas": "^6.3.0",
"@0x/subproviders": "^6.6.0",
"@0x/utils": "^6.4.4",

View File

@@ -132,6 +132,8 @@ export enum BridgeProtocol {
CurveV2,
Lido,
Clipper, // Not used: Clipper is now using PLP interface
AaveV2,
Compound,
}
// tslint:enable: enum-naming

View File

@@ -738,6 +738,7 @@
"@0x/contracts-asset-proxy@^3.7.19":
version "3.7.19"
resolved "https://registry.yarnpkg.com/@0x/contracts-asset-proxy/-/contracts-asset-proxy-3.7.19.tgz#ee621a233f4d77b439c74c5b8d70db2e1ed001c4"
integrity sha512-6lTgz8JdmceaSnqpJrbyKJXUIyAA2HZrXel+ZWpgMtO7qaSSQDRUcWUBwejTE588BcVxhNFQ7tHvOK3/QGnpHw==
dependencies:
"@0x/base-contract" "^6.4.0"
"@0x/contracts-erc1155" "^2.1.37"
@@ -959,10 +960,10 @@
typedoc "~0.16.11"
yargs "^10.0.3"
"@0x/neon-router@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.2.1.tgz#23bb3cedc0eafd55a8ba6b6ea8a59ee4c538064b"
integrity sha512-feCCKuox4staZl8lxLY4nf5U256NcDHrgvSFra5cU/TUhoblLHb8F7eWAC9ygpukZUCVFLy13mExkFQHXlEOYw==
"@0x/neon-router@^0.3.1":
version "0.3.1"
resolved "https://registry.yarnpkg.com/@0x/neon-router/-/neon-router-0.3.1.tgz#4ec13e750d1435357c4928d7f2521a2b4376f27e"
integrity sha512-M4ypTov9KyxsGJpYwobrld3Y2JOlR7U0XjR6BEQE2gQ1k3nie/1wNEI2J4ZjKw++RLDxdv/RCqhgA5VnINzjxA==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.5"
@@ -6402,6 +6403,7 @@ fake-merkle-patricia-tree@^1.0.1:
fast-abi@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/fast-abi/-/fast-abi-0.0.2.tgz#da5f796fd7c7b0c966d916ee21daae3eca61c07c"
integrity sha512-k/2s63SkFf6jU2LyF6oQC5/N+L90q6VD1wkp2NXo+DSHoTeOJD2Q6Egpcs+bTPODik0CHxjb7lORgsG+QCRq/Q==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.4"
neon-cli "^0.8.0"