Compare commits

...

17 Commits

Author SHA1 Message Date
Github Actions
b29196b983 Publish
- @0x/contracts-asset-proxy@3.7.6
 - @0x/contracts-broker@1.1.24
 - @0x/contracts-coordinator@3.1.25
 - @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-forwarder@4.2.25
 - @0x/contracts-exchange-libs@4.3.24
 - @0x/contracts-exchange@3.2.25
 - @0x/contracts-extensions@6.2.19
 - @0x/contracts-integrations@2.7.26
 - @0x/contracts-multisig@4.1.25
 - @0x/contracts-staking@2.0.32
 - @0x/contracts-test-utils@5.3.21
 - @0x/contracts-treasury@1.0.1
 - @0x/contracts-utils@4.7.3
 - @0x/contracts-zero-ex@0.18.2
 - @0x/asset-swapper@6.0.0
 - @0x/contract-addresses@5.10.0
 - @0x/contract-wrappers-test@12.2.36
 - @0x/contract-wrappers@13.12.3
 - @0x/migrations@6.6.0
 - @0x/order-utils@10.4.16
 - @0x/protocol-utils@1.2.0
2021-02-10 09:49:05 +00:00
Github Actions
ce76a7033d Updated CHANGELOGS & MD docs 2021-02-10 09:48:52 +00:00
Jacob Evans
3f4bb933d1 feat: v4 final (#136)
* v4 FillQuoteTransformer (#104)

* Update FQT to support v4 orders

* `@0x/contracts-zero-ex`: Tweak FQT
`@0x/contracts-zero-ex`: Drop `ERC20BridgeTransfer` event and add `PartialQuoteFill` event.

* `@0x/contracts-utils`: Add `LibSafeMathV06.downcastToUint128()`

* `@0x/protocol-utils`: Update transformer utils for V4 FQT

* `@0x/contracts-zero-ex`: Fixing FQT tests...

* `@0x/contracts-zero-ex`: rename FQT bridge event

* `@0x/contracts-zero-ex`: Un-`only` tests

* `@0x/migrations`: Update `BridgeAdapter` deployment

* `@0x/contracts-integrations`: Delete `mtx_tests`

* `@0x/protocol-utils`: Address review comments

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

* `@0x/migrations`: Update migrations

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

* v4: Asset-swapper (main branch) (#113)

* refactor quote_requestor

* WIP v4/asset-swapper: Clean up SwapQuoter and remove @0x/orderbook

* Start replacing SignedOrder everywhere

* wip: new order type

* wip

* remove order-utils from most places

* hack: Play around with VerboseX types (#119)

* hack: Play around with VerboseX types

* More hacks

* Fix up the bridgeData encodings

* Rework Orderbook return type

* feat: Don't charge a protocol fee for RFQ orders WIP (#121)

* fix simple build errors

* simplify types a little

* remove SwapQuoteCalculator: unnecessary abstraction

* Fix all ./src build errors; make types consistent

* export more types for use in 0x API; modify Orderbook interface

* stop overriding APIOrder

* feat: RFQ v4 + consolidated bridge encoders (#125)

* feat: check if taker address is contract

* Rework bridge data

* Worst case adjustments

* RFQT v4

* Future/v4 validate orders (#126)

* RFQT v4

* v4 validate native orders

* use default invalid signature

* refactor rfqt validations in swap quoter

* fix types

* fix RFQT unlisted api key

* remove priceAwareRFQFlag

* adjust maker/taker amounts

* update JSON schemas

* filter zero fillable orders

Co-authored-by: xianny <xianny@gmail.com>

* fix type export

Co-authored-by: xianny <xianny@gmail.com>

* remove order-utils as much as possible

* work on tests compile

* Comment out quote reporter test

* updated tests

* restore order-utils accidental changes

* some lints

* Remove old fill_test

* ts lint disable for now

* update quote report

* Re-enable quote report tests

* make fill data required field

* fix lint

* type guards

* force fillData as required

* fix lint

* fix naming

* exports

* adjust MultiBridge by slippage

* cleanups (checkpoint 1)

* cleanup types (checkpoint #2)

* remove unused deps

* `@0x/contract-addresses`: Deploy new FQT (#129)

Co-authored-by: Lawrence Forman <me@merklejerk.com>

* commit bump to republish

* DRY up the rfqt mocker

* fix: Balancer load top pools (#131)

* fix: Balancer load top 250 pools

* refetch top pools on an interval

Co-authored-by: Jacob Evans <jacob@dekz.net>
Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>

* Update post rebase

* prettier

* Remove test helpers exported in asset-swapper

* Clean up from review comments

* prettier

* lint

* recreate rfqt mocker

* change merge and INVALID_SIGNATURE

Co-authored-by: Lawrence Forman <lawrence@0xproject.com>
Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
Co-authored-by: Lawrence Forman <me@merklejerk.com>
Co-authored-by: Xianny <8582774+xianny@users.noreply.github.com>
Co-authored-by: Kim Persson <kimpers@users.noreply.github.com>
2021-02-10 19:20:15 +10:00
Greg Hysz
501b7b9b65 Updated releases (#133) 2021-02-08 18:32:21 -08:00
Daniel Pyrathon
c0ea88e864 lint fix (#130) 2021-02-05 09:56:35 -08:00
Lawrence Forman
48f8bea460 docs: Fix wrong method name for order signature generation (#128)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-02-02 23:30:26 -05:00
Github Actions
f4e5b6e38d Publish
- @0x/contracts-integrations@2.7.25
 - @0x/contracts-treasury@1.0.0
 - @0x/asset-swapper@5.8.2
2021-01-28 21:36:31 +00:00
Github Actions
477cb0a48d Updated CHANGELOGS & MD docs 2021-01-28 21:36:22 +00:00
Jacob Evans
d6bc0a3368 fix: [asset-swapper] prevent error when multihop data is not present (#80)
* fix: [asset-swapper] prevent error when multihop is not present for a route

* Update changelog

Co-authored-by: Michael Zhu <mchl.zhu.96@gmail.com>
2021-01-28 13:04:32 -08:00
mzhu25
e544a804c2 ZrxTreasury (#120)
* `@0x/contracts-treasury` (squashed)

Fix @0x/contracts-zero-ex readme

Create package for governance contracts

Rename @0x/contracts-governance to @0x/contracts-treasury and write contracts

Write tests and such

Missed a few places where "governance" should be "treasury"`

Update changelog with PR number

Appease CI

Arbitrary treasury actions

Export treasury artifacts and wrappers

Continue to do battle with CI

Address PR feedback

* More PR feedback
2021-01-26 09:33:14 -08:00
Github Actions
96e0ad7899 Publish
- @0x/contracts-asset-proxy@3.7.5
 - @0x/contracts-broker@1.1.23
 - @0x/contracts-coordinator@3.1.24
 - @0x/contracts-dev-utils@1.3.22
 - @0x/contracts-erc1155@2.1.23
 - @0x/contracts-erc20@3.3.2
 - @0x/contracts-erc721@3.1.23
 - @0x/contracts-exchange-forwarder@4.2.24
 - @0x/contracts-exchange-libs@4.3.23
 - @0x/contracts-exchange@3.2.24
 - @0x/contracts-extensions@6.2.18
 - @0x/contracts-integrations@2.7.24
 - @0x/contracts-multisig@4.1.24
 - @0x/contracts-staking@2.0.31
 - @0x/contracts-test-utils@5.3.20
 - @0x/contracts-utils@4.7.2
 - @0x/contracts-zero-ex@0.18.1
 - @0x/asset-swapper@5.8.1
 - @0x/contract-artifacts@3.11.1
 - @0x/contract-wrappers-test@12.2.35
 - @0x/contract-wrappers@13.12.2
 - @0x/migrations@6.5.11
 - @0x/order-utils@10.4.15
 - @0x/protocol-utils@1.1.5
2021-01-26 08:02:10 +00:00
Github Actions
5a1fee5d31 Updated CHANGELOGS & MD docs 2021-01-26 08:02:03 +00:00
Lawrence Forman
3557a5e5a9 @0x/contracts-zero-ex: Update tests to work with ganache chainId of 1337 (#123)
* `@0x/contracts-zero-ex`: Update tests to work with ganache chainId of 1337

* `update org deps

* `@0x/protocol-utils`: Use `Web3Wrapper.signTypedDataAsync()` instead of removed `Web3Wrapper.signTypedDataV4Async()` (#124)

Co-authored-by: Lawrence Forman <me@merklejerk.com>

* `update org deps
`@0x/order-utils`: Fix broken tests

* remove lingering console.logs

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-01-26 02:50:09 -05:00
Lawrence Forman
0bd43d5265 docs: add big-pantsuit-energy release (#122)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-01-21 19:02:44 -05:00
Lawrence Forman
8fd7c1b386 @0x/migrations: Update dockerfile to override networkId and chainId in ganache-cli (#118)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-01-20 12:10:24 -05:00
Lawrence Forman
50068750f5 EP: Swallow reverts in batchGetLimit/RfqRelevantStates() (#117)
* `@0x/contracts-zero-ex`: Swallow reverts in `batchGetLimit/RfqRelevantStates()`.

* `@0x/contracts-zero-ex`: Fix typos

* `@0x/contracts-zero-ex`: Fix misleading RFQ typehash comment in `LibNativeOrder.sol`

Co-authored-by: Lawrence Forman <me@merklejerk.com>
2021-01-20 00:25:48 -05:00
Daniel Pyrathon
a7a905de4c fix: Upgrade quote server and Asset Swapper types (and specify protocol ve… (#108)
* Upgrade quote server and Asset Swapper types (and specify protocol version in API requests)

* add package and lockfile

* fix tests
2021-01-13 17:07:45 -08:00
212 changed files with 8412 additions and 9750 deletions

4
.gitignore vendored
View File

@@ -113,6 +113,8 @@ contracts/dev-utils/generated-artifacts/
contracts/dev-utils/test/generated-artifacts/
contracts/zero-ex/generated-artifacts/
contracts/zero-ex/test/generated-artifacts/
contracts/treasury/generated-artifacts/
contracts/treasury/test/generated-artifacts/
# generated truffle contract artifacts/
contracts/broker/build/
@@ -167,6 +169,8 @@ contracts/dev-utils/generated-wrappers/
contracts/dev-utils/test/generated-wrappers/
contracts/zero-ex/generated-wrappers/
contracts/zero-ex/test/generated-wrappers/
contracts/treasury/generated-wrappers/
contracts/treasury/test/generated-wrappers/
# Doc README copy
packages/*/docs/README.md

View File

@@ -64,6 +64,10 @@ lib
/contracts/zero-ex/test/generated-wrappers
/contracts/zero-ex/generated-artifacts
/contracts/zero-ex/test/generated-artifacts
/contracts/treasury/generated-wrappers
/contracts/treasury/test/generated-wrappers
/contracts/treasury/generated-artifacts
/contracts/treasury/test/generated-artifacts
/contracts/staking/build/
/contracts/coordinator/build/
/contracts/exchange/build/

View File

@@ -13,7 +13,6 @@
"indent": ["error", 4],
"max-line-length": ["warn", 160],
"no-inline-assembly": false,
"no-empty-blocks": false,
"quotes": ["error", "double"],
"separate-by-one-line-in-contract": "error",
"space-after-comma": "error",

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "3.7.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "3.7.5",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "3.7.4",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.7.6 - _February 10, 2021_
* Dependencies updated
## v3.7.5 - _January 26, 2021_
* Dependencies updated
## v3.7.4 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.7.4",
"version": "3.7.6",
"engines": {
"node": ">=6.12"
},
@@ -51,13 +51,13 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contract-wrappers": "^13.12.1",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@types/lodash": "4.14.104",
@@ -79,16 +79,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-erc1155": "^2.1.22",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/order-utils": "^10.4.14",
"@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/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",
"lodash": "^4.17.11"
},

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "1.1.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "1.1.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "1.1.22",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.24 - _February 10, 2021_
* Dependencies updated
## v1.1.23 - _January 26, 2021_
* Dependencies updated
## v1.1.22 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-broker",
"version": "1.1.22",
"version": "1.1.24",
"engines": {
"node": ">=6.12"
},
@@ -51,20 +51,20 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/contracts-exchange": "^3.2.23",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/sol-compiler": "^4.4.1",
"@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/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -84,10 +84,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/order-utils": "^10.4.14",
"@0x/base-contract": "^6.2.18",
"@0x/order-utils": "^10.4.16",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "3.1.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "3.1.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "3.1.23",

View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.1.23",
"version": "3.1.25",
"engines": {
"node": ">=6.12"
},
@@ -52,17 +52,17 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-gen": "^2.0.24",
"@0x/dev-utils": "^4.1.3",
"@0x/order-utils": "^10.4.14",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -82,16 +82,16 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/assert": "^3.0.19",
"@0x/base-contract": "^6.2.14",
"@0x/contract-addresses": "^5.9.0",
"@0x/contracts-exchange": "^3.2.23",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/json-schemas": "^5.3.4",
"@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/json-schemas": "^5.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",
"http-status-codes": "^1.3.2"
},

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "1.3.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "1.3.22",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "1.3.21",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.23 - _February 10, 2021_
* Dependencies updated
## v1.3.22 - _January 26, 2021_
* Dependencies updated
## v1.3.21 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.3.21",
"version": "1.3.23",
"engines": {
"node": ">=6.12"
},
@@ -41,17 +41,17 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/dev-utils",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/assert": "^3.0.19",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/sol-compiler": "^4.4.1",
"@0x/abi-gen": "^5.4.19",
"@0x/assert": "^3.0.21",
"@0x/contracts-asset-proxy": "^3.7.6",
"@0x/contracts-erc20": "^3.3.3",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",
"ethers": "~4.0.4",
"npm-run-all": "^4.1.2",
@@ -63,7 +63,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/base-contract": "^6.2.18",
"@types/node": "12.12.54"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "2.1.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "2.1.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "2.1.22",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.24 - _February 10, 2021_
* Dependencies updated
## v2.1.23 - _January 26, 2021_
* Dependencies updated
## v2.1.22 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.1.22",
"version": "2.1.24",
"engines": {
"node": ">=6.12"
},
@@ -52,11 +52,11 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-utils": "^4.7.3",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
@@ -80,10 +80,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "3.3.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "3.3.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "3.3.1",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.3 - _February 10, 2021_
* Dependencies updated
## v3.3.2 - _January 26, 2021_
* Dependencies updated
## v3.3.1 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.1",
"version": "3.3.3",
"engines": {
"node": ">=6.12"
},
@@ -51,18 +51,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@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",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -82,7 +82,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14"
"@0x/base-contract": "^6.2.18"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "3.1.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "3.1.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "3.1.22",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.24 - _February 10, 2021_
* Dependencies updated
## v3.1.23 - _January 26, 2021_
* Dependencies updated
## v3.1.22 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.1.22",
"version": "3.1.24",
"engines": {
"node": ">=6.12"
},
@@ -52,18 +52,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@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",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -84,7 +84,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14"
"@0x/base-contract": "^6.2.18"
},
"publishConfig": {
"access": "public"

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "4.2.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "4.2.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "4.2.23",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.2.25 - _February 10, 2021_
* Dependencies updated
## v4.2.24 - _January 26, 2021_
* Dependencies updated
## v4.2.23 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.2.23",
"version": "4.2.25",
"engines": {
"node": ">=6.12"
},
@@ -52,25 +52,25 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-erc1155": "^2.1.22",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/contracts-exchange": "^3.2.23",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/order-utils": "^10.4.14",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -90,7 +90,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/base-contract": "^6.2.18",
"@0x/typescript-typings": "^5.1.6",
"ethereum-types": "^3.4.0"
},

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "4.3.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "4.3.23",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "4.3.22",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.3.24 - _February 10, 2021_
* Dependencies updated
## v4.3.23 - _January 26, 2021_
* Dependencies updated
## v4.3.22 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.3.22",
"version": "4.3.24",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,14 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/libs",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-gen": "^2.0.24",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@0x/subproviders": "^6.2.3",
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/subproviders": "^6.4.1",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -80,13 +80,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/order-utils": "^10.4.14",
"@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/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "3.2.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "3.2.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "3.2.23",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.25 - _February 10, 2021_
* Dependencies updated
## v3.2.24 - _January 26, 2021_
* Dependencies updated
## v3.2.23 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.2.23",
"version": "3.2.25",
"engines": {
"node": ">=6.12"
},
@@ -52,21 +52,21 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-multisig": "^4.1.23",
"@0x/contracts-staking": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -88,13 +88,13 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-erc1155": "^2.1.22",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/order-utils": "^10.4.14",
"@0x/utils": "^6.1.1",
"@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/utils": "^6.2.0",
"lodash": "^4.17.11"
},
"publishConfig": {

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "6.2.19",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "6.2.18",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "6.2.17",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.2.19 - _February 10, 2021_
* Dependencies updated
## v6.2.18 - _January 26, 2021_
* Dependencies updated
## v6.2.17 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "6.2.17",
"version": "6.2.19",
"engines": {
"node": ">=6.12"
},
@@ -52,23 +52,23 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/contracts-exchange": "^3.2.23",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/order-utils": "^10.4.14",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -90,8 +90,8 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/typescript-typings": "^5.1.6",
"ethereum-types": "^3.4.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.23",
"version": "2.7.26",
"private": true,
"engines": {
"node": ">=6.12"
@@ -52,25 +52,25 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contract-addresses": "^5.9.0",
"@0x/contract-wrappers": "^13.12.1",
"@0x/contracts-broker": "^1.1.22",
"@0x/contracts-coordinator": "^3.1.23",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-exchange-forwarder": "^4.2.23",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-extensions": "^6.2.17",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-utils": "^4.7.1",
"@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/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.1.3",
"@0x/migrations": "^6.5.10",
"@0x/order-utils": "^10.4.14",
"@0x/protocol-utils": "^1.1.4",
"@0x/sol-compiler": "^4.4.1",
"@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/tslint-config": "^4.1.3",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@azure/core-asynciterator-polyfill": "^1.0.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -93,21 +93,21 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/asset-swapper": "^5.8.0",
"@0x/base-contract": "^6.2.14",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-erc1155": "^2.1.22",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-erc721": "^3.1.22",
"@0x/contracts-exchange": "^3.2.23",
"@0x/contracts-multisig": "^4.1.23",
"@0x/contracts-staking": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-zero-ex": "^0.18.0",
"@0x/subproviders": "^6.2.3",
"@0x/asset-swapper": "^6.0.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/subproviders": "^6.4.1",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",
"ethereumjs-util": "^6.2.0",
"lodash": "^4.17.11"

View File

@@ -1,239 +0,0 @@
import { MarketBuySwapQuote, MarketSellSwapQuote, Orderbook, SwapQuoter } from '@0x/asset-swapper';
import { blockchainTests, expect, Numberish } from '@0x/contracts-test-utils';
import { assetDataUtils } from '@0x/order-utils';
import { FillResults, SignedOrder } from '@0x/types';
import { BigNumber, logUtils } from '@0x/utils';
import * as _ from 'lodash';
import { TestMainnetAggregatorFillsContract } from '../wrappers';
import { tokens } from './tokens';
blockchainTests.live('Aggregator Mainnet Tests', env => {
// Mainnet address of the `TestMainnetAggregatorFills` contract.
const TEST_CONTRACT_ADDRESS = '0x37Ca306F42748b7fe105F89FCBb2CD03D27c8146';
const TAKER_ADDRESS = '0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B'; // Vitalik
const ORDERBOOK_POLLING_MS = 1000;
const GAS_PRICE = new BigNumber(1);
const TAKER_ASSET_ETH_VALUE = 500e18;
const MIN_BALANCE = 500.1e18;
const SYMBOLS = ['ETH', 'DAI', 'USDC', 'FOAM'];
const TEST_PAIRS = _.flatten(SYMBOLS.map(m => SYMBOLS.filter(t => t !== m).map(t => [m, t])));
const FILL_VALUES = [1, 10, 1e2, 1e3, 1e4, 2.5e4, 5e4];
let testContract: TestMainnetAggregatorFillsContract;
let swapQuoter: SwapQuoter;
let takerEthBalance: BigNumber;
const orderbooks: { [name: string]: Orderbook } = {};
async function getTakerOrdersAsync(takerAssetSymbol: string): Promise<SignedOrder[]> {
if (takerAssetSymbol === 'ETH') {
return [];
}
return getOrdersAsync(takerAssetSymbol, 'ETH');
}
// Fetches ETH -> taker asset orders for the forwarder contract.
async function getOrdersAsync(makerAssetSymbol: string, takerAssetSymbol: string): Promise<SignedOrder[]> {
const takerTokenAddress = tokens[takerAssetSymbol].address;
const makerTokenAddress = tokens[makerAssetSymbol].address;
const makerAssetData = assetDataUtils.encodeERC20AssetData(makerTokenAddress);
const takerAssetData = assetDataUtils.encodeERC20AssetData(takerTokenAddress);
const orders = _.flatten(
await Promise.all(
Object.keys(orderbooks).map(async name =>
getOrdersFromOrderBookAsync(name, makerAssetData, takerAssetData),
),
),
);
const uniqueOrders: SignedOrder[] = [];
for (const order of orders) {
if (!order.makerFee.eq(0) || !order.takerFee.eq(0)) {
continue;
}
if (uniqueOrders.findIndex(o => isSameOrder(order, o)) === -1) {
uniqueOrders.push(order);
}
}
return uniqueOrders;
}
async function getOrdersFromOrderBookAsync(
name: string,
makerAssetData: string,
takerAssetData: string,
): Promise<SignedOrder[]> {
try {
return (await orderbooks[name].getOrdersAsync(makerAssetData, takerAssetData)).map(r => r.order);
} catch (err) {
logUtils.warn(`Failed to retrieve orders from orderbook "${name}".`);
}
return [];
}
function isSameOrder(a: SignedOrder, b: SignedOrder): boolean {
for (const [k, v] of Object.entries(a)) {
if (k in (b as any)) {
if (BigNumber.isBigNumber(v) && !v.eq((b as any)[k])) {
return false;
}
if (v !== (b as any)[k]) {
return false;
}
}
}
return true;
}
function toTokenUnits(symbol: string, weis: Numberish): BigNumber {
return new BigNumber(weis).div(new BigNumber(10).pow(tokens[symbol].decimals));
}
function fromTokenUnits(symbol: string, units: Numberish): BigNumber {
return new BigNumber(units)
.times(new BigNumber(10).pow(tokens[symbol].decimals))
.integerValue(BigNumber.ROUND_DOWN);
}
interface MarketOperationResult {
makerAssetBalanceBefore: BigNumber;
takerAssetBalanceBefore: BigNumber;
makerAssetBalanceAfter: BigNumber;
takerAssetBalanceAfter: BigNumber;
fillResults: FillResults;
}
// Liquidity is low right now so it's possible we didn't have
// enough taker assets to cover the orders, so occasionally we'll get incomplete
// fills. This function will catch those cases.
// TODO(dorothy-zbornak): Remove this special case when liquidity is up.
function checkHadEnoughTakerAsset(
quote: MarketBuySwapQuote | MarketSellSwapQuote,
result: MarketOperationResult,
): boolean {
if (result.takerAssetBalanceBefore.gte(quote.worstCaseQuoteInfo.takerAssetAmount)) {
return true;
}
const takerAssetPct = result.takerAssetBalanceBefore
.div(quote.worstCaseQuoteInfo.takerAssetAmount)
.times(100)
.toNumber()
.toFixed(1);
logUtils.warn(`Could not acquire enough taker asset to complete the fill: ${takerAssetPct}%`);
expect(result.fillResults.makerAssetFilledAmount).to.bignumber.lt(quote.worstCaseQuoteInfo.makerAssetAmount);
return false;
}
before(async () => {
testContract = new TestMainnetAggregatorFillsContract(TEST_CONTRACT_ADDRESS, env.provider, {
...env.txDefaults,
gasPrice: GAS_PRICE,
gas: 10e6,
});
swapQuoter = SwapQuoter.getSwapQuoterForStandardRelayerAPIUrl(env.provider, 'https://api.0x.org/sra');
// Pool orderbooks because we're desperate for liquidity.
orderbooks.swapQuoter = swapQuoter.orderbook;
orderbooks.bamboo = Orderbook.getOrderbookForPollingProvider({
httpEndpoint: 'https://sra.bamboorelay.com/0x/v3',
pollingIntervalMs: ORDERBOOK_POLLING_MS,
});
// TODO(dorothy-zbornak): Uncomment when radar's SRA is up.
// orderbooks.radar = Orderbook.getOrderbookForPollingProvider({
// httpEndpoint: 'https://api-v3.radarrelay.com/v3',
// pollingIntervalMs: ORDERBOOK_POLLING_MS,
// });
takerEthBalance = await env.web3Wrapper.getBalanceInWeiAsync(TAKER_ADDRESS);
});
it('taker has minimum ETH', async () => {
expect(takerEthBalance).to.bignumber.gte(MIN_BALANCE);
});
describe('market sells', () => {
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
for (const fillValue of FILL_VALUES) {
const fillAmount = fromTokenUnits(takerSymbol, new BigNumber(fillValue).div(tokens[takerSymbol].price));
it(`sell ${toTokenUnits(takerSymbol, fillAmount)} ${takerSymbol} for ${makerSymbol}`, async () => {
const [quote, takerOrders] = await Promise.all([
swapQuoter.getMarketSellSwapQuoteAsync(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
fillAmount,
{ gasPrice: GAS_PRICE },
),
getTakerOrdersAsync(takerSymbol),
]);
// Buy taker assets from `takerOrders` and and perform a
// market sell on the bridge orders.
const fill = await testContract
.marketSell(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
quote.orders,
takerOrders,
quote.orders.map(o => o.signature),
takerOrders.map(o => o.signature),
quote.takerAssetFillAmount,
)
.callAsync({
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
from: TAKER_ADDRESS,
gasPrice: quote.gasPrice,
});
if (checkHadEnoughTakerAsset(quote, fill)) {
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
quote.worstCaseQuoteInfo.makerAssetAmount,
);
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
quote.takerAssetFillAmount,
);
}
});
}
}
});
describe('market buys', () => {
for (const [makerSymbol, takerSymbol] of TEST_PAIRS) {
for (const fillValue of FILL_VALUES) {
const fillAmount = fromTokenUnits(makerSymbol, new BigNumber(fillValue).div(tokens[makerSymbol].price));
it(`buy ${toTokenUnits(makerSymbol, fillAmount)} ${makerSymbol} with ${takerSymbol}`, async () => {
const [quote, takerOrders] = await Promise.all([
swapQuoter.getMarketBuySwapQuoteAsync(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
fillAmount,
{ gasPrice: GAS_PRICE },
),
getTakerOrdersAsync(takerSymbol),
]);
// Buy taker assets from `takerOrders` and and perform a
// market buy on the bridge orders.
const fill = await testContract
.marketBuy(
tokens[makerSymbol].address,
tokens[takerSymbol].address,
quote.orders,
takerOrders,
quote.orders.map(o => o.signature),
takerOrders.map(o => o.signature),
quote.makerAssetFillAmount,
)
.callAsync({
value: quote.worstCaseQuoteInfo.protocolFeeInWeiAmount.plus(TAKER_ASSET_ETH_VALUE),
from: TAKER_ADDRESS,
gasPrice: quote.gasPrice,
});
if (checkHadEnoughTakerAsset(quote, fill)) {
expect(fill.fillResults.takerAssetFilledAmount, 'takerAssetFilledAmount').to.bignumber.lte(
quote.worstCaseQuoteInfo.takerAssetAmount,
);
expect(fill.fillResults.makerAssetFilledAmount, 'makerAssetFilledAmount').to.bignumber.gte(
quote.makerAssetFillAmount,
);
}
});
}
}
});
});

View File

@@ -1,77 +0,0 @@
export const tokens: { [symbol: string]: { address: string; decimals: number; price: number } } = {
ETH: {
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
decimals: 18,
price: 133,
},
SAI: {
address: '0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359',
decimals: 18,
price: 1,
},
DAI: {
address: '0x6B175474E89094C44Da98b954EedeAC495271d0F',
decimals: 18,
price: 1,
},
USDC: {
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48',
decimals: 6,
price: 1,
},
WBTC: {
address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599',
decimals: 8,
price: 6900,
},
MKR: {
address: '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2',
decimals: 18,
price: 454,
},
BAT: {
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
decimals: 18,
price: 0.17,
},
OMG: {
address: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07',
decimals: 18,
price: 0.65,
},
ZRX: {
address: '0xE41d2489571d322189246DaFA5ebDe1F4699F498',
decimals: 18,
price: 0.19,
},
ZIL: {
address: '0x05f4a42e251f2d52b8ed15E9FEdAacFcEF1FAD27',
decimals: 12,
price: 0.004,
},
FOAM: {
address: '0x4946Fcea7C692606e8908002e55A582af44AC121',
decimals: 18,
price: 0.004,
},
USDT: {
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
decimals: 6,
price: 0.019,
},
REP: {
address: '0x1985365e9f78359a9B6AD760e32412f4a445E862',
decimals: 18,
price: 8.9,
},
MANA: {
address: '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942',
decimals: 18,
price: 0.025,
},
LINK: {
address: '0x514910771AF9Ca656af840dff83E8264EcF986CA',
decimals: 18,
price: 1.8,
},
};

View File

@@ -1,360 +0,0 @@
import { ContractAddresses } from '@0x/contract-addresses';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import { IExchangeContract } from '@0x/contracts-exchange';
import { blockchainTests, constants, expect, getRandomPortion, verifyEventsFromLogs } from '@0x/contracts-test-utils';
import {
artifacts as exchangeProxyArtifacts,
IZeroExContract,
LogMetadataTransformerContract,
} from '@0x/contracts-zero-ex';
import { migrateOnceAsync } from '@0x/migrations';
import { assetDataUtils, signatureUtils, SignedExchangeProxyMetaTransaction } from '@0x/order-utils';
import {
encodeFillQuoteTransformerData,
encodePayTakerTransformerData,
ETH_TOKEN_ADDRESS,
FillQuoteTransformerSide,
findTransformerNonce,
Signature,
} from '@0x/protocol-utils';
import { AssetProxyId, Order, SignedOrder } from '@0x/types';
import { BigNumber, hexUtils } from '@0x/utils';
import * as ethjs from 'ethereumjs-util';
const { MAX_UINT256, NULL_ADDRESS, NULL_BYTES, ZERO_AMOUNT } = constants;
function sigstruct(signature: string): Signature {
return {
v: parseInt(hexUtils.slice(signature, 0, 1), 16),
signatureType: parseInt(hexUtils.slice(signature, 65, 66), 16),
r: hexUtils.slice(signature, 1, 33),
s: hexUtils.slice(signature, 33, 65),
};
}
blockchainTests.resets('exchange proxy - meta-transactions', env => {
const quoteSignerKey = hexUtils.random();
const quoteSigner = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(quoteSignerKey)));
let owner: string;
let relayer: string;
let maker: string;
let taker: string;
let flashWalletAddress: string;
let zeroEx: IZeroExContract;
let exchange: IExchangeContract;
let inputToken: DummyERC20TokenContract;
let outputToken: DummyERC20TokenContract;
let feeToken: DummyERC20TokenContract;
let addresses: ContractAddresses;
let protocolFee: BigNumber;
let metadataTransformer: LogMetadataTransformerContract;
const GAS_PRICE = new BigNumber('1e9');
const MAKER_BALANCE = new BigNumber('100e18');
const TAKER_BALANCE = new BigNumber('100e18');
const TAKER_FEE_BALANCE = new BigNumber('100e18');
before(async () => {
[, relayer, maker, taker] = await env.getAccountAddressesAsync();
addresses = await migrateOnceAsync(env.provider);
zeroEx = new IZeroExContract(addresses.exchangeProxy, env.provider, env.txDefaults, {
LogMetadataTransformer: LogMetadataTransformerContract.ABI(),
DummyERC20Token: DummyERC20TokenContract.ABI(),
});
exchange = new IExchangeContract(addresses.exchange, env.provider, env.txDefaults);
[inputToken, outputToken, feeToken] = await Promise.all(
[...new Array(3)].map(i =>
DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
env.provider,
env.txDefaults,
{},
`DummyToken-${i}`,
`TOK${i}`,
new BigNumber(18),
BigNumber.max(MAKER_BALANCE, TAKER_BALANCE),
),
),
);
// LogMetadataTransformer is not deployed in migrations.
metadataTransformer = await LogMetadataTransformerContract.deployFrom0xArtifactAsync(
exchangeProxyArtifacts.LogMetadataTransformer,
env.provider,
{
...env.txDefaults,
from: addresses.exchangeProxyTransformerDeployer,
},
{},
);
owner = await zeroEx.owner().callAsync();
protocolFee = await exchange.protocolFeeMultiplier().callAsync();
flashWalletAddress = await zeroEx.getTransformWallet().callAsync();
const erc20Proxy = await exchange.getAssetProxy(AssetProxyId.ERC20).callAsync();
const allowanceTarget = await zeroEx.getAllowanceTarget().callAsync();
await outputToken.mint(MAKER_BALANCE).awaitTransactionSuccessAsync({ from: maker });
await inputToken.mint(TAKER_BALANCE).awaitTransactionSuccessAsync({ from: taker });
await feeToken.mint(TAKER_FEE_BALANCE).awaitTransactionSuccessAsync({ from: taker });
await outputToken.approve(erc20Proxy, MAX_UINT256).awaitTransactionSuccessAsync({ from: maker });
await inputToken.approve(allowanceTarget, MAX_UINT256).awaitTransactionSuccessAsync({ from: taker });
await feeToken.approve(allowanceTarget, MAX_UINT256).awaitTransactionSuccessAsync({ from: taker });
await zeroEx.setQuoteSigner(quoteSigner).awaitTransactionSuccessAsync({ from: owner });
});
interface Transformation {
deploymentNonce: number;
data: string;
}
interface SwapInfo {
inputTokenAddress: string;
outputTokenAddress: string;
inputTokenAmount: BigNumber;
minOutputTokenAmount: BigNumber;
transformations: Transformation[];
orders: SignedOrder[];
}
async function generateSwapAsync(orderFields: Partial<Order> = {}, isRfqt: boolean = false): Promise<SwapInfo> {
const order = await signatureUtils.ecSignTypedDataOrderAsync(
env.provider,
{
chainId: 1337,
exchangeAddress: exchange.address,
expirationTimeSeconds: new BigNumber(Date.now()),
salt: new BigNumber(hexUtils.random()),
feeRecipientAddress: NULL_ADDRESS,
senderAddress: NULL_ADDRESS,
takerAddress: isRfqt ? flashWalletAddress : NULL_ADDRESS,
makerAddress: maker,
makerAssetData: assetDataUtils.encodeERC20AssetData(outputToken.address),
takerAssetData: assetDataUtils.encodeERC20AssetData(inputToken.address),
makerFeeAssetData: NULL_BYTES,
takerFeeAssetData: NULL_BYTES,
takerAssetAmount: getRandomPortion(TAKER_BALANCE),
makerAssetAmount: getRandomPortion(MAKER_BALANCE),
makerFee: ZERO_AMOUNT,
takerFee: ZERO_AMOUNT,
...orderFields,
},
maker,
);
const transformations = [
{
deploymentNonce: findTransformerNonce(
addresses.transformers.fillQuoteTransformer,
addresses.exchangeProxyTransformerDeployer,
),
data: encodeFillQuoteTransformerData({
orders: [order],
signatures: [order.signature],
buyToken: outputToken.address,
sellToken: inputToken.address,
fillAmount: order.takerAssetAmount,
maxOrderFillAmounts: [],
refundReceiver: hexUtils.leftPad(2, 20), // Send refund to sender.
rfqtTakerAddress: isRfqt ? taker : NULL_ADDRESS,
side: FillQuoteTransformerSide.Sell,
}),
},
{
deploymentNonce: findTransformerNonce(
addresses.transformers.payTakerTransformer,
addresses.exchangeProxyTransformerDeployer,
),
data: encodePayTakerTransformerData({
tokens: [inputToken.address, outputToken.address, ETH_TOKEN_ADDRESS],
amounts: [MAX_UINT256, MAX_UINT256, MAX_UINT256],
}),
},
{
deploymentNonce: findTransformerNonce(
metadataTransformer.address,
addresses.exchangeProxyTransformerDeployer,
),
data: NULL_BYTES,
},
];
return {
transformations,
orders: [order],
inputTokenAddress: inputToken.address,
outputTokenAddress: outputToken.address,
inputTokenAmount: order.takerAssetAmount,
minOutputTokenAmount: order.makerAssetAmount,
};
}
function getSwapData(swap: SwapInfo): string {
return zeroEx
.transformERC20(
swap.inputTokenAddress,
swap.outputTokenAddress,
swap.inputTokenAmount,
swap.minOutputTokenAmount,
swap.transformations,
)
.getABIEncodedTransactionData();
}
async function createMetaTransactionAsync(
data: string,
value: BigNumber,
fee?: BigNumber | number,
): Promise<SignedExchangeProxyMetaTransaction> {
return signatureUtils.ecSignTypedDataExchangeProxyMetaTransactionAsync(
env.provider,
{
value,
signer: taker,
sender: relayer,
minGasPrice: GAS_PRICE,
maxGasPrice: GAS_PRICE,
expirationTimeSeconds: new BigNumber(Math.floor(Date.now() / 1000) + 60),
salt: new BigNumber(hexUtils.random()),
callData: data,
feeToken: feeToken.address,
feeAmount: fee !== undefined ? new BigNumber(fee) : getRandomPortion(TAKER_FEE_BALANCE),
domain: {
chainId: 1,
name: 'ZeroEx',
version: '1.0.0',
verifyingContract: zeroEx.address,
},
},
taker,
);
}
it('can call `transformERC20()` with calldata and no relayer fee', async () => {
const swap = await generateSwapAsync();
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee, 0);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
.minus(GAS_PRICE.times(receipt.gasUsed));
// Ensure the relayer got back the unused protocol fees.
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
// Ensure the relayer got paid no mtx fees.
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
// Ensure the taker got output tokens.
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
// Ensure the maker got input tokens.
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
// Check events.
verifyEventsFromLogs(
receipt.logs,
[
{
taker,
sender: zeroEx.address,
data: NULL_BYTES,
},
],
'TransformerMetadata',
);
});
it('can call `transformERC20()` with calldata and a relayer fee', async () => {
const swap = await generateSwapAsync();
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
.minus(GAS_PRICE.times(receipt.gasUsed));
// Ensure the relayer got back the unused protocol fees.
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
// Ensure the relayer got paid mtx fees.
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(mtx.feeAmount);
// Ensure the taker got output tokens.
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
// Ensure the maker got input tokens.
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
// Check events.
verifyEventsFromLogs(
receipt.logs,
[
{
taker,
sender: zeroEx.address,
data: NULL_BYTES,
},
],
'TransformerMetadata',
);
});
it('`transformERC20()` can fill RFQT order', async () => {
const swap = await generateSwapAsync({}, true);
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
const mtx = await createMetaTransactionAsync(getSwapData(swap), _protocolFee, 0);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
.minus(GAS_PRICE.times(receipt.gasUsed));
// Ensure the relayer got back the unused protocol fees.
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
// Ensure the relayer got paid no mtx fees.
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
// Ensure the taker got output tokens.
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
// Ensure the maker got input tokens.
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
// Check events.
verifyEventsFromLogs(
receipt.logs,
[
{
taker,
sender: zeroEx.address,
data: NULL_BYTES,
},
],
'TransformerMetadata',
);
});
it('`transformERC20()` can fill RFQT order if quote signer configured', async () => {
const swap = await generateSwapAsync({}, true);
const callData = getSwapData(swap);
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
const mtx = await createMetaTransactionAsync(callData, _protocolFee, 0);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
await zeroEx.setQuoteSigner(NULL_ADDRESS).awaitTransactionSuccessAsync({ from: owner });
const receipt = await zeroEx
.executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
.minus(GAS_PRICE.times(receipt.gasUsed));
// Ensure the relayer got back the unused protocol fees.
expect(relayerEthRefund).to.bignumber.eq(protocolFee.times(GAS_PRICE));
// Ensure the relayer got paid no mtx fees.
expect(await feeToken.balanceOf(relayer).callAsync()).to.bignumber.eq(0);
// Ensure the taker got output tokens.
expect(await outputToken.balanceOf(taker).callAsync()).to.bignumber.eq(swap.minOutputTokenAmount);
// Ensure the maker got input tokens.
expect(await inputToken.balanceOf(maker).callAsync()).to.bignumber.eq(swap.inputTokenAmount);
// Check events.
verifyEventsFromLogs(
receipt.logs,
[
{
taker,
sender: zeroEx.address,
data: NULL_BYTES,
},
],
'TransformerMetadata',
);
});
});

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "4.1.25",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "4.1.24",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "4.1.23",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.25 - _February 10, 2021_
* Dependencies updated
## v4.1.24 - _January 26, 2021_
* Dependencies updated
## v4.1.23 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "4.1.23",
"version": "4.1.25",
"engines": {
"node": ">=6.12"
},
@@ -49,18 +49,18 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/utils": "^6.2.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/node": "12.12.54",
@@ -78,7 +78,7 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/base-contract": "^6.2.18",
"@0x/typescript-typings": "^5.1.6",
"ethereum-types": "^3.4.0"
},

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "2.0.32",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "2.0.31",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "2.0.30",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.32 - _February 10, 2021_
* Dependencies updated
## v2.0.31 - _January 26, 2021_
* Dependencies updated
## v2.0.30 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-staking",
"version": "2.0.30",
"version": "2.0.32",
"engines": {
"node": ">=6.12"
},
@@ -53,20 +53,20 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-asset-proxy": "^3.7.4",
"@0x/contracts-dev-utils": "^1.3.21",
"@0x/contracts-erc20": "^3.3.1",
"@0x/contracts-exchange-libs": "^4.3.22",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-utils": "^4.7.1",
"@0x/dev-utils": "^4.1.3",
"@0x/order-utils": "^10.4.14",
"@0x/sol-compiler": "^4.4.1",
"@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/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/lodash": "4.14.104",
"@types/node": "12.12.54",
"chai": "^4.0.1",
@@ -87,10 +87,10 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/base-contract": "^6.2.18",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"ethereum-types": "^3.4.0",
"ethereumjs-util": "^5.1.1"
},

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "5.3.21",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "5.3.20",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "5.3.19",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.3.21 - _February 10, 2021_
* Dependencies updated
## v5.3.20 - _January 26, 2021_
* Dependencies updated
## v5.3.19 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.3.19",
"version": "5.3.21",
"engines": {
"node": ">=6.12"
},
@@ -34,7 +34,7 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/test-utils",
"devDependencies": {
"@0x/sol-compiler": "^4.4.1",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",
"npm-run-all": "^4.1.2",
"shx": "^0.2.2",
@@ -42,20 +42,20 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/assert": "^3.0.19",
"@0x/base-contract": "^6.2.14",
"@0x/contract-addresses": "^5.9.0",
"@0x/dev-utils": "^4.1.3",
"@0x/json-schemas": "^5.3.4",
"@0x/order-utils": "^10.4.14",
"@0x/sol-coverage": "^4.0.24",
"@0x/sol-profiler": "^4.1.14",
"@0x/sol-trace": "^3.0.24",
"@0x/subproviders": "^6.2.3",
"@0x/assert": "^3.0.21",
"@0x/base-contract": "^6.2.18",
"@0x/contract-addresses": "^5.10.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/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",
"@types/bn.js": "^4.11.0",
"@types/js-combinatorics": "^0.5.29",
"@types/lodash": "4.14.104",

View File

@@ -0,0 +1,10 @@
# Blacklist all files
.*
*
# Whitelist lib
!lib/**/*
# Whitelist Solidity contracts
!contracts/src/**/*
# Blacklist tests in lib
/lib/test/*
# Package specific ignore

View File

@@ -0,0 +1,21 @@
[
{
"timestamp": 1612950500,
"version": "1.0.1",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"version": "1.0.0",
"changes": [
{
"note": "Create this package",
"pr": 120
}
],
"timestamp": 1611869778
}
]

View File

@@ -0,0 +1,14 @@
<!--
changelogUtils.file is auto-generated using the monorepo-scripts package. Don't edit directly.
Edit the package's CHANGELOG.json file only.
-->
CHANGELOG
## v1.0.1 - _February 10, 2021_
* Dependencies updated
## v1.0.0 - _January 28, 2021_
* Create this package (#120)

View File

@@ -0,0 +1 @@
[]

View File

@@ -0,0 +1,65 @@
## Governance
This package contains contracts for the governance of the 0x ZRX treasury.
## Installation
**Install**
```bash
npm install @0x/contracts-treasury --save
```
## Contributing
We strongly recommend that the community help us make improvements and determine the future direction of the protocol. To report bugs within this package, please create an issue in this repository.
For proposals regarding the 0x protocol's smart contract architecture, message format, or additional functionality, go to the [0x Improvement Proposals (ZEIPs)](https://github.com/0xProject/ZEIPs) repository and follow the contribution guidelines provided therein.
Please read our [contribution guidelines](../../CONTRIBUTING.md) before getting started.
### Install Dependencies
If you don't have yarn workspaces enabled (Yarn < v1.0) - enable them:
```bash
yarn config set workspaces-experimental true
```
Then install dependencies
```bash
yarn install
```
### Build
To build this package and all other monorepo packages that it depends on, run the following from the monorepo root directory:
```bash
PKG=@0x/contracts-treasury yarn build
```
Or continuously rebuild on change:
```bash
PKG=@0x/contracts-treasury yarn watch
```
### Clean
```bash
yarn clean
```
### Lint
```bash
yarn lint
```
### Run Tests
```bash
yarn test
```

View File

@@ -0,0 +1,29 @@
{
"artifactsDir": "./test/generated-artifacts",
"contractsDir": "./contracts",
"useDockerisedSolc": false,
"isOfflineMode": false,
"shouldSaveStandardInput": true,
"shouldCompileIndependently": true,
"compilerSettings": {
"evmVersion": "istanbul",
"optimizer": {
"enabled": true,
"runs": 1000000,
"details": { "yul": true, "deduplicate": true, "cse": true, "constantOptimizer": true }
},
"outputSelection": {
"*": {
"*": [
"abi",
"devdoc",
"evm.bytecode.object",
"evm.bytecode.sourceMap",
"evm.deployedBytecode.object",
"evm.deployedBytecode.sourceMap",
"evm.methodIdentifiers"
]
}
}
}
}

View File

@@ -0,0 +1,63 @@
// 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.12;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "./IStaking.sol";
contract DefaultPoolOperator {
using LibERC20TokenV06 for IERC20TokenV06;
// Immutables
IStaking public immutable stakingProxy;
IERC20TokenV06 public immutable weth;
bytes32 public immutable poolId;
/// @dev Initializes this contract and creates a staking pool.
/// @param stakingProxy_ The 0x staking proxy contract.
/// @param weth_ The WETH token contract.
constructor(
IStaking stakingProxy_,
IERC20TokenV06 weth_
)
public
{
stakingProxy = stakingProxy_;
weth = weth_;
// operator share = 100%
poolId = stakingProxy_.createStakingPool(10 ** 6, false);
}
/// @dev Sends this contract's entire WETH balance to the
/// staking proxy contract. This function exists in case
/// someone joins the default staking pool and starts
/// market making for some reason, thus earning this contract
/// some staking rewards. Note that anyone can call this
/// function at any time.
function returnStakingRewards()
external
{
uint256 wethBalance = weth.compatBalanceOf(address(this));
weth.compatTransfer(address(stakingProxy), wethBalance);
}
}

View File

@@ -0,0 +1,127 @@
// 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.12;
pragma experimental ABIEncoderV2;
interface IStaking {
/// @dev Statuses that stake can exist in.
/// Any stake can be (re)delegated effective at the next epoch
/// Undelegated stake can be withdrawn if it is available in both the current and next epoch
enum StakeStatus {
UNDELEGATED,
DELEGATED
}
/// @dev Encapsulates a balance for the current and next epochs.
/// Note that these balances may be stale if the current epoch
/// is greater than `currentEpoch`.
/// @param currentEpoch The current epoch
/// @param currentEpochBalance Balance in the current epoch.
/// @param nextEpochBalance Balance in `currentEpoch+1`.
struct StoredBalance {
uint64 currentEpoch;
uint96 currentEpochBalance;
uint96 nextEpochBalance;
}
/// @dev Holds the metadata for a staking pool.
/// @param operator Operator of the pool.
/// @param operatorShare Fraction of the total balance owned by the operator, in ppm.
struct Pool {
address operator;
uint32 operatorShare;
}
/// @dev Create a new staking pool. The sender will be the operator of this pool.
/// Note that an operator must be payable.
/// @param operatorShare Portion of rewards owned by the operator, in ppm.
/// @param addOperatorAsMaker Adds operator to the created pool as a maker for convenience iff true.
/// @return poolId The unique pool id generated for this pool.
function createStakingPool(uint32 operatorShare, bool addOperatorAsMaker)
external
returns (bytes32 poolId);
/// @dev Returns the current staking epoch number.
/// @return epoch The current epoch.
function currentEpoch()
external
view
returns (uint256 epoch);
/// @dev Returns the time (in seconds) at which the current staking epoch started.
/// @return startTime The start time of the current epoch, in seconds.
function currentEpochStartTimeInSeconds()
external
view
returns (uint256 startTime);
/// @dev Returns the duration of an epoch in seconds. This value can be updated.
/// @return duration The duration of an epoch, in seconds.
function epochDurationInSeconds()
external
view
returns (uint256 duration);
/// @dev Returns a staking pool
/// @param poolId Unique id of pool.
function getStakingPool(bytes32 poolId)
external
view
returns (Pool memory);
/// @dev Gets global stake for a given status.
/// @param stakeStatus UNDELEGATED or DELEGATED
/// @return balance Global stake for given status.
function getGlobalStakeByStatus(StakeStatus stakeStatus)
external
view
returns (StoredBalance memory balance);
/// @dev Gets an owner's stake balances by status.
/// @param staker Owner of stake.
/// @param stakeStatus UNDELEGATED or DELEGATED
/// @return balance Owner's stake balances for given status.
function getOwnerStakeByStatus(
address staker,
StakeStatus stakeStatus
)
external
view
returns (StoredBalance memory balance);
/// @dev Returns the total stake delegated to a specific staking pool,
/// across all members.
/// @param poolId Unique Id of pool.
/// @return balance Total stake delegated to pool.
function getTotalStakeDelegatedToPool(bytes32 poolId)
external
view
returns (StoredBalance memory balance);
/// @dev Returns the stake delegated to a specific staking pool, by a given staker.
/// @param staker of stake.
/// @param poolId Unique Id of pool.
/// @return balance Stake delegated to pool by staker.
function getStakeDelegatedToPoolByOwner(address staker, bytes32 poolId)
external
view
returns (StoredBalance memory balance);
}

View File

@@ -0,0 +1,169 @@
// 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.12;
pragma experimental ABIEncoderV2;
import "./DefaultPoolOperator.sol";
import "./IStaking.sol";
interface IZrxTreasury {
struct TreasuryParameters {
uint256 votingPeriod;
uint256 proposalThreshold;
uint256 quorumThreshold;
}
struct ProposedAction {
address target;
bytes data;
uint256 value;
}
struct Proposal {
bytes32 actionsHash;
uint256 executionEpoch;
uint256 voteEpoch;
uint256 votesFor;
uint256 votesAgainst;
bool executed;
}
event ProposalCreated(
address proposer,
bytes32[] operatedPoolIds,
uint256 proposalId,
ProposedAction[] actions,
uint256 executionEpoch,
string description
);
event VoteCast(
address voter,
bytes32[] operatedPoolIds,
uint256 proposalId,
bool support,
uint256 votingPower
);
event ProposalExecuted(uint256 proposalId);
function stakingProxy()
external
view
returns (IStaking);
function defaultPoolOperator()
external
view
returns (DefaultPoolOperator);
function defaultPoolId()
external
view
returns (bytes32);
function votingPeriod()
external
view
returns (uint256);
function proposalThreshold()
external
view
returns (uint256);
function quorumThreshold()
external
view
returns (uint256);
/// @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`
/// for how voting power is computed. If a proposal is successfully
/// created, voting starts at the epoch after next (currentEpoch + 2).
/// If the vote passes, the proposal is executable during the
/// `executionEpoch`. See `hasProposalPassed` for the passing criteria.
/// @param actions The proposed ZRX actions. An action specifies a
/// contract call.
/// @param executionEpoch The epoch during which the proposal is to
/// be executed if it passes. Must be at least two epochs
/// from the current epoch.
/// @param description A text description for the proposal.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return proposalId The ID of the newly created proposal.
function propose(
ProposedAction[] calldata actions,
uint256 executionEpoch,
string calldata description,
bytes32[] calldata operatedPoolIds
)
external
returns (uint256 proposalId);
/// @dev Casts a vote for the given proposal. Only callable
/// during the voting period for that proposal. See
/// `getVotingPower` for how voting power is computed.
/// @param proposalId The ID of the proposal to vote on.
/// @param support Whether to support the proposal or not.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
function castVote(
uint256 proposalId,
bool support,
bytes32[] calldata operatedPoolIds
)
external;
/// @dev Executes a proposal that has passed and is
/// currently executable.
/// @param proposalId The ID of the proposal to execute.
/// @param actions Actions associated with the proposal to execute.
function execute(uint256 proposalId, ProposedAction[] memory actions)
external
payable;
/// @dev Returns the total number of proposals.
/// @return count The number of proposals.
function proposalCount()
external
view
returns (uint256 count);
/// @dev Computes the current voting power of the given account.
/// Voting power is equal to:
/// (ZRX delegated to the default pool) +
/// 0.5 * (ZRX delegated to other pools) +
/// 0.5 * (ZRX delegated to pools operated by account)
/// @param account The address of the account.
/// @param operatedPoolIds The pools operated by `account`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return votingPower The current voting power of the given account.
function getVotingPower(address account, bytes32[] calldata operatedPoolIds)
external
view
returns (uint256 votingPower);
}

View File

@@ -0,0 +1,360 @@
// 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.12;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-zero-ex/contracts/src/features/libs/LibSignature.sol";
import "./IZrxTreasury.sol";
contract ZrxTreasury is
IZrxTreasury
{
using LibERC20TokenV06 for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibRichErrorsV06 for bytes;
using LibBytesV06 for bytes;
// Immutables
IStaking public immutable override stakingProxy;
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;
// Storage
Proposal[] public proposals;
mapping (uint256 => mapping (address => bool)) public hasVoted;
/// @dev Initializes the ZRX treasury and creates the default
/// staking pool.
/// @param stakingProxy_ The 0x staking proxy contract.
/// @param weth_ The WETH token contract.
/// @param params Immutable treasury parameters.
constructor(
IStaking stakingProxy_,
IERC20TokenV06 weth_,
TreasuryParameters memory params
)
public
{
require(
params.votingPeriod < stakingProxy_.epochDurationInSeconds(),
"VOTING_PERIOD_TOO_LONG"
);
stakingProxy = stakingProxy_;
DefaultPoolOperator defaultPoolOperator_ = new DefaultPoolOperator(
stakingProxy_,
weth_
);
defaultPoolOperator = defaultPoolOperator_;
defaultPoolId = defaultPoolOperator_.poolId();
votingPeriod = params.votingPeriod;
proposalThreshold = params.proposalThreshold;
quorumThreshold = params.quorumThreshold;
}
// solhint-disable
/// @dev Allows this contract to receive ether.
receive() external payable {}
// solhint-enable
/// @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`
/// for how voting power is computed. If a proposal is successfully
/// created, voting starts at the epoch after next (currentEpoch + 2).
/// If the vote passes, the proposal is executable during the
/// `executionEpoch`. See `hasProposalPassed` for the passing criteria.
/// @param actions The proposed ZRX actions. An action specifies a
/// contract call.
/// @param executionEpoch The epoch during which the proposal is to
/// be executed if it passes. Must be at least two epochs
/// from the current epoch.
/// @param description A text description for the proposal.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return proposalId The ID of the newly created proposal.
function propose(
ProposedAction[] memory actions,
uint256 executionEpoch,
string memory description,
bytes32[] memory operatedPoolIds
)
public
override
returns (uint256 proposalId)
{
require(
getVotingPower(msg.sender, operatedPoolIds) >= proposalThreshold,
"propose/INSUFFICIENT_VOTING_POWER"
);
require(
actions.length > 0,
"propose/NO_ACTIONS_PROPOSED"
);
uint256 currentEpoch = stakingProxy.currentEpoch();
require(
executionEpoch >= currentEpoch + 2,
"propose/INVALID_EXECUTION_EPOCH"
);
proposalId = proposalCount();
Proposal storage newProposal = proposals.push();
newProposal.actionsHash = keccak256(abi.encode(actions));
newProposal.executionEpoch = executionEpoch;
newProposal.voteEpoch = currentEpoch + 2;
emit ProposalCreated(
msg.sender,
operatedPoolIds,
proposalId,
actions,
executionEpoch,
description
);
}
/// @dev Casts a vote for the given proposal. Only callable
/// during the voting period for that proposal. See
/// `getVotingPower` for how voting power is computed.
/// @param proposalId The ID of the proposal to vote on.
/// @param support Whether to support the proposal or not.
/// @param operatedPoolIds The pools operated by `msg.sender`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
function castVote(
uint256 proposalId,
bool support,
bytes32[] memory operatedPoolIds
)
public
override
{
if (proposalId >= proposalCount()) {
revert("castVote/INVALID_PROPOSAL_ID");
}
if (hasVoted[proposalId][msg.sender]) {
revert("castVote/ALREADY_VOTED");
}
Proposal memory proposal = proposals[proposalId];
if (
proposal.voteEpoch != stakingProxy.currentEpoch() ||
_hasVoteEnded(proposal.voteEpoch)
) {
revert("castVote/VOTING_IS_CLOSED");
}
uint256 votingPower = getVotingPower(msg.sender, operatedPoolIds);
if (votingPower == 0) {
revert("castVote/NO_VOTING_POWER");
}
if (support) {
proposals[proposalId].votesFor = proposals[proposalId].votesFor
.safeAdd(votingPower);
hasVoted[proposalId][msg.sender] = true;
} else {
proposals[proposalId].votesAgainst = proposals[proposalId].votesAgainst
.safeAdd(votingPower);
hasVoted[proposalId][msg.sender] = true;
}
emit VoteCast(
msg.sender,
operatedPoolIds,
proposalId,
support,
votingPower
);
}
/// @dev Executes a proposal that has passed and is
/// currently executable.
/// @param proposalId The ID of the proposal to execute.
/// @param actions Actions associated with the proposal to execute.
function execute(uint256 proposalId, ProposedAction[] memory actions)
public
payable
override
{
if (proposalId >= proposalCount()) {
revert("execute/INVALID_PROPOSAL_ID");
}
Proposal memory proposal = proposals[proposalId];
_assertProposalExecutable(proposal, actions);
proposals[proposalId].executed = true;
for (uint256 i = 0; i != actions.length; i++) {
ProposedAction memory action = actions[i];
(bool didSucceed, ) = action.target.call{value: action.value}(action.data);
require(
didSucceed,
"execute/ACTION_EXECUTION_FAILED"
);
}
emit ProposalExecuted(proposalId);
}
/// @dev Returns the total number of proposals.
/// @return count The number of proposals.
function proposalCount()
public
override
view
returns (uint256 count)
{
return proposals.length;
}
/// @dev Computes the current voting power of the given account.
/// Voting power is equal to:
/// (ZRX delegated to the default pool) +
/// 0.5 * (ZRX delegated to other pools) +
/// 0.5 * (ZRX delegated to pools operated by account)
/// @param account The address of the account.
/// @param operatedPoolIds The pools operated by `account`. The
/// ZRX currently delegated to those pools will be accounted
/// for in the voting power.
/// @return votingPower The current voting power of the given account.
function getVotingPower(address account, bytes32[] memory operatedPoolIds)
public
override
view
returns (uint256 votingPower)
{
uint256 delegatedBalance = stakingProxy.getOwnerStakeByStatus(
account,
IStaking.StakeStatus.DELEGATED
).currentEpochBalance;
uint256 balanceDelegatedToDefaultPool = stakingProxy.getStakeDelegatedToPoolByOwner(
account,
defaultPoolId
).currentEpochBalance;
// Voting power for ZRX delegated to the default pool is not diluted,
// so we double-count the balance delegated to the default pool before
// dividing by 2.
votingPower = delegatedBalance
.safeAdd(balanceDelegatedToDefaultPool)
.safeDiv(2);
// Add voting power for operated staking pools.
for (uint256 i = 0; i != operatedPoolIds.length; i++) {
IStaking.Pool memory pool = stakingProxy.getStakingPool(operatedPoolIds[i]);
require(
pool.operator == account,
"getVotingPower/POOL_NOT_OPERATED_BY_ACCOUNT"
);
uint96 stakeDelegatedToPool = stakingProxy
.getTotalStakeDelegatedToPool(operatedPoolIds[i])
.currentEpochBalance;
uint256 poolVotingPower = uint256(stakeDelegatedToPool).safeDiv(2);
votingPower = votingPower.safeAdd(poolVotingPower);
}
return votingPower;
}
/// @dev Checks whether the given proposal is executable.
/// Reverts if not.
/// @param proposal The proposal to check.
function _assertProposalExecutable(
Proposal memory proposal,
ProposedAction[] memory actions
)
private
view
{
require(
keccak256(abi.encode(actions)) == proposal.actionsHash,
"_assertProposalExecutable/INVALID_ACTIONS"
);
require(
_hasProposalPassed(proposal),
"_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED"
);
require(
!proposal.executed,
"_assertProposalExecutable/PROPOSAL_ALREADY_EXECUTED"
);
require(
stakingProxy.currentEpoch() == proposal.executionEpoch,
"_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH"
);
}
/// @dev Checks whether the given proposal has passed or not.
/// @param proposal The proposal to check.
/// @return hasPassed Whether the proposal has passed.
function _hasProposalPassed(Proposal memory proposal)
private
view
returns (bool hasPassed)
{
// Proposal is not passed until the vote is over.
if (!_hasVoteEnded(proposal.voteEpoch)) {
return false;
}
// Must have >50% support.
if (proposal.votesFor <= proposal.votesAgainst) {
return false;
}
// Must reach quorum threshold.
if (proposal.votesFor < quorumThreshold) {
return false;
}
return true;
}
/// @dev Checks whether a vote starting at the given
/// epoch has ended or not.
/// @param voteEpoch The epoch at which the vote started.
/// @return hasEnded Whether the vote has ended.
function _hasVoteEnded(uint256 voteEpoch)
private
view
returns (bool hasEnded)
{
uint256 currentEpoch = stakingProxy.currentEpoch();
if (currentEpoch < voteEpoch) {
return false;
}
if (currentEpoch > voteEpoch) {
return true;
}
// voteEpoch == currentEpoch
// Vote ends at currentEpochStartTime + votingPeriod
uint256 voteEndTime = stakingProxy
.currentEpochStartTimeInSeconds()
.safeAdd(votingPeriod);
return block.timestamp > voteEndTime;
}
}

View File

@@ -0,0 +1,88 @@
{
"name": "@0x/contracts-treasury",
"version": "1.0.1",
"engines": {
"node": ">=6.12"
},
"description": "Smart contracts for governing the 0x ZRX treasury",
"main": "lib/src/index.js",
"directories": {
"test": "test"
},
"scripts": {
"build": "yarn pre_build && yarn build:ts",
"build:ci": "yarn build",
"build:ts": "tsc -b",
"pre_build": "run-s compile contracts:gen generate_contract_wrappers contracts:copy",
"test": "yarn run_mocha",
"rebuild_and_test": "run-s build test",
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit",
"compile": "sol-compiler",
"watch": "sol-compiler -w",
"clean": "shx rm -rf lib test/generated-artifacts test/generated-wrappers generated-artifacts generated-wrappers",
"generate_contract_wrappers": "abi-gen --debug --abis ${npm_package_config_abis} --output test/generated-wrappers --backend ethers",
"lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./test/generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"fix": "tslint --fix --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude ./test/generated-wrappers/**/* --exclude ./test/generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts",
"test:circleci": "yarn test",
"contracts:gen": "contracts-gen generate",
"contracts:copy": "contracts-gen copy",
"lint-contracts": "#solhint -c ../.solhint.json contracts/**/**/**/**/*.sol",
"docs:md": "ts-doc-gen --sourceDir='$PROJECT_FILES' --output=$MD_FILE_DIR --fileExtension=mdx --tsconfig=./typedoc-tsconfig.json",
"docs:json": "typedoc --excludePrivate --excludeExternals --excludeProtected --ignoreCompilerErrors --target ES5 --tsconfig typedoc-tsconfig.json --json $JSON_FILE_PATH $PROJECT_FILES",
"publish:private": "yarn build && gitpkg publish"
},
"config": {
"publicInterfaceContracts": "ZrxTreasury,DefaultPoolOperator",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(DefaultPoolOperator|IStaking|IZrxTreasury|ZrxTreasury).json"
},
"repository": {
"type": "git",
"url": "https://github.com/0xProject/protocol.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/0xProject/protocol/issues"
},
"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/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.3",
"@types/isomorphic-fetch": "^0.0.35",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
"@types/prompts": "^2.0.9",
"isomorphic-fetch": "^3.0.0",
"lodash": "^4.17.11",
"mocha": "^6.2.0",
"npm-run-all": "^4.1.2",
"prompts": "^2.4.0",
"shx": "^0.2.2",
"solhint": "^1.4.1",
"tslint": "5.11.0",
"typedoc": "~0.16.11",
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/protocol-utils": "^1.2.0",
"@0x/subproviders": "^6.2.3",
"@0x/types": "^3.3.1",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/web3-wrapper": "^7.3.0",
"ethereum-types": "^3.4.0",
"ethereumjs-util": "^5.1.1"
},
"publishConfig": {
"access": "public"
}
}

View File

@@ -0,0 +1,13 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as DefaultPoolOperator from '../generated-artifacts/DefaultPoolOperator.json';
import * as ZrxTreasury from '../generated-artifacts/ZrxTreasury.json';
export const artifacts = {
ZrxTreasury: ZrxTreasury as ContractArtifact,
DefaultPoolOperator: DefaultPoolOperator as ContractArtifact,
};

View File

@@ -0,0 +1,2 @@
export { artifacts } from './artifacts';
export { DefaultPoolOperatorContract, ZrxTreasuryContract } from './wrappers';

View File

@@ -0,0 +1,7 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../generated-wrappers/default_pool_operator';
export * from '../generated-wrappers/zrx_treasury';

View File

@@ -0,0 +1,17 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
import { ContractArtifact } from 'ethereum-types';
import * as DefaultPoolOperator from '../test/generated-artifacts/DefaultPoolOperator.json';
import * as IStaking from '../test/generated-artifacts/IStaking.json';
import * as IZrxTreasury from '../test/generated-artifacts/IZrxTreasury.json';
import * as ZrxTreasury from '../test/generated-artifacts/ZrxTreasury.json';
export const artifacts = {
DefaultPoolOperator: DefaultPoolOperator as ContractArtifact,
IStaking: IStaking as ContractArtifact,
IZrxTreasury: IZrxTreasury as ContractArtifact,
ZrxTreasury: ZrxTreasury as ContractArtifact,
};

View File

@@ -0,0 +1,583 @@
import { artifacts as assetProxyArtifacts, ERC20ProxyContract } from '@0x/contracts-asset-proxy';
import { artifacts as erc20Artifacts, DummyERC20TokenContract } from '@0x/contracts-erc20';
import {
artifacts as stakingArtifacts,
constants as stakingConstants,
StakeInfo,
StakeStatus,
StakingProxyContract,
TestStakingContract,
ZrxVaultContract,
} from '@0x/contracts-staking';
import {
blockchainTests,
constants,
expect,
getRandomInteger,
randomAddress,
verifyEventsFromLogs,
} from '@0x/contracts-test-utils';
import { BigNumber } from '@0x/utils';
import * as _ from 'lodash';
import { artifacts } from './artifacts';
import { DefaultPoolOperatorContract, ZrxTreasuryContract, ZrxTreasuryEvents } from './wrappers';
blockchainTests.resets('Treasury governance', env => {
const TREASURY_PARAMS = {
votingPeriod: new BigNumber(3).times(stakingConstants.ONE_DAY_IN_SECONDS),
proposalThreshold: new BigNumber(100),
quorumThreshold: new BigNumber(1000),
};
const PROPOSAL_DESCRIPTION = 'A very compelling proposal!';
const TREASURY_BALANCE = constants.INITIAL_ERC20_BALANCE;
const INVALID_PROPOSAL_ID = new BigNumber(999);
const GRANT_PROPOSALS = [
{ recipient: randomAddress(), amount: getRandomInteger(1, TREASURY_BALANCE.dividedToIntegerBy(2)) },
{ recipient: randomAddress(), amount: getRandomInteger(1, TREASURY_BALANCE.dividedToIntegerBy(2)) },
];
interface ProposedAction {
target: string;
data: string;
value: BigNumber;
}
let zrx: DummyERC20TokenContract;
let weth: DummyERC20TokenContract;
let erc20ProxyContract: ERC20ProxyContract;
let staking: TestStakingContract;
let treasury: ZrxTreasuryContract;
let defaultPoolId: string;
let defaultPoolOperator: DefaultPoolOperatorContract;
let admin: string;
let nonDefaultPoolId: string;
let poolOperator: string;
let delegator: string;
let actions: ProposedAction[];
async function deployStakingAsync(): Promise<void> {
erc20ProxyContract = await ERC20ProxyContract.deployFrom0xArtifactAsync(
assetProxyArtifacts.ERC20Proxy,
env.provider,
env.txDefaults,
assetProxyArtifacts,
);
const zrxVaultContract = await ZrxVaultContract.deployFrom0xArtifactAsync(
stakingArtifacts.ZrxVault,
env.provider,
env.txDefaults,
stakingArtifacts,
erc20ProxyContract.address,
zrx.address,
);
await erc20ProxyContract.addAuthorizedAddress(zrxVaultContract.address).awaitTransactionSuccessAsync();
await zrxVaultContract.addAuthorizedAddress(admin).awaitTransactionSuccessAsync();
const stakingLogic = await TestStakingContract.deployFrom0xArtifactAsync(
stakingArtifacts.TestStaking,
env.provider,
env.txDefaults,
artifacts,
weth.address,
zrxVaultContract.address,
);
const stakingProxyContract = await StakingProxyContract.deployFrom0xArtifactAsync(
stakingArtifacts.StakingProxy,
env.provider,
env.txDefaults,
artifacts,
stakingLogic.address,
);
await stakingProxyContract.addAuthorizedAddress(admin).awaitTransactionSuccessAsync();
await zrxVaultContract.setStakingProxy(stakingProxyContract.address).awaitTransactionSuccessAsync();
staking = new TestStakingContract(stakingProxyContract.address, env.provider, env.txDefaults);
}
async function fastForwardToNextEpochAsync(): Promise<void> {
const epochEndTime = await staking.getCurrentEpochEarliestEndTimeInSeconds().callAsync();
const lastBlockTime = await env.web3Wrapper.getBlockTimestampAsync('latest');
const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber());
await env.web3Wrapper.increaseTimeAsync(dt);
// mine next block
await env.web3Wrapper.mineBlockAsync();
await staking.endEpoch().awaitTransactionSuccessAsync();
}
before(async () => {
[admin, poolOperator, delegator] = await env.getAccountAddressesAsync();
zrx = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
env.provider,
env.txDefaults,
erc20Artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
weth = await DummyERC20TokenContract.deployFrom0xArtifactAsync(
erc20Artifacts.DummyERC20Token,
env.provider,
env.txDefaults,
erc20Artifacts,
constants.DUMMY_TOKEN_NAME,
constants.DUMMY_TOKEN_SYMBOL,
constants.DUMMY_TOKEN_DECIMALS,
constants.DUMMY_TOKEN_TOTAL_SUPPLY,
);
await deployStakingAsync();
await zrx.mint(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: poolOperator });
await zrx.mint(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: delegator });
await zrx
.approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: poolOperator });
await zrx
.approve(erc20ProxyContract.address, constants.INITIAL_ERC20_ALLOWANCE)
.awaitTransactionSuccessAsync({ from: delegator });
const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false);
nonDefaultPoolId = await createStakingPoolTx.callAsync({ from: poolOperator });
await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator });
treasury = await ZrxTreasuryContract.deployFrom0xArtifactAsync(
artifacts.ZrxTreasury,
env.provider,
env.txDefaults,
{ ...artifacts, ...erc20Artifacts },
staking.address,
weth.address,
TREASURY_PARAMS,
);
await zrx.mint(TREASURY_BALANCE).awaitTransactionSuccessAsync();
await zrx.transfer(treasury.address, TREASURY_BALANCE).awaitTransactionSuccessAsync();
actions = [
{
target: zrx.address,
data: zrx
.transfer(GRANT_PROPOSALS[0].recipient, GRANT_PROPOSALS[0].amount)
.getABIEncodedTransactionData(),
value: constants.ZERO_AMOUNT,
},
{
target: zrx.address,
data: zrx
.transfer(GRANT_PROPOSALS[1].recipient, GRANT_PROPOSALS[1].amount)
.getABIEncodedTransactionData(),
value: constants.ZERO_AMOUNT,
},
];
defaultPoolId = await treasury.defaultPoolId().callAsync();
const defaultPoolOperatorAddress = await treasury.defaultPoolOperator().callAsync();
defaultPoolOperator = new DefaultPoolOperatorContract(defaultPoolOperatorAddress, env.provider, env.txDefaults);
});
describe('getVotingPower()', () => {
it('Unstaked ZRX has no voting power', async () => {
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(votingPower).to.bignumber.equal(0);
});
it('Staked but undelegated ZRX has no voting power', async () => {
await staking.stake(constants.INITIAL_ERC20_BALANCE).awaitTransactionSuccessAsync({ from: delegator });
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(votingPower).to.bignumber.equal(0);
});
it('ZRX delegated during epoch N has no voting power during Epoch N', async () => {
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(votingPower).to.bignumber.equal(0);
await fastForwardToNextEpochAsync();
});
it('ZRX delegated to the default pool retains full voting power', async () => {
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const votingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(votingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold);
});
it('ZRX delegated to a non-default pool splits voting power between delegator and pool operator', async () => {
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const delegatorVotingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(delegatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2));
const operatorVotingPower = await treasury.getVotingPower(poolOperator, [nonDefaultPoolId]).callAsync();
expect(operatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.dividedBy(2));
});
it('Correctly sums voting power delegated to multiple pools', async () => {
await staking
.stake(TREASURY_PARAMS.proposalThreshold.times(2))
.awaitTransactionSuccessAsync({ from: delegator });
// Delegate half of total stake to the default pool.
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
// Delegate the other half to a non-default pool.
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, nonDefaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const delegatorVotingPower = await treasury.getVotingPower(delegator, []).callAsync();
expect(delegatorVotingPower).to.bignumber.equal(TREASURY_PARAMS.proposalThreshold.times(1.5));
});
it('Correctly sums voting power for operator with multiple pools', async () => {
const createStakingPoolTx = staking.createStakingPool(stakingConstants.PPM, false);
const firstPool = nonDefaultPoolId;
const secondPool = await createStakingPoolTx.callAsync({ from: poolOperator });
await createStakingPoolTx.awaitTransactionSuccessAsync({ from: poolOperator });
const amountDelegatedToDefaultPool = new BigNumber(1337);
const amountSelfDelegatedToFirstPool = new BigNumber(420);
const amountExternallyDelegatedToSecondPool = new BigNumber(2020);
await staking
.stake(amountDelegatedToDefaultPool.plus(amountSelfDelegatedToFirstPool))
.awaitTransactionSuccessAsync({ from: poolOperator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
amountDelegatedToDefaultPool,
)
.awaitTransactionSuccessAsync({ from: poolOperator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, firstPool),
amountSelfDelegatedToFirstPool,
)
.awaitTransactionSuccessAsync({ from: poolOperator });
await staking
.stake(amountExternallyDelegatedToSecondPool)
.awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, secondPool),
amountExternallyDelegatedToSecondPool,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const votingPower = await treasury.getVotingPower(poolOperator, [firstPool, secondPool]).callAsync();
expect(votingPower).to.bignumber.equal(
amountDelegatedToDefaultPool
.plus(amountSelfDelegatedToFirstPool)
.plus(amountExternallyDelegatedToSecondPool.dividedToIntegerBy(2)),
);
});
});
describe('propose()', () => {
it('Cannot create proposal without sufficient voting power', async () => {
const votingPower = TREASURY_PARAMS.proposalThreshold.minus(1);
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
votingPower,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
const tx = treasury
.propose(actions, currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
.awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('propose/INSUFFICIENT_VOTING_POWER');
});
it('Cannot create proposal with no actions', async () => {
const votingPower = TREASURY_PARAMS.proposalThreshold;
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
votingPower,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
const tx = treasury
.propose([], currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
.awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('propose/NO_ACTIONS_PROPOSED');
});
it('Cannot create proposal with an invalid execution epoch', async () => {
const votingPower = TREASURY_PARAMS.proposalThreshold;
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
votingPower,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
const tx = treasury
.propose(actions, currentEpoch.plus(1), PROPOSAL_DESCRIPTION, [])
.awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('propose/INVALID_EXECUTION_EPOCH');
});
it('Can create a valid proposal', async () => {
const votingPower = TREASURY_PARAMS.proposalThreshold;
await staking.stake(votingPower).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
votingPower,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
const executionEpoch = currentEpoch.plus(2);
const tx = await treasury
.propose(actions, executionEpoch, PROPOSAL_DESCRIPTION, [])
.awaitTransactionSuccessAsync({ from: delegator });
const proposalId = new BigNumber(0);
verifyEventsFromLogs(
tx.logs,
[
{
proposer: delegator,
operatedPoolIds: [],
proposalId,
actions,
executionEpoch,
description: PROPOSAL_DESCRIPTION,
},
],
ZrxTreasuryEvents.ProposalCreated,
);
expect(await treasury.proposalCount().callAsync()).to.bignumber.equal(1);
});
});
describe('castVote()', () => {
const VOTE_PROPOSAL_ID = new BigNumber(0);
const DELEGATOR_VOTING_POWER = new BigNumber(420);
before(async () => {
await staking.stake(DELEGATOR_VOTING_POWER).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
DELEGATOR_VOTING_POWER,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
await treasury
.propose(actions, currentEpoch.plus(2), PROPOSAL_DESCRIPTION, [])
.awaitTransactionSuccessAsync({ from: delegator });
});
it('Cannot vote on invalid proposalId', async () => {
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
const tx = treasury
.castVote(INVALID_PROPOSAL_ID, true, [])
.awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('castVote/INVALID_PROPOSAL_ID');
});
it('Cannot vote before voting period starts', async () => {
const tx = treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('castVote/VOTING_IS_CLOSED');
});
it('Cannot vote after voting period ends', async () => {
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
await env.web3Wrapper.increaseTimeAsync(TREASURY_PARAMS.votingPeriod.plus(1).toNumber());
await env.web3Wrapper.mineBlockAsync();
const tx = treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('castVote/VOTING_IS_CLOSED');
});
it('Cannot vote twice on same proposal', async () => {
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
await treasury.castVote(VOTE_PROPOSAL_ID, true, []).awaitTransactionSuccessAsync({ from: delegator });
const tx = treasury.castVote(VOTE_PROPOSAL_ID, false, []).awaitTransactionSuccessAsync({ from: delegator });
return expect(tx).to.revertWith('castVote/ALREADY_VOTED');
});
it('Can cast a valid vote', async () => {
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
const tx = await treasury
.castVote(VOTE_PROPOSAL_ID, true, [])
.awaitTransactionSuccessAsync({ from: delegator });
verifyEventsFromLogs(
tx.logs,
[
{
voter: delegator,
operatedPoolIds: [],
proposalId: VOTE_PROPOSAL_ID,
support: true,
votingPower: DELEGATOR_VOTING_POWER,
},
],
ZrxTreasuryEvents.VoteCast,
);
});
});
describe('execute()', () => {
let passedProposalId: BigNumber;
let failedProposalId: BigNumber;
let defeatedProposalId: BigNumber;
let ongoingVoteProposalId: BigNumber;
before(async () => {
// OPerator has enough ZRX to create and pass a proposal
await staking.stake(TREASURY_PARAMS.quorumThreshold).awaitTransactionSuccessAsync({ from: poolOperator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.quorumThreshold,
)
.awaitTransactionSuccessAsync({ from: poolOperator });
// Delegator only has enough ZRX to create a proposal
await staking.stake(TREASURY_PARAMS.proposalThreshold).awaitTransactionSuccessAsync({ from: delegator });
await staking
.moveStake(
new StakeInfo(StakeStatus.Undelegated),
new StakeInfo(StakeStatus.Delegated, defaultPoolId),
TREASURY_PARAMS.proposalThreshold,
)
.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
const currentEpoch = await staking.currentEpoch().callAsync();
// Proposal 0
let tx = treasury.propose(actions, currentEpoch.plus(4), PROPOSAL_DESCRIPTION, []);
passedProposalId = await tx.callAsync({ from: delegator });
await tx.awaitTransactionSuccessAsync({ from: delegator });
// Proposal 1
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
failedProposalId = await tx.callAsync({ from: delegator });
await tx.awaitTransactionSuccessAsync({ from: delegator });
// Proposal 2
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
defeatedProposalId = await tx.callAsync({ from: delegator });
await tx.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
// Proposal 3
tx = treasury.propose(actions, currentEpoch.plus(3), PROPOSAL_DESCRIPTION, []);
ongoingVoteProposalId = await tx.callAsync({ from: delegator });
await tx.awaitTransactionSuccessAsync({ from: delegator });
await fastForwardToNextEpochAsync();
/********** Start Vote Epoch for Proposals 0, 1, 2 **********/
// Proposal 0 passes
await treasury.castVote(passedProposalId, true, []).awaitTransactionSuccessAsync({ from: poolOperator });
// Proposal 1 fails to reach quorum
await treasury.castVote(failedProposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
// Proposal 2 is voted down
await treasury.castVote(defeatedProposalId, true, []).awaitTransactionSuccessAsync({ from: delegator });
await treasury.castVote(defeatedProposalId, false, []).awaitTransactionSuccessAsync({ from: poolOperator });
/********** End Vote Epoch for Proposals 0, 1, 2 **********/
await fastForwardToNextEpochAsync();
/********** Start Execution Epoch for Proposals 1, 2, 3 **********/
/********** Start Vote Epoch for Proposal 3 **********************/
// Proposal 3 has enough votes to pass, but the vote is ongoing
await treasury
.castVote(ongoingVoteProposalId, true, [])
.awaitTransactionSuccessAsync({ from: poolOperator });
});
it('Cannot execute an invalid proposalId', async () => {
const tx = treasury.execute(INVALID_PROPOSAL_ID, actions).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('execute/INVALID_PROPOSAL_ID');
});
it('Cannot execute a proposal whose vote is ongoing', async () => {
const tx = treasury.execute(ongoingVoteProposalId, actions).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
});
it('Cannot execute a proposal that failed to reach quorum', async () => {
const tx = treasury.execute(failedProposalId, actions).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
});
it('Cannot execute a proposal that was defeated in its vote', async () => {
const tx = treasury.execute(defeatedProposalId, actions).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_HAS_NOT_PASSED');
});
it('Cannot execute before or after the execution epoch', async () => {
const tooEarly = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
expect(tooEarly).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
await fastForwardToNextEpochAsync();
// Proposal 0 is executable here
await fastForwardToNextEpochAsync();
const tooLate = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
return expect(tooLate).to.revertWith('_assertProposalExecutable/CANNOT_EXECUTE_THIS_EPOCH');
});
it('Cannot execute the same proposal twice', async () => {
await fastForwardToNextEpochAsync();
await treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
const tx = treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('_assertProposalExecutable/PROPOSAL_ALREADY_EXECUTED');
});
it('Cannot execute actions that do not match the proposal `actionsHash`', async () => {
await fastForwardToNextEpochAsync();
const tx = treasury
.execute(passedProposalId, [
{
target: zrx.address,
data: zrx.transfer(randomAddress(), GRANT_PROPOSALS[0].amount).getABIEncodedTransactionData(),
value: constants.ZERO_AMOUNT,
},
])
.awaitTransactionSuccessAsync();
return expect(tx).to.revertWith('_assertProposalExecutable/INVALID_ACTIONS');
});
it('Can execute a valid proposal', async () => {
await fastForwardToNextEpochAsync();
const tx = await treasury.execute(passedProposalId, actions).awaitTransactionSuccessAsync();
verifyEventsFromLogs(tx.logs, [{ proposalId: passedProposalId }], ZrxTreasuryEvents.ProposalExecuted);
expect(await zrx.balanceOf(GRANT_PROPOSALS[0].recipient).callAsync()).to.bignumber.equal(
GRANT_PROPOSALS[0].amount,
);
expect(await zrx.balanceOf(GRANT_PROPOSALS[1].recipient).callAsync()).to.bignumber.equal(
GRANT_PROPOSALS[1].amount,
);
});
});
describe('Default pool operator contract', () => {
it('Returns WETH to the staking proxy', async () => {
const wethAmount = new BigNumber(1337);
await weth.mint(wethAmount).awaitTransactionSuccessAsync();
// Some amount of WETH ends up in the default pool operator
// contract, e.g. from errant staking rewards.
await weth.transfer(defaultPoolOperator.address, wethAmount).awaitTransactionSuccessAsync();
// This function should send all the WETH to the staking proxy.
await defaultPoolOperator.returnStakingRewards().awaitTransactionSuccessAsync();
expect(await weth.balanceOf(defaultPoolOperator.address).callAsync()).to.bignumber.equal(0);
expect(await weth.balanceOf(staking.address).callAsync()).to.bignumber.equal(wethAmount);
});
});
});

View File

@@ -0,0 +1,9 @@
/*
* -----------------------------------------------------------------------------
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
export * from '../test/generated-wrappers/default_pool_operator';
export * from '../test/generated-wrappers/i_staking';
export * from '../test/generated-wrappers/i_zrx_treasury';
export * from '../test/generated-wrappers/zrx_treasury';

View File

@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig",
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
"generated-artifacts/DefaultPoolOperator.json",
"generated-artifacts/ZrxTreasury.json",
"test/generated-artifacts/DefaultPoolOperator.json",
"test/generated-artifacts/IStaking.json",
"test/generated-artifacts/IZrxTreasury.json",
"test/generated-artifacts/ZrxTreasury.json"
],
"exclude": ["./deploy/solc/solc_bin"]
}

View File

@@ -0,0 +1,13 @@
{
"extends": ["@0x/tslint-config"],
"rules": {
"custom-no-magic-numbers": false,
"max-file-line-count": false,
"no-non-null-assertion": false,
"no-unnecessary-type-assertion": false,
"number-literal-format": false
},
"linterOptions": {
"exclude": ["src/artifacts.ts", "test/artifacts.ts"]
}
}

View File

@@ -1,4 +1,22 @@
[
{
"timestamp": 1612950500,
"version": "4.7.3",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1611648096,
"version": "4.7.2",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1610510890,
"version": "4.7.1",

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.7.3 - _February 10, 2021_
* Dependencies updated
## v4.7.2 - _January 26, 2021_
* Dependencies updated
## v4.7.1 - _January 13, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.7.1",
"version": "4.7.3",
"engines": {
"node": ">=6.12"
},
@@ -50,15 +50,15 @@
},
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/utils",
"devDependencies": {
"@0x/abi-gen": "^5.4.13",
"@0x/contracts-gen": "^2.0.24",
"@0x/contracts-test-utils": "^5.3.19",
"@0x/dev-utils": "^4.1.3",
"@0x/order-utils": "^10.4.14",
"@0x/sol-compiler": "^4.4.1",
"@0x/abi-gen": "^5.4.19",
"@0x/contracts-gen": "^2.0.30",
"@0x/contracts-test-utils": "^5.3.21",
"@0x/dev-utils": "^4.2.1",
"@0x/order-utils": "^10.4.16",
"@0x/sol-compiler": "^4.5.2",
"@0x/tslint-config": "^4.1.3",
"@0x/types": "^3.3.1",
"@0x/web3-wrapper": "^7.3.0",
"@0x/web3-wrapper": "^7.4.1",
"@types/bn.js": "^4.11.0",
"@types/lodash": "4.14.104",
"@types/mocha": "^5.2.7",
@@ -79,9 +79,9 @@
"typescript": "3.0.1"
},
"dependencies": {
"@0x/base-contract": "^6.2.14",
"@0x/base-contract": "^6.2.18",
"@0x/typescript-typings": "^5.1.6",
"@0x/utils": "^6.1.1",
"@0x/utils": "^6.2.0",
"bn.js": "^4.11.8",
"ethereum-types": "^3.4.0"
},

View File

@@ -1,4 +1,24 @@
[
{
"version": "0.18.2",
"changes": [
{
"note": "Update FQT for v4 native orders",
"pr": 104
}
],
"timestamp": 1612950500
},
{
"version": "0.18.1",
"changes": [
{
"note": "Swallow reverts in `batchGetLimitOrderRelevantStates()` and `batchGetRfqOrderRelevantStates()`",
"pr": 117
}
],
"timestamp": 1611648096
},
{
"version": "0.18.0",
"changes": [

View File

@@ -5,6 +5,14 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.18.2 - _February 10, 2021_
* Update FQT for v4 native orders (#104)
## v0.18.1 - _January 26, 2021_
* Swallow reverts in `batchGetLimitOrderRelevantStates()` and `batchGetRfqOrderRelevantStates()` (#117)
## v0.18.0 - _January 13, 2021_
* Use consistent returndatasize checks in UniswapFeature (#96)

View File

@@ -1,6 +1,6 @@
## ERC20BridgeSampler
## ZeroEx (ExchangeProxy)
This package contains contracts contracts for the ZeroEx extensible contract architecture.
This package contains contracts for the ZeroEx extensible contract architecture.
## Installation

View File

@@ -387,7 +387,9 @@ interface INativeOrdersFeature {
bool isSignatureValid
);
/// @dev Batch version of `getLimitOrderRelevantState()`.
/// @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.
@@ -406,7 +408,9 @@ interface INativeOrdersFeature {
bool[] memory isSignatureValids
);
/// @dev Batch version of `getRfqOrderRelevantState()`.
/// @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.

View File

@@ -111,7 +111,7 @@ contract NativeOrdersFeature is
/// @dev Name of this feature.
string public constant override FEATURE_NAME = "LimitOrders";
/// @dev Version of this feature.
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
/// @dev Highest bit of a uint256, used to flag cancelled orders.
uint256 private constant HIGH_BIT = 1 << 255;
@@ -772,7 +772,9 @@ contract NativeOrdersFeature is
LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
}
/// @dev Batch version of `getLimitOrderRelevantState()`.
/// @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.
@@ -800,15 +802,25 @@ contract NativeOrdersFeature is
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
(
orderInfos[i],
actualFillableTakerTokenAmounts[i],
isSignatureValids[i]
) = getLimitOrderRelevantState(orders[i], signatures[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()`.
/// @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.
@@ -836,11 +848,19 @@ contract NativeOrdersFeature is
actualFillableTakerTokenAmounts = new uint128[](orders.length);
isSignatureValids = new bool[](orders.length);
for (uint256 i = 0; i < orders.length; ++i) {
(
orderInfos[i],
actualFillableTakerTokenAmounts[i],
isSignatureValids[i]
) = getRfqOrderRelevantState(orders[i], signatures[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 {}
}
}

View File

@@ -103,8 +103,8 @@ library LibNativeOrder {
// "uint128 makerAmount,",
// "uint128 takerAmount,",
// "address maker,",
// "address txOrigin,",
// "address taker,",
// "address txOrigin,",
// "bytes32 pool,",
// "uint64 expiry,",
// "uint256 salt"

View File

@@ -22,13 +22,12 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
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 "../vendor/v3/IExchange.sol";
import "../vendor/v3/LibOrderHash.sol";
import "../features/INativeOrdersFeature.sol";
import "../features/libs/LibNativeOrder.sol";
import "./bridges/IBridgeAdapter.sol";
import "./Transformer.sol";
import "./LibERC20Transformer.sol";
@@ -41,8 +40,8 @@ contract FillQuoteTransformer is
using LibERC20TokenV06 for IERC20TokenV06;
using LibERC20Transformer for IERC20TokenV06;
using LibSafeMathV06 for uint256;
using LibSafeMathV06 for uint128;
using LibRichErrorsV06 for bytes;
using LibBytesV06 for bytes;
/// @dev Whether we are performing a market sell or buy.
enum Side {
@@ -50,6 +49,26 @@ contract FillQuoteTransformer is
Buy
}
enum OrderType {
Bridge,
Limit,
Rfq
}
struct LimitOrderInfo {
LibNativeOrder.LimitOrder order;
LibSignature.Signature signature;
// Maximum taker token amount of this limit order to fill.
uint256 maxTakerTokenFillAmount;
}
struct RfqOrderInfo {
LibNativeOrder.RfqOrder order;
LibSignature.Signature signature;
// Maximum taker token amount of this limit order to fill.
uint256 maxTakerTokenFillAmount;
}
/// @dev Transform data to ABI-encode and pass into `transform()`.
struct TransformData {
// Whether we are performing a market sell or buy.
@@ -60,31 +79,34 @@ contract FillQuoteTransformer is
// The token being bought.
// This should be an actual token, not the ETH pseudo-token.
IERC20TokenV06 buyToken;
// The orders to fill.
IExchange.Order[] orders;
// Signatures for each respective order in `orders`.
bytes[] signatures;
// Maximum fill amount for each order. This may be shorter than the
// number of orders, where missing entries will be treated as `uint256(-1)`.
// For sells, this will be the maximum sell amount (taker asset).
// For buys, this will be the maximum buy amount (maker asset).
uint256[] maxOrderFillAmounts;
// External liquidity bridge orders. Sorted by fill sequence.
IBridgeAdapter.BridgeOrder[] bridgeOrders;
// Native limit orders. Sorted by fill sequence.
LimitOrderInfo[] limitOrders;
// Native RFQ orders. Sorted by fill sequence.
RfqOrderInfo[] rfqOrders;
// The sequence to fill the orders in. Each item will fill the next
// order of that type in either `bridgeOrders`, `limitOrders`,
// or `rfqOrders.`
OrderType[] fillSequence;
// Amount of `sellToken` to sell or `buyToken` to buy.
// For sells, this may be `uint256(-1)` to sell the entire balance of
// `sellToken`.
// For sells, setting the high-bit indicates that
// `sellAmount & LOW_BITS` should be treated as a `1e18` fraction of
// the current balance of `sellToken`, where
// `1e18+ == 100%` and `0.5e18 == 50%`, etc.
uint256 fillAmount;
// Who to transfer unused protocol fees to.
// May be a valid address or one of:
// `address(0)`: Stay in flash wallet.
// `address(1)`: Send to the taker.
// `address(2)`: Send to the sender (caller of `transformERC20()`).
address payable refundReceiver;
// Required taker address for RFQT orders.
// Null means any taker can fill it.
address rfqtTakerAddress;
}
/// @dev Results of a call to `_fillOrder()`.
struct FillOrderResults {
// The amount of taker tokens sold, according to balance checks.
uint256 takerTokenSoldAmount;
@@ -101,7 +123,8 @@ contract FillQuoteTransformer is
uint256 soldAmount;
uint256 protocolFee;
uint256 takerTokenBalanceRemaining;
bool isRfqtAllowed;
uint256[3] currentIndices;
OrderType currentOrderType;
}
/// @dev Emitted when a trade is skipped due to a lack of funds
@@ -109,12 +132,12 @@ contract FillQuoteTransformer is
/// @param orderHash The hash of the order that was skipped.
event ProtocolFeeUnfunded(bytes32 orderHash);
/// @dev The Exchange ERC20Proxy ID.
bytes4 private constant ERC20_ASSET_PROXY_ID = 0xf47261b0;
/// @dev The Exchange ERC20BridgeProxy ID.
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
/// @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.
uint256 private constant LOWER_255_BITS = HIGH_BIT - 1;
/// @dev If `refundReceiver` is set to this address, unpsent
/// protocol fees will be sent to the taker.
address private constant REFUND_RECEIVER_TAKER = address(1);
@@ -122,33 +145,32 @@ contract FillQuoteTransformer is
/// protocol fees will be sent to the sender.
address private constant REFUND_RECEIVER_SENDER = address(2);
/// @dev The Exchange contract.
IExchange public immutable exchange;
/// @dev The ERC20Proxy address.
address public immutable erc20Proxy;
/// @dev The BridgeAdapter address
IBridgeAdapter public immutable bridgeAdapter;
/// @dev The exchange proxy contract.
INativeOrdersFeature public immutable zeroEx;
/// @dev Create this contract.
/// @param exchange_ The Exchange V3 instance.
constructor(IExchange exchange_, IBridgeAdapter bridgeAdapter_)
/// @param bridgeAdapter_ The bridge adapter contract.
/// @param zeroEx_ The Exchange Proxy contract.
constructor(IBridgeAdapter bridgeAdapter_, INativeOrdersFeature zeroEx_)
public
Transformer()
{
exchange = exchange_;
erc20Proxy = exchange_.getAssetProxy(ERC20_ASSET_PROXY_ID);
bridgeAdapter = bridgeAdapter_;
zeroEx = zeroEx_;
}
/// @dev Sell this contract's entire balance of of `sellToken` in exchange
/// for `buyToken` by filling `orders`. Protocol fees should be attached
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
/// @param context Context information.
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
/// @return magicBytes The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
function transform(TransformContext calldata context)
external
override
returns (bytes4 success)
returns (bytes4 magicBytes)
{
TransformData memory data = abi.decode(context.data, (TransformData));
FillState memory state;
@@ -160,7 +182,11 @@ contract FillQuoteTransformer is
context.data
).rrevert();
}
if (data.orders.length != data.signatures.length) {
if (data.bridgeOrders.length
+ data.limitOrders.length
+ data.rfqOrders.length != data.fillSequence.length
) {
LibTransformERC20RichErrors.InvalidTransformDataError(
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
context.data
@@ -168,76 +194,58 @@ contract FillQuoteTransformer is
}
state.takerTokenBalanceRemaining = data.sellToken.getTokenBalanceOf(address(this));
if (data.side == Side.Sell && data.fillAmount == MAX_UINT256) {
// If `sellAmount == -1 then we are selling
// the entire balance of `sellToken`. This is useful in cases where
// the exact sell amount is not exactly known in advance, like when
// unwrapping Chai/cUSDC/cDAI.
data.fillAmount = state.takerTokenBalanceRemaining;
if (data.side == Side.Sell) {
data.fillAmount = _normalizeFillAmount(data.fillAmount, state.takerTokenBalanceRemaining);
}
// Approve the ERC20 proxy to spend `sellToken`.
data.sellToken.approveIfBelow(erc20Proxy, data.fillAmount);
// Approve the exchange proxy to spend our sell tokens if native orders
// are present.
if (data.limitOrders.length + data.rfqOrders.length != 0) {
data.sellToken.approveIfBelow(address(zeroEx), data.fillAmount);
// Compute the protocol fee if a limit order is present.
if (data.limitOrders.length != 0) {
state.protocolFee = uint256(zeroEx.getProtocolFeeMultiplier())
.safeMul(tx.gasprice);
}
}
state.protocolFee = exchange.protocolFeeMultiplier().safeMul(tx.gasprice);
state.ethRemaining = address(this).balance;
// RFQT orders can only be filled if the actual taker matches the RFQT
// taker (if set).
state.isRfqtAllowed = data.rfqtTakerAddress == address(0)
|| context.taker == data.rfqtTakerAddress;
// Fill the orders.
for (uint256 i = 0; i < data.orders.length; ++i) {
for (uint256 i = 0; i < data.fillSequence.length; ++i) {
// Check if we've hit our targets.
if (data.side == Side.Sell) {
// Market sell check.
if (state.soldAmount >= data.fillAmount) {
break;
}
if (state.soldAmount >= data.fillAmount) { break; }
} else {
// Market buy check.
if (state.boughtAmount >= data.fillAmount) {
break;
}
if (state.boughtAmount >= data.fillAmount) { break; }
}
state.currentOrderType = OrderType(data.fillSequence[i]);
uint256 orderIndex = state.currentIndices[uint256(state.currentOrderType)];
// Fill the order.
FillOrderResults memory results;
if (data.side == Side.Sell) {
// Market sell.
results = _sellToOrder(
data.buyToken,
data.sellToken,
data.orders[i],
data.signatures[i],
data.fillAmount.safeSub(state.soldAmount).min256(
data.maxOrderFillAmounts.length > i
? data.maxOrderFillAmounts[i]
: MAX_UINT256
),
state
);
if (state.currentOrderType == OrderType.Bridge) {
results = _fillBridgeOrder(data.bridgeOrders[orderIndex], data, state);
} else if (state.currentOrderType == OrderType.Limit) {
results = _fillLimitOrder(data.limitOrders[orderIndex], data, state);
} else if (state.currentOrderType == OrderType.Rfq) {
results = _fillRfqOrder(data.rfqOrders[orderIndex], data, state);
} else {
// Market buy.
results = _buyFromOrder(
data.buyToken,
data.sellToken,
data.orders[i],
data.signatures[i],
data.fillAmount.safeSub(state.boughtAmount).min256(
data.maxOrderFillAmounts.length > i
? data.maxOrderFillAmounts[i]
: MAX_UINT256
),
state
);
revert("INVALID_ORDER_TYPE");
}
// Accumulate totals.
state.soldAmount = state.soldAmount.safeAdd(results.takerTokenSoldAmount);
state.boughtAmount = state.boughtAmount.safeAdd(results.makerTokenBoughtAmount);
state.ethRemaining = state.ethRemaining.safeSub(results.protocolFeePaid);
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining.safeSub(results.takerTokenSoldAmount);
state.soldAmount = state.soldAmount
.safeAdd(results.takerTokenSoldAmount);
state.boughtAmount = state.boughtAmount
.safeAdd(results.makerTokenBoughtAmount);
state.ethRemaining = state.ethRemaining
.safeSub(results.protocolFeePaid);
state.takerTokenBalanceRemaining = state.takerTokenBalanceRemaining
.safeSub(results.takerTokenSoldAmount);
state.currentIndices[uint256(state.currentOrderType)]++;
}
// Ensure we hit our targets.
@@ -276,223 +284,174 @@ contract FillQuoteTransformer is
return LibERC20Transformer.TRANSFORMER_SUCCESS;
}
/// @dev Try to sell up to `sellAmount` from an order.
/// @param makerToken The maker/buy token.
/// @param takerToken The taker/sell token.
/// @param order The order to fill.
/// @param signature The signature for `order`.
/// @param sellAmount Amount of taker token to sell.
/// @param state Intermediate state variables to get around stack limits.
function _sellToOrder(
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
IExchange.Order memory order,
bytes memory signature,
uint256 sellAmount,
// Fill a single bridge order.
function _fillBridgeOrder(
IBridgeAdapter.BridgeOrder memory order,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
IERC20TokenV06 takerFeeToken =
_getTokenFromERC20AssetData(order.takerFeeAssetData);
uint256 takerTokenFillAmount = sellAmount;
if (order.takerFee != 0) {
if (takerFeeToken == makerToken) {
// Taker fee is payable in the maker token, so we need to
// approve the proxy to spend the maker token.
// It isn't worth computing the actual taker fee
// since `approveIfBelow()` will set the allowance to infinite. We
// just need a reasonable upper bound to avoid unnecessarily re-approving.
takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
} else if (takerFeeToken == takerToken){
// Taker fee is payable in the taker token, so we need to
// reduce the fill amount to cover the fee.
// takerTokenFillAmount' =
// (takerTokenFillAmount * order.takerAssetAmount) /
// (order.takerAssetAmount + order.takerFee)
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
order.takerAssetAmount,
order.takerAssetAmount.safeAdd(order.takerFee),
sellAmount
);
} else {
// Only support taker or maker asset denominated taker fees.
LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
address(takerFeeToken)
).rrevert();
}
}
// Perform the fill.
return _fillOrder(
order,
signature,
takerTokenFillAmount,
uint256 takerTokenFillAmount = _computeTakerTokenFillAmount(
data,
state,
takerFeeToken == takerToken
order.takerTokenAmount,
order.makerTokenAmount,
0
);
(bool success, bytes memory resultData) = address(bridgeAdapter).delegatecall(
abi.encodeWithSelector(
IBridgeAdapter.trade.selector,
order,
data.sellToken,
data.buyToken,
takerTokenFillAmount
)
);
if (success) {
results.makerTokenBoughtAmount = abi.decode(resultData, (uint256));
results.takerTokenSoldAmount = takerTokenFillAmount;
}
}
/// @dev Try to buy up to `buyAmount` from an order.
/// @param makerToken The maker/buy token.
/// @param takerToken The taker/sell token.
/// @param order The order to fill.
/// @param signature The signature for `order`.
/// @param buyAmount Amount of maker token to buy.
/// @param state Intermediate state variables to get around stack limits.
function _buyFromOrder(
IERC20TokenV06 makerToken,
IERC20TokenV06 takerToken,
IExchange.Order memory order,
bytes memory signature,
uint256 buyAmount,
// Fill a single limit order.
function _fillLimitOrder(
LimitOrderInfo memory orderInfo,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
IERC20TokenV06 takerFeeToken =
_getTokenFromERC20AssetData(order.takerFeeAssetData);
// Compute the default taker token fill amount.
uint256 takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
buyAmount,
order.makerAssetAmount,
order.takerAssetAmount
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
_computeTakerTokenFillAmount(
data,
state,
orderInfo.order.takerAmount,
orderInfo.order.makerAmount,
orderInfo.order.takerTokenFeeAmount
),
orderInfo.maxTakerTokenFillAmount
);
if (order.takerFee != 0) {
if (takerFeeToken == makerToken) {
// Taker fee is payable in the maker token.
// Adjust the taker token fill amount to account for maker
// tokens being lost to the taker fee.
// takerTokenFillAmount' =
// (order.takerAssetAmount * buyAmount) /
// (order.makerAssetAmount - order.takerFee)
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
buyAmount,
order.makerAssetAmount.safeSub(order.takerFee),
order.takerAssetAmount
);
// Approve the proxy to spend the maker token.
// It isn't worth computing the actual taker fee
// since `approveIfBelow()` will set the allowance to infinite. We
// just need a reasonable upper bound to avoid unnecessarily re-approving.
takerFeeToken.approveIfBelow(erc20Proxy, order.takerFee);
} else if (takerFeeToken != takerToken) {
// Only support taker or maker asset denominated taker fees.
LibTransformERC20RichErrors.InvalidTakerFeeTokenError(
address(takerFeeToken)
).rrevert();
}
// Emit an event if we do not have sufficient ETH to cover the protocol fee.
if (state.ethRemaining < state.protocolFee) {
bytes32 orderHash = zeroEx.getLimitOrderHash(orderInfo.order);
emit ProtocolFeeUnfunded(orderHash);
return results; // Empty results.
}
// Perform the fill.
return _fillOrder(
order,
signature,
takerTokenFillAmount,
state,
takerFeeToken == takerToken
);
}
/// @dev Attempt to fill an order. If the fill reverts, the revert will be
/// swallowed and `results` will be zeroed out.
/// @param order The order to fill.
/// @param signature The order signature.
/// @param takerAssetFillAmount How much taker asset to fill.
/// @param state Intermediate state variables to get around stack limits.
/// @param isTakerFeeInTakerToken Whether the taker fee token is the same as the
/// taker token.
function _fillOrder(
IExchange.Order memory order,
bytes memory signature,
uint256 takerAssetFillAmount,
FillState memory state,
bool isTakerFeeInTakerToken
)
private
returns (FillOrderResults memory results)
{
// Clamp to remaining taker asset amount or order size.
uint256 availableTakerAssetFillAmount =
takerAssetFillAmount.min256(order.takerAssetAmount);
availableTakerAssetFillAmount =
availableTakerAssetFillAmount.min256(state.takerTokenBalanceRemaining);
// If it is a Bridge order we fill this directly through the BridgeAdapter
if (order.makerAssetData.readBytes4(0) == ERC20_BRIDGE_PROXY_ID) {
(bool success, bytes memory resultData) = address(bridgeAdapter).delegatecall(
abi.encodeWithSelector(
IBridgeAdapter.trade.selector,
order.makerAssetData,
address(_getTokenFromERC20AssetData(order.takerAssetData)),
availableTakerAssetFillAmount
try
zeroEx.fillLimitOrder
{value: state.protocolFee}
(
orderInfo.order,
orderInfo.signature,
takerTokenFillAmount.safeDowncastToUint128()
)
);
if (success) {
results.makerTokenBoughtAmount = abi.decode(resultData, (uint256));
results.takerTokenSoldAmount = availableTakerAssetFillAmount;
// protocol fee paid remains 0
}
return results;
} else {
// If the order taker address is set to this contract's address then
// this is an RFQT order, and we will only fill it if allowed to.
if (order.takerAddress == address(this) && !state.isRfqtAllowed) {
return results; // Empty results.
}
// Emit an event if we do not have sufficient ETH to cover the protocol fee.
if (state.ethRemaining < state.protocolFee) {
bytes32 orderHash = LibOrderHash.getTypedDataHash(
order,
exchange.EIP712_EXCHANGE_DOMAIN_HASH()
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
if (orderInfo.order.takerTokenFeeAmount > 0) {
takerTokenFilledAmount = takerTokenFilledAmount.safeAdd128(
LibMathV06.getPartialAmountFloor(
takerTokenFilledAmount,
orderInfo.order.takerAmount,
orderInfo.order.takerTokenFeeAmount
).safeDowncastToUint128()
);
emit ProtocolFeeUnfunded(orderHash);
return results;
}
try
exchange.fillOrder
{value: state.protocolFee}
(order, availableTakerAssetFillAmount, signature)
returns (IExchange.FillResults memory fillResults)
{
results.makerTokenBoughtAmount = fillResults.makerAssetFilledAmount;
results.takerTokenSoldAmount = fillResults.takerAssetFilledAmount;
results.protocolFeePaid = fillResults.protocolFeePaid;
// If the taker fee is payable in the taker asset, include the
// taker fee in the total amount sold.
if (isTakerFeeInTakerToken) {
results.takerTokenSoldAmount =
results.takerTokenSoldAmount.safeAdd(fillResults.takerFeePaid);
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
}
}
results.takerTokenSoldAmount = takerTokenFilledAmount;
results.makerTokenBoughtAmount = makerTokenFilledAmount;
results.protocolFeePaid = state.protocolFee;
} catch {}
}
/// @dev Extract the token from plain ERC20 asset data.
/// If the asset-data is empty, a zero token address will be returned.
/// @param assetData The order asset data.
function _getTokenFromERC20AssetData(bytes memory assetData)
// Fill a single RFQ order.
function _fillRfqOrder(
RfqOrderInfo memory orderInfo,
TransformData memory data,
FillState memory state
)
private
returns (FillOrderResults memory results)
{
uint256 takerTokenFillAmount = LibSafeMathV06.min256(
_computeTakerTokenFillAmount(
data,
state,
orderInfo.order.takerAmount,
orderInfo.order.makerAmount,
0
),
orderInfo.maxTakerTokenFillAmount
);
try
zeroEx.fillRfqOrder
(
orderInfo.order,
orderInfo.signature,
takerTokenFillAmount.safeDowncastToUint128()
)
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
{
results.takerTokenSoldAmount = takerTokenFilledAmount;
results.makerTokenBoughtAmount = makerTokenFilledAmount;
} catch {}
}
// Compute the next taker token fill amount of a generic order.
function _computeTakerTokenFillAmount(
TransformData memory data,
FillState memory state,
uint256 orderTakerAmount,
uint256 orderMakerAmount,
uint256 orderTakerTokenFeeAmount
)
private
pure
returns (IERC20TokenV06 token)
returns (uint256 takerTokenFillAmount)
{
if (assetData.length == 0) {
return IERC20TokenV06(address(0));
if (data.side == Side.Sell) {
takerTokenFillAmount = data.fillAmount.safeSub(state.soldAmount);
if (orderTakerTokenFeeAmount != 0) {
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
takerTokenFillAmount,
orderTakerAmount.safeAdd(orderTakerTokenFeeAmount),
orderTakerAmount
);
}
} else { // Buy
takerTokenFillAmount = LibMathV06.getPartialAmountCeil(
data.fillAmount.safeSub(state.boughtAmount),
orderMakerAmount,
orderTakerAmount
);
}
if (assetData.length != 36 ||
LibBytesV06.readBytes4(assetData, 0) != ERC20_ASSET_PROXY_ID)
{
LibTransformERC20RichErrors
.InvalidERC20AssetDataError(assetData)
.rrevert();
return LibSafeMathV06.min256(
LibSafeMathV06.min256(takerTokenFillAmount, orderTakerAmount),
state.takerTokenBalanceRemaining
);
}
// Convert possible proportional values to absolute quantities.
function _normalizeFillAmount(uint256 rawAmount, uint256 balance)
private
pure
returns (uint256 normalized)
{
if ((rawAmount & HIGH_BIT) == HIGH_BIT) {
// If the high bit of `rawAmount` is set then the lower 255 bits
// specify a fraction of `balance`.
return LibSafeMathV06.min256(
balance
* LibSafeMathV06.min256(rawAmount & LOWER_255_BITS, 1e18)
/ 1e18,
balance
);
}
return IERC20TokenV06(LibBytesV06.readAddress(assetData, 16));
return rawAmount;
}
}

View File

@@ -20,7 +20,8 @@
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "./mixins/MixinAdapterAddresses.sol";
import "./IBridgeAdapter.sol";
import "./BridgeSource.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinCoFiX.sol";
@@ -38,7 +39,7 @@ import "./mixins/MixinUniswapV2.sol";
import "./mixins/MixinZeroExBridge.sol";
contract BridgeAdapter is
MixinAdapterAddresses,
IBridgeAdapter,
MixinBalancer,
MixinBancor,
MixinCoFiX,
@@ -55,203 +56,146 @@ contract BridgeAdapter is
MixinUniswapV2,
MixinZeroExBridge
{
/// @dev Emitted when a trade occurs.
/// @param inputToken The token the bridge is converting from.
/// @param outputToken The token the bridge is converting to.
/// @param inputTokenAmount Amount of input token.
/// @param outputTokenAmount Amount of output token.
/// @param from The bridge address, indicating the underlying source of the fill.
/// @param to The `to` address, currrently `address(this)`
event ERC20BridgeTransfer(
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount,
address from,
address to
);
address private immutable BALANCER_BRIDGE_ADDRESS;
address private immutable BANCOR_BRIDGE_ADDRESS;
address private immutable COFIX_BRIDGE_ADDRESS;
address private immutable CREAM_BRIDGE_ADDRESS;
address private immutable CURVE_BRIDGE_ADDRESS;
address private immutable CRYPTO_COM_BRIDGE_ADDRESS;
address private immutable DODO_BRIDGE_ADDRESS;
address private immutable KYBER_BRIDGE_ADDRESS;
address private immutable MOONISWAP_BRIDGE_ADDRESS;
address private immutable MSTABLE_BRIDGE_ADDRESS;
address private immutable OASIS_BRIDGE_ADDRESS;
address private immutable SHELL_BRIDGE_ADDRESS;
address private immutable SNOW_SWAP_BRIDGE_ADDRESS;
address private immutable SUSHISWAP_BRIDGE_ADDRESS;
address private immutable SWERVE_BRIDGE_ADDRESS;
address private immutable UNISWAP_BRIDGE_ADDRESS;
address private immutable UNISWAP_V2_BRIDGE_ADDRESS;
constructor(AdapterAddresses memory addresses)
constructor(IEtherTokenV06 weth)
public
MixinBalancer()
MixinBancor(addresses)
MixinBancor(weth)
MixinCoFiX()
MixinCurve()
MixinCryptoCom()
MixinDodo(addresses)
MixinKyber(addresses)
MixinMooniswap(addresses)
MixinMStable(addresses)
MixinOasis(addresses)
MixinDodo()
MixinKyber(weth)
MixinMooniswap(weth)
MixinMStable()
MixinOasis()
MixinShell()
MixinSushiswap(addresses)
MixinUniswap(addresses)
MixinUniswapV2(addresses)
MixinSushiswap()
MixinUniswap(weth)
MixinUniswapV2()
MixinZeroExBridge()
{
BALANCER_BRIDGE_ADDRESS = addresses.balancerBridge;
BANCOR_BRIDGE_ADDRESS = addresses.bancorBridge;
COFIX_BRIDGE_ADDRESS = addresses.cofixBridge;
CURVE_BRIDGE_ADDRESS = addresses.curveBridge;
CRYPTO_COM_BRIDGE_ADDRESS = addresses.cryptoComBridge;
KYBER_BRIDGE_ADDRESS = addresses.kyberBridge;
MOONISWAP_BRIDGE_ADDRESS = addresses.mooniswapBridge;
MSTABLE_BRIDGE_ADDRESS = addresses.mStableBridge;
OASIS_BRIDGE_ADDRESS = addresses.oasisBridge;
SHELL_BRIDGE_ADDRESS = addresses.shellBridge;
SUSHISWAP_BRIDGE_ADDRESS = addresses.sushiswapBridge;
SWERVE_BRIDGE_ADDRESS = addresses.swerveBridge;
UNISWAP_BRIDGE_ADDRESS = addresses.uniswapBridge;
UNISWAP_V2_BRIDGE_ADDRESS = addresses.uniswapV2Bridge;
CREAM_BRIDGE_ADDRESS = addresses.creamBridge;
SNOW_SWAP_BRIDGE_ADDRESS = addresses.snowSwapBridge;
DODO_BRIDGE_ADDRESS = addresses.dodoBridge;
}
{}
function trade(
bytes calldata makerAssetData,
BridgeOrder memory order,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount
)
external
public
override
returns (uint256 boughtAmount)
{
(
IERC20TokenV06 buyToken,
address bridgeAddress,
bytes memory bridgeData
) = abi.decode(
makerAssetData[4:],
(IERC20TokenV06, address, bytes)
);
require(
bridgeAddress != address(this) && bridgeAddress != address(0),
"BridgeAdapter/INVALID_BRIDGE_ADDRESS"
);
if (bridgeAddress == CURVE_BRIDGE_ADDRESS ||
bridgeAddress == SWERVE_BRIDGE_ADDRESS ||
bridgeAddress == SNOW_SWAP_BRIDGE_ADDRESS) {
if (order.source == BridgeSource.CURVE ||
order.source == BridgeSource.SWERVE ||
order.source == BridgeSource.SNOWSWAP) {
boughtAmount = _tradeCurve(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == SUSHISWAP_BRIDGE_ADDRESS) {
boughtAmount = _tradeSushiswap(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == UNISWAP_V2_BRIDGE_ADDRESS) {
boughtAmount = _tradeUniswapV2(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == UNISWAP_BRIDGE_ADDRESS) {
boughtAmount = _tradeUniswap(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == BALANCER_BRIDGE_ADDRESS ||
bridgeAddress == CREAM_BRIDGE_ADDRESS) {
boughtAmount = _tradeBalancer(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == KYBER_BRIDGE_ADDRESS) {
boughtAmount = _tradeKyber(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == MOONISWAP_BRIDGE_ADDRESS) {
boughtAmount = _tradeMooniswap(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == MSTABLE_BRIDGE_ADDRESS) {
boughtAmount = _tradeMStable(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == OASIS_BRIDGE_ADDRESS) {
boughtAmount = _tradeOasis(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == SHELL_BRIDGE_ADDRESS) {
boughtAmount = _tradeShell(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == DODO_BRIDGE_ADDRESS) {
boughtAmount = _tradeDodo(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == CRYPTO_COM_BRIDGE_ADDRESS) {
boughtAmount = _tradeCryptoCom(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == BANCOR_BRIDGE_ADDRESS) {
boughtAmount = _tradeBancor(
buyToken,
sellAmount,
bridgeData
);
} else if (bridgeAddress == COFIX_BRIDGE_ADDRESS) {
boughtAmount = _tradeCoFiX(
buyToken,
sellAmount,
bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
bridgeAddress,
sellToken,
buyToken,
sellAmount,
bridgeData
order.bridgeData
);
} else if (order.source == BridgeSource.SUSHISWAP) {
boughtAmount = _tradeSushiswap(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.UNISWAPV2) {
boughtAmount = _tradeUniswapV2(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.UNISWAP) {
boughtAmount = _tradeUniswap(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.BALANCER ||
order.source == BridgeSource.CREAM) {
boughtAmount = _tradeBalancer(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.KYBER) {
boughtAmount = _tradeKyber(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.MOONISWAP) {
boughtAmount = _tradeMooniswap(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.MSTABLE) {
boughtAmount = _tradeMStable(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.OASIS) {
boughtAmount = _tradeOasis(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.SHELL) {
boughtAmount = _tradeShell(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.DODO) {
boughtAmount = _tradeDodo(
sellToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.CRYPTOCOM) {
boughtAmount = _tradeCryptoCom(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.BANCOR) {
boughtAmount = _tradeBancor(
buyToken,
sellAmount,
order.bridgeData
);
} else if (order.source == BridgeSource.COFIX) {
boughtAmount = _tradeCoFiX(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
} else {
boughtAmount = _tradeZeroExBridge(
sellToken,
buyToken,
sellAmount,
order.bridgeData
);
}
emit ERC20BridgeTransfer(
emit BridgeFill(
order.source,
sellToken,
buyToken,
sellAmount,
boughtAmount,
bridgeAddress,
address(this)
boughtAmount
);
}
}

View File

@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
library BridgeSource {
uint256 constant internal BALANCER = 0;
uint256 constant internal BANCOR = 1;
uint256 constant internal COFIX = 2;
uint256 constant internal CURVE = 3;
uint256 constant internal CREAM = 4;
uint256 constant internal CRYPTOCOM = 5;
uint256 constant internal DODO = 6;
uint256 constant internal KYBER = 7;
uint256 constant internal LIQUIDITYPROVIDER = 8;
uint256 constant internal MOONISWAP = 9;
uint256 constant internal MSTABLE = 10;
uint256 constant internal OASIS = 11;
uint256 constant internal SHELL = 12;
uint256 constant internal SNOWSWAP = 13;
uint256 constant internal SUSHISWAP = 14;
uint256 constant internal SWERVE = 15;
uint256 constant internal UNISWAP = 16;
uint256 constant internal UNISWAPV2 = 17;
// New sources should be APPENDED to this list, taking the next highest
// integer value.
}

View File

@@ -18,12 +18,38 @@
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
interface IBridgeAdapter {
struct BridgeOrder {
uint256 source;
uint256 takerTokenAmount;
uint256 makerTokenAmount;
bytes bridgeData;
}
/// @dev Emitted when tokens are swapped with an external source.
/// @param source The unique ID for the source. See `BridgeSource.sol`
/// @param inputToken The token the bridge is converting from.
/// @param outputToken The token the bridge is converting to.
/// @param inputTokenAmount Amount of input token sold.
/// @param outputTokenAmount Amount of output token bought.
event BridgeFill(
uint256 source,
IERC20TokenV06 inputToken,
IERC20TokenV06 outputToken,
uint256 inputTokenAmount,
uint256 outputTokenAmount
);
function trade(
bytes calldata makerAssetData,
address fromTokenAddress,
BridgeOrder calldata order,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount
)
external

View File

@@ -1,55 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
contract MixinAdapterAddresses
{
struct AdapterAddresses {
// Bridges
address balancerBridge;
address bancorBridge;
address cofixBridge;
address creamBridge;
address curveBridge;
address cryptoComBridge;
address dodoBridge;
address kyberBridge;
address mooniswapBridge;
address mStableBridge;
address oasisBridge;
address shellBridge;
address snowSwapBridge;
address swerveBridge;
address sushiswapBridge;
address uniswapBridge;
address uniswapV2Bridge;
// Exchanges
address kyberNetworkProxy;
address oasis;
address sushiswapRouter;
address uniswapV2Router;
address uniswapExchangeFactory;
address mStable;
address dodoHelper;
// Other
address weth;
}
}

View File

@@ -46,6 +46,7 @@ contract MixinBalancer {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeBalancer(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -54,9 +55,9 @@ contract MixinBalancer {
returns (uint256 boughtAmount)
{
// Decode the bridge data.
(IERC20TokenV06 sellToken, IBalancerPool pool) = abi.decode(
(IBalancerPool pool) = abi.decode(
bridgeData,
(IERC20TokenV06, IBalancerPool)
(IBalancerPool)
);
sellToken.approveIfBelow(
address(pool),

View File

@@ -24,12 +24,12 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
interface IBancorNetwork {
function convertByPath(
address[] calldata _path,
IERC20TokenV06[] calldata _path,
uint256 _amount,
uint256 _minReturn,
address _beneficiary,
@@ -42,17 +42,17 @@ interface IBancorNetwork {
}
contract MixinBancor is
MixinAdapterAddresses
{
contract MixinBancor {
/// @dev Bancor ETH pseudo-address.
address constant public BANCOR_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
IERC20TokenV06 constant public BANCOR_ETH_ADDRESS =
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
IEtherTokenV06 private immutable WETH;
constructor(AdapterAddresses memory addresses)
constructor(IEtherTokenV06 weth)
public
{
WETH = IEtherTokenV06(addresses.weth);
WETH = weth;
}
function _tradeBancor(
@@ -64,17 +64,22 @@ contract MixinBancor is
returns (uint256 boughtAmount)
{
// Decode the bridge data.
(
address[] memory path,
address bancorNetworkAddress
// solhint-disable indent
) = abi.decode(bridgeData, (address[], address));
// solhint-enable indent
IBancorNetwork bancorNetworkAddress;
IERC20TokenV06[] memory path;
{
address[] memory _path;
(
bancorNetworkAddress,
_path
) = abi.decode(bridgeData, (IBancorNetwork, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly { path := _path }
}
require(path.length >= 2, "MixinBancor/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == address(buyToken) ||
(path[path.length - 1] == BANCOR_ETH_ADDRESS && address(buyToken) == address(WETH)),
path[path.length - 1] == buyToken ||
(path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == WETH),
"MixinBancor/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
@@ -88,14 +93,14 @@ contract MixinBancor is
} else {
// Grant an allowance to the Bancor Network.
LibERC20TokenV06.approveIfBelow(
IERC20TokenV06(path[0]),
bancorNetworkAddress,
path[0],
address(bancorNetworkAddress),
sellAmount
);
}
// Convert the tokens
boughtAmount = IBancorNetwork(bancorNetworkAddress).convertByPath{value: payableAmount}(
boughtAmount = bancorNetworkAddress.convertByPath{value: payableAmount}(
path, // path originating with source token and terminating in destination token
sellAmount, // amount of source token to trade
1, // minimum amount of destination token expected to receive

View File

@@ -23,7 +23,6 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "./MixinAdapterAddresses.sol";
interface ICoFiXRouter {
@@ -53,15 +52,20 @@ interface ICoFiXPair {
function swapWithExact(address outToken, address to)
external
payable
returns (uint amountIn, uint amountOut, uint oracleFeeChange, uint256[4] memory tradeInfo);
returns (
uint amountIn,
uint amountOut,
uint oracleFeeChange,
uint256[4] memory tradeInfo
);
}
contract MixinCoFiX is
MixinAdapterAddresses
{
contract MixinCoFiX {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeCoFiX(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -69,15 +73,16 @@ contract MixinCoFiX is
internal
returns (uint256 boughtAmount)
{
(address fromTokenAddress, uint256 fee, address pool) = abi.decode(bridgeData, (address, uint256, address));
(uint256 fee, ICoFiXPair pool) = abi.decode(bridgeData, (uint256, ICoFiXPair));
// Transfer tokens into the pool
LibERC20TokenV06.compatTransfer(
IERC20TokenV06(fromTokenAddress),
pool,
sellAmount);
sellToken,
address(pool),
sellAmount
);
// Call the swap exact with the tokens now in the pool
// pay the NEST Oracle fee with ETH
(/* In */, boughtAmount, , ) = ICoFiXPair(pool).swapWithExact{value: fee}(
(/* In */, boughtAmount, , ) = pool.swapWithExact{value: fee}(
address(buyToken),
address(this)
);

View File

@@ -37,21 +37,24 @@ contract MixinCryptoCom
internal
returns (uint256 boughtAmount)
{
// solhint-disable indent
address[] memory path;
address router;
(path, router) = abi.decode(bridgeData, (address[], address));
// solhint-enable indent
IUniswapV2Router02 router;
IERC20TokenV06[] memory path;
{
address[] memory _path;
(router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly { path := _path }
}
require(path.length >= 2, "CryptoComBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(path.length >= 2, "MixinCryptoCom/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == address(buyToken),
"CryptoComBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
path[path.length - 1] == buyToken,
"MixinCryptoCom/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
// Grant the CryptoCom router an allowance to sell the first token.
IERC20TokenV06(path[0]).approveIfBelow(router, sellAmount);
path[0].approveIfBelow(address(router), sellAmount);
uint[] memory amounts = IUniswapV2Router02(router).swapExactTokensForTokens(
uint[] memory amounts = router.swapExactTokensForTokens(
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.

View File

@@ -35,12 +35,12 @@ contract MixinCurve {
struct CurveBridgeData {
address curveAddress;
bytes4 exchangeFunctionSelector;
IERC20TokenV06 sellToken;
int128 fromCoinIdx;
int128 toCoinIdx;
}
function _tradeCurve(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -50,7 +50,7 @@ contract MixinCurve {
{
// Decode the bridge data to get the Curve metadata.
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
data.sellToken.approveIfBelow(data.curveAddress, sellAmount);
sellToken.approveIfBelow(data.curveAddress, sellAmount);
uint256 beforeBalance = buyToken.balanceOf(address(this));
(bool success, bytes memory resultData) =
data.curveAddress.call(abi.encodeWithSelector(

View File

@@ -23,55 +23,60 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
interface IDODOHelper {
function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
}
import "../IBridgeAdapter.sol";
interface IDODO {
function sellBaseToken(
uint256 amount,
uint256 minReceiveQuote,
bytes calldata data
)
external
returns (uint256);
function sellBaseToken(uint256 amount, uint256 minReceiveQuote, bytes calldata data) external returns (uint256);
function buyBaseToken(uint256 amount, uint256 maxPayQuote, bytes calldata data) external returns (uint256);
function buyBaseToken(
uint256 amount,
uint256 maxPayQuote,
bytes calldata data
)
external
returns (uint256);
}
contract MixinDodo is
MixinAdapterAddresses
{
interface IDODOHelper {
function querySellQuoteToken(
IDODO dodo,
uint256 amount
)
external
view
returns (uint256);
}
contract MixinDodo {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the `DOODO Helper` contract.
IDODOHelper private immutable DODO_HELPER;
constructor(AdapterAddresses memory addresses)
public
{
DODO_HELPER = IDODOHelper(addresses.dodoHelper);
}
function _tradeDodo(
IERC20TokenV06 /* buyToken */,
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(address fromTokenAddress,
address pool,
bool isSellBase) = abi.decode(bridgeData, (address, address, bool));
(IDODOHelper helper, IDODO pool, bool isSellBase) =
abi.decode(bridgeData, (IDODOHelper, IDODO, bool));
// Grant the Dodo pool contract an allowance to sell the first token.
IERC20TokenV06(fromTokenAddress).approveIfBelow(pool, sellAmount);
sellToken.approveIfBelow(address(pool), sellAmount);
if (isSellBase) {
// Sell the Base token directly against the contract
boughtAmount = IDODO(pool).sellBaseToken(
boughtAmount = pool.sellBaseToken(
// amount to sell
sellAmount,
// min receive amount
@@ -80,11 +85,11 @@ contract MixinDodo is
);
} else {
// Need to re-calculate the sell quote amount into buyBase
boughtAmount = DODO_HELPER.querySellQuoteToken(
boughtAmount = helper.querySellQuoteToken(
pool,
sellAmount
);
IDODO(pool).buyBaseToken(
pool.buyBaseToken(
// amount to buy
boughtAmount,
// max pay amount

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
interface IKyberNetworkProxy {
@@ -54,26 +54,24 @@ interface IKyberNetworkProxy {
returns (uint256 boughtAmount);
}
contract MixinKyber is
MixinAdapterAddresses
{
contract MixinKyber {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Address indicating the trade is using ETH
address private immutable KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
IERC20TokenV06 private immutable KYBER_ETH_ADDRESS =
IERC20TokenV06(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
/// @dev Mainnet address of the KyberNetworkProxy contract.
IKyberNetworkProxy private immutable KYBER_NETWORK_PROXY;
constructor(AdapterAddresses memory addresses)
constructor(IEtherTokenV06 weth)
public
{
WETH = IEtherTokenV06(addresses.weth);
KYBER_NETWORK_PROXY = IKyberNetworkProxy(addresses.kyberNetworkProxy);
WETH = weth;
}
function _tradeKyber(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -81,15 +79,15 @@ contract MixinKyber is
internal
returns (uint256 boughtAmount)
{
(IERC20TokenV06 sellToken, bytes memory hint) =
abi.decode(bridgeData, (IERC20TokenV06, bytes));
(IKyberNetworkProxy kyber, bytes memory hint) =
abi.decode(bridgeData, (IKyberNetworkProxy, bytes));
uint256 payableAmount = 0;
if (sellToken != WETH) {
// If the input token is not WETH, grant an allowance to the exchange
// to spend them.
sellToken.approveIfBelow(
address(KYBER_NETWORK_PROXY),
address(kyber),
sellAmount
);
} else {
@@ -100,13 +98,13 @@ contract MixinKyber is
// Try to sell all of this contract's input token balance through
// `KyberNetworkProxy.trade()`.
boughtAmount = KYBER_NETWORK_PROXY.tradeWithHint{ value: payableAmount }(
boughtAmount = kyber.tradeWithHint{ value: payableAmount }(
// Input token.
sellToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : sellToken,
sellToken == WETH ? KYBER_ETH_ADDRESS : sellToken,
// Sell amount.
sellAmount,
// Output token.
buyToken == WETH ? IERC20TokenV06(KYBER_ETH_ADDRESS) : buyToken,
buyToken == WETH ? KYBER_ETH_ADDRESS : buyToken,
// Transfer to this contract
address(uint160(address(this))),
// Buy as much as possible.

View File

@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
interface IMStable {
@@ -37,21 +37,12 @@ interface IMStable {
returns (uint256 boughtAmount);
}
contract MixinMStable is
MixinAdapterAddresses
{
contract MixinMStable {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the mStable mUSD contract.
IMStable private immutable MSTABLE;
constructor(AdapterAddresses memory addresses)
public
{
MSTABLE = IMStable(addresses.mStable);
}
function _tradeMStable(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -59,12 +50,12 @@ contract MixinMStable is
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data to get the `sellToken`.
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
// Grant an allowance to the exchange to spend `sellToken` token.
sellToken.approveIfBelow(address(MSTABLE), sellAmount);
(IMStable mstable) = abi.decode(bridgeData, (IMStable));
boughtAmount = MSTABLE.swap(
// Grant an allowance to the exchange to spend `sellToken` token.
sellToken.approveIfBelow(address(mstable), sellAmount);
boughtAmount = mstable.swap(
sellToken,
buyToken,
sellAmount,

View File

@@ -24,7 +24,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
/// @dev Moooniswap pool interface.
@@ -43,22 +43,22 @@ interface IMooniswapPool {
}
/// @dev BridgeAdapter mixin for mooniswap.
contract MixinMooniswap is
MixinAdapterAddresses
{
contract MixinMooniswap {
using LibERC20TokenV06 for IERC20TokenV06;
using LibERC20TokenV06 for IEtherTokenV06;
/// @dev WETH token.
IEtherTokenV06 private immutable WETH;
constructor(AdapterAddresses memory addresses)
constructor(IEtherTokenV06 weth)
public
{
WETH = IEtherTokenV06(addresses.weth);
WETH = weth;
}
function _tradeMooniswap(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -66,8 +66,7 @@ contract MixinMooniswap is
internal
returns (uint256 boughtAmount)
{
(IERC20TokenV06 sellToken, IMooniswapPool pool) =
abi.decode(bridgeData, (IERC20TokenV06, IMooniswapPool));
(IMooniswapPool pool) = abi.decode(bridgeData, (IMooniswapPool));
// Convert WETH to ETH.
uint256 ethValue = 0;

View File

@@ -22,7 +22,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
interface IOasis {
@@ -42,21 +42,12 @@ interface IOasis {
returns (uint256 boughtAmount);
}
contract MixinOasis is
MixinAdapterAddresses
{
contract MixinOasis {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the Oasis `MatchingMarket` contract.
IOasis private immutable OASIS;
constructor(AdapterAddresses memory addresses)
public
{
OASIS = IOasis(addresses.oasis);
}
function _tradeOasis(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -64,15 +55,16 @@ contract MixinOasis is
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data to get the `sellToken`.
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
(IOasis oasis) = abi.decode(bridgeData, (IOasis));
// Grant an allowance to the exchange to spend `sellToken` token.
sellToken.approveIfBelow(
address(OASIS),
address(oasis),
sellAmount
);
// Try to sell all of this contract's `sellToken` token balance.
boughtAmount = OASIS.sellAllAmount(
boughtAmount = oasis.sellAllAmount(
sellToken,
sellAmount,
buyToken,

View File

@@ -23,13 +23,12 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
interface IShell {
function originSwap(
address from,
address to,
IERC20TokenV06 from,
IERC20TokenV06 to,
uint256 fromAmount,
uint256 minTargetAmount,
uint256 deadline
@@ -38,12 +37,12 @@ interface IShell {
returns (uint256 toAmount);
}
contract MixinShell is
MixinAdapterAddresses
{
contract MixinShell {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeShell(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -51,17 +50,17 @@ contract MixinShell is
internal
returns (uint256 boughtAmount)
{
(address fromTokenAddress, address pool) = abi.decode(bridgeData, (address, address));
IShell pool = abi.decode(bridgeData, (IShell));
// Grant the Shell contract an allowance to sell the first token.
IERC20TokenV06(fromTokenAddress).approveIfBelow(
pool,
IERC20TokenV06(sellToken).approveIfBelow(
address(pool),
sellAmount
);
boughtAmount = IShell(pool).originSwap(
fromTokenAddress,
address(buyToken),
boughtAmount = pool.originSwap(
sellToken,
buyToken,
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.

View File

@@ -23,23 +23,12 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "./MixinUniswapV2.sol";
contract MixinSushiswap is
MixinAdapterAddresses
{
contract MixinSushiswap {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the `SushiswapRouter` contract.
IUniswapV2Router02 private immutable SUSHISWAP_ROUTER;
constructor(AdapterAddresses memory addresses)
public
{
SUSHISWAP_ROUTER = IUniswapV2Router02(addresses.sushiswapRouter);
}
function _tradeSushiswap(
IERC20TokenV06 buyToken,
uint256 sellAmount,
@@ -48,22 +37,28 @@ contract MixinSushiswap is
internal
returns (uint256 boughtAmount)
{
// solhint-disable indent
address[] memory path = abi.decode(bridgeData, (address[]));
// solhint-enable indent
IERC20TokenV06[] memory path;
IUniswapV2Router02 router;
{
address[] memory _path;
(router, _path) =
abi.decode(bridgeData, (IUniswapV2Router02, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly { path := _path }
}
require(path.length >= 2, "SushiswapBridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(path.length >= 2, "MixinSushiswap/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == address(buyToken),
"SushiswapBridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
path[path.length - 1] == buyToken,
"MixinSushiswap/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
// Grant the Uniswap router an allowance to sell the first token.
IERC20TokenV06(path[0]).approveIfBelow(
address(SUSHISWAP_ROUTER),
path[0].approveIfBelow(
address(router),
sellAmount
);
uint[] memory amounts = SUSHISWAP_ROUTER.swapExactTokensForTokens(
uint[] memory amounts = router.swapExactTokensForTokens(
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
interface IUniswapExchangeFactory {
@@ -103,24 +103,21 @@ interface IUniswapExchange {
returns (uint256 tokensBought);
}
contract MixinUniswap is
MixinAdapterAddresses
{
contract MixinUniswap {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
/// @dev Mainnet address of the `UniswapExchangeFactory` contract.
IUniswapExchangeFactory private immutable UNISWAP_EXCHANGE_FACTORY;
constructor(AdapterAddresses memory addresses)
constructor(IEtherTokenV06 weth)
public
{
WETH = IEtherTokenV06(addresses.weth);
UNISWAP_EXCHANGE_FACTORY = IUniswapExchangeFactory(addresses.uniswapExchangeFactory);
WETH = weth;
}
function _tradeUniswap(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
@@ -128,11 +125,12 @@ contract MixinUniswap is
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data to get the `sellToken`.
(IERC20TokenV06 sellToken) = abi.decode(bridgeData, (IERC20TokenV06));
IUniswapExchangeFactory exchangeFactory =
abi.decode(bridgeData, (IUniswapExchangeFactory));
// Get the exchange for the token pair.
IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
exchangeFactory,
sellToken,
buyToken
);
@@ -197,10 +195,12 @@ contract MixinUniswap is
/// @dev Retrieves the uniswap exchange for a given token pair.
/// In the case of a WETH-token exchange, this will be the non-WETH token.
/// In th ecase of a token-token exchange, this will be the first token.
/// @param exchangeFactory The exchange factory.
/// @param sellToken The address of the token we are converting from.
/// @param buyToken The address of the token we are converting to.
/// @return exchange The uniswap exchange.
function _getUniswapExchangeForTokenPair(
IUniswapExchangeFactory exchangeFactory,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken
)
@@ -210,8 +210,8 @@ contract MixinUniswap is
{
// Whichever isn't WETH is the exchange token.
exchange = sellToken == WETH
? UNISWAP_EXCHANGE_FACTORY.getExchange(buyToken)
: UNISWAP_EXCHANGE_FACTORY.getExchange(sellToken);
require(address(exchange) != address(0), "NO_UNISWAP_EXCHANGE_FOR_TOKEN");
? exchangeFactory.getExchange(buyToken)
: exchangeFactory.getExchange(sellToken);
require(address(exchange) != address(0), "MixinUniswap/NO_EXCHANGE");
}
}

View File

@@ -23,7 +23,7 @@ pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./MixinAdapterAddresses.sol";
import "../IBridgeAdapter.sol";
/*
UniswapV2
@@ -42,26 +42,16 @@ interface IUniswapV2Router02 {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
IERC20TokenV06[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
}
contract MixinUniswapV2 is
MixinAdapterAddresses
{
contract MixinUniswapV2 {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the `UniswapV2Router02` contract.
IUniswapV2Router02 private immutable UNISWAP_V2_ROUTER;
constructor(AdapterAddresses memory addresses)
public
{
UNISWAP_V2_ROUTER = IUniswapV2Router02(addresses.uniswapV2Router);
}
function _tradeUniswapV2(
IERC20TokenV06 buyToken,
uint256 sellAmount,
@@ -70,22 +60,24 @@ contract MixinUniswapV2 is
internal
returns (uint256 boughtAmount)
{
// solhint-disable indent
address[] memory path = abi.decode(bridgeData, (address[]));
// solhint-enable indent
IUniswapV2Router02 router;
IERC20TokenV06[] memory path;
{
address[] memory _path;
(router, _path) = abi.decode(bridgeData, (IUniswapV2Router02, address[]));
// To get around `abi.decode()` not supporting interface array types.
assembly { path := _path }
}
require(path.length >= 2, "UniswapV2Bridge/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(path.length >= 2, "MixinUniswapV3/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == address(buyToken),
"UniswapV2Bridge/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
path[path.length - 1] == buyToken,
"MixinUniswapV2/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
// Grant the Uniswap router an allowance to sell the first token.
IERC20TokenV06(path[0]).approveIfBelow(
address(UNISWAP_V2_ROUTER),
sellAmount
);
path[0].approveIfBelow(address(router), sellAmount);
uint[] memory amounts = UNISWAP_V2_ROUTER.swapExactTokensForTokens(
uint[] memory amounts = router.swapExactTokensForTokens(
// Sell all tokens we hold.
sellAmount,
// Minimum buy amount.

View File

@@ -31,7 +31,6 @@ contract MixinZeroExBridge {
using LibSafeMathV06 for uint256;
function _tradeZeroExBridge(
address bridgeAddress,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
@@ -40,17 +39,19 @@ contract MixinZeroExBridge {
internal
returns (uint256 boughtAmount)
{
(ILiquidityProvider provider, bytes memory lpData) =
abi.decode(bridgeData, (ILiquidityProvider, bytes));
// Trade the good old fashioned way
sellToken.compatTransfer(
bridgeAddress,
address(provider),
sellAmount
);
boughtAmount = ILiquidityProvider(bridgeAddress).sellTokenForToken(
boughtAmount = provider.sellTokenForToken(
address(sellToken),
address(buyToken),
address(this), // recipient
1, // minBuyAmount
bridgeData
lpData
);
}
}

View File

@@ -1,113 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
/// @dev Interface to the V3 Exchange.
interface IExchange {
/// @dev V3 Order structure.
struct Order {
// Address that created the order.
address makerAddress;
// Address that is allowed to fill the order.
// If set to 0, any address is allowed to fill the order.
address takerAddress;
// Address that will recieve fees when order is filled.
address feeRecipientAddress;
// Address that is allowed to call Exchange contract methods that affect this order.
// If set to 0, any address is allowed to call these methods.
address senderAddress;
// Amount of makerAsset being offered by maker. Must be greater than 0.
uint256 makerAssetAmount;
// Amount of takerAsset being bid on by maker. Must be greater than 0.
uint256 takerAssetAmount;
// Fee paid to feeRecipient by maker when order is filled.
uint256 makerFee;
// Fee paid to feeRecipient by taker when order is filled.
uint256 takerFee;
// Timestamp in seconds at which order expires.
uint256 expirationTimeSeconds;
// Arbitrary number to facilitate uniqueness of the order's hash.
uint256 salt;
// Encoded data that can be decoded by a specified proxy contract when transferring makerAsset.
// The leading bytes4 references the id of the asset proxy.
bytes makerAssetData;
// Encoded data that can be decoded by a specified proxy contract when transferring takerAsset.
// The leading bytes4 references the id of the asset proxy.
bytes takerAssetData;
// Encoded data that can be decoded by a specified proxy contract when transferring makerFeeAsset.
// The leading bytes4 references the id of the asset proxy.
bytes makerFeeAssetData;
// Encoded data that can be decoded by a specified proxy contract when transferring takerFeeAsset.
// The leading bytes4 references the id of the asset proxy.
bytes takerFeeAssetData;
}
/// @dev V3 `fillOrder()` results.`
struct FillResults {
// Total amount of makerAsset(s) filled.
uint256 makerAssetFilledAmount;
// Total amount of takerAsset(s) filled.
uint256 takerAssetFilledAmount;
// Total amount of fees paid by maker(s) to feeRecipient(s).
uint256 makerFeePaid;
// Total amount of fees paid by taker to feeRecipients(s).
uint256 takerFeePaid;
// Total amount of fees paid by taker to the staking contract.
uint256 protocolFeePaid;
}
/// @dev Fills the input order.
/// @param order Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
/// @param signature Proof that order has been created by maker.
/// @return fillResults Amounts filled and fees paid by maker and taker.
function fillOrder(
Order calldata order,
uint256 takerAssetFillAmount,
bytes calldata signature
)
external
payable
returns (FillResults memory fillResults);
/// @dev Returns the protocolFeeMultiplier
/// @return multiplier The multiplier for protocol fees.
function protocolFeeMultiplier()
external
view
returns (uint256 multiplier);
/// @dev Gets an asset proxy.
/// @param assetProxyId Id of the asset proxy.
/// @return proxyAddress The asset proxy registered to assetProxyId.
/// Returns 0x0 if no proxy is registered.
function getAssetProxy(bytes4 assetProxyId)
external
view
returns (address proxyAddress);
function EIP712_EXCHANGE_DOMAIN_HASH()
external
view
returns (bytes32 domainHash);
}

View File

@@ -1,38 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
interface IGasToken {
/// @dev Frees up to `value` sub-tokens
/// @param value The amount of tokens to free
/// @return freed How many tokens were freed
function freeUpTo(uint256 value) external returns (uint256 freed);
/// @dev Frees up to `value` sub-tokens owned by `from`
/// @param from The owner of tokens to spend
/// @param value The amount of tokens to free
/// @return freed How many tokens were freed
function freeFromUpTo(address from, uint256 value) external returns (uint256 freed);
/// @dev Mints `value` amount of tokens
/// @param value The amount of tokens to mint
function mint(uint256 value) external;
}

View File

@@ -1,168 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2020 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
import "./IExchange.sol";
library LibOrderHash {
using LibOrderHash for IExchange.Order;
// Hash for the EIP712 Order Schema:
// keccak256(abi.encodePacked(
// "Order(",
// "address makerAddress,",
// "address takerAddress,",
// "address feeRecipientAddress,",
// "address senderAddress,",
// "uint256 makerAssetAmount,",
// "uint256 takerAssetAmount,",
// "uint256 makerFee,",
// "uint256 takerFee,",
// "uint256 expirationTimeSeconds,",
// "uint256 salt,",
// "bytes makerAssetData,",
// "bytes takerAssetData,",
// "bytes makerFeeAssetData,",
// "bytes takerFeeAssetData",
// ")"
// ))
bytes32 constant internal _EIP712_ORDER_SCHEMA_HASH =
0xf80322eb8376aafb64eadf8f0d7623f22130fd9491a221e902b713cb984a7534;
/// @dev Calculates the EIP712 typed data hash of an order with a given domain separator.
/// @param order The order structure.
/// @param eip712ExchangeDomainHash Domain hash for the Exchange.
/// @return orderHash EIP712 typed data hash of the order.
function getTypedDataHash(IExchange.Order memory order, bytes32 eip712ExchangeDomainHash)
internal
pure
returns (bytes32 orderHash)
{
orderHash = _hashEIP712Message(
eip712ExchangeDomainHash,
order.getStructHash()
);
return orderHash;
}
/// @dev Calculates EIP712 hash of the order struct.
/// @param order The order structure.
/// @return result EIP712 hash of the order struct.
function getStructHash(IExchange.Order memory order)
internal
pure
returns (bytes32 result)
{
bytes32 schemaHash = _EIP712_ORDER_SCHEMA_HASH;
bytes memory makerAssetData = order.makerAssetData;
bytes memory takerAssetData = order.takerAssetData;
bytes memory makerFeeAssetData = order.makerFeeAssetData;
bytes memory takerFeeAssetData = order.takerFeeAssetData;
// Assembly for more efficiently computing:
// keccak256(abi.encodePacked(
// EIP712_ORDER_SCHEMA_HASH,
// uint256(order.makerAddress),
// uint256(order.takerAddress),
// uint256(order.feeRecipientAddress),
// uint256(order.senderAddress),
// order.makerAssetAmount,
// order.takerAssetAmount,
// order.makerFee,
// order.takerFee,
// order.expirationTimeSeconds,
// order.salt,
// keccak256(order.makerAssetData),
// keccak256(order.takerAssetData),
// keccak256(order.makerFeeAssetData),
// keccak256(order.takerFeeAssetData)
// ));
assembly {
// Assert order offset (this is an internal error that should never be triggered)
if lt(order, 32) {
invalid()
}
// Calculate memory addresses that will be swapped out before hashing
let pos1 := sub(order, 32)
let pos2 := add(order, 320)
let pos3 := add(order, 352)
let pos4 := add(order, 384)
let pos5 := add(order, 416)
// Backup
let temp1 := mload(pos1)
let temp2 := mload(pos2)
let temp3 := mload(pos3)
let temp4 := mload(pos4)
let temp5 := mload(pos5)
// Hash in place
mstore(pos1, schemaHash)
mstore(pos2, keccak256(add(makerAssetData, 32), mload(makerAssetData))) // store hash of makerAssetData
mstore(pos3, keccak256(add(takerAssetData, 32), mload(takerAssetData))) // store hash of takerAssetData
mstore(pos4, keccak256(add(makerFeeAssetData, 32), mload(makerFeeAssetData))) // store hash of makerFeeAssetData
mstore(pos5, keccak256(add(takerFeeAssetData, 32), mload(takerFeeAssetData))) // store hash of takerFeeAssetData
result := keccak256(pos1, 480)
// Restore
mstore(pos1, temp1)
mstore(pos2, temp2)
mstore(pos3, temp3)
mstore(pos4, temp4)
mstore(pos5, temp5)
}
return result;
}
/// @dev Calculates EIP712 encoding for a hash struct with a given domain hash.
/// @param eip712DomainHash Hash of the domain domain separator data, computed
/// with getDomainHash().
/// @param hashStruct The EIP712 hash struct.
/// @return result EIP712 hash applied to the given EIP712 Domain.
function _hashEIP712Message(bytes32 eip712DomainHash, bytes32 hashStruct)
internal
pure
returns (bytes32 result)
{
// Assembly for more efficient computing:
// keccak256(abi.encodePacked(
// EIP191_HEADER,
// EIP712_DOMAIN_HASH,
// hashStruct
// ));
assembly {
// Load free memory pointer
let memPtr := mload(64)
mstore(memPtr, 0x1901000000000000000000000000000000000000000000000000000000000000) // EIP191 header
mstore(add(memPtr, 2), eip712DomainHash) // EIP712 domain hash
mstore(add(memPtr, 34), hashStruct) // Hash of struct
// Compute hash
result := keccak256(memPtr, 66)
}
return result;
}
}

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