Compare commits

..

17 Commits

Author SHA1 Message Date
Github Actions
08e1c5109f Publish
- @0x/contracts-asset-proxy@3.7.7
 - @0x/contracts-broker@1.1.25
 - @0x/contracts-coordinator@3.1.26
 - @0x/contracts-dev-utils@1.3.24
 - @0x/contracts-erc1155@2.1.25
 - @0x/contracts-erc20@3.3.4
 - @0x/contracts-erc721@3.1.25
 - @0x/contracts-exchange-forwarder@4.2.26
 - @0x/contracts-exchange-libs@4.3.25
 - @0x/contracts-exchange@3.2.26
 - @0x/contracts-extensions@6.2.20
 - @0x/contracts-integrations@2.7.27
 - @0x/contracts-multisig@4.1.26
 - @0x/contracts-staking@2.0.33
 - @0x/contracts-test-utils@5.3.22
 - @0x/contracts-treasury@1.0.2
 - @0x/contracts-utils@4.7.4
 - @0x/contracts-zero-ex@0.19.0
 - @0x/asset-swapper@6.1.0
 - @0x/contract-addresses@5.11.0
 - @0x/contract-artifacts@3.12.0
 - @0x/contract-wrappers-test@12.2.37
 - @0x/contract-wrappers@13.13.0
 - @0x/migrations@7.0.0
 - @0x/order-utils@10.4.17
 - @0x/protocol-utils@1.3.0
2021-02-24 04:42:41 +00:00
Github Actions
6f7a843742 Updated CHANGELOGS & MD docs 2021-02-24 04:42:32 +00:00
Jacob Evans
c9c9615bb5 Fix contract merge conflict 2021-02-24 14:08:54 +10:00
Romain Butteaud
f98609686d feat: opt-in positive slippage fee for integrators (#101)
* feat: Positive Slippage Fee

* fix: rename ethToTakerAssetRate to takerAssetPriceForOneEth

* fix: rename takerAssetPriceForOneEth to takerAssetsPerEth

* fix: export AffiliateFeeType

* rebased off development

* Add a gasOverhead for non-deterministic operations

* CHANGELOGs

* rename outputTokens to outputAmount

* Confirm transformer addresses on Mainnet and Ropsten

* fix import

Co-authored-by: Jacob Evans <jacob@dekz.net>
2021-02-24 12:51:58 +10:00
Jacob Evans
5b8bbc34e8 fix: FQT in migrations (#157) 2021-02-24 12:36:58 +10:00
Jacob Evans
49cb00a9ab feat: DODO V2, Linkswap (#152)
* feat: DODO V2

* Fix typo

* feat: Linkswap (#153)

* fix: intermediate hops WBTC (#154)

* feat: Linkswap

* fix: Re-add WBTC in default hop tokens

* Update review changes

* FQT deploy + no gas limit ETH refund (#155)

* `@0x/contracts-zero-ex`: refund ETH with no gas limit in FQT
`@0x/contract-addresses`: Deploy FQT

* Update packages/contract-addresses/CHANGELOG.json

Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: mzhu25 <mchl.zhu.96@gmail.com>
2021-02-24 12:19:26 +10:00
Alex Kroeger
74b240fb88 add exchange proxy overhead penalty to comparison price (#156)
* add exchange proxy overhead penalty to comparison price

* prettier
2021-02-23 17:32:15 -08:00
Alex Kroeger
514f9d2621 feat/alt RFQ MM implementation (#139)
* baseline adapter code [WIP]

* fixed adapter logic, quote_requester instantiation

* modified quote-requestor test to include alt implementation

* type changes, fixes to quote requestor test

* small fixes

* working tests, made alt utils more readable

* lint errors

* added alt indicative quote tests, minor fixes

* export alt MM market offering types

* altered alt market offering to have id instead of symbols

* addressed minor comments

* updated changelog

* got rid of unnecessary, large if-block, fixed the buy-sell assignment to be from the MM's perspective

* extra logging for debugging

* fixed existingOrder size

* get rid of only flag on test, get rid of extra logging

* prettier
2021-02-22 16:07:30 -08:00
Jacob Evans
fa78d1092a feat: asset-swapper Fake Taker contract (#151) 2021-02-22 08:32:53 +10:00
Lawrence Forman
297342092b @0x/asset-swapper: special case BNB in uni v1 sampler (#147)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-02-19 16:56:16 +10:00
Jacob Evans
076f263a86 feat: Validate v4 limit orders (#148)
re-renable tests
2021-02-19 16:42:10 +10:00
Jacob Evans
0c56207abc fix: Protocol fee in fee schedule (#146)
* fix: Protocol fee in fee schedule

* CHANGELOG

* hack imports
2021-02-18 14:12:11 +10:00
Lawrence Forman
23953d8a5a Update artifacts and wrappers (#145)
* `@0x/contract-artifacts`: Update artifacts.
`@0x/contract-wrappers`: Regenerate wrappers

* fix doc gen

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-02-17 14:34:43 -05:00
Jacob Evans
c6919eb25a feat: Mirror Protocol tokens (#142)
* feat: Mirror Protocol tokens

* added .tap to builder
2021-02-17 10:14:38 +10:00
Lawrence Forman
d509604b52 @0x/contracts-zero-ex: Export CurveLiquidityProviderContract (#144)
`@0x/asset-swapper`: Add deployed `CurveLiquidityProvider` addresses

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-02-16 16:17:08 -05:00
Lawrence Forman
a74a3450eb @0x/contracts-zero-ex: Add CurveLiquidityProvider and misc refactors (#127)
* `@0x/contracts-zero-ex`: Add `CurveLiquidityProvider` and misc refactors

* `@0x/asset-swapper`: Fix compiler error on `ILiquidityProvider` call
`@0x/protocol-utils`: Add VIP utils.

* `@0x/contracts-zero-ex`: Rebase and fix comiler warnings

* `@0x/asset-swapper`: Clean up curve VIP integration

* `@0x/protocol-utils`: Update changelog

* `@0x/protocol-utils`: tsdoc new functions

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-02-11 19:13:17 -05:00
Jacob Evans
72c5399b9d fix: Second hop source is missing (#138)
* fix: Second hop source is missing

* CHANGELOGs
2021-02-11 00:13:58 +10:00
171 changed files with 3588 additions and 1090 deletions

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "3.7.7",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "3.7.6",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.7.7 - _February 24, 2021_
* Dependencies updated
## v3.7.6 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.7.6",
"version": "3.7.7",
"engines": {
"node": ">=6.12"
},
@@ -52,10 +52,10 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contract-wrappers": "^13.12.3",
"@0x/contract-wrappers": "^13.13.0",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
@@ -80,11 +80,11 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-erc1155": "^2.1.24",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/order-utils": "^10.4.16",
"@0x/contracts-erc1155": "^2.1.25",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/order-utils": "^10.4.17",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "1.1.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "1.1.24",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.25 - _February 24, 2021_
* Dependencies updated
## v1.1.24 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-broker",
"version": "1.1.24",
"version": "1.1.25",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,14 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -85,7 +85,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "3.1.26",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "3.1.25",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.26 - _February 24, 2021_
* Dependencies updated
## v3.1.25 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.1.25",
"version": "3.1.26",
"engines": {
"node": ">=6.12"
},
@@ -53,12 +53,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-gen": "^2.0.30",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -84,10 +84,10 @@
"dependencies": {
"@0x/assert": "^3.0.21",
"@0x/base-contract": "^6.2.18",
"@0x/contract-addresses": "^5.10.0",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contract-addresses": "^5.11.0",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/json-schemas": "^5.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "1.3.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "1.3.23",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.24 - _February 24, 2021_
* Dependencies updated
## v1.3.23 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.3.23",
"version": "1.3.24",
"engines": {
"node": ">=6.12"
},
@@ -43,10 +43,10 @@
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/assert": "^3.0.21",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "2.1.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "2.1.24",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.25 - _February 24, 2021_
* Dependencies updated
## v2.1.24 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.1.24",
"version": "2.1.25",
"engines": {
"node": ">=6.12"
},
@@ -54,7 +54,7 @@
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
@@ -81,7 +81,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"lodash": "^4.17.11"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "3.3.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "3.3.3",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.4 - _February 24, 2021_
* Dependencies updated
## v3.3.3 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.3",
"version": "3.3.4",
"engines": {
"node": ">=6.12"
},
@@ -53,8 +53,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "3.1.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "3.1.24",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.25 - _February 24, 2021_
* Dependencies updated
## v3.1.24 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.1.24",
"version": "3.1.25",
"engines": {
"node": ">=6.12"
},
@@ -54,8 +54,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "4.2.26",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "4.2.25",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.2.26 - _February 24, 2021_
* Dependencies updated
## v4.2.25 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.2.25",
"version": "4.2.26",
"engines": {
"node": ">=6.12"
},
@@ -53,18 +53,18 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-erc1155": "^2.1.24",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-erc1155": "^2.1.25",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "4.3.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "4.3.24",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.3.25 - _February 24, 2021_
* Dependencies updated
## v4.3.24 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.3.24",
"version": "4.3.25",
"engines": {
"node": ">=6.12"
},
@@ -81,9 +81,9 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/order-utils": "^10.4.16",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/order-utils": "^10.4.17",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "3.2.26",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "3.2.25",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.26 - _February 24, 2021_
* Dependencies updated
## v3.2.25 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.2.25",
"version": "3.2.26",
"engines": {
"node": ">=6.12"
},
@@ -53,13 +53,13 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-multisig": "^4.1.25",
"@0x/contracts-staking": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-multisig": "^4.1.26",
"@0x/contracts-staking": "^2.0.33",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
@@ -89,11 +89,11 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-erc1155": "^2.1.24",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/order-utils": "^10.4.16",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-erc1155": "^2.1.25",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/order-utils": "^10.4.17",
"@0x/utils": "^6.2.0",
"lodash": "^4.17.11"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "6.2.20",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "6.2.19",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.2.20 - _February 24, 2021_
* Dependencies updated
## v6.2.19 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "6.2.19",
"version": "6.2.20",
"engines": {
"node": ">=6.12"
},
@@ -53,16 +53,16 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -91,7 +91,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/typescript-typings": "^5.1.6",
"ethereum-types": "^3.4.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.26",
"version": "2.7.27",
"private": true,
"engines": {
"node": ">=6.12"
@@ -53,21 +53,21 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contract-addresses": "^5.10.0",
"@0x/contract-wrappers": "^13.12.3",
"@0x/contracts-broker": "^1.1.24",
"@0x/contracts-coordinator": "^3.1.25",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-exchange-forwarder": "^4.2.25",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-extensions": "^6.2.19",
"@0x/contract-addresses": "^5.11.0",
"@0x/contract-wrappers": "^13.13.0",
"@0x/contracts-broker": "^1.1.25",
"@0x/contracts-coordinator": "^3.1.26",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-exchange-forwarder": "^4.2.26",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-extensions": "^6.2.20",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-utils": "^4.7.4",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.2.1",
"@0x/migrations": "^6.6.0",
"@0x/order-utils": "^10.4.16",
"@0x/protocol-utils": "^1.2.0",
"@0x/migrations": "^7.0.0",
"@0x/order-utils": "^10.4.17",
"@0x/protocol-utils": "^1.3.0",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.4.1",
@@ -93,17 +93,17 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/asset-swapper": "^6.0.0",
"@0x/asset-swapper": "^6.1.0",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc1155": "^2.1.24",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-erc721": "^3.1.24",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-multisig": "^4.1.25",
"@0x/contracts-staking": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-zero-ex": "^0.18.2",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc1155": "^2.1.25",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-erc721": "^3.1.25",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-multisig": "^4.1.26",
"@0x/contracts-staking": "^2.0.33",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-zero-ex": "^0.19.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "4.1.26",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "4.1.25",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.26 - _February 24, 2021_
* Dependencies updated
## v4.1.25 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "4.1.25",
"version": "4.1.26",
"engines": {
"node": ">=6.12"
},
@@ -50,11 +50,11 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "2.0.33",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "2.0.32",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.33 - _February 24, 2021_
* Dependencies updated
## v2.0.32 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-staking",
"version": "2.0.32",
"version": "2.0.33",
"engines": {
"node": ">=6.12"
},
@@ -54,14 +54,14 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-dev-utils": "^1.3.23",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-dev-utils": "^1.3.24",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-utils": "^4.7.4",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -88,7 +88,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "5.3.22",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "5.3.21",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.3.22 - _February 24, 2021_
* Dependencies updated
## v5.3.21 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.3.21",
"version": "5.3.22",
"engines": {
"node": ">=6.12"
},
@@ -44,10 +44,10 @@
"dependencies": {
"@0x/assert": "^3.0.21",
"@0x/base-contract": "^6.2.18",
"@0x/contract-addresses": "^5.10.0",
"@0x/contract-addresses": "^5.11.0",
"@0x/dev-utils": "^4.2.1",
"@0x/json-schemas": "^5.4.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-coverage": "^4.0.29",
"@0x/sol-profiler": "^4.1.19",
"@0x/sol-trace": "^3.0.29",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "1.0.1",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.0.2 - _February 24, 2021_
* Dependencies updated
## v1.0.1 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.0.1",
"version": "1.0.2",
"engines": {
"node": ">=6.12"
},
@@ -47,12 +47,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contract-addresses": "^5.10.0",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contract-addresses": "^5.11.0",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-staking": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-staking": "^2.0.33",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/sol-compiler": "^4.4.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/protocol-utils": "^1.2.0",
"@0x/protocol-utils": "^1.3.0",
"@0x/subproviders": "^6.2.3",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1614141718,
"version": "4.7.4",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "4.7.3",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.7.4 - _February 24, 2021_
* Dependencies updated
## v4.7.3 - _February 10, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.7.3",
"version": "4.7.4",
"engines": {
"node": ">=6.12"
},
@@ -52,9 +52,9 @@
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",

View File

@@ -1,4 +1,34 @@
[
{
"version": "0.19.0",
"changes": [
{
"note": "Add `CurveLiquidityProvider` and misc refactors",
"pr": 127
},
{
"note": "Export `CurveLiquidityProviderContract`",
"pr": 144
},
{
"note": "Add `DodoV2`",
"pr": 152
},
{
"note": "Add `Linkswap`",
"pr": 153
},
{
"note": "refund ETH with no gas limit in FQT",
"pr": 155
},
{
"note": "Added an opt-in `PositiveSlippageAffiliateFee`",
"pr": 101
}
],
"timestamp": 1614141718
},
{
"version": "0.18.2",
"changes": [

View File

@@ -5,6 +5,15 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.19.0 - _February 24, 2021_
* Add `CurveLiquidityProvider` and misc refactors (#127)
* Export `CurveLiquidityProviderContract` (#144)
* Add `DodoV2` (#152)
* Add `Linkswap` (#153)
* refund ETH with no gas limit in FQT (#155)
* Added an opt-in `PositiveSlippageAffiliateFee` (#101)
## v0.18.2 - _February 10, 2021_
* Update FQT for v4 native orders (#104)

View File

@@ -20,6 +20,9 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../vendor/ILiquidityProvider.sol";
interface ILiquidityProviderSandbox {
@@ -32,9 +35,9 @@ interface ILiquidityProviderSandbox {
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForToken(
address provider,
address inputToken,
address outputToken,
ILiquidityProvider provider,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -49,8 +52,8 @@ interface ILiquidityProviderSandbox {
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellEthForToken(
address provider,
address outputToken,
ILiquidityProvider provider,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -65,8 +68,8 @@ interface ILiquidityProviderSandbox {
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForEth(
address provider,
address inputToken,
ILiquidityProvider provider,
IERC20TokenV06 inputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData

View File

@@ -17,6 +17,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibOwnableRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../vendor/ILiquidityProvider.sol";
import "../vendor/v3/IERC20Bridge.sol";
import "./ILiquidityProviderSandbox.sol";
@@ -58,9 +59,9 @@ contract LiquidityProviderSandbox is
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForToken(
address provider,
address inputToken,
address outputToken,
ILiquidityProvider provider,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -69,7 +70,7 @@ contract LiquidityProviderSandbox is
onlyOwner
override
{
ILiquidityProvider(provider).sellTokenForToken(
provider.sellTokenForToken(
inputToken,
outputToken,
recipient,
@@ -86,8 +87,8 @@ contract LiquidityProviderSandbox is
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellEthForToken(
address provider,
address outputToken,
ILiquidityProvider provider,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -96,7 +97,7 @@ contract LiquidityProviderSandbox is
onlyOwner
override
{
ILiquidityProvider(provider).sellEthForToken(
provider.sellEthForToken(
outputToken,
recipient,
minBuyAmount,
@@ -112,8 +113,8 @@ contract LiquidityProviderSandbox is
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
function executeSellTokenForEth(
address provider,
address inputToken,
ILiquidityProvider provider,
IERC20TokenV06 inputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -122,7 +123,7 @@ contract LiquidityProviderSandbox is
onlyOwner
override
{
ILiquidityProvider(provider).sellTokenForEth(
provider.sellTokenForEth(
inputToken,
payable(recipient),
minBuyAmount,

View File

@@ -20,10 +20,23 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../vendor/ILiquidityProvider.sol";
/// @dev Feature to swap directly with an on-chain liquidity provider.
interface ILiquidityProviderFeature {
/// @dev Event for data pipeline.
event LiquidityProviderSwap(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
ILiquidityProvider provider,
address recipient
);
/// @dev Sells `sellAmount` of `inputToken` to the liquidity provider
/// at the given `provider` address.
/// @param inputToken The token being sold.
@@ -38,9 +51,9 @@ interface ILiquidityProviderFeature {
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellToLiquidityProvider(
address inputToken,
address outputToken,
address payable provider,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
ILiquidityProvider provider,
address recipient,
uint256 sellAmount,
uint256 minBuyAmount,

View File

@@ -23,12 +23,14 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../errors/LibLiquidityProviderRichErrors.sol";
import "../external/ILiquidityProviderSandbox.sol";
import "../external/LiquidityProviderSandbox.sol";
import "../fixins/FixinCommon.sol";
import "../fixins/FixinTokenSpender.sol";
import "../migrations/LibMigrate.sol";
import "../transformers/LibERC20Transformer.sol";
import "./IFeature.sol";
import "./ILiquidityProviderFeature.sol";
@@ -45,23 +47,11 @@ contract LiquidityProviderFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "LiquidityProviderFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 2);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 3);
/// @dev ETH pseudo-token address.
address constant internal ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev The sandbox contract address.
ILiquidityProviderSandbox public immutable sandbox;
/// @dev Event for data pipeline.
event LiquidityProviderSwap(
address inputToken,
address outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
address provider,
address recipient
);
constructor(LiquidityProviderSandbox sandbox_, bytes32 greedyTokensBloomFilter)
public
FixinCommon()
@@ -95,9 +85,9 @@ contract LiquidityProviderFeature is
/// @param auxiliaryData Auxiliary data supplied to the `provider` contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellToLiquidityProvider(
address inputToken,
address outputToken,
address payable provider,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
ILiquidityProvider provider,
address recipient,
uint256 sellAmount,
uint256 minBuyAmount,
@@ -114,21 +104,21 @@ contract LiquidityProviderFeature is
// Forward all attached ETH to the provider.
if (msg.value > 0) {
provider.transfer(msg.value);
payable(address(provider)).transfer(msg.value);
}
if (inputToken != ETH_TOKEN_ADDRESS) {
if (!LibERC20Transformer.isTokenETH(inputToken)) {
// Transfer input ERC20 tokens to the provider.
_transferERC20Tokens(
IERC20TokenV06(inputToken),
inputToken,
msg.sender,
provider,
address(provider),
sellAmount
);
}
if (inputToken == ETH_TOKEN_ADDRESS) {
uint256 balanceBefore = IERC20TokenV06(outputToken).balanceOf(recipient);
if (LibERC20Transformer.isTokenETH(inputToken)) {
uint256 balanceBefore = outputToken.balanceOf(recipient);
sandbox.executeSellEthForToken(
provider,
outputToken,
@@ -137,7 +127,7 @@ contract LiquidityProviderFeature is
auxiliaryData
);
boughtAmount = IERC20TokenV06(outputToken).balanceOf(recipient).safeSub(balanceBefore);
} else if (outputToken == ETH_TOKEN_ADDRESS) {
} else if (LibERC20Transformer.isTokenETH(outputToken)) {
uint256 balanceBefore = recipient.balance;
sandbox.executeSellTokenForEth(
provider,
@@ -148,7 +138,7 @@ contract LiquidityProviderFeature is
);
boughtAmount = recipient.balance.safeSub(balanceBefore);
} else {
uint256 balanceBefore = IERC20TokenV06(outputToken).balanceOf(recipient);
uint256 balanceBefore = outputToken.balanceOf(recipient);
sandbox.executeSellTokenForToken(
provider,
inputToken,
@@ -157,14 +147,14 @@ contract LiquidityProviderFeature is
minBuyAmount,
auxiliaryData
);
boughtAmount = IERC20TokenV06(outputToken).balanceOf(recipient).safeSub(balanceBefore);
boughtAmount = outputToken.balanceOf(recipient).safeSub(balanceBefore);
}
if (boughtAmount < minBuyAmount) {
LibLiquidityProviderRichErrors.LiquidityProviderIncompleteSellError(
provider,
outputToken,
inputToken,
address(provider),
address(outputToken),
address(inputToken),
sellAmount,
boughtAmount,
minBuyAmount

View File

@@ -0,0 +1,208 @@
// 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-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../transformers/LibERC20Transformer.sol";
import "../vendor/ILiquidityProvider.sol";
contract CurveLiquidityProvider is
ILiquidityProvider
{
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
struct CurveData {
address curveAddress;
bytes4 exchangeFunctionSelector;
int128 fromCoinIdx;
int128 toCoinIdx;
}
/// @dev This contract must be payable because takers can transfer funds
/// in prior to calling the swap function.
receive() external payable {}
/// @dev Trades `inputToken` for `outputToken`. The amount of `inputToken`
/// to sell must be transferred to the contract prior to calling this
/// function to trigger the trade.
/// @param inputToken The token being sold.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellTokenForToken(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
override
returns (uint256 boughtAmount)
{
require(
!LibERC20Transformer.isTokenETH(inputToken)
&& !LibERC20Transformer.isTokenETH(outputToken),
"CurveLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
inputToken,
outputToken,
minBuyAmount,
abi.decode(auxiliaryData, (CurveData))
);
// Every pool contract currently checks this but why not.
require(boughtAmount >= minBuyAmount, "CurveLiquidityProvider/UNDERBOUGHT");
outputToken.compatTransfer(recipient, boughtAmount);
}
/// @dev Trades ETH for token. ETH must either be attached to this function
/// call or sent to the contract prior to calling this function to
/// trigger the trade.
/// @param outputToken The token being bought.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of `outputToken` to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellEthForToken(
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
payable
override
returns (uint256 boughtAmount)
{
require(
!LibERC20Transformer.isTokenETH(outputToken),
"CurveLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
LibERC20Transformer.ETH_TOKEN,
outputToken,
minBuyAmount,
abi.decode(auxiliaryData, (CurveData))
);
// Every pool contract currently checks this but why not.
require(boughtAmount >= minBuyAmount, "CurveLiquidityProvider/UNDERBOUGHT");
outputToken.compatTransfer(recipient, boughtAmount);
}
/// @dev Trades token for ETH. The token must be sent to the contract prior
/// to calling this function to trigger the trade.
/// @param inputToken The token being sold.
/// @param recipient The recipient of the bought tokens.
/// @param minBuyAmount The minimum acceptable amount of ETH to buy.
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of ETH bought.
function sellTokenForEth(
IERC20TokenV06 inputToken,
address payable recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
)
external
override
returns (uint256 boughtAmount)
{
require(
!LibERC20Transformer.isTokenETH(inputToken),
"CurveLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
inputToken,
LibERC20Transformer.ETH_TOKEN,
minBuyAmount,
abi.decode(auxiliaryData, (CurveData))
);
// Every pool contract currently checks this but why not.
require(boughtAmount >= minBuyAmount, "CurveLiquidityProvider/UNDERBOUGHT");
recipient.transfer(boughtAmount);
}
/// @dev Quotes the amount of `outputToken` that would be obtained by
/// selling `sellAmount` of `inputToken`.
function getSellQuote(
IERC20TokenV06 /* inputToken */,
IERC20TokenV06 /* outputToken */,
uint256 /* sellAmount */
)
external
view
override
returns (uint256)
{
revert("CurveLiquidityProvider/NOT_IMPLEMENTED");
}
/// @dev Perform the swap against the curve pool. Handles any combination of
/// tokens
function _executeSwap(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 minBuyAmount,
CurveData memory data
)
private
returns (uint256 boughtAmount)
{
uint256 sellAmount =
LibERC20Transformer.getTokenBalanceOf(inputToken, address(this));
if (!LibERC20Transformer.isTokenETH(inputToken)) {
inputToken.approveIfBelow(data.curveAddress, sellAmount);
}
(bool success, bytes memory resultData) =
data.curveAddress.call
{ value: LibERC20Transformer.isTokenETH(inputToken) ? sellAmount : 0 }
(abi.encodeWithSelector(
data.exchangeFunctionSelector,
data.fromCoinIdx,
data.toCoinIdx,
// dx
sellAmount,
// min dy
minBuyAmount
));
if (!success) {
resultData.rrevert();
}
if (resultData.length == 32) {
// Pool returned a boughtAmount
boughtAmount = abi.decode(resultData, (uint256));
} else {
// Not all pool contracts return a `boughtAmount`, so we return
// our balance of the output token if it wasn't returned.
boughtAmount = LibERC20Transformer
.getTokenBalanceOf(outputToken, address(this));
}
}
}

View File

@@ -273,13 +273,15 @@ contract FillQuoteTransformer is
// Refund unspent protocol fees.
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
bool transferSuccess;
if (data.refundReceiver == REFUND_RECEIVER_TAKER) {
context.taker.transfer(state.ethRemaining);
(transferSuccess,) = context.taker.call{value: state.ethRemaining}("");
} else if (data.refundReceiver == REFUND_RECEIVER_SENDER) {
context.sender.transfer(state.ethRemaining);
(transferSuccess,) = context.sender.call{value: state.ethRemaining}("");
} else {
data.refundReceiver.transfer(state.ethRemaining);
(transferSuccess,) = data.refundReceiver.call{value: state.ethRemaining}("");
}
require(transferSuccess, "FillQuoteTransformer/ETHER_TRANSFER_FALIED");
}
return LibERC20Transformer.TRANSFORMER_SUCCESS;
}

View File

@@ -0,0 +1,68 @@
// 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-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "../errors/LibTransformERC20RichErrors.sol";
import "./Transformer.sol";
import "./LibERC20Transformer.sol";
/// @dev A transformer that transfers tokens to arbitrary addresses.
contract PositiveSlippageFeeTransformer is
Transformer
{
using LibRichErrorsV06 for bytes;
using LibSafeMathV06 for uint256;
using LibERC20Transformer for IERC20TokenV06;
/// @dev Information for a single fee.
struct TokenFee {
// The token to transfer to `recipient`.
IERC20TokenV06 token;
// Amount of each `token` to transfer to `recipient`.
uint256 bestCaseAmount;
// Recipient of `token`.
address payable recipient;
}
/// @dev Transfers tokens to recipients.
/// @param context Context information.
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
function transform(TransformContext calldata context)
external
override
returns (bytes4 success)
{
TokenFee memory fee = abi.decode(context.data, (TokenFee));
uint256 transformerAmount = LibERC20Transformer.getTokenBalanceOf(fee.token, address(this));
if (transformerAmount > fee.bestCaseAmount) {
uint256 positiveSlippageAmount = transformerAmount - fee.bestCaseAmount;
fee.token.transformerTransfer(fee.recipient, positiveSlippageAmount);
}
return LibERC20Transformer.TRANSFORMER_SUCCESS;
}
}

View File

@@ -28,6 +28,7 @@ import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCryptoCom.sol";
import "./mixins/MixinDodo.sol";
import "./mixins/MixinDodoV2.sol";
import "./mixins/MixinKyber.sol";
import "./mixins/MixinMooniswap.sol";
import "./mixins/MixinMStable.sol";
@@ -46,6 +47,7 @@ contract BridgeAdapter is
MixinCurve,
MixinCryptoCom,
MixinDodo,
MixinDodoV2,
MixinKyber,
MixinMooniswap,
MixinMStable,
@@ -64,6 +66,7 @@ contract BridgeAdapter is
MixinCurve()
MixinCryptoCom()
MixinDodo()
MixinDodoV2()
MixinKyber(weth)
MixinMooniswap(weth)
MixinMStable()
@@ -100,7 +103,8 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.UNISWAPV2) {
} else if (order.source == BridgeSource.UNISWAPV2 ||
order.source == BridgeSource.LINKSWAP) {
boughtAmount = _tradeUniswapV2(
buyToken,
sellAmount,
@@ -162,6 +166,12 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.DODOV2) {
boughtAmount = _tradeDodoV2(
sellToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.CRYPTOCOM) {
boughtAmount = _tradeCryptoCom(
buyToken,

View File

@@ -40,6 +40,8 @@ library BridgeSource {
uint256 constant internal SWERVE = 15;
uint256 constant internal UNISWAP = 16;
uint256 constant internal UNISWAPV2 = 17;
uint256 constant internal DODOV2 = 18;
uint256 constant internal LINKSWAP = 19;
// New sources should be APPENDED to this list, taking the next highest
// integer value.
}

View File

@@ -0,0 +1,62 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../IBridgeAdapter.sol";
interface IDODOV2 {
function sellBase(address recipient)
external
returns (uint256);
function sellQuote(address recipient)
external
returns (uint256);
}
contract MixinDodoV2 {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeDodoV2(
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(IDODOV2 pool, bool isSellBase) =
abi.decode(bridgeData, (IDODOV2, bool));
// Transfer the tokens into the pool
sellToken.compatTransfer(address(pool), sellAmount);
boughtAmount = isSellBase ?
pool.sellBase(address(this))
: pool.sellQuote(address(this));
}
}

View File

@@ -47,8 +47,8 @@ contract MixinZeroExBridge {
sellAmount
);
boughtAmount = provider.sellTokenForToken(
address(sellToken),
address(buyToken),
sellToken,
buyToken,
address(this), // recipient
1, // minBuyAmount
lpData

View File

@@ -19,6 +19,9 @@
pragma solidity ^0.6.5;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface ILiquidityProvider {
/// @dev Trades `inputToken` for `outputToken`. The amount of `inputToken`
@@ -31,8 +34,8 @@ interface ILiquidityProvider {
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellTokenForToken(
address inputToken,
address outputToken,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -49,7 +52,7 @@ interface ILiquidityProvider {
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of `outputToken` bought.
function sellEthForToken(
address outputToken,
IERC20TokenV06 outputToken,
address recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -66,7 +69,7 @@ interface ILiquidityProvider {
/// @param auxiliaryData Arbitrary auxiliary data supplied to the contract.
/// @return boughtAmount The amount of ETH bought.
function sellTokenForEth(
address inputToken,
IERC20TokenV06 inputToken,
address payable recipient,
uint256 minBuyAmount,
bytes calldata auxiliaryData
@@ -83,8 +86,8 @@ interface ILiquidityProvider {
/// @param sellAmount Amount of `inputToken` to sell.
/// @return outputTokenAmount Amount of `outputToken` that would be obtained.
function getSellQuote(
address inputToken,
address outputToken,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 sellAmount
)
external

View File

@@ -0,0 +1,100 @@
// 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/IERC20TokenV06.sol";
import "./TestMintableERC20Token.sol";
contract TestCurve {
event CurveCalled(
uint256 value,
bytes4 selector,
int128 fromCoinIdx,
int128 toCoinIdx,
uint256 sellAmount,
uint256 minBuyAmount
);
// The lower 16 bits of the selector are reserved for flags.
bytes4 public constant BASE_SWAP_SELECTOR = 0x12340000;
bytes4 public constant RETURN_BOUGHT_AMOUNT_SELECTOR_FLAG = 0x00000001;
int128 public constant SELL_TOKEN_COIN_IDX = 0;
int128 public constant BUY_TOKEN_COIN_IDX = 1;
int128 public constant ETH_COIN_IDX = 2;
uint256 public buyAmount;
IERC20TokenV06 public sellToken;
TestMintableERC20Token public buyToken;
constructor(
IERC20TokenV06 sellToken_,
TestMintableERC20Token buyToken_,
uint256 buyAmount_
)
public
payable
{
sellToken = sellToken_;
buyToken = buyToken_;
buyAmount = buyAmount_;
}
receive() external payable {}
fallback() external payable {
bytes4 selector = abi.decode(msg.data, (bytes4));
bool shouldReturnBoughtAmount =
(selector & RETURN_BOUGHT_AMOUNT_SELECTOR_FLAG) != 0x0;
bytes4 baseSelector = selector & 0xffff0000;
require(baseSelector == BASE_SWAP_SELECTOR, "TestCurve/REVERT");
(
int128 fromCoinIdx,
int128 toCoinIdx,
uint256 sellAmount,
uint256 minBuyAmount
) = abi.decode(msg.data[4:], (int128, int128, uint256, uint256));
if (fromCoinIdx == SELL_TOKEN_COIN_IDX) {
sellToken.transferFrom(msg.sender, address(this), sellAmount);
}
if (toCoinIdx == BUY_TOKEN_COIN_IDX) {
buyToken.mint(msg.sender, buyAmount);
} else if (toCoinIdx == ETH_COIN_IDX) {
msg.sender.transfer(buyAmount);
}
emit CurveCalled(
msg.value,
selector,
fromCoinIdx,
toCoinIdx,
sellAmount,
minBuyAmount
);
if (shouldReturnBoughtAmount) {
assembly {
mstore(0, sload(buyAmount_slot))
return(0, 32)
}
}
assembly { return(0, 0) }
}
}

View File

@@ -74,7 +74,7 @@ contract TestLiquidityProvider {
bytes calldata // auxiliaryData
)
external
returns (uint256 boughtAmount)
returns (uint256)
{
emit SellTokenForToken(
inputToken,
@@ -98,7 +98,7 @@ contract TestLiquidityProvider {
bytes calldata // auxiliaryData
)
external
returns (uint256 boughtAmount)
returns (uint256)
{
emit SellEthForToken(
outputToken,
@@ -121,7 +121,7 @@ contract TestLiquidityProvider {
bytes calldata // auxiliaryData
)
external
returns (uint256 boughtAmount)
returns (uint256)
{
emit SellTokenForEth(
inputToken,

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.18.2",
"version": "0.19.0",
"engines": {
"node": ">=6.12"
},
@@ -41,9 +41,9 @@
"rollback": "node ./lib/scripts/rollback.js"
},
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector",
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITokenSpenderFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,TokenSpenderFeature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|BridgeSource|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|BootstrapFeature|BridgeAdapter|BridgeSource|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IAllowanceTarget|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|INativeOrdersFeature|IOwnableFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBancor|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinMStable|MixinMooniswap|MixinOasis|MixinShell|MixinSushiswap|MixinUniswap|MixinUniswapV2|MixinZeroExBridge|NativeOrdersFeature|OwnableFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestNativeOrdersFeature|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpenderFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@@ -56,12 +56,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contract-addresses": "^5.10.0",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contract-addresses": "^5.11.0",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.17",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -83,7 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.2.0",
"@0x/protocol-utils": "^1.3.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -7,6 +7,7 @@ import { ContractArtifact } from 'ethereum-types';
import * as AffiliateFeeTransformer from '../generated-artifacts/AffiliateFeeTransformer.json';
import * as BridgeAdapter from '../generated-artifacts/BridgeAdapter.json';
import * as CurveLiquidityProvider from '../generated-artifacts/CurveLiquidityProvider.json';
import * as FeeCollector from '../generated-artifacts/FeeCollector.json';
import * as FeeCollectorController from '../generated-artifacts/FeeCollectorController.json';
import * as FillQuoteTransformer from '../generated-artifacts/FillQuoteTransformer.json';
@@ -28,6 +29,7 @@ import * as MetaTransactionsFeature from '../generated-artifacts/MetaTransaction
import * as NativeOrdersFeature from '../generated-artifacts/NativeOrdersFeature.json';
import * as OwnableFeature from '../generated-artifacts/OwnableFeature.json';
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
import * as PositiveSlippageFeeTransformer from '../generated-artifacts/PositiveSlippageFeeTransformer.json';
import * as SimpleFunctionRegistryFeature from '../generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TokenSpenderFeature from '../generated-artifacts/TokenSpenderFeature.json';
import * as TransformERC20Feature from '../generated-artifacts/TransformERC20Feature.json';
@@ -47,6 +49,7 @@ export const artifacts = {
ITransformERC20Feature: ITransformERC20Feature as ContractArtifact,
FillQuoteTransformer: FillQuoteTransformer as ContractArtifact,
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
WethTransformer: WethTransformer as ContractArtifact,
OwnableFeature: OwnableFeature as ContractArtifact,
SimpleFunctionRegistryFeature: SimpleFunctionRegistryFeature as ContractArtifact,
@@ -62,4 +65,5 @@ export const artifacts = {
INativeOrdersFeature: INativeOrdersFeature as ContractArtifact,
FeeCollectorController: FeeCollectorController as ContractArtifact,
FeeCollector: FeeCollector as ContractArtifact,
CurveLiquidityProvider: CurveLiquidityProvider as ContractArtifact,
};

View File

@@ -46,6 +46,7 @@ export {
IZeroExContract,
LogMetadataTransformerContract,
PayTakerTransformerContract,
PositiveSlippageFeeTransformerContract,
WethTransformerContract,
ZeroExContract,
} from './wrappers';

View File

@@ -5,6 +5,7 @@
*/
export * from '../generated-wrappers/affiliate_fee_transformer';
export * from '../generated-wrappers/bridge_adapter';
export * from '../generated-wrappers/curve_liquidity_provider';
export * from '../generated-wrappers/fee_collector';
export * from '../generated-wrappers/fee_collector_controller';
export * from '../generated-wrappers/fill_quote_transformer';
@@ -26,6 +27,7 @@ export * from '../generated-wrappers/meta_transactions_feature';
export * from '../generated-wrappers/native_orders_feature';
export * from '../generated-wrappers/ownable_feature';
export * from '../generated-wrappers/pay_taker_transformer';
export * from '../generated-wrappers/positive_slippage_fee_transformer';
export * from '../generated-wrappers/simple_function_registry_feature';
export * from '../generated-wrappers/token_spender_feature';
export * from '../generated-wrappers/transform_erc20_feature';

View File

@@ -10,6 +10,7 @@ import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.js
import * as BootstrapFeature from '../test/generated-artifacts/BootstrapFeature.json';
import * as BridgeAdapter from '../test/generated-artifacts/BridgeAdapter.json';
import * as BridgeSource from '../test/generated-artifacts/BridgeSource.json';
import * as CurveLiquidityProvider from '../test/generated-artifacts/CurveLiquidityProvider.json';
import * as FeeCollector from '../test/generated-artifacts/FeeCollector.json';
import * as FeeCollectorController from '../test/generated-artifacts/FeeCollectorController.json';
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
@@ -77,6 +78,7 @@ import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json';
import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
@@ -90,9 +92,11 @@ import * as NativeOrdersFeature from '../test/generated-artifacts/NativeOrdersFe
import * as OwnableFeature from '../test/generated-artifacts/OwnableFeature.json';
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
import * as PermissionlessTransformerDeployer from '../test/generated-artifacts/PermissionlessTransformerDeployer.json';
import * as PositiveSlippageFeeTransformer from '../test/generated-artifacts/PositiveSlippageFeeTransformer.json';
import * as SimpleFunctionRegistryFeature from '../test/generated-artifacts/SimpleFunctionRegistryFeature.json';
import * as TestBridge from '../test/generated-artifacts/TestBridge.json';
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
import * as TestCurve from '../test/generated-artifacts/TestCurve.json';
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
import * as TestFeeCollectorController from '../test/generated-artifacts/TestFeeCollectorController.json';
import * as TestFillQuoteTransformerBridge from '../test/generated-artifacts/TestFillQuoteTransformerBridge.json';
@@ -186,6 +190,7 @@ export const artifacts = {
FixinProtocolFees: FixinProtocolFees as ContractArtifact,
FixinReentrancyGuard: FixinReentrancyGuard as ContractArtifact,
FixinTokenSpender: FixinTokenSpender as ContractArtifact,
CurveLiquidityProvider: CurveLiquidityProvider as ContractArtifact,
FullMigration: FullMigration as ContractArtifact,
InitialMigration: InitialMigration as ContractArtifact,
LibBootstrap: LibBootstrap as ContractArtifact,
@@ -205,6 +210,7 @@ export const artifacts = {
LibERC20Transformer: LibERC20Transformer as ContractArtifact,
LogMetadataTransformer: LogMetadataTransformer as ContractArtifact,
PayTakerTransformer: PayTakerTransformer as ContractArtifact,
PositiveSlippageFeeTransformer: PositiveSlippageFeeTransformer as ContractArtifact,
Transformer: Transformer as ContractArtifact,
WethTransformer: WethTransformer as ContractArtifact,
BridgeAdapter: BridgeAdapter as ContractArtifact,
@@ -216,6 +222,7 @@ export const artifacts = {
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,
MixinDodo: MixinDodo as ContractArtifact,
MixinDodoV2: MixinDodoV2 as ContractArtifact,
MixinKyber: MixinKyber as ContractArtifact,
MixinMStable: MixinMStable as ContractArtifact,
MixinMooniswap: MixinMooniswap as ContractArtifact,
@@ -231,6 +238,7 @@ export const artifacts = {
ITestSimpleFunctionRegistryFeature: ITestSimpleFunctionRegistryFeature as ContractArtifact,
TestBridge: TestBridge as ContractArtifact,
TestCallTarget: TestCallTarget as ContractArtifact,
TestCurve: TestCurve as ContractArtifact,
TestDelegateCaller: TestDelegateCaller as ContractArtifact,
TestFeeCollectorController: TestFeeCollectorController as ContractArtifact,
TestFillQuoteTransformerBridge: TestFillQuoteTransformerBridge as ContractArtifact,

View File

@@ -0,0 +1,294 @@
import { blockchainTests, constants, expect, getRandomInteger, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import { BigNumber, hexUtils } from '@0x/utils';
import { artifacts } from '../artifacts';
import { CurveLiquidityProviderContract, TestCurveContract, TestMintableERC20TokenContract } from '../wrappers';
blockchainTests.resets('CurveLiquidityProvider feature', env => {
let lp: CurveLiquidityProviderContract;
let sellToken: TestMintableERC20TokenContract;
let buyToken: TestMintableERC20TokenContract;
let testCurve: TestCurveContract;
let owner: string;
let taker: string;
const RECIPIENT = hexUtils.random(20);
const SELL_AMOUNT = getRandomInteger('1e6', '1e18');
const BUY_AMOUNT = getRandomInteger('1e6', '10e18');
const REVERTING_SELECTOR = '0xdeaddead';
const SWAP_SELECTOR = '0x12340000';
const SWAP_WITH_RETURN_SELECTOR = '0x12340001';
const ETH_TOKEN_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
const SELL_TOKEN_COIN_IDX = 0;
const BUY_TOKEN_COIN_IDX = 1;
const ETH_COIN_IDX = 2;
const { ZERO_AMOUNT } = constants;
before(async () => {
[owner, taker] = await env.getAccountAddressesAsync();
[sellToken, buyToken] = await Promise.all(
new Array(2)
.fill(0)
.map(async () =>
TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestMintableERC20Token,
env.provider,
env.txDefaults,
artifacts,
),
),
);
testCurve = await TestCurveContract.deployFrom0xArtifactAsync(
artifacts.TestCurve,
env.provider,
{ ...env.txDefaults, value: BUY_AMOUNT },
artifacts,
sellToken.address,
buyToken.address,
BUY_AMOUNT,
);
lp = await CurveLiquidityProviderContract.deployFrom0xArtifactAsync(
artifacts.CurveLiquidityProvider,
env.provider,
{ ...env.txDefaults, from: taker },
artifacts,
);
});
interface CurveDataFields {
curveAddress: string;
exchangeFunctionSelector: string;
fromCoinIdx: number;
toCoinIdx: number;
}
async function fundProviderContractAsync(fromCoinIdx: number, amount: BigNumber = SELL_AMOUNT): Promise<void> {
if (fromCoinIdx === SELL_TOKEN_COIN_IDX) {
await sellToken.mint(lp.address, SELL_AMOUNT).awaitTransactionSuccessAsync();
} else {
await env.web3Wrapper.awaitTransactionSuccessAsync(
await env.web3Wrapper.sendTransactionAsync({
from: taker,
to: lp.address,
value: SELL_AMOUNT,
}),
);
}
}
function encodeCurveData(fields: Partial<CurveDataFields> = {}): string {
const _fields = {
curveAddress: testCurve.address,
exchangeFunctionSelector: SWAP_SELECTOR,
fromCoinIdx: SELL_TOKEN_COIN_IDX,
toCoinIdx: BUY_TOKEN_COIN_IDX,
...fields,
};
return hexUtils.concat(
hexUtils.leftPad(_fields.curveAddress),
hexUtils.rightPad(_fields.exchangeFunctionSelector),
hexUtils.leftPad(_fields.fromCoinIdx),
hexUtils.leftPad(_fields.toCoinIdx),
);
}
it('can swap ERC20->ERC20', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForToken(
sellToken.address,
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData(),
);
const boughtAmount = await call.callAsync();
const { logs } = await call.awaitTransactionSuccessAsync();
expect(boughtAmount).to.bignumber.eq(BUY_AMOUNT);
expect(await buyToken.balanceOf(RECIPIENT).callAsync()).to.bignumber.eq(BUY_AMOUNT);
verifyEventsFromLogs(
logs,
[
{
value: ZERO_AMOUNT,
selector: SWAP_SELECTOR,
fromCoinIdx: new BigNumber(SELL_TOKEN_COIN_IDX),
toCoinIdx: new BigNumber(BUY_TOKEN_COIN_IDX),
sellAmount: SELL_AMOUNT,
minBuyAmount: BUY_AMOUNT,
},
],
'CurveCalled',
);
});
it('can swap ERC20->ETH', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForEth(
sellToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData({ toCoinIdx: ETH_COIN_IDX }),
);
const boughtAmount = await call.callAsync();
const { logs } = await call.awaitTransactionSuccessAsync();
expect(boughtAmount).to.bignumber.eq(BUY_AMOUNT);
expect(await env.web3Wrapper.getBalanceInWeiAsync(RECIPIENT)).to.bignumber.eq(BUY_AMOUNT);
verifyEventsFromLogs(
logs,
[
{
value: ZERO_AMOUNT,
selector: SWAP_SELECTOR,
fromCoinIdx: new BigNumber(SELL_TOKEN_COIN_IDX),
toCoinIdx: new BigNumber(ETH_COIN_IDX),
sellAmount: SELL_AMOUNT,
minBuyAmount: BUY_AMOUNT,
},
],
'CurveCalled',
);
});
it('can swap ETH->ERC20', async () => {
await fundProviderContractAsync(ETH_COIN_IDX);
const call = lp.sellEthForToken(
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData({ fromCoinIdx: ETH_COIN_IDX }),
);
const boughtAmount = await call.callAsync();
const { logs } = await call.awaitTransactionSuccessAsync();
expect(boughtAmount).to.bignumber.eq(BUY_AMOUNT);
expect(await buyToken.balanceOf(RECIPIENT).callAsync()).to.bignumber.eq(BUY_AMOUNT);
verifyEventsFromLogs(
logs,
[
{
value: SELL_AMOUNT,
selector: SWAP_SELECTOR,
fromCoinIdx: new BigNumber(ETH_COIN_IDX),
toCoinIdx: new BigNumber(BUY_TOKEN_COIN_IDX),
sellAmount: SELL_AMOUNT,
minBuyAmount: BUY_AMOUNT,
},
],
'CurveCalled',
);
});
it('can swap ETH->ERC20 with attached ETH', async () => {
const call = lp.sellEthForToken(
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData({ fromCoinIdx: ETH_COIN_IDX }),
);
const boughtAmount = await call.callAsync({ value: SELL_AMOUNT });
const { logs } = await call.awaitTransactionSuccessAsync({ value: SELL_AMOUNT });
expect(boughtAmount).to.bignumber.eq(BUY_AMOUNT);
expect(await buyToken.balanceOf(RECIPIENT).callAsync()).to.bignumber.eq(BUY_AMOUNT);
verifyEventsFromLogs(
logs,
[
{
value: SELL_AMOUNT,
selector: SWAP_SELECTOR,
fromCoinIdx: new BigNumber(ETH_COIN_IDX),
toCoinIdx: new BigNumber(BUY_TOKEN_COIN_IDX),
sellAmount: SELL_AMOUNT,
minBuyAmount: BUY_AMOUNT,
},
],
'CurveCalled',
);
});
it('can swap with a pool that returns bought amount', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForToken(
sellToken.address,
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData({ exchangeFunctionSelector: SWAP_WITH_RETURN_SELECTOR }),
);
const boughtAmount = await call.callAsync();
const { logs } = await call.awaitTransactionSuccessAsync();
expect(boughtAmount).to.bignumber.eq(BUY_AMOUNT);
expect(await buyToken.balanceOf(RECIPIENT).callAsync()).to.bignumber.eq(BUY_AMOUNT);
verifyEventsFromLogs(
logs,
[
{
value: ZERO_AMOUNT,
selector: SWAP_WITH_RETURN_SELECTOR,
fromCoinIdx: new BigNumber(SELL_TOKEN_COIN_IDX),
toCoinIdx: new BigNumber(BUY_TOKEN_COIN_IDX),
sellAmount: SELL_AMOUNT,
minBuyAmount: BUY_AMOUNT,
},
],
'CurveCalled',
);
});
it('reverts if pool reverts', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForToken(
sellToken.address,
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData({ exchangeFunctionSelector: REVERTING_SELECTOR }),
);
return expect(call.callAsync()).to.revertWith('TestCurve/REVERT');
});
it('reverts if underbought', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForToken(
sellToken.address,
buyToken.address,
RECIPIENT,
BUY_AMOUNT.plus(1),
encodeCurveData(),
);
return expect(call.callAsync()).to.revertWith('CurveLiquidityProvider/UNDERBOUGHT');
});
it('reverts if ERC20->ERC20 receives an ETH input token', async () => {
await fundProviderContractAsync(ETH_COIN_IDX);
const call = lp.sellTokenForToken(
ETH_TOKEN_ADDRESS,
buyToken.address,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData(),
);
return expect(call.callAsync()).to.revertWith('CurveLiquidityProvider/INVALID_ARGS');
});
it('reverts if ERC20->ERC20 receives an ETH output token', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForToken(
sellToken.address,
ETH_TOKEN_ADDRESS,
RECIPIENT,
BUY_AMOUNT,
encodeCurveData(),
);
return expect(call.callAsync()).to.revertWith('CurveLiquidityProvider/INVALID_ARGS');
});
it('reverts if ERC20->ETH receives an ETH input token', async () => {
await fundProviderContractAsync(SELL_TOKEN_COIN_IDX);
const call = lp.sellTokenForEth(ETH_TOKEN_ADDRESS, RECIPIENT, BUY_AMOUNT, encodeCurveData());
return expect(call.callAsync()).to.revertWith('CurveLiquidityProvider/INVALID_ARGS');
});
it('reverts if ETH->ERC20 receives an ETH output token', async () => {
await fundProviderContractAsync(ETH_COIN_IDX);
const call = lp.sellEthForToken(ETH_TOKEN_ADDRESS, RECIPIENT, BUY_AMOUNT, encodeCurveData());
return expect(call.callAsync()).to.revertWith('CurveLiquidityProvider/INVALID_ARGS');
});
});

View File

@@ -0,0 +1,127 @@
import { blockchainTests, constants, expect, getRandomInteger, randomAddress } from '@0x/contracts-test-utils';
import { encodePositiveSlippageFeeTransformerData } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { artifacts } from '../artifacts';
import {
PositiveSlippageFeeTransformerContract,
TestMintableERC20TokenContract,
TestTransformerHostContract,
} from '../wrappers';
const { ZERO_AMOUNT } = constants;
blockchainTests.resets('PositiveSlippageFeeTransformer', env => {
const recipient = randomAddress();
let caller: string;
let token: TestMintableERC20TokenContract;
let transformer: PositiveSlippageFeeTransformerContract;
let host: TestTransformerHostContract;
before(async () => {
[caller] = await env.getAccountAddressesAsync();
token = await TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
artifacts.TestMintableERC20Token,
env.provider,
env.txDefaults,
artifacts,
);
transformer = await PositiveSlippageFeeTransformerContract.deployFrom0xArtifactAsync(
artifacts.PositiveSlippageFeeTransformer,
env.provider,
env.txDefaults,
artifacts,
);
host = await TestTransformerHostContract.deployFrom0xArtifactAsync(
artifacts.TestTransformerHost,
env.provider,
{ ...env.txDefaults, from: caller },
artifacts,
);
});
interface Balances {
ethBalance: BigNumber;
tokenBalance: BigNumber;
}
async function getBalancesAsync(owner: string): Promise<Balances> {
return {
ethBalance: await env.web3Wrapper.getBalanceInWeiAsync(owner),
tokenBalance: await token.balanceOf(owner).callAsync(),
};
}
async function mintHostTokensAsync(amount: BigNumber): Promise<void> {
await token.mint(host.address, amount).awaitTransactionSuccessAsync();
}
it('does not transfer positive slippage fees when bestCaseAmount is equal to amount', async () => {
const amount = getRandomInteger(1, '1e18');
const data = encodePositiveSlippageFeeTransformerData({
token: token.address,
bestCaseAmount: amount,
recipient,
});
await mintHostTokensAsync(amount);
const beforeBalanceHost = await getBalancesAsync(host.address);
const beforeBalanceRecipient = await getBalancesAsync(recipient);
await host
.rawExecuteTransform(transformer.address, {
data,
sender: randomAddress(),
taker: randomAddress(),
})
.awaitTransactionSuccessAsync();
expect(await getBalancesAsync(host.address)).to.deep.eq(beforeBalanceHost);
expect(await getBalancesAsync(recipient)).to.deep.eq(beforeBalanceRecipient);
});
it('does not transfer positive slippage fees when bestCaseAmount is higher than amount', async () => {
const amount = getRandomInteger(1, '1e18');
const bestCaseAmount = amount.times(1.1).decimalPlaces(0, BigNumber.ROUND_FLOOR);
const data = encodePositiveSlippageFeeTransformerData({
token: token.address,
bestCaseAmount,
recipient,
});
await mintHostTokensAsync(amount);
const beforeBalanceHost = await getBalancesAsync(host.address);
const beforeBalanceRecipient = await getBalancesAsync(recipient);
await host
.rawExecuteTransform(transformer.address, {
data,
sender: randomAddress(),
taker: randomAddress(),
})
.awaitTransactionSuccessAsync();
expect(await getBalancesAsync(host.address)).to.deep.eq(beforeBalanceHost);
expect(await getBalancesAsync(recipient)).to.deep.eq(beforeBalanceRecipient);
});
it('send positive slippage fee to recipient when bestCaseAmount is lower than amount', async () => {
const amount = getRandomInteger(1, '1e18');
const bestCaseAmount = amount.times(0.95).decimalPlaces(0, BigNumber.ROUND_FLOOR);
const data = encodePositiveSlippageFeeTransformerData({
token: token.address,
bestCaseAmount,
recipient,
});
await mintHostTokensAsync(amount);
await host
.rawExecuteTransform(transformer.address, {
data,
sender: randomAddress(),
taker: randomAddress(),
})
.awaitTransactionSuccessAsync();
expect(await getBalancesAsync(host.address)).to.deep.eq({
tokenBalance: bestCaseAmount,
ethBalance: ZERO_AMOUNT,
});
expect(await getBalancesAsync(recipient)).to.deep.eq({
tokenBalance: amount.minus(bestCaseAmount), // positive slippage
ethBalance: ZERO_AMOUNT,
});
});
});

View File

@@ -8,6 +8,7 @@ export * from '../test/generated-wrappers/allowance_target';
export * from '../test/generated-wrappers/bootstrap_feature';
export * from '../test/generated-wrappers/bridge_adapter';
export * from '../test/generated-wrappers/bridge_source';
export * from '../test/generated-wrappers/curve_liquidity_provider';
export * from '../test/generated-wrappers/fee_collector';
export * from '../test/generated-wrappers/fee_collector_controller';
export * from '../test/generated-wrappers/fill_quote_transformer';
@@ -75,6 +76,7 @@ export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';
export * from '../test/generated-wrappers/mixin_dodo';
export * from '../test/generated-wrappers/mixin_dodo_v2';
export * from '../test/generated-wrappers/mixin_kyber';
export * from '../test/generated-wrappers/mixin_m_stable';
export * from '../test/generated-wrappers/mixin_mooniswap';
@@ -88,9 +90,11 @@ export * from '../test/generated-wrappers/native_orders_feature';
export * from '../test/generated-wrappers/ownable_feature';
export * from '../test/generated-wrappers/pay_taker_transformer';
export * from '../test/generated-wrappers/permissionless_transformer_deployer';
export * from '../test/generated-wrappers/positive_slippage_fee_transformer';
export * from '../test/generated-wrappers/simple_function_registry_feature';
export * from '../test/generated-wrappers/test_bridge';
export * from '../test/generated-wrappers/test_call_target';
export * from '../test/generated-wrappers/test_curve';
export * from '../test/generated-wrappers/test_delegate_caller';
export * from '../test/generated-wrappers/test_fee_collector_controller';
export * from '../test/generated-wrappers/test_fill_quote_transformer_bridge';

View File

@@ -5,6 +5,7 @@
"files": [
"generated-artifacts/AffiliateFeeTransformer.json",
"generated-artifacts/BridgeAdapter.json",
"generated-artifacts/CurveLiquidityProvider.json",
"generated-artifacts/FeeCollector.json",
"generated-artifacts/FeeCollectorController.json",
"generated-artifacts/FillQuoteTransformer.json",
@@ -26,6 +27,7 @@
"generated-artifacts/NativeOrdersFeature.json",
"generated-artifacts/OwnableFeature.json",
"generated-artifacts/PayTakerTransformer.json",
"generated-artifacts/PositiveSlippageFeeTransformer.json",
"generated-artifacts/SimpleFunctionRegistryFeature.json",
"generated-artifacts/TokenSpenderFeature.json",
"generated-artifacts/TransformERC20Feature.json",
@@ -36,6 +38,7 @@
"test/generated-artifacts/BootstrapFeature.json",
"test/generated-artifacts/BridgeAdapter.json",
"test/generated-artifacts/BridgeSource.json",
"test/generated-artifacts/CurveLiquidityProvider.json",
"test/generated-artifacts/FeeCollector.json",
"test/generated-artifacts/FeeCollectorController.json",
"test/generated-artifacts/FillQuoteTransformer.json",
@@ -103,6 +106,7 @@
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",
"test/generated-artifacts/MixinDodo.json",
"test/generated-artifacts/MixinDodoV2.json",
"test/generated-artifacts/MixinKyber.json",
"test/generated-artifacts/MixinMStable.json",
"test/generated-artifacts/MixinMooniswap.json",
@@ -116,9 +120,11 @@
"test/generated-artifacts/OwnableFeature.json",
"test/generated-artifacts/PayTakerTransformer.json",
"test/generated-artifacts/PermissionlessTransformerDeployer.json",
"test/generated-artifacts/PositiveSlippageFeeTransformer.json",
"test/generated-artifacts/SimpleFunctionRegistryFeature.json",
"test/generated-artifacts/TestBridge.json",
"test/generated-artifacts/TestCallTarget.json",
"test/generated-artifacts/TestCurve.json",
"test/generated-artifacts/TestDelegateCaller.json",
"test/generated-artifacts/TestFeeCollectorController.json",
"test/generated-artifacts/TestFillQuoteTransformerBridge.json",

View File

@@ -1,4 +1,62 @@
[
{
"version": "6.1.0",
"changes": [
{
"note": "Filter MultiHop where second source is not present",
"pr": 138
},
{
"note": "Add CurveLiquidityProvider \"direct\" route to EP consumer.",
"pr": 127
},
{
"note": "Fix compiler error on `ILiquidityProvider` call",
"pr": 127
},
{
"note": "Add deployed `CurveLiquidityProvider` addresses",
"pr": 144
},
{
"note": "Support `Mirror Protocol` with hops to `UST`",
"pr": 142
},
{
"note": "Fix protocol fee in fee schedule for `RfqOrder`",
"pr": 146
},
{
"note": "Special case BNB in uni v1 sampler",
"pr": 147
},
{
"note": "Create `FakeTaker` contract to get result data and gas used",
"pr": 151
},
{
"note": "Added support for `Dodo` v2",
"pr": 152
},
{
"note": "Added support for `Linkswap`",
"pr": 153
},
{
"note": "Re-add WBTC in default intermediate hops",
"pr": 154
},
{
"note": "Add an alternative RFQ market making implementation",
"pr": 139
},
{
"note": "Added an opt-in `PositiveSlippageAffiliateFee`",
"pr": 101
}
],
"timestamp": 1614141718
},
{
"version": "6.0.0",
"changes": [

View File

@@ -5,6 +5,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.1.0 - _February 24, 2021_
* Filter MultiHop where second source is not present (#138)
* Add CurveLiquidityProvider "direct" route to EP consumer. (#127)
* Fix compiler error on `ILiquidityProvider` call (#127)
* Add deployed `CurveLiquidityProvider` addresses (#144)
* Support `Mirror Protocol` with hops to `UST` (#142)
* Fix protocol fee in fee schedule for `RfqOrder` (#146)
* Special case BNB in uni v1 sampler (#147)
* Create `FakeTaker` contract to get result data and gas used (#151)
* Added support for `Dodo` v2 (#152)
* Added support for `Linkswap` (#153)
* Re-add WBTC in default intermediate hops (#154)
* Add an alternative RFQ market making implementation (#139)
* Added an opt-in `PositiveSlippageAffiliateFee` (#101)
## v6.0.0 - _February 10, 2021_
* Pull top 250 Balancer pairs on initialization (#113)

View File

@@ -0,0 +1,206 @@
// 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 "./DeploymentConstants.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
interface IDODOV2Registry {
function getDODOPool(address baseToken, address quoteToken)
external
view
returns (address[] memory machines);
}
interface IDODOV2Pool {
function querySellBase(address trader, uint256 payBaseAmount)
external
view
returns (uint256 receiveQuoteAmount, uint256 mtFee);
function querySellQuote(address trader, uint256 payQuoteAmount)
external
view
returns (uint256 receiveBaseAmount, uint256 mtFee);
}
contract DODOV2Sampler is
DeploymentConstants,
SamplerUtils,
ApproximateBuys
{
/// @dev Gas limit for DODO V2 calls.
uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k
/// @dev Sample sell quotes from DODO V2.
/// @param registry Address of the registry to look up.
/// @param offset offset index for the pool in the registry.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromDODOV2(
address registry,
uint256 offset,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
if (pool == address(0)) {
return (sellBase, pool, makerTokenAmounts);
}
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2(
abi.encode(takerToken, pool, sellBase), // taker token data
abi.encode(makerToken, pool, sellBase), // maker token data
takerTokenAmounts[i]
);
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
/// @dev Sample buy quotes from DODO.
/// @param registry Address of the registry to look up.
/// @param offset offset index for the pool in the registry.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromDODOV2(
address registry,
uint256 offset,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
view
returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
if (pool == address(0)) {
return (sellBase, pool, takerTokenAmounts);
}
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool, !sellBase),
takerTokenData: abi.encode(takerToken, pool, sellBase),
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromDODOV2(
bytes memory takerTokenData,
bytes memory /* makerTokenData */,
uint256 sellAmount
)
private
view
returns (uint256)
{
(address takerToken, address pool, bool sellBase) = abi.decode(
takerTokenData,
(address, address, bool)
);
// We will get called to sell both the taker token and also to sell the maker token
// since we use approximate buy for sell and buy functions
if (sellBase) {
try
IDODOV2Pool(pool).querySellBase
{ gas: DODO_V2_CALL_GAS }
(address(0), sellAmount)
returns (uint256 amount, uint256)
{
return amount;
} catch {
return 0;
}
} else {
try
IDODOV2Pool(pool).querySellQuote
{ gas: DODO_V2_CALL_GAS }
(address(0), sellAmount)
returns (uint256 amount, uint256)
{
return amount;
} catch {
return 0;
}
}
}
function _getNextDODOV2Pool(
address registry,
uint256 offset,
address takerToken,
address makerToken
)
internal
view
returns (address machine, bool sellBase)
{
// Query in base -> quote direction, if a pool is found then we are selling the base
address[] memory machines = IDODOV2Registry(registry).getDODOPool(takerToken, makerToken);
sellBase = true;
if (machines.length == 0) {
// Query in quote -> base direction, if a pool is found then we are selling the quote
machines = IDODOV2Registry(registry).getDODOPool(makerToken, takerToken);
sellBase = false;
}
if (offset >= machines.length) {
return (address(0), false);
}
machine = machines[offset];
}
}

View File

@@ -24,6 +24,7 @@ import "./BalancerSampler.sol";
import "./BancorSampler.sol";
import "./CurveSampler.sol";
import "./DODOSampler.sol";
import "./DODOV2Sampler.sol";
import "./Eth2DaiSampler.sol";
import "./KyberSampler.sol";
import "./LiquidityProviderSampler.sol";
@@ -44,6 +45,7 @@ contract ERC20BridgeSampler is
BancorSampler,
CurveSampler,
DODOSampler,
DODOV2Sampler,
Eth2DaiSampler,
KyberSampler,
LiquidityProviderSampler,

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
contract FakeTaker {
struct Result {
bool success;
bytes resultData;
uint256 gasUsed;
}
receive() payable external {}
function execute(address payable to, bytes calldata data)
public
payable
returns (Result memory result)
{
uint256 gasBefore = gasleft();
(
result.success,
result.resultData
) = to.call{ value: msg.value }(data);
result.gasUsed = gasBefore - gasleft();
}
}

View File

@@ -58,7 +58,11 @@ contract LiquidityProviderSampler is
try
ILiquidityProvider(providerAddress).getSellQuote
{gas: DEFAULT_CALL_GAS}
(takerToken, makerToken, takerTokenAmounts[i])
(
IERC20TokenV06(takerToken),
IERC20TokenV06(makerToken),
takerTokenAmounts[i]
)
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;

View File

@@ -42,6 +42,8 @@ contract UniswapSampler is
{
/// @dev Gas limit for Uniswap calls.
uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
// @dev The BNB token is poisoned on uniswap v1.
address constant private BAD_MAKER_TOKEN = 0xB8c77482e45F1F44dE1745F52C74426C631bDD52;
/// @dev Sample sell quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
@@ -61,6 +63,10 @@ contract UniswapSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (makerToken == BAD_MAKER_TOKEN) {
// BNB is poisoned on v1. You can only sell to it.
return makerTokenAmounts;
}
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?
@@ -120,6 +126,10 @@ contract UniswapSampler is
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
if (makerToken == BAD_MAKER_TOKEN) {
// BNB is poisoned on v1. You can only sell to it.
return takerTokenAmounts;
}
IUniswapExchangeQuotes takerTokenExchange = takerToken == _getWethAddress() ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == _getWethAddress() ?

View File

@@ -45,7 +45,7 @@ contract TestNativeOrderSampler is
UtilitySampler
{
uint8 private constant MAX_ORDER_STATUS = uint8(IExchange.OrderStatus.CANCELLED) + 1;
bytes32 private constant VALID_SIGNATURE_HASH = keccak256(hex"01");
bytes32 private constant VALID_SIGNATURE_HASH = bytes32(hex"01");
function createTokens(uint256 count)
external

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/asset-swapper",
"version": "6.0.0",
"version": "6.1.0",
"engines": {
"node": ">=6.12"
},
@@ -36,9 +36,9 @@
"publish:private": "yarn build && gitpkg publish"
},
"config": {
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker",
"publicInterfaceContracts": "ERC20BridgeSampler,BalanceChecker,FakeTaker",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"abis": "./test/generated-artifacts/@(ApproximateBuys|BalanceChecker|BalancerSampler|BancorSampler|CurveSampler|DODOSampler|DODOV2Sampler|DeploymentConstants|DummyLiquidityProvider|ERC20BridgeSampler|Eth2DaiSampler|FakeTaker|IBalancer|IBancor|ICurve|IEth2Dai|IKyberNetwork|IMStable|IMooniswap|IMultiBridge|IShell|IUniswapExchangeQuotes|IUniswapV2Router01|KyberSampler|LiquidityProviderSampler|MStableSampler|MooniswapSampler|MultiBridgeSampler|NativeOrderSampler|SamplerUtils|ShellSampler|SushiSwapSampler|TestERC20BridgeSampler|TestNativeOrderSampler|TwoHopSampler|UniswapSampler|UniswapV2Sampler|UtilitySampler).json",
"postpublish": {
"assets": []
}
@@ -59,11 +59,11 @@
"dependencies": {
"@0x/assert": "^3.0.21",
"@0x/base-contract": "^6.2.18",
"@0x/contract-addresses": "^5.10.0",
"@0x/contract-wrappers": "^13.12.3",
"@0x/contract-addresses": "^5.11.0",
"@0x/contract-wrappers": "^13.13.0",
"@0x/dev-utils": "^4.2.1",
"@0x/json-schemas": "^5.4.1",
"@0x/protocol-utils": "^1.2.0",
"@0x/protocol-utils": "^1.3.0",
"@0x/quote-server": "^4.0.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
@@ -86,16 +86,16 @@
},
"devDependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-exchange": "^3.2.25",
"@0x/contracts-exchange-libs": "^4.3.24",
"@0x/contracts-asset-proxy": "^3.7.7",
"@0x/contracts-erc20": "^3.3.4",
"@0x/contracts-exchange": "^3.2.26",
"@0x/contracts-exchange-libs": "^4.3.25",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/contracts-zero-ex": "^0.18.2",
"@0x/contracts-test-utils": "^5.3.22",
"@0x/contracts-utils": "^4.7.4",
"@0x/contracts-zero-ex": "^0.19.0",
"@0x/mesh-rpc-client": "^9.4.2",
"@0x/migrations": "^6.6.0",
"@0x/migrations": "^7.0.0",
"@0x/sol-compiler": "^4.5.2",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -7,7 +7,9 @@ import { ContractArtifact } from 'ethereum-types';
import * as BalanceChecker from '../generated-artifacts/BalanceChecker.json';
import * as ERC20BridgeSampler from '../generated-artifacts/ERC20BridgeSampler.json';
import * as FakeTaker from '../generated-artifacts/FakeTaker.json';
export const artifacts = {
ERC20BridgeSampler: ERC20BridgeSampler as ContractArtifact,
BalanceChecker: BalanceChecker as ContractArtifact,
FakeTaker: FakeTaker as ContractArtifact,
};

View File

@@ -3,6 +3,7 @@ import { SignatureType } from '@0x/protocol-utils';
import { BigNumber, logUtils } from '@0x/utils';
import {
AffiliateFeeType,
ExchangeProxyContractOpts,
LogFunction,
OrderPrunerOpts,
@@ -12,7 +13,11 @@ import {
SwapQuoteRequestOpts,
SwapQuoterOpts,
} from './types';
import { DEFAULT_GET_MARKET_ORDERS_OPTS, TOKENS } from './utils/market_operation_utils/constants';
import {
DEFAULT_GET_MARKET_ORDERS_OPTS,
DEFAULT_INTERMEDIATE_TOKENS,
DEFAULT_TOKEN_ADJACENCY_GRAPH,
} from './utils/market_operation_utils/constants';
const ETH_GAS_STATION_API_URL = 'https://ethgasstation.info/api/ethgasAPI.json';
const NULL_BYTES = '0x';
@@ -24,6 +29,7 @@ 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 = {
expiryBufferMs: 120000, // 2 minutes
@@ -37,23 +43,24 @@ const PROTOCOL_FEE_MULTIPLIER = new BigNumber(70000);
// default 50% buffer for selecting native orders to be aggregated with other sources
const MARKET_UTILS_AMOUNT_BUFFER_PERCENTAGE = 0.5;
const DEFAULT_INTERMEDIATE_TOKENS = [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC];
const DEFAULT_SWAP_QUOTER_OPTS: SwapQuoterOpts = {
chainId: ChainId.Mainnet,
orderRefreshIntervalMs: 10000, // 10 seconds
...DEFAULT_ORDER_PRUNER_OPTS,
samplerGasLimit: 250e6,
samplerGasLimit: 500e6,
ethGasStationUrl: ETH_GAS_STATION_API_URL,
rfqt: {
takerApiKeyWhitelist: [],
makerAssetOfferings: {},
},
tokenAdjacencyGraph: DEFAULT_TOKEN_ADJACENCY_GRAPH,
};
const DEFAULT_EXCHANGE_PROXY_EXTENSION_CONTRACT_OPTS: ExchangeProxyContractOpts = {
isFromETH: false,
isToETH: false,
affiliateFee: {
feeType: AffiliateFeeType.None,
recipient: NULL_ADDRESS,
buyTokenFeeAmount: ZERO_AMOUNT,
sellTokenFeeAmount: ZERO_AMOUNT,
@@ -83,15 +90,14 @@ export const DEFAULT_WARNING_LOGGER: LogFunction = (obj, msg) =>
const EMPTY_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
export const INVALID_SIGNATURE = { signatureType: SignatureType.Invalid, v: 1, r: EMPTY_BYTES32, s: EMPTY_BYTES32 };
export {
BRIDGE_ADDRESSES_BY_CHAIN,
DEFAULT_FEE_SCHEDULE,
DEFAULT_GAS_SCHEDULE,
} from './utils/market_operation_utils/constants';
export { DEFAULT_FEE_SCHEDULE, DEFAULT_GAS_SCHEDULE } from './utils/market_operation_utils/constants';
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(30000);
export const constants = {
ETH_GAS_STATION_API_URL,
PROTOCOL_FEE_MULTIPLIER,
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS,
NULL_BYTES,
ZERO_AMOUNT,
NULL_ADDRESS,
@@ -115,4 +121,5 @@ export const constants = {
DEFAULT_INFO_LOGGER,
DEFAULT_WARNING_LOGGER,
EMPTY_BYTES32,
ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS,
};

View File

@@ -74,7 +74,10 @@ export { InsufficientAssetLiquidityError } from './errors';
export { SwapQuoteConsumer } from './quote_consumers/swap_quote_consumer';
export { SwapQuoter, Orderbook } from './swap_quoter';
export {
AffiliateFee,
AltOffering,
AltRfqtMakerAssetOfferings,
AffiliateFeeType,
AffiliateFeeAmount,
AssetSwapperContractAddresses,
CalldataInfo,
ExchangeProxyContractOpts,
@@ -107,7 +110,7 @@ export {
} from './types';
export { affiliateFeeUtils } from './utils/affiliate_fee_utils';
export {
BRIDGE_ADDRESSES_BY_CHAIN,
DEFAULT_TOKEN_ADJACENCY_GRAPH,
DEFAULT_GAS_SCHEDULE,
SOURCE_FLAGS,
} from './utils/market_operation_utils/constants';
@@ -162,7 +165,7 @@ export {
QuoteReportEntry,
} from './utils/quote_report_generator';
export { QuoteRequestor } from './utils/quote_requestor';
export { ERC20BridgeSamplerContract, BalanceCheckerContract } from './wrappers';
export { ERC20BridgeSamplerContract, BalanceCheckerContract, FakeTakerContract } from './wrappers';
import { ERC20BridgeSource } from './utils/market_operation_utils/types';
export type Native = ERC20BridgeSource.Native;
export type MultiHop = ERC20BridgeSource.MultiHop;

View File

@@ -2,8 +2,10 @@ import { ContractAddresses } from '@0x/contract-addresses';
import { IZeroExContract } from '@0x/contract-wrappers';
import {
encodeAffiliateFeeTransformerData,
encodeCurveLiquidityProviderData,
encodeFillQuoteTransformerData,
encodePayTakerTransformerData,
encodePositiveSlippageFeeTransformerData,
encodeWethTransformerData,
ETH_TOKEN_ADDRESS,
FillQuoteTransformerData,
@@ -15,8 +17,9 @@ import { BigNumber, providerUtils } from '@0x/utils';
import { SupportedProvider, ZeroExProvider } from '@0x/web3-wrapper';
import * as _ from 'lodash';
import { constants } from '../constants';
import { constants, POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS } from '../constants';
import {
AffiliateFeeType,
CalldataInfo,
ExchangeProxyContractOpts,
MarketBuySwapQuote,
@@ -29,11 +32,13 @@ import {
SwapQuoteGetOutputOpts,
} from '../types';
import { assert } from '../utils/assert';
import { CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID } from '../utils/market_operation_utils/constants';
import {
createBridgeDataForBridgeOrder,
getERC20BridgeSourceToBridgeSource,
} from '../utils/market_operation_utils/orders';
import {
CurveFillData,
ERC20BridgeSource,
LiquidityProviderFillData,
NativeLimitOrderFillData,
@@ -56,6 +61,7 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
payTakerTransformer: number;
fillQuoteTransformer: number;
affiliateFeeTransformer: number;
positiveSlippageFeeTransformer: number;
};
private readonly _exchangeProxy: IZeroExContract;
@@ -89,6 +95,10 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
contractAddresses.transformers.affiliateFeeTransformer,
contractAddresses.exchangeProxyTransformerDeployer,
),
positiveSlippageFeeTransformer: findTransformerNonce(
contractAddresses.transformers.positiveSlippageFeeTransformer,
contractAddresses.exchangeProxyTransformerDeployer,
),
};
}
@@ -114,7 +124,6 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
if (isFromETH) {
ethAmount = ethAmount.plus(sellAmount);
}
const { buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee;
// VIP routes.
if (
@@ -141,7 +150,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
@@ -162,7 +172,34 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
if (isDirectSwapCompatible(quote, optsWithDefaults, [ERC20BridgeSource.Curve, ERC20BridgeSource.Swerve])) {
const fillData = quote.orders[0].fills[0].fillData as CurveFillData;
return {
calldataHexString: this._exchangeProxy
.sellToLiquidityProvider(
isFromETH ? ETH_TOKEN_ADDRESS : sellToken,
isToETH ? ETH_TOKEN_ADDRESS : buyToken,
CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID[this.chainId],
NULL_ADDRESS,
sellAmount,
minBuyAmount,
encodeCurveLiquidityProviderData({
curveAddress: fillData.pool.poolAddress,
exchangeFunctionSelector: fillData.pool.exchangeFunctionSelector,
fromCoinIdx: new BigNumber(fillData.fromTokenIdx),
toCoinIdx: new BigNumber(fillData.toTokenIdx),
}),
)
.getABIEncodedTransactionData(),
ethAmount: isFromETH ? sellAmount : ZERO_AMOUNT,
toAddress: this._exchangeProxy.address,
allowanceTarget: this._exchangeProxy.address,
gasOverhead: ZERO_AMOUNT,
};
}
@@ -234,25 +271,52 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
});
}
// This transformer pays affiliate fees.
if (buyTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
const { feeType, buyTokenFeeAmount, sellTokenFeeAmount, recipient: feeRecipient } = affiliateFee;
let gasOverhead = ZERO_AMOUNT;
if (feeType === AffiliateFeeType.PositiveSlippageFee && feeRecipient !== NULL_ADDRESS) {
// bestCaseAmountWithSurplus is used to cover gas cost of sending positive slipapge fee to fee recipient
// this helps avoid sending dust amounts which are not worth the gas cost to transfer
let bestCaseAmountWithSurplus = quote.bestCaseQuoteInfo.makerAmount
.plus(
POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS.multipliedBy(quote.gasPrice).multipliedBy(
quote.makerAmountPerEth,
),
)
.integerValue();
// In the event makerAmountPerEth is unknown, we only allow for positive slippage which is greater than
// the best case amount
bestCaseAmountWithSurplus = BigNumber.max(bestCaseAmountWithSurplus, quote.bestCaseQuoteInfo.makerAmount);
transforms.push({
deploymentNonce: this.transformerNonces.affiliateFeeTransformer,
data: encodeAffiliateFeeTransformerData({
fees: [
{
token: isToETH ? ETH_TOKEN_ADDRESS : buyToken,
amount: buyTokenFeeAmount,
recipient: feeRecipient,
},
],
deploymentNonce: this.transformerNonces.positiveSlippageFeeTransformer,
data: encodePositiveSlippageFeeTransformerData({
token: isToETH ? ETH_TOKEN_ADDRESS : buyToken,
bestCaseAmount: BigNumber.max(bestCaseAmountWithSurplus, quote.bestCaseQuoteInfo.makerAmount),
recipient: feeRecipient,
}),
});
// Adjust the minimum buy amount by the fee.
minBuyAmount = BigNumber.max(0, minBuyAmount.minus(buyTokenFeeAmount));
}
if (sellTokenFeeAmount.isGreaterThan(0) && feeRecipient !== NULL_ADDRESS) {
throw new Error('Affiliate fees denominated in sell token are not yet supported');
// This may not be visible at eth_estimateGas time, so we explicitly add overhead
gasOverhead = POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS;
} else if (feeType === AffiliateFeeType.PercentageFee && feeRecipient !== NULL_ADDRESS) {
// This transformer pays affiliate fees.
if (buyTokenFeeAmount.isGreaterThan(0)) {
transforms.push({
deploymentNonce: this.transformerNonces.affiliateFeeTransformer,
data: encodeAffiliateFeeTransformerData({
fees: [
{
token: isToETH ? ETH_TOKEN_ADDRESS : buyToken,
amount: buyTokenFeeAmount,
recipient: feeRecipient,
},
],
}),
});
// Adjust the minimum buy amount by the fee.
minBuyAmount = BigNumber.max(0, minBuyAmount.minus(buyTokenFeeAmount));
}
if (sellTokenFeeAmount.isGreaterThan(0)) {
throw new Error('Affiliate fees denominated in sell token are not yet supported');
}
}
// The final transformer will send all funds to the taker.
@@ -278,7 +342,8 @@ export class ExchangeProxySwapQuoteConsumer implements SwapQuoteConsumerBase {
calldataHexString,
ethAmount,
toAddress: this._exchangeProxy.address,
allowanceTarget: this.contractAddresses.exchangeProxyAllowanceTarget,
allowanceTarget: this._exchangeProxy.address,
gasOverhead,
};
}
@@ -304,6 +369,10 @@ function isDirectSwapCompatible(
if (!opts.affiliateFee.buyTokenFeeAmount.eq(0) || !opts.affiliateFee.sellTokenFeeAmount.eq(0)) {
return false;
}
// Must not have a positive slippage fee.
if (opts.affiliateFee.feeType === AffiliateFeeType.PositiveSlippageFee) {
return false;
}
// Must be a single order.
if (quote.orders.length !== 1) {
return false;

View File

@@ -5,7 +5,7 @@ import { BlockParamLiteral, SupportedProvider, ZeroExProvider } from 'ethereum-t
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { BRIDGE_ADDRESSES_BY_CHAIN, constants, INVALID_SIGNATURE } from './constants';
import { constants, INVALID_SIGNATURE } from './constants';
import {
AssetSwapperContractAddresses,
MarketBuySwapQuote,
@@ -102,7 +102,6 @@ export class SwapQuoter {
this._rfqtOptions = rfqt;
this._contractAddresses = options.contractAddresses || {
...getContractAddressesForChainOrThrow(chainId),
...BRIDGE_ADDRESSES_BY_CHAIN[chainId],
};
this._protocolFeeUtils = ProtocolFeeUtils.getInstance(
constants.PROTOCOL_FEE_UTILS_POLLING_INTERVAL_IN_MS,
@@ -349,6 +348,7 @@ export class SwapQuoter {
if (calcOpts.rfqt !== undefined) {
calcOpts.rfqt.quoteRequestor = new QuoteRequestor(
rfqtOptions ? rfqtOptions.makerAssetOfferings || {} : {},
rfqtOptions ? rfqtOptions.altRfqCreds : undefined,
rfqtOptions ? rfqtOptions.warningLogger : undefined,
rfqtOptions ? rfqtOptions.infoLogger : undefined,
this.expiryBufferMs,
@@ -454,7 +454,7 @@ function createSwapQuote(
gasSchedule: FeeSchedule,
slippage: number,
): SwapQuote {
const { optimizedOrders, quoteReport, sourceFlags, takerTokenToEthRate, makerTokenToEthRate } = optimizerResult;
const { optimizedOrders, quoteReport, sourceFlags, takerAmountPerEth, makerAmountPerEth } = optimizerResult;
const isTwoHop = sourceFlags === SOURCE_FLAGS[ERC20BridgeSource.MultiHop];
// Calculate quote info
@@ -474,8 +474,8 @@ function createSwapQuote(
sourceBreakdown,
makerTokenDecimals,
takerTokenDecimals,
takerTokenToEthRate,
makerTokenToEthRate,
takerAmountPerEth,
makerAmountPerEth,
quoteReport,
isTwoHop,
};

View File

@@ -7,7 +7,7 @@ import {
RfqOrderFields,
Signature,
} from '@0x/protocol-utils';
import { TakerRequestQueryParams } from '@0x/quote-server';
import { TakerRequestQueryParams, V4SignedRfqOrder } from '@0x/quote-server';
import { BigNumber } from '@0x/utils';
import {
@@ -54,12 +54,15 @@ export interface NativeOrderFillableAmountFields {
* toAddress: The contract address to call.
* ethAmount: The eth amount in wei to send with the smart contract call.
* allowanceTarget: The address the taker should grant an allowance to.
* gasOverhead: The gas overhead needed to be added to the gas limit to allow for optional
* operations which may not visible at eth_estimateGas time
*/
export interface CalldataInfo {
calldataHexString: string;
toAddress: string;
ethAmount: BigNumber;
allowanceTarget: string;
gasOverhead: BigNumber;
}
/**
@@ -98,7 +101,14 @@ export interface SwapQuoteExecutionOpts extends SwapQuoteGetOutputOpts {
gasLimit?: number;
}
export interface AffiliateFee {
export enum AffiliateFeeType {
None,
PercentageFee,
PositiveSlippageFee,
}
export interface AffiliateFeeAmount {
feeType: AffiliateFeeType;
recipient: string;
buyTokenFeeAmount: BigNumber;
sellTokenFeeAmount: BigNumber;
@@ -130,7 +140,7 @@ export enum ExchangeProxyRefundReceiver {
export interface ExchangeProxyContractOpts {
isFromETH: boolean;
isToETH: boolean;
affiliateFee: AffiliateFee;
affiliateFee: AffiliateFeeAmount;
refundReceiver: string | ExchangeProxyRefundReceiver;
isMetaTransaction: boolean;
shouldSellEntireBalance: boolean;
@@ -161,8 +171,8 @@ export interface SwapQuoteBase {
isTwoHop: boolean;
makerTokenDecimals: number;
takerTokenDecimals: number;
takerTokenToEthRate: BigNumber;
makerTokenToEthRate: BigNumber;
takerAmountPerEth: BigNumber;
makerAmountPerEth: BigNumber;
}
/**
@@ -228,6 +238,7 @@ export interface RfqtRequestOpts {
isIndicative?: boolean;
makerEndpointMaxResponseTimeMs?: number;
nativeExclusivelyRFQT?: boolean;
altRfqtAssetOfferings?: AltRfqtMakerAssetOfferings;
}
/**
@@ -246,6 +257,25 @@ export interface RfqtMakerAssetOfferings {
[endpoint: string]: Array<[string, string]>;
}
export interface AltOffering {
id: string;
baseAsset: string;
quoteAsset: string;
baseAssetDecimals: number;
quoteAssetDecimals: number;
}
export interface AltRfqtMakerAssetOfferings {
[endpoint: string]: AltOffering[];
}
export enum RfqPairType {
Standard = 'standard',
Alt = 'alt',
}
export interface TypedMakerUrl {
url: string;
pairType: RfqPairType;
}
export type LogFunction = (obj: object, msg?: string, ...args: any[]) => void;
export interface RfqtFirmQuoteValidator {
@@ -255,11 +285,15 @@ export interface RfqtFirmQuoteValidator {
export interface SwapQuoterRfqtOpts {
takerApiKeyWhitelist: string[];
makerAssetOfferings: RfqtMakerAssetOfferings;
altRfqCreds?: {
altRfqApiKey: string;
altRfqProfile: string;
};
warningLogger?: LogFunction;
infoLogger?: LogFunction;
}
export type AssetSwapperContractAddresses = ContractAddresses & BridgeContractAddresses;
export type AssetSwapperContractAddresses = ContractAddresses;
/**
* chainId: The ethereum chain id. Defaults to 1 (mainnet).
@@ -333,6 +367,17 @@ export interface MockedRfqtQuoteResponse {
responseCode: number;
}
/**
* Represents a mocked RFQT maker responses.
*/
export interface AltMockedRfqtQuoteResponse {
endpoint: string;
mmApiKey: string;
requestData: AltQuoteRequestData;
responseData: any;
responseCode: number;
}
export interface SamplerOverrides {
overrides: GethCallOverrides;
block: BlockParam;
@@ -344,25 +389,50 @@ export interface SamplerCallResult {
}
export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
/**
* The Contract addresses of the deployed Bridges
*/
export interface BridgeContractAddresses {
uniswapBridge: string;
uniswapV2Bridge: string;
eth2DaiBridge: string;
kyberBridge: string;
curveBridge: string;
multiBridge: string;
balancerBridge: string;
bancorBridge: string;
mStableBridge: string;
mooniswapBridge: string;
sushiswapBridge: string;
shellBridge: string;
dodoBridge: string;
creamBridge: string;
swerveBridge: string;
snowswapBridge: string;
cryptoComBridge: string;
export enum AltQuoteModel {
Firm = 'firm',
Indicative = 'indicative',
}
export enum AltQuoteSide {
Buy = 'buy',
Sell = 'sell',
}
export interface AltQuoteRequestData {
market: string;
model: AltQuoteModel;
profile: string;
side: AltQuoteSide;
value?: string;
amount?: string;
meta: {
txOrigin: string;
taker: string;
client: string;
existingOrder?: {
price: string;
value?: string;
amount?: string;
};
};
}
export interface AltBaseRfqResponse extends AltQuoteRequestData {
id: string;
price?: string;
}
export interface AltIndicativeQuoteResponse extends AltBaseRfqResponse {
model: AltQuoteModel.Indicative;
status: 'live' | 'rejected';
}
export interface AltFirmQuoteResponse extends AltBaseRfqResponse {
model: AltQuoteModel.Firm;
data: {
'0xv4order': V4SignedRfqOrder;
};
status: 'active' | 'rejected';
}

View File

@@ -0,0 +1,233 @@
import { Web3Wrapper } from '@0x/dev-utils';
import { TakerRequestQueryParams, V4RFQFirmQuote, V4RFQIndicativeQuote } from '@0x/quote-server';
import { BigNumber } from '@0x/utils';
import { AxiosInstance } from 'axios';
import { constants } from '../constants';
import {
AltFirmQuoteResponse,
AltIndicativeQuoteResponse,
AltOffering,
AltQuoteModel,
AltQuoteRequestData,
AltQuoteSide,
AltRfqtMakerAssetOfferings,
} from '../types';
function getAltMarketInfo(
offerings: AltOffering[],
buyTokenAddress: string,
sellTokenAddress: string,
): AltOffering | undefined {
for (const offering of offerings) {
if (
(buyTokenAddress.toLowerCase() === offering.baseAsset.toLowerCase() &&
sellTokenAddress.toLowerCase() === offering.quoteAsset.toLowerCase()) ||
(sellTokenAddress.toLowerCase() === offering.baseAsset.toLowerCase() &&
buyTokenAddress.toLowerCase() === offering.quoteAsset.toLowerCase())
) {
return offering;
}
}
return undefined;
}
function parseFirmQuoteResponseFromAltMM(altFirmQuoteReponse: AltFirmQuoteResponse): V4RFQFirmQuote {
return {
signedOrder: altFirmQuoteReponse.data['0xv4order'],
};
}
function parseIndicativeQuoteResponseFromAltMM(
altIndicativeQuoteResponse: AltIndicativeQuoteResponse,
altPair: AltOffering,
makerToken: string,
takerToken: string,
): V4RFQIndicativeQuote {
let makerAmount: BigNumber;
let takerAmount: BigNumber;
let quoteAmount: BigNumber;
let baseAmount: BigNumber;
if (!altIndicativeQuoteResponse.price) {
throw new Error('Price not returned by alt MM');
}
if (altIndicativeQuoteResponse.amount) {
// if amount is specified, amount is the base token amount
baseAmount = Web3Wrapper.toBaseUnitAmount(
new BigNumber(altIndicativeQuoteResponse.amount),
altPair.baseAssetDecimals,
);
// if amount is specified, use the price (quote/base) to get the quote amount
quoteAmount = Web3Wrapper.toBaseUnitAmount(
new BigNumber(altIndicativeQuoteResponse.amount)
.times(new BigNumber(altIndicativeQuoteResponse.price))
.decimalPlaces(altPair.quoteAssetDecimals, BigNumber.ROUND_DOWN),
altPair.quoteAssetDecimals,
);
} else if (altIndicativeQuoteResponse.value) {
// if value is specified, value is the quote token amount
quoteAmount = Web3Wrapper.toBaseUnitAmount(
new BigNumber(altIndicativeQuoteResponse.value),
altPair.quoteAssetDecimals,
);
// if value is specified, use the price (quote/base) to get the base amount
baseAmount = Web3Wrapper.toBaseUnitAmount(
new BigNumber(altIndicativeQuoteResponse.value)
.dividedBy(new BigNumber(altIndicativeQuoteResponse.price))
.decimalPlaces(altPair.baseAssetDecimals, BigNumber.ROUND_DOWN),
altPair.baseAssetDecimals,
);
} else {
throw new Error('neither amount or value were specified');
}
if (makerToken.toLowerCase() === altPair.baseAsset.toLowerCase()) {
makerAmount = baseAmount;
takerAmount = quoteAmount;
} else if (makerToken.toLowerCase() === altPair.quoteAsset.toLowerCase()) {
makerAmount = quoteAmount;
takerAmount = baseAmount;
} else {
throw new Error(`Base, quote tokens don't align with maker, taker tokens`);
}
return {
makerToken,
makerAmount,
takerToken,
takerAmount,
// HACK: alt implementation does not return an expiration with indicative quotes
// return now + { IMPUTED EXPIRY SECONDS } to have it included after order checks
expiry:
// tslint:disable-next-line:custom-no-magic-numbers
new BigNumber(Date.now() / 1000)
.integerValue(BigNumber.ROUND_DOWN)
.plus(constants.ALT_MM_IMPUTED_INDICATIVE_EXPIRY_SECONDS),
};
}
/**
* Turn a standard quote request into an alt quote request
* and return the appropriate standard quote response
*/
export async function returnQuoteFromAltMMAsync<ResponseT>(
url: string,
apiKey: string,
profile: string,
integratorKey: string,
quoteModel: AltQuoteModel,
makerToken: string,
takerToken: string,
maxResponseTimeMs: number,
altRfqtAssetOfferings: AltRfqtMakerAssetOfferings,
takerRequestQueryParams: TakerRequestQueryParams,
axiosInstance: AxiosInstance,
): Promise<{ data: ResponseT; status: number }> {
const altPair = getAltMarketInfo(
altRfqtAssetOfferings[url],
takerRequestQueryParams.buyTokenAddress,
takerRequestQueryParams.sellTokenAddress,
);
if (!altPair) {
throw new Error(`Alt pair not found`);
}
const side = altPair.baseAsset === takerRequestQueryParams.buyTokenAddress ? AltQuoteSide.Sell : AltQuoteSide.Buy;
// comparison price needs to be quote/base
// in the standard implementation, it's maker/taker
let altComparisonPrice: string | undefined;
if (altPair.quoteAsset === makerToken) {
altComparisonPrice = takerRequestQueryParams.comparisonPrice
? takerRequestQueryParams.comparisonPrice
: undefined;
} else {
altComparisonPrice = takerRequestQueryParams.comparisonPrice
? new BigNumber(takerRequestQueryParams.comparisonPrice).pow(-1).toString()
: undefined;
}
let data: AltQuoteRequestData;
data = {
market: `${altPair.id}`,
model: quoteModel,
profile,
side,
meta: {
txOrigin: takerRequestQueryParams.txOrigin!,
taker: takerRequestQueryParams.takerAddress,
client: integratorKey,
},
};
// specify a comparison price if it exists
if (altComparisonPrice) {
data.meta.existingOrder = {
price: altComparisonPrice,
};
}
// need to specify amount or value
// amount is units of the base asset
// value is units of the quote asset
let requestSize: string;
if (takerRequestQueryParams.buyAmountBaseUnits) {
requestSize = Web3Wrapper.toUnitAmount(
new BigNumber(takerRequestQueryParams.buyAmountBaseUnits),
takerRequestQueryParams.buyTokenAddress === altPair.baseAsset
? altPair.baseAssetDecimals
: altPair.quoteAssetDecimals,
).toString();
if (takerRequestQueryParams.buyTokenAddress === altPair.baseAsset) {
data.amount = requestSize;
// add to 'existing order' if there is a comparison price
if (data.meta.existingOrder) {
data.meta.existingOrder.amount = requestSize;
}
} else {
data.value = requestSize;
// add to 'existing order' if there is a comparison price
if (data.meta.existingOrder) {
data.meta.existingOrder.value = requestSize;
}
}
} else if (takerRequestQueryParams.sellAmountBaseUnits) {
requestSize = Web3Wrapper.toUnitAmount(
new BigNumber(takerRequestQueryParams.sellAmountBaseUnits),
takerRequestQueryParams.sellTokenAddress === altPair.baseAsset
? altPair.baseAssetDecimals
: altPair.quoteAssetDecimals,
).toString();
if (takerRequestQueryParams.sellTokenAddress === altPair.baseAsset) {
data.amount = requestSize;
if (data.meta.existingOrder) {
data.meta.existingOrder.amount = requestSize;
}
} else {
data.value = requestSize;
if (data.meta.existingOrder) {
data.meta.existingOrder.value = requestSize;
}
}
}
const response = await axiosInstance.post(`${url}/quotes`, data, {
headers: { Authorization: `Bearer ${apiKey}` },
timeout: maxResponseTimeMs,
});
if (response.data.status === 'rejected') {
throw new Error('alt MM rejected quote');
}
const parsedResponse =
quoteModel === 'firm'
? parseFirmQuoteResponseFromAltMM(response.data)
: parseIndicativeQuoteResponseFromAltMM(response.data, altPair, makerToken, takerToken);
return {
// hack to appease type checking
data: (parsedResponse as unknown) as ResponseT,
status: response.status,
};
}

View File

@@ -1,6 +1,47 @@
import { MAINNET_CURVE_INFOS, MAINNET_SNOWSWAP_INFOS, MAINNET_SWERVE_INFOS } from './constants';
import { BigNumber, NULL_BYTES } from '@0x/utils';
import {
KYBER_BRIDGED_LIQUIDITY_PREFIX,
MAINNET_CURVE_INFOS,
MAINNET_SHELL_POOLS,
MAINNET_SNOWSWAP_INFOS,
MAINNET_SWERVE_INFOS,
MAX_DODOV2_POOLS_QUERIED,
MAX_KYBER_RESERVES_QUERIED,
} from './constants';
import { CurveInfo, SnowSwapInfo, SwerveInfo } from './types';
/**
* Filter Kyber reserves which should not be used (0xbb bridged reserves)
* @param reserveId Kyber reserveId
*/
export function isAllowedKyberReserveId(reserveId: string): boolean {
return reserveId !== NULL_BYTES && !reserveId.startsWith(KYBER_BRIDGED_LIQUIDITY_PREFIX);
}
/**
* Returns the offsets to be used to discover Kyber reserves
*/
export function getKyberOffsets(): BigNumber[] {
return Array(MAX_KYBER_RESERVES_QUERIED)
.fill(0)
.map((_v, i) => new BigNumber(i));
}
// tslint:disable completed-docs
export function getDodoV2Offsets(): BigNumber[] {
return Array(MAX_DODOV2_POOLS_QUERIED)
.fill(0)
.map((_v, i) => new BigNumber(i));
}
// tslint:disable completed-docs
export function getShellsForPair(takerToken: string, makerToken: string): string[] {
return Object.values(MAINNET_SHELL_POOLS)
.filter(c => [makerToken, takerToken].every(t => c.tokens.includes(t)))
.map(i => i.poolAddress);
}
// tslint:disable completed-docs
export function getCurveInfosForPair(takerToken: string, makerToken: string): CurveInfo[] {
return Object.values(MAINNET_CURVE_INFOS).filter(c =>

View File

@@ -5,8 +5,15 @@ import * as _ from 'lodash';
import { MarketOperation } from '../../types';
import { COMPARISON_PRICE_DECIMALS } from './constants';
import { ComparisonPrice, ERC20BridgeSource, FeeEstimate, FeeSchedule, MarketSideLiquidity } from './types';
import { COMPARISON_PRICE_DECIMALS, SOURCE_FLAGS } from './constants';
import {
ComparisonPrice,
ERC20BridgeSource,
ExchangeProxyOverhead,
FeeEstimate,
FeeSchedule,
MarketSideLiquidity,
} from './types';
/**
* Takes in an optimizer response and returns a price for RFQT MMs to beat
@@ -23,6 +30,7 @@ export function getComparisonPrices(
amount: BigNumber,
marketSideLiquidity: MarketSideLiquidity,
feeSchedule: FeeSchedule,
exchangeProxyOverhead: ExchangeProxyOverhead,
): ComparisonPrice {
let wholeOrder: BigNumber | undefined;
let feeInEth: BigNumber | number;
@@ -39,9 +47,11 @@ export function getComparisonPrices(
return { wholeOrder };
} else {
try {
feeInEth = new BigNumber(
const fillFeeInEth = new BigNumber(
(feeSchedule[ERC20BridgeSource.Native] as FeeEstimate)({ type: FillQuoteTransformerOrderType.Rfq }),
);
const exchangeProxyOverheadInEth = new BigNumber(exchangeProxyOverhead(SOURCE_FLAGS.Native));
feeInEth = fillFeeInEth.plus(exchangeProxyOverheadInEth);
} catch {
logUtils.warn('Native order fee schedule requires fill data');
@@ -50,10 +60,10 @@ export function getComparisonPrices(
}
// Calc native order fee penalty in output unit (maker units for sells, taker unit for buys)
const feePenalty = !marketSideLiquidity.ethToOutputRate.isZero()
? marketSideLiquidity.ethToOutputRate.times(feeInEth)
const feePenalty = !marketSideLiquidity.outputAmountPerEth.isZero()
? marketSideLiquidity.outputAmountPerEth.times(feeInEth)
: // if it's a sell, the input token is the taker token
marketSideLiquidity.ethToInputRate
marketSideLiquidity.inputAmountPerEth
.times(feeInEth)
.times(marketSideLiquidity.side === MarketOperation.Sell ? adjustedRate : adjustedRate.pow(-1));

View File

@@ -1,7 +1,7 @@
import { ChainId } from '@0x/contract-addresses';
import { FillQuoteTransformerOrderType } from '@0x/protocol-utils';
import { BigNumber } from '@0x/utils';
import { BridgeContractAddresses } from '../../types';
import { TokenAdjacencyGraphBuilder } from '../token_adjacency_graph_builder';
import { SourceFilters } from './source_filters';
import {
@@ -19,11 +19,25 @@ import {
MultiHopFillData,
SnowSwapFillData,
SushiSwapFillData,
TokenAdjacencyGraph,
UniswapV2FillData,
} from './types';
// tslint:disable: custom-no-magic-numbers no-bitwise
export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04';
export const ONE_ETHER = new BigNumber(1e18);
export const NEGATIVE_INF = new BigNumber('-Infinity');
export const POSITIVE_INF = new BigNumber('Infinity');
export const ZERO_AMOUNT = new BigNumber(0);
export const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
export const ONE_HOUR_IN_SECONDS = 60 * 60;
export const ONE_SECOND_MS = 1000;
export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const COMPARISON_PRICE_DECIMALS = 10;
/**
* Valid sources for market sell.
*/
@@ -44,9 +58,11 @@ export const SELL_SOURCE_FILTER = new SourceFilters([
ERC20BridgeSource.Shell,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]);
/**
@@ -69,9 +85,11 @@ export const BUY_SOURCE_FILTER = new SourceFilters([
ERC20BridgeSource.SushiSwap,
ERC20BridgeSource.MultiHop,
ERC20BridgeSource.Dodo,
ERC20BridgeSource.DodoV2,
ERC20BridgeSource.Cream,
ERC20BridgeSource.LiquidityProvider,
ERC20BridgeSource.CryptoCom,
ERC20BridgeSource.Linkswap,
]);
/**
@@ -89,6 +107,23 @@ export const SOURCE_FLAGS: { [source in ERC20BridgeSource]: number } = Object.as
...Object.values(ERC20BridgeSource).map((source: ERC20BridgeSource, index) => ({ [source]: 1 << index })),
);
const MIRROR_WRAPPED_TOKENS = {
mAAPL: '0xd36932143f6ebdedd872d5fb0651f4b72fd15a84',
mSLV: '0x9d1555d8cb3c846bb4f7d5b1b1080872c3166676',
mIAU: '0x1d350417d9787e000cc1b95d70e9536dcd91f373',
mAMZN: '0x0cae9e4d663793c2a2a0b211c1cf4bbca2b9caa7',
mGOOGL: '0x4b70ccd1cf9905be1faed025eadbd3ab124efe9a',
mTSLA: '0x21ca39943e91d704678f5d00b6616650f066fd63',
mQQQ: '0x13b02c8de71680e71f0820c996e4be43c2f57d15',
mTWTR: '0xedb0414627e6f1e3f082de65cd4f9c693d78cca9',
mMSFT: '0x41bbedd7286daab5910a1f15d12cbda839852bd7',
mNFLX: '0xc8d674114bac90148d11d3c1d33c61835a0f9dcd',
mBABA: '0x676ce85f66adb8d7b8323aeefe17087a3b8cb363',
mUSO: '0x31c63146a635eb7465e5853020b39713ac356991',
mVIXY: '0xf72fcd9dcf0190923fadd44811e240ef4533fc86',
mLUNA: '0xd2877702675e6ceb975b4a1dff9fb7baf4c91ea9',
};
// Mainnet tokens
// Not an exhaustive list, just enough so we don't repeat ourselves
export const TOKENS = {
@@ -106,7 +141,6 @@ export const TOKENS = {
mUSD: '0xe2f2a5c287993345a840db3b0845fbc70f5935a5',
USDN: '0x674c6ad92fd080e4004b2312b45f796a192d27a0',
dUSD: '0x5bc25f649fc4e26069ddf4cf4010f9f706c23831',
UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd',
// Bitcoins
WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599',
RenBTC: '0xeb4c2781e4eba804ce9a9803c67d0893436bb27d',
@@ -125,6 +159,11 @@ export const TOKENS = {
EURS: '0xdb25f211ab05b1c97d595516f45794528a807ad8',
sEUR: '0xd71ecff9342a5ced620049e616c5035f1db98620',
sETH: '0x5e74c9036fb86bd7ecdcb084a0673efc32ea31cb',
LINK: '0x514910771af9ca656af840dff83e8264ecf986ca',
// Mirror Protocol
UST: '0xa47c8bf37f92abed4a126bda807a7b7498661acd',
MIR: '0x09a3ecafa817268f77be1283176b946c4ff2e608',
...MIRROR_WRAPPED_TOKENS,
};
export const POOLS = {
@@ -156,6 +195,22 @@ export const POOLS = {
curve_aave: '0xdebf20617708857ebe4f679508e7b7863a8a8eee', // 25.aave
};
export const DEFAULT_INTERMEDIATE_TOKENS = [TOKENS.WETH, TOKENS.USDT, TOKENS.DAI, TOKENS.USDC, TOKENS.WBTC];
export const DEFAULT_TOKEN_ADJACENCY_GRAPH: TokenAdjacencyGraph = new TokenAdjacencyGraphBuilder({
default: DEFAULT_INTERMEDIATE_TOKENS,
})
// Mirror Protocol
.tap(builder => {
builder
.add(TOKENS.MIR, TOKENS.UST)
.add(TOKENS.UST, [TOKENS.MIR, ...Object.values(MIRROR_WRAPPED_TOKENS)])
.add(TOKENS.USDT, TOKENS.UST);
Object.values(MIRROR_WRAPPED_TOKENS).forEach(t => builder.add(t, TOKENS.UST));
})
// Build
.build();
/**
* Mainnet Curve configuration
* The tokens are in order of their index, which each curve defines
@@ -413,6 +468,8 @@ export const MAINNET_UNISWAP_V1_ROUTER = '0xc0a47dfe034b400b47bdad5fecda2621de6c
export const MAINNET_UNISWAP_V2_ROUTER = '0xf164fc0ec4e93095b804a4795bbe1e041497b92a';
export const MAINNET_SUSHI_SWAP_ROUTER = '0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f';
export const MAINNET_CRYPTO_COM_ROUTER = '0xceb90e4c17d626be0facd78b79c9c87d7ca181b3';
export const MAINNET_LINKSWAP_ROUTER = '0xa7ece0911fe8c60bff9e99f8fafcdbe56e07aff1';
export const MAINNET_MSTABLE_ROUTER = '0xe2f2a5c287993345a840db3b0845fbc70f5935a5';
export const MAINNET_OASIS_ROUTER = '0x794e6e91555438afc3ccf1c5076a74f42133d08d';
@@ -421,6 +478,15 @@ export const MAINNET_MOONISWAP_V2_REGISTRY = '0xc4a8b7e29e3c8ec560cd4945c1cf3461
export const MAINNET_MOONISWAP_V2_1_REGISTRY = '0xbaf9a5d4b0052359326a6cdab54babaa3a3a9643';
export const MAINNET_DODO_HELPER = '0x533da777aedce766ceae696bf90f8541a4ba80eb';
export const MAINNET_DODOV2_PRIVATE_POOL_FACTORY = '0x6b4fa0bc61eddc928e0df9c7f01e407bfcd3e5ef';
export const MAINNET_DODOV2_VENDING_MACHINE_FACTORY = '0x72d220ce168c4f361dd4dee5d826a01ad8598f6c';
export const MAX_DODOV2_POOLS_QUERIED = 3;
export const CURVE_LIQUIDITY_PROVIDER_BY_CHAIN_ID: { [id: string]: string } = {
'1': '0x7a6F6a048fE2Dc1397ABa0bf7879d3eacF371C53',
'3': '0xAa213dcDFbF104e08cbAeC3d1628eD197553AfCc',
'1337': NULL_ADDRESS,
};
export const MAINNET_SHELL_POOLS = {
StableCoins: {
@@ -437,73 +503,6 @@ export const BALANCER_SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/ba
export const BALANCER_TOP_POOLS_FETCHED = 250;
export const BALANCER_MAX_POOLS_FETCHED = 3;
export const ERC20_PROXY_ID = '0xf47261b0';
export const WALLET_SIGNATURE = '0x04';
export const ONE_ETHER = new BigNumber(1e18);
export const NEGATIVE_INF = new BigNumber('-Infinity');
export const POSITIVE_INF = new BigNumber('Infinity');
export const ZERO_AMOUNT = new BigNumber(0);
export const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
export const ONE_HOUR_IN_SECONDS = 60 * 60;
export const ONE_SECOND_MS = 1000;
export const NULL_BYTES = '0x';
export const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
export const COMPARISON_PRICE_DECIMALS = 10;
const EMPTY_BRIDGE_ADDRESSES: BridgeContractAddresses = {
uniswapBridge: NULL_ADDRESS,
uniswapV2Bridge: NULL_ADDRESS,
eth2DaiBridge: NULL_ADDRESS,
kyberBridge: NULL_ADDRESS,
curveBridge: NULL_ADDRESS,
multiBridge: NULL_ADDRESS,
balancerBridge: NULL_ADDRESS,
bancorBridge: NULL_ADDRESS,
mStableBridge: NULL_ADDRESS,
mooniswapBridge: NULL_ADDRESS,
sushiswapBridge: NULL_ADDRESS,
shellBridge: NULL_ADDRESS,
dodoBridge: NULL_ADDRESS,
creamBridge: NULL_ADDRESS,
snowswapBridge: NULL_ADDRESS,
swerveBridge: NULL_ADDRESS,
cryptoComBridge: NULL_ADDRESS,
};
export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAddresses } = {
[ChainId.Mainnet]: {
uniswapBridge: '0x36691c4f426eb8f42f150ebde43069a31cb080ad',
uniswapV2Bridge: '0xdcd6011f4c6b80e470d9487f5871a0cba7c93f48',
kyberBridge: '0xadd97271402590564ddd8ad23cb5317b1fb0fffb',
eth2DaiBridge: '0x991c745401d5b5e469b8c3e2cb02c748f08754f1',
curveBridge: '0x1796cd592d19e3bcd744fbb025bb61a6d8cb2c09',
multiBridge: '0xc03117a8c9bde203f70aa911cb64a7a0df5ba1e1',
balancerBridge: '0xfe01821ca163844203220cd08e4f2b2fb43ae4e4',
bancorBridge: '0xc880c252db7c51f74161633338a3bdafa8e65276',
mStableBridge: '0x2bf04fcea05f0989a14d9afa37aa376baca6b2b3',
mooniswapBridge: '0x02b7eca484ad960fca3f7709e0b2ac81eec3069c',
sushiswapBridge: '0x47ed0262a0b688dcb836d254c6a2e96b6c48a9f5',
shellBridge: '0xf1c0811e3788caae7dbfae43da9d9131b1a8a148',
dodoBridge: '0xe9da66965a9344aab2167e6813c03f043cc7a6ca',
creamBridge: '0xb9d4bf2c8dab828f4ffb656acdb6c2b497d44f25',
swerveBridge: '0xf9786d5eb1de47fa56a8f7bb387653c6d410bfee',
snowswapBridge: '0xb1dbe83d15236ec10fdb214c6b89774b454754fd',
cryptoComBridge: '0x015850307f6aab4ac6631923ceefe71b57492c9b',
},
[ChainId.Kovan]: {
...EMPTY_BRIDGE_ADDRESSES,
uniswapBridge: '0x0e85f89f29998df65402391478e5924700c0079d',
uniswapV2Bridge: '0x7b3530a635d099de0534dc27e46cd7c57578c3c8',
eth2DaiBridge: '0x2d47147429b474d2e4f83e658015858a1312ed5b',
kyberBridge: '0xaecfa25920f892b6eb496e1f6e84037f59da7f44',
curveBridge: '0x81c0ab53a7352d2e97f682a37cba44e54647eefb',
balancerBridge: '0x407b4128e9ecad8769b2332312a9f655cb9f5f3a',
},
[ChainId.Rinkeby]: EMPTY_BRIDGE_ADDRESSES,
[ChainId.Ropsten]: EMPTY_BRIDGE_ADDRESSES,
[ChainId.Ganache]: EMPTY_BRIDGE_ADDRESSES,
};
/**
* Calculated gross gas cost of the underlying exchange.
* The cost of switching from one source to another, assuming
@@ -513,9 +512,13 @@ export const BRIDGE_ADDRESSES_BY_CHAIN: { [chainId in ChainId]: BridgeContractAd
*/
// tslint:disable:custom-no-magic-numbers
export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
[ERC20BridgeSource.Native]: _fillData => {
// const nativeFillData = (_fillData as NativeRfqOrderFillData|NativeLimitOrderFillData)
return 100e3;
[ERC20BridgeSource.Native]: fillData => {
// TODO jacob re-order imports so there is no circular rependency with SignedNativeOrder
const nativeFillData = fillData as ({ type: FillQuoteTransformerOrderType });
return nativeFillData && nativeFillData.type === FillQuoteTransformerOrderType.Limit
? PROTOCOL_FEE_MULTIPLIER.plus(100e3).toNumber()
: // TODO jacob revisit wth v4 LimitOrders
100e3;
},
[ERC20BridgeSource.Uniswap]: () => 90e3,
[ERC20BridgeSource.LiquidityProvider]: fillData => {
@@ -581,8 +584,17 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
},
[ERC20BridgeSource.CryptoCom]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3 + 20e3 + 60e3; // temporary allowance diff, unrolled FQT
const path = (fillData as SushiSwapFillData).tokenAddressPath;
let gas = 90e3;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
return gas;
},
[ERC20BridgeSource.Linkswap]: (fillData?: FillData) => {
// TODO: Different base cost if to/from ETH.
let gas = 90e3;
const path = (fillData as UniswapV2FillData).tokenAddressPath;
if (path.length > 2) {
gas += (path.length - 2) * 60e3; // +60k for each hop.
}
@@ -610,6 +622,7 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
// sell quote requires additional calculation and overhead
return isSellBase ? 180e3 : 300e3;
},
[ERC20BridgeSource.DodoV2]: (_fillData?: FillData) => 100e3,
[ERC20BridgeSource.SnowSwap]: fillData => {
switch ((fillData as SnowSwapFillData).pool.poolAddress.toLowerCase()) {
case '0xbf7ccd6c446acfcc5df023043f2167b62e81899b':
@@ -630,15 +643,9 @@ export const DEFAULT_GAS_SCHEDULE: Required<FeeSchedule> = {
},
};
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = Object.assign(
{},
...(Object.keys(DEFAULT_GAS_SCHEDULE) as ERC20BridgeSource[]).map(k => ({
[k]:
k === ERC20BridgeSource.Native
? (fillData: FillData) => PROTOCOL_FEE_MULTIPLIER.plus(DEFAULT_GAS_SCHEDULE[k](fillData))
: (fillData: FillData) => DEFAULT_GAS_SCHEDULE[k](fillData),
})),
);
export const DEFAULT_FEE_SCHEDULE: Required<FeeSchedule> = { ...DEFAULT_GAS_SCHEDULE };
export const POSITIVE_SLIPPAGE_FEE_TRANSFORMER_GAS = new BigNumber(20000);
// tslint:enable:custom-no-magic-numbers

View File

@@ -16,8 +16,8 @@ export function createFills(opts: {
orders?: NativeOrderWithFillableAmounts[];
dexQuotes?: DexSample[][];
targetInput?: BigNumber;
ethToOutputRate?: BigNumber;
ethToInputRate?: BigNumber;
outputAmountPerEth?: BigNumber;
inputAmountPerEth?: BigNumber;
excludedSources?: ERC20BridgeSource[];
feeSchedule?: FeeSchedule;
}): Fill[][] {
@@ -26,20 +26,20 @@ export function createFills(opts: {
const feeSchedule = opts.feeSchedule || {};
const orders = opts.orders || [];
const dexQuotes = opts.dexQuotes || [];
const ethToOutputRate = opts.ethToOutputRate || ZERO_AMOUNT;
const ethToInputRate = opts.ethToInputRate || ZERO_AMOUNT;
const outputAmountPerEth = opts.outputAmountPerEth || ZERO_AMOUNT;
const inputAmountPerEth = opts.inputAmountPerEth || ZERO_AMOUNT;
// Create native fills.
const nativeFills = nativeOrdersToFills(
side,
orders.filter(o => o.fillableTakerAmount.isGreaterThan(0)),
opts.targetInput,
ethToOutputRate,
ethToInputRate,
outputAmountPerEth,
inputAmountPerEth,
feeSchedule,
);
// Create DEX fills.
const dexFills = dexQuotes.map(singleSourceSamples =>
dexSamplesToFills(side, singleSourceSamples, ethToOutputRate, ethToInputRate, feeSchedule),
dexSamplesToFills(side, singleSourceSamples, outputAmountPerEth, inputAmountPerEth, feeSchedule),
);
return [...dexFills, nativeFills]
.map(p => clipFillsToInput(p, opts.targetInput))
@@ -75,8 +75,8 @@ function nativeOrdersToFills(
side: MarketOperation,
orders: NativeOrderWithFillableAmounts[],
targetInput: BigNumber = POSITIVE_INF,
ethToOutputRate: BigNumber,
ethToInputRate: BigNumber,
outputAmountPerEth: BigNumber,
inputAmountPerEth: BigNumber,
fees: FeeSchedule,
): Fill[] {
const sourcePathId = hexUtils.random();
@@ -89,9 +89,9 @@ function nativeOrdersToFills(
const input = side === MarketOperation.Sell ? takerAmount : makerAmount;
const output = side === MarketOperation.Sell ? makerAmount : takerAmount;
const fee = fees[ERC20BridgeSource.Native] === undefined ? 0 : fees[ERC20BridgeSource.Native]!(o);
const outputPenalty = !ethToOutputRate.isZero()
? ethToOutputRate.times(fee)
: ethToInputRate.times(fee).times(output.dividedToIntegerBy(input));
const outputPenalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(fee)
: inputAmountPerEth.times(fee).times(output.dividedToIntegerBy(input));
// targetInput can be less than the order size
// whilst the penalty is constant, it affects the adjusted output
// only up until the target has been exhausted.
@@ -135,8 +135,8 @@ function nativeOrdersToFills(
function dexSamplesToFills(
side: MarketOperation,
samples: DexSample[],
ethToOutputRate: BigNumber,
ethToInputRate: BigNumber,
outputAmountPerEth: BigNumber,
inputAmountPerEth: BigNumber,
fees: FeeSchedule,
): Fill[] {
const sourcePathId = hexUtils.random();
@@ -156,9 +156,9 @@ function dexSamplesToFills(
let penalty = ZERO_AMOUNT;
if (i === 0) {
// Only the first fill in a DEX path incurs a penalty.
penalty = !ethToOutputRate.isZero()
? ethToOutputRate.times(fee)
: ethToInputRate.times(fee).times(output.dividedToIntegerBy(input));
penalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(fee)
: inputAmountPerEth.times(fee).times(output.dividedToIntegerBy(input));
}
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);

View File

@@ -30,7 +30,8 @@ import {
import { createFills } from './fills';
import { getBestTwoHopQuote } from './multihop_utils';
import { createOrdersFromTwoHopSample } from './orders';
import { findOptimalPathAsync } from './path_optimizer';
import { PathPenaltyOpts } from './path';
import { fillsToSortedPaths, findOptimalPathAsync } from './path_optimizer';
import { DexOrderSampler, getSampleAmounts } from './sampler';
import { SourceFilters } from './source_filters';
import {
@@ -167,8 +168,8 @@ export class MarketOperationUtils {
[
tokenDecimals,
orderFillableTakerAmounts,
ethToMakerAssetRate,
ethToTakerAssetRate,
outputAmountPerEth,
inputAmountPerEth,
dexQuotes,
rawTwoHopQuotes,
isTxOriginContract,
@@ -178,7 +179,9 @@ export class MarketOperationUtils {
] = await Promise.all([samplerPromise, offChainBalancerPromise, offChainCreamPromise]);
// 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);
const twoHopQuotes = rawTwoHopQuotes.filter(
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
);
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
@@ -193,8 +196,8 @@ export class MarketOperationUtils {
inputAmount: takerAmount,
inputToken: takerToken,
outputToken: makerToken,
ethToOutputRate: ethToMakerAssetRate,
ethToInputRate: ethToTakerAssetRate,
outputAmountPerEth,
inputAmountPerEth,
quoteSourceFilters,
makerTokenDecimals: makerTokenDecimals.toNumber(),
takerTokenDecimals: takerTokenDecimals.toNumber(),
@@ -302,7 +305,9 @@ export class MarketOperationUtils {
] = await Promise.all([samplerPromise, offChainBalancerPromise, offChainCreamPromise]);
// 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);
const twoHopQuotes = rawTwoHopQuotes.filter(
q => q && q.fillData && q.fillData.firstHopSource && q.fillData.secondHopSource,
);
const [makerTokenDecimals, takerTokenDecimals] = tokenDecimals;
const isRfqSupported = !isTxOriginContract;
@@ -317,8 +322,8 @@ export class MarketOperationUtils {
inputAmount: makerAmount,
inputToken: makerToken,
outputToken: takerToken,
ethToOutputRate: ethToTakerAssetRate,
ethToInputRate: ethToMakerAssetRate,
outputAmountPerEth: ethToTakerAssetRate,
inputAmountPerEth: ethToMakerAssetRate,
quoteSourceFilters,
makerTokenDecimals: makerTokenDecimals.toNumber(),
takerTokenDecimals: takerTokenDecimals.toNumber(),
@@ -388,7 +393,7 @@ export class MarketOperationUtils {
const batchEthToTakerAssetRate = executeResults.splice(0, batchNativeOrders.length) as BigNumber[];
const batchDexQuotes = executeResults.splice(0, batchNativeOrders.length) as DexSample[][][];
const batchTokenDecimals = executeResults.splice(0, batchNativeOrders.length) as number[][];
const ethToInputRate = ZERO_AMOUNT;
const inputAmountPerEth = ZERO_AMOUNT;
return Promise.all(
batchNativeOrders.map(async (nativeOrders, i) => {
@@ -397,7 +402,7 @@ export class MarketOperationUtils {
}
const { makerToken, takerToken } = nativeOrders[0].order;
const orderFillableMakerAmounts = batchOrderFillableMakerAmounts[i];
const ethToTakerAssetRate = batchEthToTakerAssetRate[i];
const outputAmountPerEth = batchEthToTakerAssetRate[i];
const dexQuotes = batchDexQuotes[i];
const makerAmount = makerAmounts[i];
try {
@@ -407,8 +412,8 @@ export class MarketOperationUtils {
inputToken: makerToken,
outputToken: takerToken,
inputAmount: makerAmount,
ethToOutputRate: ethToTakerAssetRate,
ethToInputRate,
outputAmountPerEth,
inputAmountPerEth,
quoteSourceFilters,
makerTokenDecimals: batchTokenDecimals[i][0],
takerTokenDecimals: batchTokenDecimals[i][1],
@@ -451,8 +456,8 @@ export class MarketOperationUtils {
side,
inputAmount,
quotes,
ethToOutputRate,
ethToInputRate,
outputAmountPerEth,
inputAmountPerEth,
} = marketSideLiquidity;
const { nativeOrders, rfqtIndicativeQuotes, dexQuotes } = quotes;
const maxFallbackSlippage = opts.maxFallbackSlippage || 0;
@@ -485,25 +490,29 @@ export class MarketOperationUtils {
orders: [...nativeOrders, ...augmentedRfqtIndicativeQuotes],
dexQuotes,
targetInput: inputAmount,
ethToOutputRate,
ethToInputRate,
outputAmountPerEth,
inputAmountPerEth,
excludedSources: opts.excludedSources,
feeSchedule: opts.feeSchedule,
});
// Find the optimal path.
const optimizerOpts = {
ethToOutputRate,
ethToInputRate,
const penaltyOpts: PathPenaltyOpts = {
outputAmountPerEth,
inputAmountPerEth,
exchangeProxyOverhead: opts.exchangeProxyOverhead || (() => ZERO_AMOUNT),
};
// NOTE: For sell quotes input is the taker asset and for buy quotes input is the maker asset
const takerTokenToEthRate = side === MarketOperation.Sell ? ethToInputRate : ethToOutputRate;
const makerTokenToEthRate = side === MarketOperation.Sell ? ethToOutputRate : ethToInputRate;
const takerAmountPerEth = side === MarketOperation.Sell ? inputAmountPerEth : outputAmountPerEth;
const makerAmountPerEth = side === MarketOperation.Sell ? outputAmountPerEth : inputAmountPerEth;
// Find the unoptimized best rate to calculate savings from optimizer
const _unoptimizedPath = fillsToSortedPaths(fills, side, inputAmount, penaltyOpts)[0];
const unoptimizedPath = _unoptimizedPath ? _unoptimizedPath.collapse(orderOpts) : undefined;
// Find the optimal path
const optimalPath = await findOptimalPathAsync(side, fills, inputAmount, opts.runLimit, optimizerOpts);
const optimalPath = await findOptimalPathAsync(side, fills, inputAmount, opts.runLimit, penaltyOpts);
const optimalPathRate = optimalPath ? optimalPath.adjustedRate() : ZERO_AMOUNT;
const { adjustedRate: bestTwoHopRate, quote: bestTwoHopQuote } = getBestTwoHopQuote(
@@ -519,8 +528,9 @@ export class MarketOperationUtils {
sourceFlags: SOURCE_FLAGS[ERC20BridgeSource.MultiHop],
marketSideLiquidity,
adjustedRate: bestTwoHopRate,
takerTokenToEthRate,
makerTokenToEthRate,
unoptimizedPath,
takerAmountPerEth,
makerAmountPerEth,
};
}
@@ -553,8 +563,9 @@ export class MarketOperationUtils {
sourceFlags: collapsedPath.sourceFlags,
marketSideLiquidity,
adjustedRate: optimalPathRate,
takerTokenToEthRate,
makerTokenToEthRate,
unoptimizedPath,
takerAmountPerEth,
makerAmountPerEth,
};
}
@@ -608,6 +619,7 @@ export class MarketOperationUtils {
amount,
marketSideLiquidity,
_opts.feeSchedule,
_opts.exchangeProxyOverhead,
).wholeOrder;
}

View File

@@ -1,20 +0,0 @@
import { BigNumber, NULL_BYTES } from '@0x/utils';
import { KYBER_BRIDGED_LIQUIDITY_PREFIX, MAX_KYBER_RESERVES_QUERIED } from './constants';
/**
* Filter Kyber reserves which should not be used (0xbb bridged reserves)
* @param reserveId Kyber reserveId
*/
export function isAllowedKyberReserveId(reserveId: string): boolean {
return reserveId !== NULL_BYTES && !reserveId.startsWith(KYBER_BRIDGED_LIQUIDITY_PREFIX);
}
/**
* Returns the offsets to be used to discover Kyber reserves
*/
export function getKyberOffsets(): BigNumber[] {
return Array(MAX_KYBER_RESERVES_QUERIED)
.fill(0)
.map((_v, i) => new BigNumber(i));
}

View File

@@ -39,7 +39,7 @@ export function getBestTwoHopQuote(
feeSchedule?: FeeSchedule,
exchangeProxyOverhead?: ExchangeProxyOverhead,
): { quote: DexSample<MultiHopFillData> | undefined; adjustedRate: BigNumber } {
const { side, inputAmount, ethToOutputRate, quotes } = marketSideLiquidity;
const { side, inputAmount, outputAmountPerEth, quotes } = marketSideLiquidity;
const { twoHopQuotes } = quotes;
// Ensure the expected data we require exists. In the case where all hops reverted
// or there were no sources included that allowed for multi hop,
@@ -57,7 +57,7 @@ export function getBestTwoHopQuote(
}
const best = filteredQuotes
.map(quote =>
getTwoHopAdjustedRate(side, quote, inputAmount, ethToOutputRate, feeSchedule, exchangeProxyOverhead),
getTwoHopAdjustedRate(side, quote, inputAmount, outputAmountPerEth, feeSchedule, exchangeProxyOverhead),
)
.reduce(
(prev, curr, i) =>
@@ -67,7 +67,7 @@ export function getBestTwoHopQuote(
side,
filteredQuotes[0],
inputAmount,
ethToOutputRate,
outputAmountPerEth,
feeSchedule,
exchangeProxyOverhead,
),

View File

@@ -118,6 +118,10 @@ export function getERC20BridgeSourceToBridgeSource(source: ERC20BridgeSource): B
return BridgeSource.Uniswap;
case ERC20BridgeSource.UniswapV2:
return BridgeSource.UniswapV2;
case ERC20BridgeSource.DodoV2:
return BridgeSource.DodoV2;
case ERC20BridgeSource.Linkswap:
return BridgeSource.Linkswap;
default:
throw new Error(AggregationError.NoBridgeForSource);
}
@@ -164,6 +168,7 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
case ERC20BridgeSource.UniswapV2:
case ERC20BridgeSource.SushiSwap:
case ERC20BridgeSource.CryptoCom:
case ERC20BridgeSource.Linkswap:
const uniswapV2FillData = (order as OptimizedMarketBridgeOrder<UniswapV2FillData | SushiSwapFillData>)
.fillData;
bridgeData = encoder.encode([uniswapV2FillData.router, uniswapV2FillData.tokenAddressPath]);
@@ -180,6 +185,10 @@ export function createBridgeDataForBridgeOrder(order: OptimizedMarketBridgeOrder
const dodoFillData = (order as OptimizedMarketBridgeOrder<DODOFillData>).fillData;
bridgeData = encoder.encode([MAINNET_DODO_HELPER, dodoFillData.poolAddress, dodoFillData.isSellBase]);
break;
case ERC20BridgeSource.DodoV2:
const dodoV2FillData = (order as OptimizedMarketBridgeOrder<DODOFillData>).fillData;
bridgeData = encoder.encode([dodoV2FillData.poolAddress, dodoV2FillData.isSellBase]);
break;
case ERC20BridgeSource.Shell:
const shellFillData = (order as OptimizedMarketBridgeOrder<ShellFillData>).fillData;
bridgeData = encoder.encode([shellFillData.poolAddress]);
@@ -258,6 +267,10 @@ export const BRIDGE_ENCODERS: {
{ name: 'poolAddress', type: 'address' },
{ name: 'isSellBase', type: 'bool' },
]),
[ERC20BridgeSource.DodoV2]: AbiEncoder.create([
{ name: 'poolAddress', type: 'address' },
{ name: 'isSellBase', type: 'bool' },
]),
// Curve like
[ERC20BridgeSource.Curve]: curveEncoder,
[ERC20BridgeSource.Swerve]: curveEncoder,
@@ -267,6 +280,7 @@ export const BRIDGE_ENCODERS: {
[ERC20BridgeSource.UniswapV2]: routerAddressPathEncoder,
[ERC20BridgeSource.SushiSwap]: routerAddressPathEncoder,
[ERC20BridgeSource.CryptoCom]: routerAddressPathEncoder,
[ERC20BridgeSource.Linkswap]: routerAddressPathEncoder,
// Generic pools
[ERC20BridgeSource.Shell]: poolEncoder,
[ERC20BridgeSource.Mooniswap]: poolEncoder,

View File

@@ -22,14 +22,14 @@ export interface PathSize {
}
export interface PathPenaltyOpts {
ethToOutputRate: BigNumber;
ethToInputRate: BigNumber;
outputAmountPerEth: BigNumber;
inputAmountPerEth: BigNumber;
exchangeProxyOverhead: ExchangeProxyOverhead;
}
export const DEFAULT_PATH_PENALTY_OPTS: PathPenaltyOpts = {
ethToOutputRate: ZERO_AMOUNT,
ethToInputRate: ZERO_AMOUNT,
outputAmountPerEth: ZERO_AMOUNT,
inputAmountPerEth: ZERO_AMOUNT,
exchangeProxyOverhead: () => ZERO_AMOUNT,
};
@@ -131,11 +131,11 @@ export class Path {
public adjustedSize(): PathSize {
const { input, output } = this._adjustedSize;
const { exchangeProxyOverhead, ethToOutputRate, ethToInputRate } = this.pathPenaltyOpts;
const { exchangeProxyOverhead, outputAmountPerEth, inputAmountPerEth } = this.pathPenaltyOpts;
const gasOverhead = exchangeProxyOverhead(this.sourceFlags);
const pathPenalty = !ethToOutputRate.isZero()
? ethToOutputRate.times(gasOverhead)
: ethToInputRate.times(gasOverhead).times(output.dividedToIntegerBy(input));
const pathPenalty = !outputAmountPerEth.isZero()
? outputAmountPerEth.times(gasOverhead)
: inputAmountPerEth.times(gasOverhead).times(output.dividedToIntegerBy(input));
return {
input,
output: this.side === MarketOperation.Sell ? output.minus(pathPenalty) : output.plus(pathPenalty),

View File

@@ -13,7 +13,7 @@ export function getTwoHopAdjustedRate(
side: MarketOperation,
twoHopQuote: DexSample<MultiHopFillData>,
targetInput: BigNumber,
ethToOutputRate: BigNumber,
outputAmountPerEth: BigNumber,
fees: FeeSchedule = {},
exchangeProxyOverhead: ExchangeProxyOverhead = () => ZERO_AMOUNT,
): BigNumber {
@@ -21,7 +21,7 @@ export function getTwoHopAdjustedRate(
if (input.isLessThan(targetInput) || output.isZero()) {
return ZERO_AMOUNT;
}
const penalty = ethToOutputRate.times(
const penalty = outputAmountPerEth.times(
exchangeProxyOverhead(SOURCE_FLAGS.MultiHop).plus(fees[ERC20BridgeSource.MultiHop]!(fillData)),
);
const adjustedOutput = side === MarketOperation.Sell ? output.minus(penalty) : output.plus(penalty);

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