Compare commits

..

31 Commits

Author SHA1 Message Date
Github Actions
4c3fbe83ac Publish
- @0x/contracts-asset-proxy@3.7.8
 - @0x/contracts-broker@1.1.26
 - @0x/contracts-coordinator@3.1.27
 - @0x/contracts-dev-utils@1.3.25
 - @0x/contracts-erc1155@2.1.26
 - @0x/contracts-erc20@3.3.5
 - @0x/contracts-erc721@3.1.26
 - @0x/contracts-exchange-forwarder@4.2.27
 - @0x/contracts-exchange-libs@4.3.26
 - @0x/contracts-exchange@3.2.27
 - @0x/contracts-extensions@6.2.21
 - @0x/contracts-integrations@2.7.29
 - @0x/contracts-multisig@4.1.27
 - @0x/contracts-staking@2.0.34
 - @0x/contracts-test-utils@5.3.23
 - @0x/contracts-treasury@1.1.0
 - @0x/contracts-utils@4.7.5
 - @0x/contracts-zero-ex@0.20.0
 - @0x/asset-swapper@6.3.0
 - @0x/contract-artifacts@3.13.0
 - @0x/contract-wrappers-test@12.2.38
 - @0x/contract-wrappers@13.14.0
 - @0x/migrations@7.0.1
 - @0x/order-utils@10.4.18
 - @0x/protocol-utils@1.3.1
2021-03-17 18:23:51 +00:00
Github Actions
7caa43d02c Updated CHANGELOGS & MD docs 2021-03-17 18:23:47 +00:00
mzhu25
3d4c03c9df Update asset-swapper to support MultiplexFeature (#168)
* Update asset-swapper to support MultiplexFeature

* Address PR feedback

* Update changelogs
2021-03-16 22:20:33 -07:00
Lawrence Forman
22e1ed35d3 docs: Fix EIP712 hashing docs for limit and RFQ orders (#173) 2021-03-16 15:09:00 -04:00
Alex Kroeger
dabe6fd793 add tx origin blacklist to RFQ options (#169)
* add tx origin blacklist to RFQ options

* Fix typo

* use set instead of array

* make sure tx origin is lower case

* changed default blacklist value from array to set
2021-03-11 18:05:16 -08:00
mzhu25
3cc639c8d0 MultiplexFeature and BatchFillNativeOrdersFeature (#140)
* WrappedFillFeature

* Address internal feedback

* create features/interfaces/ directory

* Split NativeOrdersFeature into mixins

* Rename mixins to use NativeOrders namespace

* Add BatchFillNativeOrdersFeature

* Rename WrapperFillFeature => MultiplexFeature and add natspec comments

* Emit LiquidityProviderSwap event

* post-rebase fixes

* Multiplex mainnet fork tests

* lint

* Add tests for batch fill functions

* Remove market functions

* Addres PR feedback

* Remove nested _batchFill calls from _multiHopFill

* Add BatchFillIncompleteRevertError type

* Use call{value: amount}() instead of transfer(amount)

* Remove outdated comment

* Update some comments

* Add events

* Address spot-check recommendations

* Remove-top level events, add ExpiredRfqOrder event

* Update changelog

* Change ExpiredRfqOrder event

* Update IZeroEx artifact and contract wrapper
2021-03-08 15:45:49 -08:00
mzhu25
22c8e0b6db Make the proposal/quorum thresholds updatable (#165)
* Make the proposal/quorum thresholds updatable

* Test threshold updating
2021-03-04 10:56:37 -08:00
Oskar Paolini
f3ca4293bc feat: add proxy configuration to axios instance in RFQT asset-swapper (#159)
* add proxy configuration to axios instance

* add support for additional axios instance opts

* move quoteRequestorHttpClient parameter

* fix tests, build errors

* prettier

* bump axios

* bumped mock-axios-adapter version

* downgrade axios again

* re-upgrade axios 🤦

* updated yarn.lock

* updated changelog

Co-authored-by: Alex Kroeger <alexrkroeger@gmail.com>
2021-03-03 11:38:54 -08:00
Lawrence Forman
db3e076d03 update deps and publish gh action for prerelease support 2021-03-03 01:16:22 -05:00
Lawrence Forman
1a6759820a Mooniswap LP (#143)
* `@0x/asset-swapper`: Fix compiler error on `ILiquidityProvider` call
`@0x/protocol-utils`: Add VIP utils.

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

* `@0x/contracts-zero-ex`: Add `MooniswapLiquidityProvider`.
`@0x/asset-swapper`: Add Mooniswap "vip" to EP quote consumer.

* rebase and prettier

* fix linter error

* `@0x/contracts-zero-ex`: Add `MooniswapLiquidityProvider` tests.

* review feedback

* `@0x/contracts-zero-ex`: Emit `LiquidityProviderFill` events in LPs

* `@0x/asset-swapper`: Fix compilation error

* `@0x/asset-swapper`: Add EP gas overhead to Curve and Mooni LP bridge routes

* `@0x/asset-swapper`: Remove consumer gas overhead for LP VIPs

* `@0x/contracts-zero-ex`: Add more params to `LiquidityProviderFill` event

* `@0x/contracts-zero-ex`: Address review comments.

* `@0x/asset-swapper`: Update deployed Curve and Mooni LPs

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-03-02 16:50:37 -05:00
Github Actions
61c5e7b948 Publish
- @0x/contracts-integrations@2.7.28
 - @0x/asset-swapper@6.2.0
2021-03-02 00:44:13 +00:00
Github Actions
5fd78ef32f Updated CHANGELOGS & MD docs 2021-03-02 00:44:06 +00:00
Lawrence Forman
14ff9b827c @0x/asset-swapper: Drop Y and BUSD curve pools (#161) 2021-03-02 07:56:43 +10:00
Lawrence Forman
598dc2cd71 docs: update allowances blurb (#160) 2021-02-25 17:11:44 -05:00
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
230 changed files with 10647 additions and 3090 deletions

View File

@@ -7,6 +7,9 @@ on:
description: 'required CI status'
default: 'success'
required: true
prerelease:
description: 'prerelease name'
required: false
jobs:
publish:
@@ -21,7 +24,7 @@ jobs:
(echo "::error ::${{ github.ref }} does not have a successful CI status" && false)
- uses: actions/checkout@v2
with:
ref: 'development'
ref: ${{ github.ref }}
fetch-depth: 0
- uses: actions/setup-node@v1
with:
@@ -41,7 +44,9 @@ jobs:
env:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
GITHUB_TOKEN: ${{ github.token }}
PUBLISH_PRERELEASE: ${{ github.event.inputs.prerelease }}
- name: 'merge into main branch'
if: github.event.inputs.prerelease == '' # unless it's a prerelease
run: |
git checkout main && \
git merge ${{ github.ref }} && \

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.7.8 - _March 17, 2021_
* Dependencies updated
## 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.8",
"engines": {
"node": ">=6.12"
},
@@ -51,13 +51,13 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contract-wrappers": "^13.12.3",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contract-wrappers": "^13.14.0",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@types/lodash": "4.14.104",
@@ -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.26",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/order-utils": "^10.4.18",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.26 - _March 17, 2021_
* Dependencies updated
## 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.26",
"engines": {
"node": ">=6.12"
},
@@ -51,16 +51,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-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-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/sol-compiler": "^4.5.2",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/contracts-exchange": "^3.2.27",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -85,7 +85,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/order-utils": "^10.4.16",
"@0x/order-utils": "^10.4.18",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0"

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.27 - _March 17, 2021_
* Dependencies updated
## 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.27",
"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-dev-utils": "^1.3.23",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-gen": "^2.0.30",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-dev-utils": "^1.3.25",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-gen": "^2.0.32",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/order-utils": "^10.4.18",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.4.1",
@@ -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.27",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/json-schemas": "^5.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.25 - _March 17, 2021_
* Dependencies updated
## 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.25",
"engines": {
"node": ">=6.12"
},
@@ -41,13 +41,13 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/dev-utils",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/abi-gen": "^5.4.21",
"@0x/assert": "^3.0.21",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/sol-compiler": "^4.5.2",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.26 - _March 17, 2021_
* Dependencies updated
## 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.26",
"engines": {
"node": ">=6.12"
},
@@ -52,11 +52,11 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -81,7 +81,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"lodash": "^4.17.11"

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.5 - _March 17, 2021_
* Dependencies updated
## 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.5",
"engines": {
"node": ">=6.12"
},
@@ -51,12 +51,12 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"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/abi-gen": "^5.4.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",

View File

@@ -6,6 +6,7 @@ export {
WETH9Events,
WETH9DepositEventArgs,
WETH9TransferEventArgs,
WETH9WithdrawalEventArgs,
ZRXTokenContract,
DummyERC20TokenTransferEventArgs,
ERC20TokenEventArgs,

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.26 - _March 17, 2021_
* Dependencies updated
## 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.26",
"engines": {
"node": ">=6.12"
},
@@ -52,12 +52,12 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"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/abi-gen": "^5.4.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.2.27 - _March 17, 2021_
* Dependencies updated
## 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.27",
"engines": {
"node": ">=6.12"
},
@@ -52,20 +52,20 @@
},
"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-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-dev-utils": "^1.3.25",
"@0x/contracts-erc1155": "^2.1.26",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/contracts-exchange": "^3.2.27",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/order-utils": "^10.4.18",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.3.26 - _March 17, 2021_
* Dependencies updated
## 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.26",
"engines": {
"node": ">=6.12"
},
@@ -52,10 +52,10 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/libs",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
@@ -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.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/order-utils": "^10.4.18",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.27 - _March 17, 2021_
* Dependencies updated
## 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.27",
"engines": {
"node": ">=6.12"
},
@@ -52,16 +52,16 @@
},
"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-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/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-multisig": "^4.1.27",
"@0x/contracts-staking": "^2.0.34",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -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.25",
"@0x/contracts-erc1155": "^2.1.26",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/order-utils": "^10.4.18",
"@0x/utils": "^6.2.0",
"lodash": "^4.17.11"
},

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.2.21 - _March 17, 2021_
* Dependencies updated
## 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.21",
"engines": {
"node": ">=6.12"
},
@@ -52,18 +52,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-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-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-dev-utils": "^1.3.25",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/contracts-exchange": "^3.2.27",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/order-utils": "^10.4.18",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -91,7 +91,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.23",
"@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.29",
"private": true,
"engines": {
"node": ">=6.12"
@@ -52,23 +52,23 @@
},
"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/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contract-addresses": "^5.11.0",
"@0x/contract-wrappers": "^13.14.0",
"@0x/contracts-broker": "^1.1.26",
"@0x/contracts-coordinator": "^3.1.27",
"@0x/contracts-dev-utils": "^1.3.25",
"@0x/contracts-exchange-forwarder": "^4.2.27",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-extensions": "^6.2.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-utils": "^4.7.5",
"@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/sol-compiler": "^4.5.2",
"@0x/migrations": "^7.0.1",
"@0x/order-utils": "^10.4.18",
"@0x/protocol-utils": "^1.3.1",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.4.1",
"@azure/core-asynciterator-polyfill": "^1.0.0",
@@ -93,17 +93,17 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/asset-swapper": "^6.0.0",
"@0x/asset-swapper": "^6.3.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.8",
"@0x/contracts-erc1155": "^2.1.26",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-erc721": "^3.1.26",
"@0x/contracts-exchange": "^3.2.27",
"@0x/contracts-multisig": "^4.1.27",
"@0x/contracts-staking": "^2.0.34",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-zero-ex": "^0.20.0",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.27 - _March 17, 2021_
* Dependencies updated
## 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.27",
"engines": {
"node": ">=6.12"
},
@@ -49,14 +49,14 @@
},
"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-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/utils": "^6.2.0",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.34 - _March 17, 2021_
* Dependencies updated
## 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.34",
"engines": {
"node": ">=6.12"
},
@@ -53,16 +53,16 @@
},
"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-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-dev-utils": "^1.3.25",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-exchange-libs": "^4.3.26",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-utils": "^4.7.5",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/order-utils": "^10.4.18",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -88,7 +88,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.3.23 - _March 17, 2021_
* Dependencies updated
## 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.23",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
"devDependencies": {
"@0x/sol-compiler": "^4.5.2",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -44,13 +44,13 @@
"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/sol-coverage": "^4.0.29",
"@0x/sol-profiler": "^4.1.19",
"@0x/sol-trace": "^3.0.29",
"@0x/order-utils": "^10.4.18",
"@0x/sol-coverage": "^4.0.31",
"@0x/sol-profiler": "^4.1.21",
"@0x/sol-trace": "^3.0.31",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",

View File

@@ -1,4 +1,23 @@
[
{
"version": "1.1.0",
"changes": [
{
"note": "Make the proposal/quorum thresholds updatable",
"pr": 165
}
],
"timestamp": 1616005394
},
{
"timestamp": 1614141718,
"version": "1.0.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1612950500,
"version": "1.0.1",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.0 - _March 17, 2021_
* Make the proposal/quorum thresholds updatable (#165)
## v1.0.2 - _February 24, 2021_
* Dependencies updated
## v1.0.1 - _February 10, 2021_
* Dependencies updated

View File

@@ -96,6 +96,18 @@ interface IZrxTreasury {
view
returns (uint256);
/// @dev Updates the proposal and quorum thresholds to the given
/// values. Note that this function is only callable by the
/// treasury contract itself, so the threshold can only be
/// updated via a successful treasury proposal.
/// @param newProposalThreshold The new value for the proposal threshold.
/// @param newQuorumThreshold The new value for the quorum threshold.
function updateThresholds(
uint256 newProposalThreshold,
uint256 newQuorumThreshold
)
external;
/// @dev Creates a proposal to send ZRX from this treasury on the
/// the given actions. Must have at least `proposalThreshold`
/// of voting power to call this function. See `getVotingPower`

View File

@@ -42,8 +42,8 @@ contract ZrxTreasury is
DefaultPoolOperator public immutable override defaultPoolOperator;
bytes32 public immutable override defaultPoolId;
uint256 public immutable override votingPeriod;
uint256 public immutable override proposalThreshold;
uint256 public immutable override quorumThreshold;
uint256 public override proposalThreshold;
uint256 public override quorumThreshold;
// Storage
Proposal[] public proposals;
@@ -82,6 +82,24 @@ contract ZrxTreasury is
receive() external payable {}
// solhint-enable
/// @dev Updates the proposal and quorum thresholds to the given
/// values. Note that this function is only callable by the
/// treasury contract itself, so the threshold can only be
/// updated via a successful treasury proposal.
/// @param newProposalThreshold The new value for the proposal threshold.
/// @param newQuorumThreshold The new value for the quorum threshold.
function updateThresholds(
uint256 newProposalThreshold,
uint256 newQuorumThreshold
)
external
override
{
require(msg.sender == address(this), "updateThresholds/ONLY_SELF");
proposalThreshold = newProposalThreshold;
quorumThreshold = newQuorumThreshold;
}
/// @dev Creates a proposal to send ZRX from this treasury on the
/// the given actions. Must have at least `proposalThreshold`
/// of voting power to call this function. See `getVotingPower`

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.0.1",
"version": "1.1.0",
"engines": {
"node": ">=6.12"
},
@@ -46,14 +46,14 @@
},
"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/contracts-gen": "^2.0.24",
"@0x/contracts-staking": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/sol-compiler": "^4.4.1",
"@0x/abi-gen": "^5.4.21",
"@0x/contract-addresses": "^5.11.0",
"@0x/contracts-asset-proxy": "^3.7.8",
"@0x/contracts-erc20": "^3.3.5",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-staking": "^2.0.34",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/sol-compiler": "^4.6.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@types/isomorphic-fetch": "^0.0.35",
@@ -72,13 +72,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/protocol-utils": "^1.2.0",
"@0x/subproviders": "^6.2.3",
"@0x/base-contract": "^6.2.18",
"@0x/protocol-utils": "^1.3.1",
"@0x/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"ethereum-types": "^3.4.0",
"ethereumjs-util": "^5.1.1"
},

View File

@@ -580,4 +580,47 @@ blockchainTests.resets('Treasury governance', env => {
expect(await weth.balanceOf(staking.address).callAsync()).to.bignumber.equal(wethAmount);
});
});
describe('Can update thresholds via proposal', () => {
it('Updates proposal and quorum thresholds', async () => {
// Delegator has enough ZRX to create and pass a proposal
await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.quorumThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
const newProposalThreshold = new BigNumber(420);
const newQuorumThreshold = new BigNumber(1337);
const updateThresholdsAction = {
target: treasury.address,
data: treasury
.updateThresholds(newProposalThreshold, newQuorumThreshold)
.getABIEncodedTransactionData(),
value: constants.ZERO_AMOUNT,
};
const tx = treasury.propose(
[updateThresholdsAction],
currentEpoch.plus(3),
`Updates proposal threshold to ${newProposalThreshold} and quorum threshold to ${newQuorumThreshold}`,
[],
);
const proposalId = await tx.callAsync({ from: delegator });
await tx.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
await treasury.castVote(proposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
await treasury
.execute(proposalId, [updateThresholdsAction])
.awaitTransactionSuccessAsync({ from: delegator });
const proposalThreshold = await treasury.proposalThreshold().callAsync();
const quorumThreshold = await treasury.quorumThreshold().callAsync();
expect(proposalThreshold).to.bignumber.equal(newProposalThreshold);
expect(quorumThreshold).to.bignumber.equal(newQuorumThreshold);
});
});
});

View File

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

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.7.5 - _March 17, 2021_
* Dependencies updated
## 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.5",
"engines": {
"node": ">=6.12"
},
@@ -50,12 +50,12 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/abi-gen": "^5.4.21",
"@0x/contracts-gen": "^2.0.32",
"@0x/contracts-test-utils": "^5.3.23",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/order-utils": "^10.4.18",
"@0x/sol-compiler": "^4.6.1",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/web3-wrapper": "^7.4.1",

View File

@@ -1,4 +1,56 @@
[
{
"version": "0.20.0",
"changes": [
{
"note": "Add `MooniswapLiquidityProvider`",
"pr": 143
},
{
"note": "Emit `LiquidityProviderFill` event in `CurveLiquidityProvider`",
"pr": 143
},
{
"note": "Add BatchFillNativeOrdersFeature and MultiplexFeature",
"pr": 140
},
{
"note": "Export MultiplexFeatureContract",
"pr": 168
}
],
"timestamp": 1616005394
},
{
"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,22 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.20.0 - _March 17, 2021_
* Add `MooniswapLiquidityProvider` (#143)
* Emit `LiquidityProviderFill` event in `CurveLiquidityProvider` (#143)
* Add BatchFillNativeOrdersFeature and MultiplexFeature (#140)
* Export MultiplexFeatureContract (#168)
## 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,14 +20,16 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "./features/IOwnableFeature.sol";
import "./features/ISimpleFunctionRegistryFeature.sol";
import "./features/ITokenSpenderFeature.sol";
import "./features/ITransformERC20Feature.sol";
import "./features/IMetaTransactionsFeature.sol";
import "./features/IUniswapFeature.sol";
import "./features/ILiquidityProviderFeature.sol";
import "./features/INativeOrdersFeature.sol";
import "./features/interfaces/IOwnableFeature.sol";
import "./features/interfaces/ISimpleFunctionRegistryFeature.sol";
import "./features/interfaces/ITokenSpenderFeature.sol";
import "./features/interfaces/ITransformERC20Feature.sol";
import "./features/interfaces/IMetaTransactionsFeature.sol";
import "./features/interfaces/IUniswapFeature.sol";
import "./features/interfaces/ILiquidityProviderFeature.sol";
import "./features/interfaces/INativeOrdersFeature.sol";
import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
import "./features/interfaces/IMultiplexFeature.sol";
/// @dev Interface for a fully featured Exchange Proxy.
@@ -39,7 +41,9 @@ interface IZeroEx is
IMetaTransactionsFeature,
IUniswapFeature,
ILiquidityProviderFeature,
INativeOrdersFeature
INativeOrdersFeature,
IBatchFillNativeOrdersFeature,
IMultiplexFeature
{
// solhint-disable state-visibility

View File

@@ -170,4 +170,21 @@ library LibNativeOrdersRichErrors {
maker
);
}
function BatchFillIncompleteError(
bytes32 orderHash,
uint256 takerTokenFilledAmount,
uint256 takerTokenFillAmount
)
internal
pure
returns (bytes memory)
{
return abi.encodeWithSelector(
bytes4(keccak256("BatchFillIncompleteError(bytes32,uint256,uint256)")),
orderHash,
takerTokenFilledAmount,
takerTokenFillAmount
);
}
}

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

@@ -0,0 +1,198 @@
// 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-utils/contracts/src/v06/LibMathV06.sol";
import "../errors/LibNativeOrdersRichErrors.sol";
import "../fixins/FixinCommon.sol";
import "../fixins/FixinEIP712.sol";
import "../migrations/LibMigrate.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IBatchFillNativeOrdersFeature.sol";
import "./interfaces/INativeOrdersFeature.sol";
import "./libs/LibNativeOrder.sol";
import "./libs/LibSignature.sol";
/// @dev Feature for batch/market filling limit and RFQ orders.
contract BatchFillNativeOrdersFeature is
IFeature,
IBatchFillNativeOrdersFeature,
FixinCommon,
FixinEIP712
{
using LibSafeMathV06 for uint128;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "BatchFill";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
constructor(address zeroExAddress)
public
FixinEIP712(zeroExAddress)
{
// solhint-disable no-empty-blocks
}
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.
/// @return success `LibMigrate.SUCCESS` on success.
function migrate()
external
returns (bytes4 success)
{
_registerFeatureFunction(this.batchFillLimitOrders.selector);
_registerFeatureFunction(this.batchFillRfqOrders.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Fills multiple limit orders.
/// @param orders Array of limit orders.
/// @param signatures Array of signatures corresponding to each order.
/// @param takerTokenFillAmounts Array of desired amounts to fill each order.
/// @param revertIfIncomplete If true, reverts if this function fails to
/// fill the full fill amount for any individual order.
/// @return takerTokenFilledAmounts Array of amounts filled, in taker token.
/// @return makerTokenFilledAmounts Array of amounts filled, in maker token.
function batchFillLimitOrders(
LibNativeOrder.LimitOrder[] calldata orders,
LibSignature.Signature[] calldata signatures,
uint128[] calldata takerTokenFillAmounts,
bool revertIfIncomplete
)
external
payable
override
returns (
uint128[] memory takerTokenFilledAmounts,
uint128[] memory makerTokenFilledAmounts
)
{
require(
orders.length == signatures.length && orders.length == takerTokenFillAmounts.length,
'BatchFillNativeOrdersFeature::batchFillLimitOrders/MISMATCHED_ARRAY_LENGTHS'
);
takerTokenFilledAmounts = new uint128[](orders.length);
makerTokenFilledAmounts = new uint128[](orders.length);
uint256 protocolFee = uint256(INativeOrdersFeature(address(this)).getProtocolFeeMultiplier())
.safeMul(tx.gasprice);
uint256 ethProtocolFeePaid;
for (uint256 i = 0; i != orders.length; i++) {
try
INativeOrdersFeature(address(this))._fillLimitOrder
(
orders[i],
signatures[i],
takerTokenFillAmounts[i],
msg.sender,
msg.sender
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
// Update amounts filled.
(takerTokenFilledAmounts[i], makerTokenFilledAmounts[i]) =
(takerTokenFilledAmount, makerTokenFilledAmount);
ethProtocolFeePaid = ethProtocolFeePaid.safeAdd(protocolFee);
} catch {}
if (
revertIfIncomplete &&
takerTokenFilledAmounts[i] < takerTokenFillAmounts[i]
) {
bytes32 orderHash = _getEIP712Hash(
LibNativeOrder.getLimitOrderStructHash(orders[i])
);
// Did not fill the amount requested.
LibNativeOrdersRichErrors.BatchFillIncompleteError(
orderHash,
takerTokenFilledAmounts[i],
takerTokenFillAmounts[i]
).rrevert();
}
}
LibNativeOrder.refundExcessProtocolFeeToSender(ethProtocolFeePaid);
}
/// @dev Fills multiple RFQ orders.
/// @param orders Array of RFQ orders.
/// @param signatures Array of signatures corresponding to each order.
/// @param takerTokenFillAmounts Array of desired amounts to fill each order.
/// @param revertIfIncomplete If true, reverts if this function fails to
/// fill the full fill amount for any individual order.
/// @return takerTokenFilledAmounts Array of amounts filled, in taker token.
/// @return makerTokenFilledAmounts Array of amounts filled, in maker token.
function batchFillRfqOrders(
LibNativeOrder.RfqOrder[] calldata orders,
LibSignature.Signature[] calldata signatures,
uint128[] calldata takerTokenFillAmounts,
bool revertIfIncomplete
)
external
override
returns (
uint128[] memory takerTokenFilledAmounts,
uint128[] memory makerTokenFilledAmounts
)
{
require(
orders.length == signatures.length && orders.length == takerTokenFillAmounts.length,
'BatchFillNativeOrdersFeature::batchFillRfqOrders/MISMATCHED_ARRAY_LENGTHS'
);
takerTokenFilledAmounts = new uint128[](orders.length);
makerTokenFilledAmounts = new uint128[](orders.length);
for (uint256 i = 0; i != orders.length; i++) {
try
INativeOrdersFeature(address(this))._fillRfqOrder
(
orders[i],
signatures[i],
takerTokenFillAmounts[i],
msg.sender
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
// Update amounts filled.
(takerTokenFilledAmounts[i], makerTokenFilledAmounts[i]) =
(takerTokenFilledAmount, makerTokenFilledAmount);
} catch {}
if (
revertIfIncomplete &&
takerTokenFilledAmounts[i] < takerTokenFillAmounts[i]
) {
// Did not fill the amount requested.
bytes32 orderHash = _getEIP712Hash(
LibNativeOrder.getRfqOrderStructHash(orders[i])
);
LibNativeOrdersRichErrors.BatchFillIncompleteError(
orderHash,
takerTokenFilledAmounts[i],
takerTokenFillAmounts[i]
).rrevert();
}
}
}
}

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "../migrations/LibBootstrap.sol";
import "../storage/LibProxyStorage.sol";
import "./IBootstrapFeature.sol";
import "./interfaces/IBootstrapFeature.sol";
/// @dev Detachable `bootstrap()` feature.

View File

@@ -23,14 +23,16 @@ 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 "./IFeature.sol";
import "./ILiquidityProviderFeature.sol";
import "../transformers/LibERC20Transformer.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/ILiquidityProviderFeature.sol";
contract LiquidityProviderFeature is
@@ -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

@@ -30,11 +30,11 @@ import "../fixins/FixinTokenSpender.sol";
import "../fixins/FixinEIP712.sol";
import "../migrations/LibMigrate.sol";
import "../storage/LibMetaTransactionsStorage.sol";
import "./IMetaTransactionsFeature.sol";
import "./ITransformERC20Feature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IMetaTransactionsFeature.sol";
import "./interfaces/INativeOrdersFeature.sol";
import "./interfaces/ITransformERC20Feature.sol";
import "./libs/LibSignature.sol";
import "./IFeature.sol";
import "./INativeOrdersFeature.sol";
/// @dev MetaTransactions feature.
contract MetaTransactionsFeature is

View File

@@ -0,0 +1,805 @@
// 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 "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../external/ILiquidityProviderSandbox.sol";
import "../fixins/FixinCommon.sol";
import "../fixins/FixinEIP712.sol";
import "../fixins/FixinTokenSpender.sol";
import "../migrations/LibMigrate.sol";
import "../transformers/LibERC20Transformer.sol";
import "../vendor/ILiquidityProvider.sol";
import "../vendor/IUniswapV2Pair.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IMultiplexFeature.sol";
import "./interfaces/INativeOrdersFeature.sol";
import "./interfaces/ITransformERC20Feature.sol";
import "./libs/LibNativeOrder.sol";
/// @dev This feature enables efficient batch and multi-hop trades
/// using different liquidity sources.
contract MultiplexFeature is
IFeature,
IMultiplexFeature,
FixinCommon,
FixinEIP712,
FixinTokenSpender
{
using LibERC20Transformer for IERC20TokenV06;
using LibSafeMathV06 for uint128;
using LibSafeMathV06 for uint256;
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "MultiplexFeature";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
/// @dev The WETH token contract.
IEtherTokenV06 private immutable weth;
/// @dev The sandbox contract address.
ILiquidityProviderSandbox public immutable sandbox;
// address of the UniswapV2Factory contract.
address private constant UNISWAP_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
// address of the (Sushiswap) UniswapV2Factory contract.
address private constant SUSHISWAP_FACTORY = 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
// Init code hash of the UniswapV2Pair contract.
uint256 private constant UNISWAP_PAIR_INIT_CODE_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;
// Init code hash of the (Sushiswap) UniswapV2Pair contract.
uint256 private constant SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;
constructor(
address zeroExAddress,
IEtherTokenV06 weth_,
ILiquidityProviderSandbox sandbox_,
bytes32 greedyTokensBloomFilter
)
public
FixinEIP712(zeroExAddress)
FixinTokenSpender(greedyTokensBloomFilter)
{
weth = weth_;
sandbox = sandbox_;
}
/// @dev Initialize and register this feature.
/// Should be delegatecalled by `Migrate.migrate()`.
/// @return success `LibMigrate.SUCCESS` on success.
function migrate()
external
returns (bytes4 success)
{
_registerFeatureFunction(this.batchFill.selector);
_registerFeatureFunction(this.multiHopFill.selector);
return LibMigrate.MIGRATE_SUCCESS;
}
/// @dev Executes a batch of fills selling `fillData.inputToken`
/// for `fillData.outputToken` in sequence. Refer to the
/// internal variant `_batchFill` for the allowed nested
/// operations.
/// @param fillData Encodes the input/output tokens, the sell
/// amount, and the nested operations for this batch fill.
/// @param minBuyAmount The minimum amount of `fillData.outputToken`
/// to buy. Reverts if this amount is not met.
/// @return outputTokenAmount The amount of the output token bought.
function batchFill(
BatchFillData memory fillData,
uint256 minBuyAmount
)
public
payable
override
returns (uint256 outputTokenAmount)
{
// Cache the sender's balance of the output token.
outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender);
// Cache the contract's ETH balance prior to this call.
uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value);
// Perform the batch fill.
_batchFill(fillData);
// The `outputTokenAmount` returned by `_batchFill` may not
// be fully accurate (e.g. due to some janky token).
outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender)
.safeSub(outputTokenAmount);
require(
outputTokenAmount >= minBuyAmount,
"MultiplexFeature::batchFill/UNDERBOUGHT"
);
uint256 ethBalanceAfter = address(this).balance;
require(
ethBalanceAfter >= ethBalanceBefore,
"MultiplexFeature::batchFill/OVERSPENT_ETH"
);
// Refund ETH
if (ethBalanceAfter > ethBalanceBefore) {
_transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore);
}
}
/// @dev Executes a sequence of fills "hopping" through the
/// path of tokens given by `fillData.tokens`. Refer to the
/// internal variant `_multiHopFill` for the allowed nested
/// operations.
/// @param fillData Encodes the path of tokens, the sell amount,
/// and the nested operations for this multi-hop fill.
/// @param minBuyAmount The minimum amount of the output token
/// to buy. Reverts if this amount is not met.
/// @return outputTokenAmount The amount of the output token bought.
function multiHopFill(
MultiHopFillData memory fillData,
uint256 minBuyAmount
)
public
payable
override
returns (uint256 outputTokenAmount)
{
IERC20TokenV06 outputToken = IERC20TokenV06(fillData.tokens[fillData.tokens.length - 1]);
// Cache the sender's balance of the output token.
outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender);
// Cache the contract's ETH balance prior to this call.
uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value);
// Perform the multi-hop fill. Pass in `msg.value` as the maximum
// allowable amount of ETH for the wrapped calls to consume.
_multiHopFill(fillData, msg.value);
// The `outputTokenAmount` returned by `_multiHopFill` may not
// be fully accurate (e.g. due to some janky token).
outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender)
.safeSub(outputTokenAmount);
require(
outputTokenAmount >= minBuyAmount,
"MultiplexFeature::multiHopFill/UNDERBOUGHT"
);
uint256 ethBalanceAfter = address(this).balance;
require(
ethBalanceAfter >= ethBalanceBefore,
"MultiplexFeature::multiHopFill/OVERSPENT_ETH"
);
// Refund ETH
if (ethBalanceAfter > ethBalanceBefore) {
_transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore);
}
}
// Similar to FQT. If `fillData.sellAmount` is set to `type(uint256).max`,
// this is effectively a batch fill. Otherwise it can be set to perform a
// market sell of some amount. Note that the `outputTokenAmount` returned
// by this function could theoretically be inaccurate if `msg.sender` has
// set a token allowance on an external contract that gets called during
// the execution of this function.
function _batchFill(BatchFillData memory fillData)
internal
returns (uint256 outputTokenAmount, uint256 remainingEth)
{
// Track the remaining ETH allocated to this call.
remainingEth = msg.value;
// Track the amount of input token sold.
uint256 soldAmount;
for (uint256 i = 0; i != fillData.calls.length; i++) {
// Check if we've hit our target.
if (soldAmount >= fillData.sellAmount) { break; }
WrappedBatchCall memory wrappedCall = fillData.calls[i];
// Compute the fill amount.
uint256 inputTokenAmount = LibSafeMathV06.min256(
wrappedCall.sellAmount,
fillData.sellAmount.safeSub(soldAmount)
);
if (wrappedCall.selector == INativeOrdersFeature._fillRfqOrder.selector) {
// Decode the RFQ order and signature.
(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature
) = abi.decode(
wrappedCall.data,
(LibNativeOrder.RfqOrder, LibSignature.Signature)
);
if (order.expiry <= uint64(block.timestamp)) {
bytes32 orderHash = _getEIP712Hash(
LibNativeOrder.getRfqOrderStructHash(order)
);
emit ExpiredRfqOrder(
orderHash,
order.maker,
order.expiry
);
continue;
}
require(
order.takerToken == fillData.inputToken &&
order.makerToken == fillData.outputToken,
"MultiplexFeature::_batchFill/RFQ_ORDER_INVALID_TOKENS"
);
// Try filling the RFQ order. Swallows reverts.
try
INativeOrdersFeature(address(this))._fillRfqOrder
(
order,
signature,
inputTokenAmount.safeDowncastToUint128(),
msg.sender
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
// Increment the sold and bought amounts.
soldAmount = soldAmount.safeAdd(takerTokenFilledAmount);
outputTokenAmount = outputTokenAmount.safeAdd(makerTokenFilledAmount);
} catch {}
} else if (wrappedCall.selector == this._sellToUniswap.selector) {
(address[] memory tokens, bool isSushi) = abi.decode(
wrappedCall.data,
(address[], bool)
);
require(
tokens.length >= 2 &&
tokens[0] == address(fillData.inputToken) &&
tokens[tokens.length - 1] == address(fillData.outputToken),
"MultiplexFeature::_batchFill/UNISWAP_INVALID_TOKENS"
);
// Perform the Uniswap/Sushiswap trade.
uint256 outputTokenAmount_ = _sellToUniswap(
tokens,
inputTokenAmount,
isSushi,
address(0),
msg.sender
);
// Increment the sold and bought amounts.
soldAmount = soldAmount.safeAdd(inputTokenAmount);
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
} else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) {
(address provider, bytes memory auxiliaryData) = abi.decode(
wrappedCall.data,
(address, bytes)
);
if (fillData.inputToken.isTokenETH()) {
inputTokenAmount = LibSafeMathV06.min256(
inputTokenAmount,
remainingEth
);
// Transfer the input ETH to the provider.
_transferEth(payable(provider), inputTokenAmount);
// Count that ETH as spent.
remainingEth -= inputTokenAmount;
} else {
// Transfer input ERC20 tokens to the provider.
_transferERC20Tokens(
fillData.inputToken,
msg.sender,
provider,
inputTokenAmount
);
}
// Perform the PLP trade.
uint256 outputTokenAmount_ = _sellToLiquidityProvider(
fillData.inputToken,
fillData.outputToken,
inputTokenAmount,
ILiquidityProvider(provider),
msg.sender,
auxiliaryData
);
// Increment the sold and bought amounts.
soldAmount = soldAmount.safeAdd(inputTokenAmount);
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
ITransformERC20Feature.TransformERC20Args memory args;
args.taker = msg.sender;
args.inputToken = fillData.inputToken;
args.outputToken = fillData.outputToken;
args.inputTokenAmount = inputTokenAmount;
args.minOutputTokenAmount = 0;
uint256 ethValue;
(args.transformations, ethValue) = abi.decode(
wrappedCall.data,
(ITransformERC20Feature.Transformation[], uint256)
);
// Do not spend more than the remaining ETH.
ethValue = LibSafeMathV06.min256(
ethValue,
remainingEth
);
if (ethValue > 0) {
require(
args.inputToken.isTokenETH(),
"MultiplexFeature::_batchFill/ETH_TRANSFORM_ONLY"
);
}
try ITransformERC20Feature(address(this))._transformERC20
{value: ethValue}
(args)
returns (uint256 outputTokenAmount_)
{
remainingEth -= ethValue;
soldAmount = soldAmount.safeAdd(inputTokenAmount);
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
} catch {}
} else if (wrappedCall.selector == this._multiHopFill.selector) {
MultiHopFillData memory multiHopFillData;
uint256 ethValue;
(
multiHopFillData.tokens,
multiHopFillData.calls,
ethValue
) = abi.decode(
wrappedCall.data,
(address[], WrappedMultiHopCall[], uint256)
);
multiHopFillData.sellAmount = inputTokenAmount;
// Do not spend more than the remaining ETH.
ethValue = LibSafeMathV06.min256(
ethValue,
remainingEth
);
// Subtract the ethValue allocated to the nested multi-hop fill.
remainingEth -= ethValue;
(uint256 outputTokenAmount_, uint256 leftoverEth) =
_multiHopFill(multiHopFillData, ethValue);
// Increment the sold and bought amounts.
soldAmount = soldAmount.safeAdd(inputTokenAmount);
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
// Add back any ETH that wasn't used by the nested multi-hop fill.
remainingEth += leftoverEth;
} else {
revert("MultiplexFeature::_batchFill/UNRECOGNIZED_SELECTOR");
}
}
}
// Internal variant of `multiHopFill`. This function can be nested within
// a `_batchFill`.
// This function executes a sequence of fills "hopping" through the
// path of tokens given by `fillData.tokens`. The nested operations that
// can be used as "hops" are:
// - WETH.deposit (wraps ETH)
// - WETH.withdraw (unwraps WETH)
// - _sellToUniswap (executes a Uniswap/Sushiswap swap)
// - _sellToLiquidityProvider (executes a PLP swap)
// - _transformERC20 (executes arbitrary ERC20 Transformations)
// This function optimizes the number of ERC20 transfers performed
// by having each hop transfer its output tokens directly to the
// target address of the next hop. Note that the `outputTokenAmount` returned
// by this function could theoretically be inaccurate if `msg.sender` has
// set a token allowance on an external contract that gets called during
// the execution of this function.
function _multiHopFill(MultiHopFillData memory fillData, uint256 totalEth)
public
returns (uint256 outputTokenAmount, uint256 remainingEth)
{
// There should be one call/hop between every two tokens
// in the path.
// tokens[0]calls[0]>tokens[1]...calls[n-1]>tokens[n]
require(
fillData.tokens.length == fillData.calls.length + 1,
"MultiplexFeature::_multiHopFill/MISMATCHED_ARRAY_LENGTHS"
);
// Track the remaining ETH allocated to this call.
remainingEth = totalEth;
// This variable is used as the input and output amounts of
// each hop. After the final hop, this will contain the output
// amount of the multi-hop fill.
outputTokenAmount = fillData.sellAmount;
// This variable is used to cache the address to target in the
// next hop. See `_computeHopRecipient` for details.
address nextTarget;
for (uint256 i = 0; i != fillData.calls.length; i++) {
WrappedMultiHopCall memory wrappedCall = fillData.calls[i];
if (wrappedCall.selector == this._sellToUniswap.selector) {
// If the next hop supports a "transfer then execute" pattern,
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
// for details.
address recipient = _computeHopRecipient(fillData.calls, i);
(address[] memory tokens, bool isSushi) = abi.decode(
wrappedCall.data,
(address[], bool)
);
// Perform the Uniswap/Sushiswap trade.
outputTokenAmount = _sellToUniswap(
tokens,
outputTokenAmount,
isSushi,
nextTarget,
recipient
);
// If the recipient was not `msg.sender`, it must be the target
// contract for the next hop.
nextTarget = recipient == msg.sender ? address(0) : recipient;
} else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) {
// If the next hop supports a "transfer then execute" pattern,
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
// for details.
address recipient = _computeHopRecipient(fillData.calls, i);
// If `nextTarget` was not set in the previous hop, then we
// need to send in the input ETH/tokens to the liquidity provider
// contract before executing the trade.
if (nextTarget == address(0)) {
(address provider, bytes memory auxiliaryData) = abi.decode(
wrappedCall.data,
(address, bytes)
);
// Transfer input ETH or ERC20 tokens to the liquidity
// provider contract.
if (IERC20TokenV06(fillData.tokens[i]).isTokenETH()) {
outputTokenAmount = LibSafeMathV06.min256(
outputTokenAmount,
remainingEth
);
_transferEth(payable(provider), outputTokenAmount);
remainingEth -= outputTokenAmount;
} else {
_transferERC20Tokens(
IERC20TokenV06(fillData.tokens[i]),
msg.sender,
provider,
outputTokenAmount
);
}
outputTokenAmount = _sellToLiquidityProvider(
IERC20TokenV06(fillData.tokens[i]),
IERC20TokenV06(fillData.tokens[i + 1]),
outputTokenAmount,
ILiquidityProvider(provider),
recipient,
auxiliaryData
);
} else {
(, bytes memory auxiliaryData) = abi.decode(
wrappedCall.data,
(address, bytes)
);
// Tokens and ETH have already been transferred to
// the liquidity provider contract in the previous hop.
outputTokenAmount = _sellToLiquidityProvider(
IERC20TokenV06(fillData.tokens[i]),
IERC20TokenV06(fillData.tokens[i + 1]),
outputTokenAmount,
ILiquidityProvider(nextTarget),
recipient,
auxiliaryData
);
}
// If the recipient was not `msg.sender`, it must be the target
// contract for the next hop.
nextTarget = recipient == msg.sender ? address(0) : recipient;
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
ITransformERC20Feature.TransformERC20Args memory args;
args.inputToken = IERC20TokenV06(fillData.tokens[i]);
args.outputToken = IERC20TokenV06(fillData.tokens[i + 1]);
args.minOutputTokenAmount = 0;
args.taker = payable(_computeHopRecipient(fillData.calls, i));
if (nextTarget != address(0)) {
// If `nextTarget` was set in the previous hop, then the input
// token was already sent to the FlashWallet. Setting
// `inputTokenAmount` to 0 indicates that no tokens need to
// be pulled into the FlashWallet before executing the
// transformations.
args.inputTokenAmount = 0;
} else if (
args.taker != msg.sender &&
!args.inputToken.isTokenETH()
) {
address flashWallet = address(
ITransformERC20Feature(address(this)).getTransformWallet()
);
// The input token has _not_ already been sent to the
// FlashWallet. We also want PayTakerTransformer to
// send the output token to some address other than
// msg.sender, so we must transfer the input token
// to the FlashWallet here.
_transferERC20Tokens(
args.inputToken,
msg.sender,
flashWallet,
outputTokenAmount
);
args.inputTokenAmount = 0;
} else {
// Otherwise, either:
// (1) args.taker == msg.sender, in which case
// `_transformERC20` will pull the input token
// into the FlashWallet, or
// (2) args.inputToken == ETH_TOKEN_ADDRESS, in which
// case ETH is attached to the call and no token
// transfer occurs.
args.inputTokenAmount = outputTokenAmount;
}
uint256 ethValue;
(args.transformations, ethValue) = abi.decode(
wrappedCall.data,
(ITransformERC20Feature.Transformation[], uint256)
);
// Do not spend more than the remaining ETH.
ethValue = LibSafeMathV06.min256(ethValue, remainingEth);
if (ethValue > 0) {
require(
args.inputToken.isTokenETH(),
"MultiplexFeature::_multiHopFill/ETH_TRANSFORM_ONLY"
);
}
// Call `_transformERC20`.
outputTokenAmount = ITransformERC20Feature(address(this))
._transformERC20{value: ethValue}(args);
// Decrement the remaining ETH.
remainingEth -= ethValue;
// If the recipient was not `msg.sender`, it must be the target
// contract for the next hop.
nextTarget = args.taker == msg.sender ? address(0) : args.taker;
} else if (wrappedCall.selector == IEtherTokenV06.deposit.selector) {
require(
i == 0,
"MultiplexFeature::_multiHopFill/DEPOSIT_FIRST_HOP_ONLY"
);
uint256 ethValue = LibSafeMathV06.min256(outputTokenAmount, remainingEth);
// Wrap ETH.
weth.deposit{value: ethValue}();
nextTarget = _computeHopRecipient(fillData.calls, i);
weth.transfer(nextTarget, ethValue);
remainingEth -= ethValue;
} else if (wrappedCall.selector == IEtherTokenV06.withdraw.selector) {
require(
i == fillData.calls.length - 1,
"MultiplexFeature::_multiHopFill/WITHDRAW_LAST_HOP_ONLY"
);
// Unwrap WETH and send to `msg.sender`.
weth.withdraw(outputTokenAmount);
_transferEth(msg.sender, outputTokenAmount);
nextTarget = address(0);
} else {
revert("MultiplexFeature::_multiHopFill/UNRECOGNIZED_SELECTOR");
}
}
}
// Similar to the UniswapFeature, but with a couple of differences:
// - Does not perform the transfer in if `pairAddress` is given,
// which indicates that the transfer in was already performed
// in the previous hop of a multi-hop fill.
// - Does not include a minBuyAmount check (which is performed in
// either `batchFill` or `multiHopFill`).
// - Takes a `recipient` address parameter, so the output of the
// final `swap` call can be sent to an address other than `msg.sender`.
function _sellToUniswap(
address[] memory tokens,
uint256 sellAmount,
bool isSushi,
address pairAddress,
address recipient
)
public
returns (uint256 outputTokenAmount)
{
require(tokens.length > 1, "MultiplexFeature::_sellToUniswap/InvalidTokensLength");
if (pairAddress == address(0)) {
pairAddress = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
_transferERC20Tokens(
IERC20TokenV06(tokens[0]),
msg.sender,
pairAddress,
sellAmount
);
}
for (uint256 i = 0; i < tokens.length - 1; i++) {
(address inputToken, address outputToken) = (tokens[i], tokens[i + 1]);
outputTokenAmount = _computeUniswapOutputAmount(
pairAddress,
inputToken,
outputToken,
sellAmount
);
(uint256 amount0Out, uint256 amount1Out) = inputToken < outputToken
? (uint256(0), outputTokenAmount)
: (outputTokenAmount, uint256(0));
address to = i < tokens.length - 2
? _computeUniswapPairAddress(outputToken, tokens[i + 2], isSushi)
: recipient;
IUniswapV2Pair(pairAddress).swap(
amount0Out,
amount1Out,
to,
new bytes(0)
);
pairAddress = to;
sellAmount = outputTokenAmount;
}
}
// Same as the LiquidityProviderFeature, but without the transfer in
// (which is potentially done in the previous hop of a multi-hop fill)
// and without the minBuyAmount check (which is performed at the top, i.e.
// in either `batchFill` or `multiHopFill`).
function _sellToLiquidityProvider(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 inputTokenAmount,
ILiquidityProvider provider,
address recipient,
bytes memory auxiliaryData
)
public
returns (uint256 outputTokenAmount)
{
uint256 balanceBefore = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient);
if (IERC20TokenV06(inputToken).isTokenETH()) {
sandbox.executeSellEthForToken(
provider,
outputToken,
recipient,
0,
auxiliaryData
);
} else if (IERC20TokenV06(outputToken).isTokenETH()) {
sandbox.executeSellTokenForEth(
provider,
inputToken,
recipient,
0,
auxiliaryData
);
} else {
sandbox.executeSellTokenForToken(
provider,
inputToken,
outputToken,
recipient,
0,
auxiliaryData
);
}
outputTokenAmount = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient)
.safeSub(balanceBefore);
emit LiquidityProviderSwap(
address(inputToken),
address(outputToken),
inputTokenAmount,
outputTokenAmount,
address(provider),
recipient
);
return outputTokenAmount;
}
function _transferEth(address payable recipient, uint256 amount)
private
{
(bool success,) = recipient.call{value: amount}("");
require(success, "MultiplexFeature::_transferEth/TRANSFER_FALIED");
}
// Some liquidity sources (e.g. Uniswap, Sushiswap, and PLP) can be passed
// a `recipient` parameter so the boguht tokens are transferred to the
// `recipient` address rather than `msg.sender`.
// Some liquidity sources (also Uniswap, Sushiswap, and PLP incidentally)
// support a "transfer then execute" pattern, where the token being sold
// can be transferred into the contract before calling a swap function to
// execute the trade.
// If the current hop in a multi-hop fill satisfies the first condition,
// and the next hop satisfies the second condition, the tokens bought
// in the current hop can be directly sent to the target contract of
// the next hop to save a transfer.
function _computeHopRecipient(
WrappedMultiHopCall[] memory calls,
uint256 i
)
private
view
returns (address recipient)
{
recipient = msg.sender;
if (i < calls.length - 1) {
WrappedMultiHopCall memory nextCall = calls[i + 1];
if (nextCall.selector == this._sellToUniswap.selector) {
(address[] memory tokens, bool isSushi) = abi.decode(
nextCall.data,
(address[], bool)
);
recipient = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
} else if (nextCall.selector == this._sellToLiquidityProvider.selector) {
(recipient,) = abi.decode(
nextCall.data,
(address, bytes)
);
} else if (nextCall.selector == IEtherTokenV06.withdraw.selector) {
recipient = address(this);
} else if (nextCall.selector == ITransformERC20Feature._transformERC20.selector) {
recipient = address(
ITransformERC20Feature(address(this)).getTransformWallet()
);
}
}
require(
recipient != address(0),
"MultiplexFeature::_computeHopRecipient/RECIPIENT_IS_NULL"
);
}
// Computes the the amount of output token that would be bought
// from Uniswap/Sushiswap given the input amount.
function _computeUniswapOutputAmount(
address pairAddress,
address inputToken,
address outputToken,
uint256 inputAmount
)
private
view
returns (uint256 outputAmount)
{
require(
inputAmount > 0,
"MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_INPUT_AMOUNT"
);
(uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairAddress).getReserves();
require(
reserve0 > 0 && reserve1 > 0,
'MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_LIQUIDITY'
);
(uint256 inputReserve, uint256 outputReserve) = inputToken < outputToken
? (reserve0, reserve1)
: (reserve1, reserve0);
uint256 inputAmountWithFee = inputAmount.safeMul(997);
uint256 numerator = inputAmountWithFee.safeMul(outputReserve);
uint256 denominator = inputReserve.safeMul(1000).safeAdd(inputAmountWithFee);
return numerator / denominator;
}
// Computes the Uniswap/Sushiswap pair contract address for the
// given tokens.
function _computeUniswapPairAddress(
address tokenA,
address tokenB,
bool isSushi
)
private
pure
returns (address pairAddress)
{
(address token0, address token1) = tokenA < tokenB
? (tokenA, tokenB)
: (tokenB, tokenA);
if (isSushi) {
return address(uint256(keccak256(abi.encodePacked(
hex'ff',
SUSHISWAP_FACTORY,
keccak256(abi.encodePacked(token0, token1)),
SUSHISWAP_PAIR_INIT_CODE_HASH
))));
} else {
return address(uint256(keccak256(abi.encodePacked(
hex'ff',
UNISWAP_FACTORY,
keccak256(abi.encodePacked(token0, token1)),
UNISWAP_PAIR_INIT_CODE_HASH
))));
}
}
}

View File

@@ -26,8 +26,8 @@ import "../errors/LibOwnableRichErrors.sol";
import "../storage/LibOwnableStorage.sol";
import "../migrations/LibBootstrap.sol";
import "../migrations/LibMigrate.sol";
import "./IFeature.sol";
import "./IOwnableFeature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IOwnableFeature.sol";
import "./SimpleFunctionRegistryFeature.sol";

View File

@@ -26,8 +26,8 @@ import "../storage/LibProxyStorage.sol";
import "../storage/LibSimpleFunctionRegistryStorage.sol";
import "../errors/LibSimpleFunctionRegistryRichErrors.sol";
import "../migrations/LibBootstrap.sol";
import "./IFeature.sol";
import "./ISimpleFunctionRegistryFeature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/ISimpleFunctionRegistryFeature.sol";
/// @dev Basic registry management features.

View File

@@ -29,8 +29,8 @@ import "../fixins/FixinCommon.sol";
import "../migrations/LibMigrate.sol";
import "../external/IAllowanceTarget.sol";
import "../storage/LibTokenSpenderStorage.sol";
import "./ITokenSpenderFeature.sol";
import "./IFeature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/ITokenSpenderFeature.sol";
/// @dev Feature that allows spending token allowances.

View File

@@ -33,8 +33,8 @@ import "../external/FlashWallet.sol";
import "../storage/LibTransformERC20Storage.sol";
import "../transformers/IERC20Transformer.sol";
import "../transformers/LibERC20Transformer.sol";
import "./ITransformERC20Feature.sol";
import "./IFeature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/ITransformERC20Feature.sol";
/// @dev Feature to composably transform between ERC20 tokens.
@@ -313,7 +313,7 @@ contract TransformERC20Feature is
to.transfer(msg.value);
}
// Transfer input tokens.
if (!LibERC20Transformer.isTokenETH(inputToken)) {
if (!LibERC20Transformer.isTokenETH(inputToken) && amount != 0) {
// Token is not ETH, so pull ERC20 tokens.
_transferERC20Tokens(
inputToken,

View File

@@ -25,8 +25,8 @@ import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../migrations/LibMigrate.sol";
import "../external/IAllowanceTarget.sol";
import "../fixins/FixinCommon.sol";
import "./IFeature.sol";
import "./IUniswapFeature.sol";
import "./interfaces/IFeature.sol";
import "./interfaces/IUniswapFeature.sol";
/// @dev VIP uniswap fill functions.
@@ -380,7 +380,7 @@ contract UniswapFeature is
// will eat all our gas.
if isTokenPossiblyGreedy(token) {
// Check if we have enough direct allowance by calling
// `token.allowance()``
// `token.allowance()`
mstore(0xB00, ALLOWANCE_CALL_SELECTOR_32)
mstore(0xB04, caller())
mstore(0xB24, address())

View File

@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../libs/LibNativeOrder.sol";
import "../libs/LibSignature.sol";
/// @dev Feature for batch/market filling limit and RFQ orders.
interface IBatchFillNativeOrdersFeature {
/// @dev Fills multiple limit orders.
/// @param orders Array of limit orders.
/// @param signatures Array of signatures corresponding to each order.
/// @param takerTokenFillAmounts Array of desired amounts to fill each order.
/// @param revertIfIncomplete If true, reverts if this function fails to
/// fill the full fill amount for any individual order.
/// @return takerTokenFilledAmounts Array of amounts filled, in taker token.
/// @return makerTokenFilledAmounts Array of amounts filled, in maker token.
function batchFillLimitOrders(
LibNativeOrder.LimitOrder[] calldata orders,
LibSignature.Signature[] calldata signatures,
uint128[] calldata takerTokenFillAmounts,
bool revertIfIncomplete
)
external
payable
returns (
uint128[] memory takerTokenFilledAmounts,
uint128[] memory makerTokenFilledAmounts
);
/// @dev Fills multiple RFQ orders.
/// @param orders Array of RFQ orders.
/// @param signatures Array of signatures corresponding to each order.
/// @param takerTokenFillAmounts Array of desired amounts to fill each order.
/// @param revertIfIncomplete If true, reverts if this function fails to
/// fill the full fill amount for any individual order.
/// @return takerTokenFilledAmounts Array of amounts filled, in taker token.
/// @return makerTokenFilledAmounts Array of amounts filled, in maker token.
function batchFillRfqOrders(
LibNativeOrder.RfqOrder[] calldata orders,
LibSignature.Signature[] calldata signatures,
uint128[] calldata takerTokenFillAmounts,
bool revertIfIncomplete
)
external
returns (
uint128[] memory takerTokenFilledAmounts,
uint128[] memory makerTokenFilledAmounts
);
}

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

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./libs/LibSignature.sol";
import "../libs/LibSignature.sol";
/// @dev Meta-transactions feature.
interface IMetaTransactionsFeature {

View File

@@ -0,0 +1,117 @@
// 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";
interface IMultiplexFeature {
// Parameters for `batchFill`.
struct BatchFillData {
// The token being sold.
IERC20TokenV06 inputToken;
// The token being bought.
IERC20TokenV06 outputToken;
// The amount of `inputToken` to sell.
uint256 sellAmount;
// The nested calls to perform.
WrappedBatchCall[] calls;
}
// Represents a call nested within a `batchFill`.
struct WrappedBatchCall {
// The selector of the function to call.
bytes4 selector;
// Amount of `inputToken` to sell.
uint256 sellAmount;
// ABI-encoded parameters needed to perform the call.
bytes data;
}
// Parameters for `multiHopFill`.
struct MultiHopFillData {
// The sell path, i.e.
// tokens = [inputToken, hopToken1, ..., hopTokenN, outputToken]
address[] tokens;
// The amount of `tokens[0]` to sell.
uint256 sellAmount;
// The nested calls to perform.
WrappedMultiHopCall[] calls;
}
// Represents a call nested within a `multiHopFill`.
struct WrappedMultiHopCall {
// The selector of the function to call.
bytes4 selector;
// ABI-encoded parameters needed to perform the call.
bytes data;
}
event LiquidityProviderSwap(
address inputToken,
address outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
address provider,
address recipient
);
event ExpiredRfqOrder(
bytes32 orderHash,
address maker,
uint64 expiry
);
/// @dev Executes a batch of fills selling `fillData.inputToken`
/// for `fillData.outputToken` in sequence. Refer to the
/// internal variant `_batchFill` for the allowed nested
/// operations.
/// @param fillData Encodes the input/output tokens, the sell
/// amount, and the nested operations for this batch fill.
/// @param minBuyAmount The minimum amount of `fillData.outputToken`
/// to buy. Reverts if this amount is not met.
/// @return outputTokenAmount The amount of the output token bought.
function batchFill(
BatchFillData calldata fillData,
uint256 minBuyAmount
)
external
payable
returns (uint256 outputTokenAmount);
/// @dev Executes a sequence of fills "hopping" through the
/// path of tokens given by `fillData.tokens`. Refer to the
/// internal variant `_multiHopFill` for the allowed nested
/// operations.
/// @param fillData Encodes the path of tokens, the sell amount,
/// and the nested operations for this multi-hop fill.
/// @param minBuyAmount The minimum amount of the output token
/// to buy. Reverts if this amount is not met.
/// @return outputTokenAmount The amount of the output token bought.
function multiHopFill(
MultiHopFillData calldata fillData,
uint256 minBuyAmount
)
external
payable
returns (uint256 outputTokenAmount);
}

View File

@@ -0,0 +1,116 @@
// 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 "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
/// @dev Events emitted by NativeOrdersFeature.
interface INativeOrdersEvents {
/// @dev Emitted whenever a `LimitOrder` is filled.
/// @param orderHash The canonical hash of the order.
/// @param maker The maker of the order.
/// @param taker The taker of the order.
/// @param feeRecipient Fee recipient of the order.
/// @param takerTokenFilledAmount How much taker token was filled.
/// @param makerTokenFilledAmount How much maker token was filled.
/// @param protocolFeePaid How much protocol fee was paid.
/// @param pool The fee pool associated with this order.
event LimitOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address feeRecipient,
address makerToken,
address takerToken,
uint128 takerTokenFilledAmount,
uint128 makerTokenFilledAmount,
uint128 takerTokenFeeFilledAmount,
uint256 protocolFeePaid,
bytes32 pool
);
/// @dev Emitted whenever an `RfqOrder` is filled.
/// @param orderHash The canonical hash of the order.
/// @param maker The maker of the order.
/// @param taker The taker of the order.
/// @param takerTokenFilledAmount How much taker token was filled.
/// @param makerTokenFilledAmount How much maker token was filled.
/// @param pool The fee pool associated with this order.
event RfqOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address makerToken,
address takerToken,
uint128 takerTokenFilledAmount,
uint128 makerTokenFilledAmount,
bytes32 pool
);
/// @dev Emitted whenever a limit or RFQ order is cancelled.
/// @param orderHash The canonical hash of the order.
/// @param maker The order maker.
event OrderCancelled(
bytes32 orderHash,
address maker
);
/// @dev Emitted whenever Limit orders are cancelled by pair by a maker.
/// @param maker The maker of the order.
/// @param makerToken The maker token in a pair for the orders cancelled.
/// @param takerToken The taker token in a pair for the orders cancelled.
/// @param minValidSalt The new minimum valid salt an order with this pair must
/// have.
event PairCancelledLimitOrders(
address maker,
address makerToken,
address takerToken,
uint256 minValidSalt
);
/// @dev Emitted whenever RFQ orders are cancelled by pair by a maker.
/// @param maker The maker of the order.
/// @param makerToken The maker token in a pair for the orders cancelled.
/// @param takerToken The taker token in a pair for the orders cancelled.
/// @param minValidSalt The new minimum valid salt an order with this pair must
/// have.
event PairCancelledRfqOrders(
address maker,
address makerToken,
address takerToken,
uint256 minValidSalt
);
/// @dev Emitted when new addresses are allowed or disallowed to fill
/// orders with a given txOrigin.
/// @param origin The address doing the allowing.
/// @param addrs The address being allowed/disallowed.
/// @param allowed Indicates whether the address should be allowed.
event RfqOrderOriginsAllowed(
address origin,
address[] addrs,
bool allowed
);
}

View File

@@ -21,98 +21,15 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./libs/LibSignature.sol";
import "./libs/LibNativeOrder.sol";
import "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
import "./INativeOrdersEvents.sol";
/// @dev Feature for interacting with limit orders.
interface INativeOrdersFeature {
/// @dev Emitted whenever a `LimitOrder` is filled.
/// @param orderHash The canonical hash of the order.
/// @param maker The maker of the order.
/// @param taker The taker of the order.
/// @param feeRecipient Fee recipient of the order.
/// @param takerTokenFilledAmount How much taker token was filled.
/// @param makerTokenFilledAmount How much maker token was filled.
/// @param protocolFeePaid How much protocol fee was paid.
/// @param pool The fee pool associated with this order.
event LimitOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address feeRecipient,
address makerToken,
address takerToken,
uint128 takerTokenFilledAmount,
uint128 makerTokenFilledAmount,
uint128 takerTokenFeeFilledAmount,
uint256 protocolFeePaid,
bytes32 pool
);
/// @dev Emitted whenever an `RfqOrder` is filled.
/// @param orderHash The canonical hash of the order.
/// @param maker The maker of the order.
/// @param taker The taker of the order.
/// @param takerTokenFilledAmount How much taker token was filled.
/// @param makerTokenFilledAmount How much maker token was filled.
/// @param pool The fee pool associated with this order.
event RfqOrderFilled(
bytes32 orderHash,
address maker,
address taker,
address makerToken,
address takerToken,
uint128 takerTokenFilledAmount,
uint128 makerTokenFilledAmount,
bytes32 pool
);
/// @dev Emitted whenever a limit or RFQ order is cancelled.
/// @param orderHash The canonical hash of the order.
/// @param maker The order maker.
event OrderCancelled(
bytes32 orderHash,
address maker
);
/// @dev Emitted whenever Limit orders are cancelled by pair by a maker.
/// @param maker The maker of the order.
/// @param makerToken The maker token in a pair for the orders cancelled.
/// @param takerToken The taker token in a pair for the orders cancelled.
/// @param minValidSalt The new minimum valid salt an order with this pair must
/// have.
event PairCancelledLimitOrders(
address maker,
address makerToken,
address takerToken,
uint256 minValidSalt
);
/// @dev Emitted whenever RFQ orders are cancelled by pair by a maker.
/// @param maker The maker of the order.
/// @param makerToken The maker token in a pair for the orders cancelled.
/// @param takerToken The taker token in a pair for the orders cancelled.
/// @param minValidSalt The new minimum valid salt an order with this pair must
/// have.
event PairCancelledRfqOrders(
address maker,
address makerToken,
address takerToken,
uint256 minValidSalt
);
/// @dev Emitted when new addresses are allowed or disallowed to fill
/// orders with a given txOrigin.
/// @param origin The address doing the allowing.
/// @param addrs The address being allowed/disallowed.
/// @param allowed Indicates whether the address should be allowed.
event RfqOrderOriginsAllowed(
address origin,
address[] addrs,
bool allowed
);
interface INativeOrdersFeature is
INativeOrdersEvents
{
/// @dev Transfers protocol fees from the `FeeCollector` pools into
/// the staking contract.

View File

@@ -21,8 +21,8 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../transformers/IERC20Transformer.sol";
import "../external/IFlashWallet.sol";
import "../../transformers/IERC20Transformer.sol";
import "../../external/IFlashWallet.sol";
/// @dev Feature to composably transform between ERC20 tokens.

View File

@@ -21,10 +21,15 @@ pragma solidity ^0.6.5;
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 "../../errors/LibNativeOrdersRichErrors.sol";
/// @dev A library for common native order operations.
library LibNativeOrder {
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
enum OrderStatus {
INVALID,
@@ -216,4 +221,23 @@ library LibNativeOrder {
structHash := keccak256(mem, 0x160)
}
}
/// @dev Refund any leftover protocol fees in `msg.value` to `msg.sender`.
/// @param ethProtocolFeePaid How much ETH was paid in protocol fees.
function refundExcessProtocolFeeToSender(uint256 ethProtocolFeePaid)
internal
{
if (msg.value > ethProtocolFeePaid && msg.sender != address(this)) {
uint256 refundAmount = msg.value.safeSub(ethProtocolFeePaid);
(bool success,) = msg
.sender
.call{value: refundAmount}("");
if (!success) {
LibNativeOrdersRichErrors.ProtocolFeeRefundFailed(
msg.sender,
refundAmount
).rrevert();
}
}
}
}

View File

@@ -0,0 +1,266 @@
// 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 "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "../../errors/LibNativeOrdersRichErrors.sol";
import "../../storage/LibNativeOrdersStorage.sol";
import "../interfaces/INativeOrdersEvents.sol";
import "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
import "./NativeOrdersInfo.sol";
/// @dev Feature for cancelling limit and RFQ orders.
abstract contract NativeOrdersCancellation is
INativeOrdersEvents,
NativeOrdersInfo
{
using LibRichErrorsV06 for bytes;
/// @dev Highest bit of a uint256, used to flag cancelled orders.
uint256 private constant HIGH_BIT = 1 << 255;
constructor(
address zeroExAddress,
bytes32 greedyTokensBloomFilter
)
internal
NativeOrdersInfo(zeroExAddress, greedyTokensBloomFilter)
{
// solhint-disable no-empty-blocks
}
/// @dev Cancel a single limit order. The caller must be the maker.
/// Silently succeeds if the order has already been cancelled.
/// @param order The limit order.
function cancelLimitOrder(LibNativeOrder.LimitOrder memory order)
public
{
bytes32 orderHash = getLimitOrderHash(order);
if (msg.sender != order.maker) {
LibNativeOrdersRichErrors.OnlyOrderMakerAllowed(
orderHash,
msg.sender,
order.maker
).rrevert();
}
_cancelOrderHash(orderHash, order.maker);
}
/// @dev Cancel a single RFQ order. The caller must be the maker.
/// Silently succeeds if the order has already been cancelled.
/// @param order The RFQ order.
function cancelRfqOrder(LibNativeOrder.RfqOrder memory order)
public
{
bytes32 orderHash = getRfqOrderHash(order);
if (msg.sender != order.maker) {
LibNativeOrdersRichErrors.OnlyOrderMakerAllowed(
orderHash,
msg.sender,
order.maker
).rrevert();
}
_cancelOrderHash(orderHash, order.maker);
}
/// @dev Cancel multiple limit orders. The caller must be the maker.
/// Silently succeeds if the order has already been cancelled.
/// @param orders The limit orders.
function batchCancelLimitOrders(LibNativeOrder.LimitOrder[] memory orders)
public
{
for (uint256 i = 0; i < orders.length; ++i) {
cancelLimitOrder(orders[i]);
}
}
/// @dev Cancel multiple RFQ orders. The caller must be the maker.
/// Silently succeeds if the order has already been cancelled.
/// @param orders The RFQ orders.
function batchCancelRfqOrders(LibNativeOrder.RfqOrder[] memory orders)
public
{
for (uint256 i = 0; i < orders.length; ++i) {
cancelRfqOrder(orders[i]);
}
}
/// @dev Cancel all limit orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairLimitOrders(
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
public
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
uint256 oldMinValidSalt =
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
}
stor.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledLimitOrders(
msg.sender,
address(makerToken),
address(takerToken),
minValidSalt
);
}
/// @dev Cancel all limit orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairLimitOrders(
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
public
{
require(
makerTokens.length == takerTokens.length &&
makerTokens.length == minValidSalts.length,
"NativeOrdersFeature/MISMATCHED_PAIR_ORDERS_ARRAY_LENGTHS"
);
for (uint256 i = 0; i < makerTokens.length; ++i) {
cancelPairLimitOrders(
makerTokens[i],
takerTokens[i],
minValidSalts[i]
);
}
}
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerToken The maker token.
/// @param takerToken The taker token.
/// @param minValidSalt The new minimum valid salt.
function cancelPairRfqOrders(
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
uint256 minValidSalt
)
public
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
uint256 oldMinValidSalt =
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)];
// New min salt must >= the old one.
if (oldMinValidSalt > minValidSalt) {
LibNativeOrdersRichErrors.
CancelSaltTooLowError(minValidSalt, oldMinValidSalt)
.rrevert();
}
stor.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[msg.sender]
[address(makerToken)]
[address(takerToken)] = minValidSalt;
emit PairCancelledRfqOrders(
msg.sender,
address(makerToken),
address(takerToken),
minValidSalt
);
}
/// @dev Cancel all RFQ orders for a given maker and pair with a salt less
/// than the value provided. The caller must be the maker. Subsequent
/// calls to this function with the same caller and pair require the
/// new salt to be >= the old salt.
/// @param makerTokens The maker tokens.
/// @param takerTokens The taker tokens.
/// @param minValidSalts The new minimum valid salts.
function batchCancelPairRfqOrders(
IERC20TokenV06[] memory makerTokens,
IERC20TokenV06[] memory takerTokens,
uint256[] memory minValidSalts
)
public
{
require(
makerTokens.length == takerTokens.length &&
makerTokens.length == minValidSalts.length,
"NativeOrdersFeature/MISMATCHED_PAIR_ORDERS_ARRAY_LENGTHS"
);
for (uint256 i = 0; i < makerTokens.length; ++i) {
cancelPairRfqOrders(
makerTokens[i],
takerTokens[i],
minValidSalts[i]
);
}
}
/// @dev Cancel a limit or RFQ order directly by its order hash.
/// @param orderHash The order's order hash.
/// @param maker The order's maker.
function _cancelOrderHash(bytes32 orderHash, address maker)
private
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
// Set the high bit on the raw taker token fill amount to indicate
// a cancel. It's OK to cancel twice.
stor.orderHashToTakerTokenFilledAmount[orderHash] |= HIGH_BIT;
emit OrderCancelled(orderHash, maker);
}
}

View File

@@ -0,0 +1,394 @@
// 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 "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "../../fixins/FixinEIP712.sol";
import "../../fixins/FixinTokenSpender.sol";
import "../../storage/LibNativeOrdersStorage.sol";
import "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
/// @dev Feature for getting info about limit and RFQ orders.
abstract contract NativeOrdersInfo is
FixinEIP712,
FixinTokenSpender
{
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
// @dev Params for `_getActualFillableTakerTokenAmount()`.
struct GetActualFillableTakerTokenAmountParams {
address maker;
IERC20TokenV06 makerToken;
uint128 orderMakerAmount;
uint128 orderTakerAmount;
LibNativeOrder.OrderInfo orderInfo;
}
/// @dev Highest bit of a uint256, used to flag cancelled orders.
uint256 private constant HIGH_BIT = 1 << 255;
constructor(
address zeroExAddress,
bytes32 greedyTokensBloomFilter
)
internal
FixinEIP712(zeroExAddress)
FixinTokenSpender(greedyTokensBloomFilter)
{
// solhint-disable no-empty-blocks
}
/// @dev Get the order info for a limit order.
/// @param order The limit order.
/// @return orderInfo Info about the order.
function getLimitOrderInfo(LibNativeOrder.LimitOrder memory order)
public
view
returns (LibNativeOrder.OrderInfo memory orderInfo)
{
// Recover maker and compute order hash.
orderInfo.orderHash = getLimitOrderHash(order);
uint256 minValidSalt = LibNativeOrdersStorage.getStorage()
.limitOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[order.maker]
[address(order.makerToken)]
[address(order.takerToken)];
_populateCommonOrderInfoFields(
orderInfo,
order.takerAmount,
order.expiry,
order.salt,
minValidSalt
);
}
/// @dev Get the order info for an RFQ order.
/// @param order The RFQ order.
/// @return orderInfo Info about the order.
function getRfqOrderInfo(LibNativeOrder.RfqOrder memory order)
public
view
returns (LibNativeOrder.OrderInfo memory orderInfo)
{
// Recover maker and compute order hash.
orderInfo.orderHash = getRfqOrderHash(order);
uint256 minValidSalt = LibNativeOrdersStorage.getStorage()
.rfqOrdersMakerToMakerTokenToTakerTokenToMinValidOrderSalt
[order.maker]
[address(order.makerToken)]
[address(order.takerToken)];
_populateCommonOrderInfoFields(
orderInfo,
order.takerAmount,
order.expiry,
order.salt,
minValidSalt
);
// Check for missing txOrigin.
if (order.txOrigin == address(0)) {
orderInfo.status = LibNativeOrder.OrderStatus.INVALID;
}
}
/// @dev Get the canonical hash of a limit order.
/// @param order The limit order.
/// @return orderHash The order hash.
function getLimitOrderHash(LibNativeOrder.LimitOrder memory order)
public
view
returns (bytes32 orderHash)
{
return _getEIP712Hash(
LibNativeOrder.getLimitOrderStructHash(order)
);
}
/// @dev Get the canonical hash of an RFQ order.
/// @param order The RFQ order.
/// @return orderHash The order hash.
function getRfqOrderHash(LibNativeOrder.RfqOrder memory order)
public
view
returns (bytes32 orderHash)
{
return _getEIP712Hash(
LibNativeOrder.getRfqOrderStructHash(order)
);
}
/// @dev Get order info, fillable amount, and signature validity for a limit order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The limit order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getLimitOrderRelevantState(
LibNativeOrder.LimitOrder memory order,
LibSignature.Signature calldata signature
)
public
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfo = getLimitOrderInfo(order);
actualFillableTakerTokenAmount = _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams({
maker: order.maker,
makerToken: order.makerToken,
orderMakerAmount: order.makerAmount,
orderTakerAmount: order.takerAmount,
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
}
/// @dev Get order info, fillable amount, and signature validity for an RFQ order.
/// Fillable amount is determined using balances and allowances of the maker.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @return orderInfo Info about the order.
/// @return actualFillableTakerTokenAmount How much of the order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValid Whether the signature is valid.
function getRfqOrderRelevantState(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature
)
public
view
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfo = getRfqOrderInfo(order);
actualFillableTakerTokenAmount = _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams({
maker: order.maker,
makerToken: order.makerToken,
orderMakerAmount: order.makerAmount,
orderTakerAmount: order.takerAmount,
orderInfo: orderInfo
})
);
isSignatureValid = order.maker ==
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
}
/// @dev Batch version of `getLimitOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getLimitOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The limit orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetLimitOrderRelevantStates(
LibNativeOrder.LimitOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
)
{
require(
orders.length == signatures.length,
"NativeOrdersFeature/MISMATCHED_ARRAY_LENGTHS"
);
orderInfos = new LibNativeOrder.OrderInfo[](orders.length);
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
try
this.getLimitOrderRelevantState(orders[i], signatures[i])
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfos[i] = orderInfo;
actualFillableTakerTokenAmounts[i] = actualFillableTakerTokenAmount;
isSignatureValids[i] = isSignatureValid;
}
catch {}
}
}
/// @dev Batch version of `getRfqOrderRelevantState()`, without reverting.
/// Orders that would normally cause `getRfqOrderRelevantState()`
/// to revert will have empty results.
/// @param orders The RFQ orders.
/// @param signatures The order signatures.
/// @return orderInfos Info about the orders.
/// @return actualFillableTakerTokenAmounts How much of each order is fillable
/// based on maker funds, in taker tokens.
/// @return isSignatureValids Whether each signature is valid for the order.
function batchGetRfqOrderRelevantStates(
LibNativeOrder.RfqOrder[] calldata orders,
LibSignature.Signature[] calldata signatures
)
external
view
returns (
LibNativeOrder.OrderInfo[] memory orderInfos,
uint128[] memory actualFillableTakerTokenAmounts,
bool[] memory isSignatureValids
)
{
require(
orders.length == signatures.length,
"NativeOrdersFeature/MISMATCHED_ARRAY_LENGTHS"
);
orderInfos = new LibNativeOrder.OrderInfo[](orders.length);
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
try
this.getRfqOrderRelevantState(orders[i], signatures[i])
returns (
LibNativeOrder.OrderInfo memory orderInfo,
uint128 actualFillableTakerTokenAmount,
bool isSignatureValid
)
{
orderInfos[i] = orderInfo;
actualFillableTakerTokenAmounts[i] = actualFillableTakerTokenAmount;
isSignatureValids[i] = isSignatureValid;
}
catch {}
}
}
/// @dev Populate `status` and `takerTokenFilledAmount` fields in
/// `orderInfo`, which use the same code path for both limit and
/// RFQ orders.
/// @param orderInfo `OrderInfo` with `orderHash` and `maker` filled.
/// @param takerAmount The order's taker token amount..
/// @param expiry The order's expiry.
/// @param salt The order's salt.
/// @param salt The minimum valid salt for the maker and pair combination.
function _populateCommonOrderInfoFields(
LibNativeOrder.OrderInfo memory orderInfo,
uint128 takerAmount,
uint64 expiry,
uint256 salt,
uint256 minValidSalt
)
private
view
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
// Get the filled and direct cancel state.
{
// The high bit of the raw taker token filled amount will be set
// if the order was cancelled.
uint256 rawTakerTokenFilledAmount =
stor.orderHashToTakerTokenFilledAmount[orderInfo.orderHash];
orderInfo.takerTokenFilledAmount = uint128(rawTakerTokenFilledAmount);
if (orderInfo.takerTokenFilledAmount >= takerAmount) {
orderInfo.status = LibNativeOrder.OrderStatus.FILLED;
return;
}
if (rawTakerTokenFilledAmount & HIGH_BIT != 0) {
orderInfo.status = LibNativeOrder.OrderStatus.CANCELLED;
return;
}
}
// Check for expiration.
if (expiry <= uint64(block.timestamp)) {
orderInfo.status = LibNativeOrder.OrderStatus.EXPIRED;
return;
}
// Check if the order was cancelled by salt.
if (minValidSalt > salt) {
orderInfo.status = LibNativeOrder.OrderStatus.CANCELLED;
return;
}
orderInfo.status = LibNativeOrder.OrderStatus.FILLABLE;
}
/// @dev Calculate the actual fillable taker token amount of an order
/// based on maker allowance and balances.
function _getActualFillableTakerTokenAmount(
GetActualFillableTakerTokenAmountParams memory params
)
private
view
returns (uint128 actualFillableTakerTokenAmount)
{
if (params.orderMakerAmount == 0 || params.orderTakerAmount == 0) {
// Empty order.
return 0;
}
if (params.orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
// Not fillable.
return 0;
}
// Get the fillable maker amount based on the order quantities and
// previously filled amount
uint256 fillableMakerTokenAmount = LibMathV06.getPartialAmountFloor(
uint256(
params.orderTakerAmount
- params.orderInfo.takerTokenFilledAmount
),
uint256(params.orderTakerAmount),
uint256(params.orderMakerAmount)
);
// Clamp it to the amount of maker tokens we can spend on behalf of the
// maker.
fillableMakerTokenAmount = LibSafeMathV06.min256(
fillableMakerTokenAmount,
_getSpendableERC20BalanceOf(params.makerToken, params.maker)
);
// Convert to taker token amount.
return LibMathV06.getPartialAmountCeil(
fillableMakerTokenAmount,
uint256(params.orderMakerAmount),
uint256(params.orderTakerAmount)
).safeDowncastToUint128();
}
}

View File

@@ -0,0 +1,71 @@
// 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/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../../fixins/FixinProtocolFees.sol";
import "../../errors/LibNativeOrdersRichErrors.sol";
import "../../vendor/v3/IStaking.sol";
/// @dev Mixin for protocol fee utility functions.
abstract contract NativeOrdersProtocolFees is
FixinProtocolFees
{
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
constructor(
IEtherTokenV06 weth,
IStaking staking,
FeeCollectorController feeCollectorController,
uint32 protocolFeeMultiplier
)
internal
FixinProtocolFees(weth, staking, feeCollectorController, protocolFeeMultiplier)
{
// solhint-disable no-empty-blocks
}
/// @dev Transfers protocol fees from the `FeeCollector` pools into
/// the staking contract.
/// @param poolIds Staking pool IDs
function transferProtocolFeesForPools(bytes32[] calldata poolIds)
external
{
for (uint256 i = 0; i < poolIds.length; ++i) {
_transferFeesForPool(poolIds[i]);
}
}
/// @dev Get the protocol fee multiplier. This should be multiplied by the
/// gas price to arrive at the required protocol fee to fill a native order.
/// @return multiplier The protocol fee multiplier.
function getProtocolFeeMultiplier()
external
view
returns (uint32 multiplier)
{
return PROTOCOL_FEE_MULTIPLIER;
}
}

View File

@@ -0,0 +1,569 @@
// 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 "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "../../errors/LibNativeOrdersRichErrors.sol";
import "../../fixins/FixinCommon.sol";
import "../../storage/LibNativeOrdersStorage.sol";
import "../../vendor/v3/IStaking.sol";
import "../interfaces/INativeOrdersEvents.sol";
import "../libs/LibSignature.sol";
import "../libs/LibNativeOrder.sol";
import "./NativeOrdersCancellation.sol";
import "./NativeOrdersProtocolFees.sol";
/// @dev Mixin for settling limit and RFQ orders.
abstract contract NativeOrdersSettlement is
INativeOrdersEvents,
NativeOrdersCancellation,
NativeOrdersProtocolFees,
FixinCommon
{
using LibSafeMathV06 for uint128;
using LibRichErrorsV06 for bytes;
/// @dev Params for `_settleOrder()`.
struct SettleOrderInfo {
// Order hash.
bytes32 orderHash;
// Maker of the order.
address maker;
// Taker of the order.
address taker;
// Maker token.
IERC20TokenV06 makerToken;
// Taker token.
IERC20TokenV06 takerToken;
// Maker token amount.
uint128 makerAmount;
// Taker token amount.
uint128 takerAmount;
// Maximum taker token amount to fill.
uint128 takerTokenFillAmount;
// How much taker token amount has already been filled in this order.
uint128 takerTokenFilledAmount;
}
/// @dev Params for `_fillLimitOrderPrivate()`
struct FillLimitOrderPrivateParams {
// The limit order.
LibNativeOrder.LimitOrder order;
// The order signature.
LibSignature.Signature signature;
// Maximum taker token to fill this order with.
uint128 takerTokenFillAmount;
// The order taker.
address taker;
// The order sender.
address sender;
}
// @dev Fill results returned by `_fillLimitOrderPrivate()` and
/// `_fillRfqOrderPrivate()`.
struct FillNativeOrderResults {
uint256 ethProtocolFeePaid;
uint128 takerTokenFilledAmount;
uint128 makerTokenFilledAmount;
uint128 takerTokenFeeFilledAmount;
}
constructor(
address zeroExAddress,
IEtherTokenV06 weth,
IStaking staking,
FeeCollectorController feeCollectorController,
uint32 protocolFeeMultiplier,
bytes32 greedyTokensBloomFilter
)
public
NativeOrdersCancellation(zeroExAddress, greedyTokensBloomFilter)
NativeOrdersProtocolFees(weth, staking, feeCollectorController, protocolFeeMultiplier)
{
// solhint-disable no-empty-blocks
}
/// @dev Fill a limit order. The taker and sender will be the caller.
/// @param order The limit order. ETH protocol fees can be
/// attached to this call. Any unspent ETH will be refunded to
/// the caller.
/// @param signature The order signature.
/// @param takerTokenFillAmount Maximum taker token amount to fill this order with.
/// @return takerTokenFilledAmount How much maker token was filled.
/// @return makerTokenFilledAmount How much maker token was filled.
function fillLimitOrder(
LibNativeOrder.LimitOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount
)
public
payable
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillLimitOrderPrivate(FillLimitOrderPrivateParams({
order: order,
signature: signature,
takerTokenFillAmount: takerTokenFillAmount,
taker: msg.sender,
sender: msg.sender
}));
LibNativeOrder.refundExcessProtocolFeeToSender(results.ethProtocolFeePaid);
(takerTokenFilledAmount, makerTokenFilledAmount) = (
results.takerTokenFilledAmount,
results.makerTokenFilledAmount
);
}
/// @dev Fill an RFQ order for up to `takerTokenFillAmount` taker tokens.
/// The taker will be the caller. ETH should be attached to pay the
/// protocol fee.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @param takerTokenFillAmount Maximum taker token amount to fill this order with.
/// @return takerTokenFilledAmount How much maker token was filled.
/// @return makerTokenFilledAmount How much maker token was filled.
function fillRfqOrder(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount
)
public
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillRfqOrderPrivate(
order,
signature,
takerTokenFillAmount,
msg.sender
);
(takerTokenFilledAmount, makerTokenFilledAmount) = (
results.takerTokenFilledAmount,
results.makerTokenFilledAmount
);
}
/// @dev Fill an RFQ order for exactly `takerTokenFillAmount` taker tokens.
/// The taker will be the caller. ETH protocol fees can be
/// attached to this call. Any unspent ETH will be refunded to
/// the caller.
/// @param order The limit order.
/// @param signature The order signature.
/// @param takerTokenFillAmount How much taker token to fill this order with.
/// @return makerTokenFilledAmount How much maker token was filled.
function fillOrKillLimitOrder(
LibNativeOrder.LimitOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount
)
public
payable
returns (uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillLimitOrderPrivate(FillLimitOrderPrivateParams({
order: order,
signature: signature,
takerTokenFillAmount: takerTokenFillAmount,
taker: msg.sender,
sender: msg.sender
}));
// Must have filled exactly the amount requested.
if (results.takerTokenFilledAmount < takerTokenFillAmount) {
LibNativeOrdersRichErrors.FillOrKillFailedError(
getLimitOrderHash(order),
results.takerTokenFilledAmount,
takerTokenFillAmount
).rrevert();
}
LibNativeOrder.refundExcessProtocolFeeToSender(results.ethProtocolFeePaid);
makerTokenFilledAmount = results.makerTokenFilledAmount;
}
/// @dev Fill an RFQ order for exactly `takerTokenFillAmount` taker tokens.
/// The taker will be the caller. ETH protocol fees can be
/// attached to this call. Any unspent ETH will be refunded to
/// the caller.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @param takerTokenFillAmount How much taker token to fill this order with.
/// @return makerTokenFilledAmount How much maker token was filled.
function fillOrKillRfqOrder(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount
)
public
returns (uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillRfqOrderPrivate(
order,
signature,
takerTokenFillAmount,
msg.sender
);
// Must have filled exactly the amount requested.
if (results.takerTokenFilledAmount < takerTokenFillAmount) {
LibNativeOrdersRichErrors.FillOrKillFailedError(
getRfqOrderHash(order),
results.takerTokenFilledAmount,
takerTokenFillAmount
).rrevert();
}
makerTokenFilledAmount = results.makerTokenFilledAmount;
}
/// @dev Fill a limit order. Internal variant. ETH protocol fees can be
/// attached to this call.
/// @param order The limit order.
/// @param signature The order signature.
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
/// @param taker The order taker.
/// @param sender The order sender.
/// @return takerTokenFilledAmount How much maker token was filled.
/// @return makerTokenFilledAmount How much maker token was filled.
function _fillLimitOrder(
LibNativeOrder.LimitOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount,
address taker,
address sender
)
public
virtual
payable
onlySelf
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillLimitOrderPrivate(FillLimitOrderPrivateParams({
order: order,
signature: signature,
takerTokenFillAmount: takerTokenFillAmount,
taker: taker,
sender: sender
}));
(takerTokenFilledAmount, makerTokenFilledAmount) = (
results.takerTokenFilledAmount,
results.makerTokenFilledAmount
);
}
/// @dev Fill an RFQ order. Internal variant. ETH protocol fees can be
/// attached to this call. Any unspent ETH will be refunded to
/// `msg.sender` (not `sender`).
/// @param order The RFQ order.
/// @param signature The order signature.
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
/// @param taker The order taker.
/// @return takerTokenFilledAmount How much maker token was filled.
/// @return makerTokenFilledAmount How much maker token was filled.
function _fillRfqOrder(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount,
address taker
)
public
virtual
onlySelf
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
FillNativeOrderResults memory results =
_fillRfqOrderPrivate(
order,
signature,
takerTokenFillAmount,
taker
);
(takerTokenFilledAmount, makerTokenFilledAmount) = (
results.takerTokenFilledAmount,
results.makerTokenFilledAmount
);
}
/// @dev Mark what tx.origin addresses are allowed to fill an order that
/// specifies the message sender as its txOrigin.
/// @param origins An array of origin addresses to update.
/// @param allowed True to register, false to unregister.
function registerAllowedRfqOrigins(
address[] memory origins,
bool allowed
)
external
{
require(msg.sender == tx.origin,
"NativeOrdersFeature/NO_CONTRACT_ORIGINS");
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
for (uint256 i = 0; i < origins.length; i++) {
stor.originRegistry[msg.sender][origins[i]] = allowed;
}
emit RfqOrderOriginsAllowed(msg.sender, origins, allowed);
}
/// @dev Fill a limit order. Private variant. Does not refund protocol fees.
/// @param params Function params.
/// @return results Results of the fill.
function _fillLimitOrderPrivate(FillLimitOrderPrivateParams memory params)
private
returns (FillNativeOrderResults memory results)
{
LibNativeOrder.OrderInfo memory orderInfo = getLimitOrderInfo(params.order);
// Must be fillable.
if (orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
LibNativeOrdersRichErrors.OrderNotFillableError(
orderInfo.orderHash,
uint8(orderInfo.status)
).rrevert();
}
// Must be fillable by the taker.
if (params.order.taker != address(0) && params.order.taker != params.taker) {
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
orderInfo.orderHash,
params.taker,
params.order.taker
).rrevert();
}
// Must be fillable by the sender.
if (params.order.sender != address(0) && params.order.sender != params.sender) {
LibNativeOrdersRichErrors.OrderNotFillableBySenderError(
orderInfo.orderHash,
params.sender,
params.order.sender
).rrevert();
}
// Signature must be valid for the order.
{
address signer = LibSignature.getSignerOfHash(
orderInfo.orderHash,
params.signature
);
if (signer != params.order.maker) {
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
orderInfo.orderHash,
signer,
params.order.maker
).rrevert();
}
}
// Pay the protocol fee.
results.ethProtocolFeePaid = _collectProtocolFee(params.order.pool);
// Settle between the maker and taker.
(results.takerTokenFilledAmount, results.makerTokenFilledAmount) = _settleOrder(
SettleOrderInfo({
orderHash: orderInfo.orderHash,
maker: params.order.maker,
taker: params.taker,
makerToken: IERC20TokenV06(params.order.makerToken),
takerToken: IERC20TokenV06(params.order.takerToken),
makerAmount: params.order.makerAmount,
takerAmount: params.order.takerAmount,
takerTokenFillAmount: params.takerTokenFillAmount,
takerTokenFilledAmount: orderInfo.takerTokenFilledAmount
})
);
// Pay the fee recipient.
if (params.order.takerTokenFeeAmount > 0) {
results.takerTokenFeeFilledAmount = uint128(LibMathV06.getPartialAmountFloor(
results.takerTokenFilledAmount,
params.order.takerAmount,
params.order.takerTokenFeeAmount
));
_transferERC20Tokens(
params.order.takerToken,
params.taker,
params.order.feeRecipient,
uint256(results.takerTokenFeeFilledAmount)
);
}
emit LimitOrderFilled(
orderInfo.orderHash,
params.order.maker,
params.taker,
params.order.feeRecipient,
address(params.order.makerToken),
address(params.order.takerToken),
results.takerTokenFilledAmount,
results.makerTokenFilledAmount,
results.takerTokenFeeFilledAmount,
results.ethProtocolFeePaid,
params.order.pool
);
}
/// @dev Fill an RFQ order. Private variant. Does not refund protocol fees.
/// @param order The RFQ order.
/// @param signature The order signature.
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
/// @param taker The order taker.
/// @return results Results of the fill.
function _fillRfqOrderPrivate(
LibNativeOrder.RfqOrder memory order,
LibSignature.Signature memory signature,
uint128 takerTokenFillAmount,
address taker
)
private
returns (FillNativeOrderResults memory results)
{
LibNativeOrder.OrderInfo memory orderInfo = getRfqOrderInfo(order);
// Must be fillable.
if (orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
LibNativeOrdersRichErrors.OrderNotFillableError(
orderInfo.orderHash,
uint8(orderInfo.status)
).rrevert();
}
{
LibNativeOrdersStorage.Storage storage stor =
LibNativeOrdersStorage.getStorage();
// Must be fillable by the tx.origin.
if (order.txOrigin != tx.origin && !stor.originRegistry[order.txOrigin][tx.origin]) {
LibNativeOrdersRichErrors.OrderNotFillableByOriginError(
orderInfo.orderHash,
tx.origin,
order.txOrigin
).rrevert();
}
}
// Must be fillable by the taker.
if (order.taker != address(0) && order.taker != taker) {
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
orderInfo.orderHash,
taker,
order.taker
).rrevert();
}
// Signature must be valid for the order.
{
address signer = LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
if (signer != order.maker) {
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
orderInfo.orderHash,
signer,
order.maker
).rrevert();
}
}
// Settle between the maker and taker.
(results.takerTokenFilledAmount, results.makerTokenFilledAmount) = _settleOrder(
SettleOrderInfo({
orderHash: orderInfo.orderHash,
maker: order.maker,
taker: taker,
makerToken: IERC20TokenV06(order.makerToken),
takerToken: IERC20TokenV06(order.takerToken),
makerAmount: order.makerAmount,
takerAmount: order.takerAmount,
takerTokenFillAmount: takerTokenFillAmount,
takerTokenFilledAmount: orderInfo.takerTokenFilledAmount
})
);
emit RfqOrderFilled(
orderInfo.orderHash,
order.maker,
taker,
address(order.makerToken),
address(order.takerToken),
results.takerTokenFilledAmount,
results.makerTokenFilledAmount,
order.pool
);
}
/// @dev Settle the trade between an order's maker and taker.
/// @param settleInfo Information needed to execute the settlement.
/// @return takerTokenFilledAmount How much taker token was filled.
/// @return makerTokenFilledAmount How much maker token was filled.
function _settleOrder(SettleOrderInfo memory settleInfo)
private
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
// Clamp the taker token fill amount to the fillable amount.
takerTokenFilledAmount = LibSafeMathV06.min128(
settleInfo.takerTokenFillAmount,
settleInfo.takerAmount.safeSub128(settleInfo.takerTokenFilledAmount)
);
// Compute the maker token amount.
// This should never overflow because the values are all clamped to
// (2^128-1).
makerTokenFilledAmount = uint128(LibMathV06.getPartialAmountFloor(
uint256(takerTokenFilledAmount),
uint256(settleInfo.takerAmount),
uint256(settleInfo.makerAmount)
));
if (takerTokenFilledAmount == 0 || makerTokenFilledAmount == 0) {
// Nothing to do.
return (0, 0);
}
// Update filled state for the order.
LibNativeOrdersStorage
.getStorage()
.orderHashToTakerTokenFilledAmount[settleInfo.orderHash] =
// OK to overwrite the whole word because we shouldn't get to this
// function if the order is cancelled.
settleInfo.takerTokenFilledAmount.safeAdd128(takerTokenFilledAmount);
// Transfer taker -> maker.
_transferERC20Tokens(
settleInfo.takerToken,
settleInfo.taker,
settleInfo.maker,
takerTokenFilledAmount
);
// Transfer maker -> taker.
_transferERC20Tokens(
settleInfo.makerToken,
settleInfo.maker,
settleInfo.taker,
makerTokenFilledAmount
);
}
}

View File

@@ -23,8 +23,8 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "../errors/LibCommonRichErrors.sol";
import "../errors/LibOwnableRichErrors.sol";
import "../features/IOwnableFeature.sol";
import "../features/ISimpleFunctionRegistryFeature.sol";
import "../features/interfaces/IOwnableFeature.sol";
import "../features/interfaces/ISimpleFunctionRegistryFeature.sol";
/// @dev Common feature utilities.

View File

@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../features/ITokenSpenderFeature.sol";
import "../features/interfaces/ITokenSpenderFeature.sol";
import "../errors/LibSpenderRichErrors.sol";
import "../external/FeeCollector.sol";
import "../vendor/v3/IStaking.sol";

View File

@@ -0,0 +1,223 @@
// 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)),
recipient
);
// 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)),
recipient
);
// 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)),
recipient
);
// 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,
address recipient // Only used to log event.
)
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));
}
emit LiquidityProviderFill(
inputToken,
outputToken,
sellAmount,
boughtAmount,
bytes32("Curve"),
address(data.curveAddress),
msg.sender,
recipient
);
}
}

View File

@@ -0,0 +1,227 @@
// 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-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "../transformers/LibERC20Transformer.sol";
import "../vendor/ILiquidityProvider.sol";
import "../vendor/IMooniswapPool.sol";
contract MooniswapLiquidityProvider is
ILiquidityProvider
{
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth) public {
WETH = weth;
}
/// @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)
&& inputToken != outputToken,
"MooniswapLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
inputToken,
outputToken,
minBuyAmount,
abi.decode(auxiliaryData, (IMooniswapPool)),
recipient
);
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),
"MooniswapLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
LibERC20Transformer.ETH_TOKEN,
outputToken,
minBuyAmount,
abi.decode(auxiliaryData, (IMooniswapPool)),
recipient
);
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),
"MooniswapLiquidityProvider/INVALID_ARGS"
);
boughtAmount = _executeSwap(
inputToken,
LibERC20Transformer.ETH_TOKEN,
minBuyAmount,
abi.decode(auxiliaryData, (IMooniswapPool)),
recipient
);
recipient.call{value: 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("MooniswapLiquidityProvider/NOT_IMPLEMENTED");
}
/// @dev Perform the swap against the curve pool. Handles any combination of
/// tokens
function _executeSwap(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 minBuyAmount,
IMooniswapPool pool,
address recipient // Only used to log event
)
private
returns (uint256 boughtAmount)
{
uint256 sellAmount =
LibERC20Transformer.getTokenBalanceOf(inputToken, address(this));
uint256 ethValue = 0;
if (inputToken == WETH) {
// Selling WETH. Unwrap to ETH.
require(!_isTokenEthLike(outputToken), 'MooniswapLiquidityProvider/ETH_TO_ETH');
WETH.withdraw(sellAmount);
ethValue = sellAmount;
} else if (LibERC20Transformer.isTokenETH(inputToken)) {
// Selling ETH directly.
ethValue = sellAmount;
require(!_isTokenEthLike(outputToken), 'MooniswapLiquidityProvider/ETH_TO_ETH');
} else {
// Selling a regular ERC20.
require(inputToken != outputToken, 'MooniswapLiquidityProvider/SAME_TOKEN');
inputToken.approveIfBelow(address(pool), sellAmount);
}
boughtAmount = pool.swap{value: ethValue}(
_isTokenEthLike(inputToken) ? IERC20TokenV06(0) : inputToken,
_isTokenEthLike(outputToken) ? IERC20TokenV06(0) : outputToken,
sellAmount,
minBuyAmount,
address(0)
);
if (outputToken == WETH) {
WETH.deposit{value: boughtAmount}();
}
emit LiquidityProviderFill(
inputToken,
outputToken,
sellAmount,
boughtAmount,
bytes32("Mooniswap"),
address(pool),
msg.sender,
recipient
);
}
/// @dev Check if a token is ETH or WETH.
function _isTokenEthLike(IERC20TokenV06 token)
private
view
returns (bool isEthOrWeth)
{
return LibERC20Transformer.isTokenETH(token) || token == WETH;
}
}

View File

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../ZeroEx.sol";
import "../features/IOwnableFeature.sol";
import "../features/interfaces/IOwnableFeature.sol";
import "../features/TokenSpenderFeature.sol";
import "../features/TransformERC20Feature.sol";
import "../features/MetaTransactionsFeature.sol";

View File

@@ -21,7 +21,7 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "../ZeroEx.sol";
import "../features/IBootstrapFeature.sol";
import "../features/interfaces/IBootstrapFeature.sol";
import "../features/SimpleFunctionRegistryFeature.sol";
import "../features/OwnableFeature.sol";
import "./LibBootstrap.sol";

View File

@@ -26,7 +26,7 @@ import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
import "../errors/LibTransformERC20RichErrors.sol";
import "../features/INativeOrdersFeature.sol";
import "../features/interfaces/INativeOrdersFeature.sol";
import "../features/libs/LibNativeOrder.sol";
import "./bridges/IBridgeAdapter.sol";
import "./Transformer.sol";
@@ -132,8 +132,6 @@ contract FillQuoteTransformer is
/// @param orderHash The hash of the order that was skipped.
event ProtocolFeeUnfunded(bytes32 orderHash);
/// @dev Maximum uint256 value.
uint256 private constant MAX_UINT256 = uint256(-1);
/// @dev The highest bit of a uint256 value.
uint256 private constant HIGH_BIT = 2 ** 255;
/// @dev Mask of the lower 255 bits of a uint256 value.
@@ -273,13 +271,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));
}
}

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