Compare commits

..

34 Commits

Author SHA1 Message Date
Github Actions
859c36cb10 Publish
- @0x/contracts-asset-proxy@3.7.17
 - @0x/contracts-broker@1.1.35
 - @0x/contracts-coordinator@3.1.36
 - @0x/contracts-dev-utils@1.3.34
 - @0x/contracts-erc1155@2.1.35
 - @0x/contracts-erc20@3.3.14
 - @0x/contracts-erc721@3.1.35
 - @0x/contracts-exchange-forwarder@4.2.36
 - @0x/contracts-exchange-libs@4.3.35
 - @0x/contracts-exchange@3.2.36
 - @0x/contracts-extensions@6.2.30
 - @0x/contracts-integrations@2.7.62
 - @0x/contracts-multisig@4.1.36
 - @0x/contracts-staking@2.0.43
 - @0x/contracts-test-utils@5.4.6
 - @0x/contracts-treasury@1.3.0
 - @0x/contracts-utils@4.7.14
 - @0x/contracts-zero-ex@0.27.0
 - @0x/asset-swapper@16.24.0
 - @0x/contract-addresses@6.5.0
 - @0x/contract-wrappers-test@12.2.51
 - @0x/contract-wrappers@13.17.3
 - @0x/migrations@8.0.12
 - @0x/order-utils@10.4.27
 - @0x/protocol-utils@1.8.0
2021-08-06 04:54:38 +00:00
Github Actions
3e34386812 Updated CHANGELOGS & MD docs 2021-08-06 04:54:34 +00:00
Jacob Evans
a35af11981 feat: Clipper (#299)
* feat: Clipper

* feat: Curve tricrypto2 (#302)

* Scope down the token list search space for Clipper

* update deployed addresses
2021-08-06 14:12:43 +10:00
mzhu25
59eabec71e Add proposal 1 and test (#298)
* Add proposal 1 and test

* update changelog
2021-08-05 13:10:36 -07:00
Github Actions
bf4c7e7d50 Publish
- @0x/contracts-integrations@2.7.61
 - @0x/asset-swapper@16.23.1
2021-07-29 15:23:54 +00:00
Github Actions
fd098ca4df Updated CHANGELOGS & MD docs 2021-07-29 15:23:49 +00:00
Lawrence Forman
c360f8d8fd Publish (#296)
- @0x/contracts-integrations@2.7.60
 - @0x/asset-swapper@16.23.0

Co-authored-by: Github Actions <github-actions@github.com>
2021-07-29 10:54:37 -04:00
Github Actions
b358559421 Publish
- @0x/contracts-integrations@2.7.60
 - @0x/asset-swapper@16.23.0
2021-07-16 22:11:44 +00:00
Github Actions
df5aad8e8e Updated CHANGELOGS & MD docs 2021-07-16 22:11:39 +00:00
Andreas Andreakis
8dbf79db59 fix: lint (#289) 2021-07-16 12:24:56 -07:00
Andreas Andreakis
1839608e84 feat: ACryptoS StableSwap (#284) 2021-07-15 15:19:40 -07:00
Github Actions
6e3e795b8b Publish
- @0x/contracts-integrations@2.7.59
 - @0x/asset-swapper@16.22.0
2021-07-13 22:19:54 +00:00
Github Actions
d9c410a7e3 Updated CHANGELOGS & MD docs 2021-07-13 22:19:49 +00:00
Romain Butteaud
609727afe8 feat: Saddle alETH, D4 pools (#283) 2021-07-13 14:22:16 -07:00
Romain Butteaud
8a5c74c0b4 feat: IronSwap (#281)
* feat: IronSwap

* feat: IronSwap, changelog

* feat: IronSwap, prettier
2021-07-13 14:07:45 -07:00
Github Actions
cd93f3b07e Publish
- @0x/contracts-integrations@2.7.58
 - @0x/asset-swapper@16.21.0
2021-07-10 08:00:34 +00:00
Github Actions
8397b12de6 Updated CHANGELOGS & MD docs 2021-07-10 08:00:29 +00:00
Romain Butteaud
3f85acec3a feat: JetSwap (#280)
* feat: JetSwap

* feat: JetSwap, changelog
2021-07-10 00:18:29 -07:00
phil-ociraptor
d6a9e3d600 fix: uncaught type error while logging (#277) 2021-07-07 14:28:42 -05:00
phil-ociraptor
361569ac2f chore: emit a log when a quote is returned that is between 99-100% of quote (#275) 2021-07-06 17:19:41 -05:00
Github Actions
719664c145 Publish
- @0x/contracts-integrations@2.7.57
 - @0x/asset-swapper@16.20.0
2021-07-06 21:34:44 +00:00
Github Actions
f800d6c24c Updated CHANGELOGS & MD docs 2021-07-06 21:34:39 +00:00
Romain Butteaud
a9a81bcafb feat: ShibaSwap (#276) 2021-07-06 13:53:39 -07:00
Github Actions
4280307a15 Publish
- @0x/contracts-integrations@2.7.56
 - @0x/asset-swapper@16.19.1
2021-07-06 04:03:16 +00:00
Github Actions
7b57d3ae51 Updated CHANGELOGS & MD docs 2021-07-06 04:03:11 +00:00
Romain Butteaud
8a8a5bbda0 fix: adding MultiHop for Polygon (#271) 2021-07-06 13:33:41 +10:00
Jacob Evans
76987c8db1 fix: PLP fallback (#272)
* fix: PLP fallback

* CHANGELOG
2021-07-06 13:32:34 +10:00
Github Actions
6f8971cc42 Publish
- @0x/contracts-integrations@2.7.55
 - @0x/asset-swapper@16.19.0
2021-07-02 01:48:13 +00:00
Github Actions
71ab882143 Updated CHANGELOGS & MD docs 2021-07-02 01:48:08 +00:00
Romain Butteaud
5a4961c8d9 fix: KyberDMM pick best pools, Polygon KyberDMM (#269)
* fix: KyberDMM all pools, Polygon KyberDMM

* fix: KyberDMM, remove getPoolsLength interface and revert if no pools found
2021-07-01 18:07:02 -07:00
mzhu25
4c4fb99d87 Add PLP as a source for Polygon (#270)
* Whitelist PLP as a source for Polygon

* changelog
2021-07-01 17:21:23 -07:00
Github Actions
872abf09e9 Publish
- @0x/contracts-integrations@2.7.54
 - @0x/asset-swapper@6.18.3
2021-06-29 17:20:15 +00:00
Github Actions
f10bfe7d04 Updated CHANGELOGS & MD docs 2021-06-29 17:20:11 +00:00
Romain Butteaud
a74d8deff3 feat: Balancer V2 Polygon (#267) 2021-06-29 09:46:17 -07:00
169 changed files with 4936 additions and 3662 deletions

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "3.7.17",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "3.7.16",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.7.17 - _August 6, 2021_
* Dependencies updated
## v3.7.16 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-asset-proxy",
"version": "3.7.16",
"version": "3.7.17",
"engines": {
"node": ">=6.12"
},
@@ -52,10 +52,10 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-wrappers": "^13.17.2",
"@0x/contract-wrappers": "^13.17.3",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
@@ -80,11 +80,11 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-erc1155": "^2.1.34",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/order-utils": "^10.4.26",
"@0x/contracts-erc1155": "^2.1.35",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/order-utils": "^10.4.27",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "1.1.35",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "1.1.34",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.1.35 - _August 6, 2021_
* Dependencies updated
## v1.1.34 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-broker",
"version": "1.1.34",
"version": "1.1.35",
"engines": {
"node": ">=6.12"
},
@@ -52,14 +52,14 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/contracts-exchange": "^3.2.35",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/contracts-exchange": "^3.2.36",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -85,7 +85,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "3.1.36",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "3.1.35",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.36 - _August 6, 2021_
* Dependencies updated
## v3.1.35 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-coordinator",
"version": "3.1.35",
"version": "3.1.36",
"engines": {
"node": ">=6.12"
},
@@ -53,12 +53,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-gen": "^2.0.38",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -84,10 +84,10 @@
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.4.0",
"@0x/contracts-exchange": "^3.2.35",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contract-addresses": "^6.5.0",
"@0x/contracts-exchange": "^3.2.36",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/json-schemas": "^6.1.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "1.3.34",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "1.3.33",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.34 - _August 6, 2021_
* Dependencies updated
## v1.3.33 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-dev-utils",
"version": "1.3.33",
"version": "1.3.34",
"engines": {
"node": ">=6.12"
},
@@ -43,10 +43,10 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/assert": "^3.0.27",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "2.1.35",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "2.1.34",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.1.35 - _August 6, 2021_
* Dependencies updated
## v2.1.34 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc1155",
"version": "2.1.34",
"version": "2.1.35",
"engines": {
"node": ">=6.12"
},
@@ -54,7 +54,7 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
@@ -81,7 +81,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/utils": "^6.4.3",
"@0x/web3-wrapper": "^7.5.3",
"lodash": "^4.17.11"

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "3.3.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "3.3.13",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.3.14 - _August 6, 2021_
* Dependencies updated
## v3.3.13 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc20",
"version": "3.3.13",
"version": "3.3.14",
"engines": {
"node": ">=6.12"
},
@@ -53,8 +53,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "3.1.35",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "3.1.34",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.1.35 - _August 6, 2021_
* Dependencies updated
## v3.1.34 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-erc721",
"version": "3.1.34",
"version": "3.1.35",
"engines": {
"node": ">=6.12"
},
@@ -54,8 +54,8 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "4.2.36",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "4.2.35",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.2.36 - _August 6, 2021_
* Dependencies updated
## v4.2.35 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-forwarder",
"version": "4.2.35",
"version": "4.2.36",
"engines": {
"node": ">=6.12"
},
@@ -53,18 +53,18 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-erc1155": "^2.1.34",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/contracts-exchange": "^3.2.35",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-erc1155": "^2.1.35",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/contracts-exchange": "^3.2.36",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "4.3.35",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "4.3.34",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.3.35 - _August 6, 2021_
* Dependencies updated
## v4.3.34 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange-libs",
"version": "4.3.34",
"version": "4.3.35",
"engines": {
"node": ">=6.12"
},
@@ -81,9 +81,9 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/order-utils": "^10.4.26",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/order-utils": "^10.4.27",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "3.2.36",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "3.2.35",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v3.2.36 - _August 6, 2021_
* Dependencies updated
## v3.2.35 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-exchange",
"version": "3.2.35",
"version": "3.2.36",
"engines": {
"node": ">=6.12"
},
@@ -53,13 +53,13 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/protocol",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-multisig": "^4.1.35",
"@0x/contracts-staking": "^2.0.42",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-multisig": "^4.1.36",
"@0x/contracts-staking": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
@@ -89,11 +89,11 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-erc1155": "^2.1.34",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/order-utils": "^10.4.26",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-erc1155": "^2.1.35",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/order-utils": "^10.4.27",
"@0x/utils": "^6.4.3",
"lodash": "^4.17.11"
},

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "6.2.30",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "6.2.29",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v6.2.30 - _August 6, 2021_
* Dependencies updated
## v6.2.29 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-extensions",
"version": "6.2.29",
"version": "6.2.30",
"engines": {
"node": ">=6.12"
},
@@ -53,16 +53,16 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/contracts-exchange": "^3.2.35",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/contracts-exchange": "^3.2.36",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -91,7 +91,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/typescript-typings": "^5.2.0",
"ethereum-types": "^3.5.0"
},

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-integrations",
"version": "2.7.53",
"version": "2.7.62",
"private": true,
"engines": {
"node": ">=6.12"
@@ -53,21 +53,21 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/extensions",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.4.0",
"@0x/contract-wrappers": "^13.17.2",
"@0x/contracts-broker": "^1.1.34",
"@0x/contracts-coordinator": "^3.1.35",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-exchange-forwarder": "^4.2.35",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-extensions": "^6.2.29",
"@0x/contract-addresses": "^6.5.0",
"@0x/contract-wrappers": "^13.17.3",
"@0x/contracts-broker": "^1.1.35",
"@0x/contracts-coordinator": "^3.1.36",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-exchange-forwarder": "^4.2.36",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-extensions": "^6.2.30",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-utils": "^4.7.14",
"@0x/coordinator-server": "^1.0.5",
"@0x/dev-utils": "^4.2.7",
"@0x/migrations": "^8.0.11",
"@0x/order-utils": "^10.4.26",
"@0x/protocol-utils": "^1.7.2",
"@0x/migrations": "^8.0.12",
"@0x/order-utils": "^10.4.27",
"@0x/protocol-utils": "^1.8.0",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"@0x/web3-wrapper": "^7.5.3",
@@ -93,17 +93,17 @@
"typescript": "4.2.2"
},
"dependencies": {
"@0x/asset-swapper": "^6.18.2",
"@0x/asset-swapper": "^16.24.0",
"@0x/base-contract": "^6.4.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-erc1155": "^2.1.34",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-erc721": "^3.1.34",
"@0x/contracts-exchange": "^3.2.35",
"@0x/contracts-multisig": "^4.1.35",
"@0x/contracts-staking": "^2.0.42",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-zero-ex": "^0.26.0",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-erc1155": "^2.1.35",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-erc721": "^3.1.35",
"@0x/contracts-exchange": "^3.2.36",
"@0x/contracts-multisig": "^4.1.36",
"@0x/contracts-staking": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-zero-ex": "^0.27.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "4.1.36",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "4.1.35",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.1.36 - _August 6, 2021_
* Dependencies updated
## v4.1.35 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-multisig",
"version": "4.1.35",
"version": "4.1.36",
"engines": {
"node": ">=6.12"
},
@@ -50,11 +50,11 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/multisig",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "2.0.43",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "2.0.42",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v2.0.43 - _August 6, 2021_
* Dependencies updated
## v2.0.42 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-staking",
"version": "2.0.42",
"version": "2.0.43",
"engines": {
"node": ">=6.12"
},
@@ -54,14 +54,14 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/tokens",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-dev-utils": "^1.3.33",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contracts-exchange-libs": "^4.3.34",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-dev-utils": "^1.3.34",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-exchange-libs": "^4.3.35",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-utils": "^4.7.13",
"@0x/contracts-utils": "^4.7.14",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -88,7 +88,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/typescript-typings": "^5.2.0",
"@0x/utils": "^6.4.3",
"ethereum-types": "^3.5.0",

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "5.4.6",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "5.4.5",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v5.4.6 - _August 6, 2021_
* Dependencies updated
## v5.4.5 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-test-utils",
"version": "5.4.5",
"version": "5.4.6",
"engines": {
"node": ">=6.12"
},
@@ -44,10 +44,10 @@
"dependencies": {
"@0x/assert": "^3.0.27",
"@0x/base-contract": "^6.4.0",
"@0x/contract-addresses": "^6.4.0",
"@0x/contract-addresses": "^6.5.0",
"@0x/dev-utils": "^4.2.7",
"@0x/json-schemas": "^6.1.3",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-coverage": "^4.0.37",
"@0x/sol-profiler": "^4.1.27",
"@0x/sol-trace": "^3.0.37",

View File

@@ -1,4 +1,14 @@
[
{
"version": "1.3.0",
"changes": [
{
"note": "Added proposal 1 params and test",
"pr": 298
}
],
"timestamp": 1628225642
},
{
"timestamp": 1624356181,
"version": "1.2.3",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v1.3.0 - _August 6, 2021_
* Added proposal 1 params and test (#298)
## v1.2.3 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-treasury",
"version": "1.2.3",
"version": "1.3.0",
"engines": {
"node": ">=6.12"
},
@@ -47,12 +47,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/treasury",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.4.0",
"@0x/contracts-asset-proxy": "^3.7.16",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contract-addresses": "^6.5.0",
"@0x/contracts-asset-proxy": "^3.7.17",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-staking": "^2.0.42",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-staking": "^2.0.43",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -73,7 +73,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.7.2",
"@0x/protocol-utils": "^1.8.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",

File diff suppressed because one or more lines are too long

View File

@@ -151,4 +151,72 @@ blockchainTests.fork.skip('Treasury proposal mainnet fork tests', env => {
);
});
});
describe('Proposal 1', () => {
it('works', async () => {
const proposal = proposals[1];
let executionEpoch: BigNumber;
if (proposal.executionEpoch) {
executionEpoch = proposal.executionEpoch;
} else {
const currentEpoch = await staking.currentEpoch().callAsync();
executionEpoch = currentEpoch.plus(2);
}
const pools = await querySubgraphAsync(PROPOSER);
const proposeTx = treasury.propose(proposal.actions, executionEpoch, proposal.description, pools);
const calldata = proposeTx.getABIEncodedTransactionData();
logUtils.log('ZrxTreasury.propose calldata:');
logUtils.log(calldata);
const proposalId = await proposeTx.callAsync({ from: PROPOSER });
const receipt = await proposeTx.awaitTransactionSuccessAsync({ from: PROPOSER });
verifyEventsFromLogs(
receipt.logs,
[
{
...proposal,
proposalId,
executionEpoch,
proposer: PROPOSER,
operatedPoolIds: pools,
},
],
ZrxTreasuryEvents.ProposalCreated,
);
await fastForwardToNextEpochAsync();
await fastForwardToNextEpochAsync();
await treasury
.castVote(proposalId, true, VOTER_OPERATED_POOLS)
.awaitTransactionSuccessAsync({ from: VOTER });
await env.web3Wrapper.increaseTimeAsync(votingPeriod.plus(1).toNumber());
await env.web3Wrapper.mineBlockAsync();
const executeTx = await treasury.execute(proposalId, proposal.actions).awaitTransactionSuccessAsync();
verifyEventsFromLogs(
executeTx.logs,
[
{
proposalId,
},
],
ZrxTreasuryEvents.ProposalExecuted,
);
const recipient = '0xab66cc8fd10457ebc9d13b9760c835f0a4cbc487';
verifyEventsFromLogs(
executeTx.logs,
[
{
_from: TREASURY_ADDRESS,
_to: recipient,
_value: new BigNumber(330_813).times('1e18'),
},
{
_from: TREASURY_ADDRESS,
_to: recipient,
_value: new BigNumber(420000).times('1e18'),
},
],
ERC20TokenEvents.Transfer,
);
});
});
});

View File

@@ -1,4 +1,13 @@
[
{
"timestamp": 1628225642,
"version": "4.7.14",
"changes": [
{
"note": "Dependencies updated"
}
]
},
{
"timestamp": 1624356181,
"version": "4.7.13",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v4.7.14 - _August 6, 2021_
* Dependencies updated
## v4.7.13 - _June 22, 2021_
* Dependencies updated

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-utils",
"version": "4.7.13",
"version": "4.7.14",
"engines": {
"node": ">=6.12"
},
@@ -52,9 +52,9 @@
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/tslint-config": "^4.1.4",
"@0x/types": "^3.3.3",

View File

@@ -3,10 +3,10 @@
"version": "0.27.0",
"changes": [
{
"note": "Refactor Mixins which use WETH to also have an Internal variant, allowing WETH to be passed in for SwapRevertSampler",
"pr": 245
"note": "Add `Clipper` as a custom liquidity source"
}
]
],
"timestamp": 1628225642
},
{
"version": "0.26.0",

View File

@@ -5,6 +5,10 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v0.27.0 - _August 6, 2021_
* Add `Clipper` as a custom liquidity source
## v0.26.0 - _June 22, 2021_
* Add Lido stETH deposit integration (#260)

View File

@@ -25,7 +25,7 @@ import "./BridgeProtocols.sol";
import "./mixins/MixinBalancer.sol";
import "./mixins/MixinBalancerV2.sol";
import "./mixins/MixinBancor.sol";
import "./mixins/MixinBooster.sol";
import "./mixins/MixinClipper.sol";
import "./mixins/MixinCoFiX.sol";
import "./mixins/MixinCurve.sol";
import "./mixins/MixinCurveV2.sol";
@@ -51,7 +51,7 @@ contract BridgeAdapter is
MixinBalancer,
MixinBalancerV2,
MixinBancor,
MixinBooster,
MixinClipper,
MixinCoFiX,
MixinCurve,
MixinCurveV2,
@@ -77,7 +77,7 @@ contract BridgeAdapter is
MixinBalancer()
MixinBalancerV2()
MixinBancor(weth)
MixinBooster()
MixinClipper(weth)
MixinCoFiX()
MixinCurve(weth)
MixinCurveV2()
@@ -248,8 +248,8 @@ contract BridgeAdapter is
sellAmount,
order.bridgeData
);
} else if (protocolId == BridgeProtocols.BOOSTER) {
boughtAmount = _tradeBooster(
} else if (protocolId == BridgeProtocols.CLIPPER) {
boughtAmount = _tradeClipper(
sellToken,
buyToken,
sellAmount,

View File

@@ -49,5 +49,5 @@ library BridgeProtocols {
uint128 internal constant KYBERDMM = 19;
uint128 internal constant CURVEV2 = 20;
uint128 internal constant LIDO = 21;
uint128 internal constant BOOSTER = 22;
uint128 internal constant CLIPPER = 22;
}

View File

@@ -39,13 +39,6 @@ interface IBancorNetwork {
external
payable
returns (uint256);
function conversionPath(address _sourceToken, address _targetToken) external view returns (address[] memory);
function rateByPath(address[] memory _path, uint256 _amount) external view returns (uint256);
}
interface IBancorRegistry {
function getAddress(bytes32 _contractName) external view returns (address);
function BANCOR_NETWORK() external view returns (bytes32);
}
@@ -69,18 +62,6 @@ contract MixinBancor {
)
internal
returns (uint256 boughtAmount)
{
return _tradeBancorInternal(WETH, buyToken, sellAmount, bridgeData);
}
function _tradeBancorInternal(
IEtherTokenV06 weth,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data.
IBancorNetwork bancorNetworkAddress;
@@ -98,7 +79,7 @@ contract MixinBancor {
require(path.length >= 2, "MixinBancor/PATH_LENGTH_MUST_BE_AT_LEAST_TWO");
require(
path[path.length - 1] == buyToken ||
(path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == weth),
(path[path.length - 1] == BANCOR_ETH_ADDRESS && buyToken == WETH),
"MixinBancor/LAST_ELEMENT_OF_PATH_MUST_MATCH_OUTPUT_TOKEN"
);
@@ -107,7 +88,7 @@ contract MixinBancor {
// The Bancor path will have ETH as the 0xeee address
// Bancor expects to be paid in ETH not WETH
if (path[0] == BANCOR_ETH_ADDRESS) {
weth.withdraw(sellAmount);
WETH.withdraw(sellAmount);
payableAmount = sellAmount;
} else {
// Grant an allowance to the Bancor Network.
@@ -128,7 +109,7 @@ contract MixinBancor {
0 // affiliateFee; no fee paid
);
if (path[path.length - 1] == BANCOR_ETH_ADDRESS) {
weth.deposit{value: boughtAmount}();
WETH.deposit{value: boughtAmount}();
}
return boughtAmount;

View File

@@ -1,68 +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;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "../IBridgeAdapter.sol";
interface IBooster {
function swap(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
address recipient
)
external
returns (uint256);
}
contract MixinBooster {
using LibERC20TokenV06 for IERC20TokenV06;
function _tradeBooster(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data.
(IBooster pool) = abi.decode(bridgeData, (IBooster));
// Grant the pool an allowance.
sellToken.approveIfBelow(address(pool), sellAmount);
// Convert the tokens
boughtAmount = pool.swap(
sellToken,
buyToken,
sellAmount,
address(this)
);
return boughtAmount;
}
}

View File

@@ -0,0 +1,148 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "../IBridgeAdapter.sol";
import "../../../vendor/ILiquidityProvider.sol";
contract MixinClipper {
using LibERC20TokenV06 for IERC20TokenV06;
/// @dev Mainnet address of the WETH contract.
IEtherTokenV06 private immutable WETH;
constructor(IEtherTokenV06 weth)
public
{
WETH = weth;
}
function _tradeClipper(
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// We can only use ETH with Clipper, no WETH available
(ILiquidityProvider clipper, bytes memory auxiliaryData) =
abi.decode(bridgeData, (ILiquidityProvider, bytes));
if (sellToken == WETH) {
boughtAmount = _executeSellEthForToken(
clipper,
buyToken,
sellAmount,
auxiliaryData
);
} else if (buyToken == WETH) {
boughtAmount = _executeSellTokenForEth(
clipper,
sellToken,
sellAmount,
auxiliaryData
);
} else {
boughtAmount = _executeSellTokenForToken(
clipper,
sellToken,
buyToken,
sellAmount,
auxiliaryData
);
}
return boughtAmount;
}
function _executeSellEthForToken(
ILiquidityProvider clipper,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Clipper requires ETH and doesn't support WETH
WETH.withdraw(sellAmount);
boughtAmount = clipper.sellEthForToken{ value: sellAmount }(
buyToken,
address(this),
1,
auxiliaryData
);
}
function _executeSellTokenForEth(
ILiquidityProvider clipper,
IERC20TokenV06 sellToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Optimization: We can transfer the tokens into clipper rather than
// have an allowance updated
sellToken.compatTransfer(address(clipper), sellAmount);
boughtAmount = clipper.sellTokenForEth(
sellToken,
payable(address(this)),
1,
auxiliaryData
);
// we want WETH for possible future trades
WETH.deposit{ value: boughtAmount }();
}
function _executeSellTokenForToken(
ILiquidityProvider clipper,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory auxiliaryData
)
private
returns (uint256 boughtAmount)
{
// Optimization: We can transfer the tokens into clipper rather than
// have an allowance updated
sellToken.compatTransfer(address(clipper), sellAmount);
boughtAmount = clipper.sellTokenForToken(
sellToken,
buyToken,
address(this),
1,
auxiliaryData
);
}
}

View File

@@ -57,26 +57,13 @@ contract MixinCurve {
)
internal
returns (uint256 boughtAmount)
{
return _tradeCurveInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
}
function _tradeCurveInternal(
IEtherTokenV06 weth,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
// Decode the bridge data to get the Curve metadata.
CurveBridgeData memory data = abi.decode(bridgeData, (CurveBridgeData));
uint256 payableAmount;
if (sellToken == weth) {
if (sellToken == WETH) {
payableAmount = sellAmount;
weth.withdraw(sellAmount);
WETH.withdraw(sellAmount);
} else {
sellToken.approveIfBelow(data.curveAddress, sellAmount);
}
@@ -96,9 +83,9 @@ contract MixinCurve {
resultData.rrevert();
}
if (buyToken == weth) {
if (buyToken == WETH) {
boughtAmount = address(this).balance;
weth.deposit{ value: boughtAmount }();
WETH.deposit{ value: boughtAmount }();
}
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.

View File

@@ -52,7 +52,6 @@ interface IKyberNetworkProxy {
external
payable
returns (uint256 boughtAmount);
}
contract MixinKyber {
@@ -79,26 +78,12 @@ contract MixinKyber {
)
internal
returns (uint256 boughtAmount)
{
return _tradeKyberInternal(KYBER_ETH_ADDRESS, WETH, sellToken, buyToken, sellAmount, bridgeData);
}
function _tradeKyberInternal(
IERC20TokenV06 kyberEthAddress,
IEtherTokenV06 weth,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(IKyberNetworkProxy kyber, bytes memory hint) =
abi.decode(bridgeData, (IKyberNetworkProxy, bytes));
uint256 payableAmount = 0;
if (sellToken != weth) {
if (sellToken != WETH) {
// If the input token is not WETH, grant an allowance to the exchange
// to spend them.
sellToken.approveIfBelow(
@@ -108,18 +93,18 @@ contract MixinKyber {
} else {
// If the input token is WETH, unwrap it and attach it to the call.
payableAmount = sellAmount;
weth.withdraw(payableAmount);
WETH.withdraw(payableAmount);
}
// Try to sell all of this contract's input token balance through
// `KyberNetworkProxy.trade()`.
boughtAmount = kyber.tradeWithHint{ value: payableAmount }(
// Input token.
sellToken == weth ? kyberEthAddress : sellToken,
sellToken == WETH ? KYBER_ETH_ADDRESS : sellToken,
// Sell amount.
sellAmount,
// Output token.
buyToken == weth ? kyberEthAddress : buyToken,
buyToken == WETH ? KYBER_ETH_ADDRESS : buyToken,
// Transfer to this contract
address(uint160(address(this))),
// Buy as much as possible.
@@ -131,8 +116,8 @@ contract MixinKyber {
hint
);
// If receving ETH, wrap it to WETH.
if (buyToken == weth) {
weth.deposit{ value: boughtAmount }();
if (buyToken == WETH) {
WETH.deposit{ value: boughtAmount }();
}
return boughtAmount;
}

View File

@@ -30,8 +30,6 @@ import "../IBridgeAdapter.sol";
*/
interface IKyberDmmRouter {
function factory() external view returns (address);
/// @dev Swaps an exact amount of input tokens for as many output tokens as possible, along the route determined by the path.
/// The first element of path is the input token, the last is the output token, and any intermediate elements represent
/// intermediate pairs to trade through (if, for example, a direct pair does not exist).

View File

@@ -58,23 +58,10 @@ contract MixinLido {
)
internal
returns (uint256 boughtAmount)
{
return _tradeLidoInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
}
function _tradeLidoInternal(
IEtherTokenV06 weth,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(ILido lido) = abi.decode(bridgeData, (ILido));
if (address(sellToken) == address(weth) && address(buyToken) == address(lido)) {
weth.withdraw(sellAmount);
if (address(sellToken) == address(WETH) && address(buyToken) == address(lido)) {
WETH.withdraw(sellAmount);
boughtAmount = lido.getPooledEthByShares(lido.submit{ value: sellAmount}(address(0)));
} else {
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");

View File

@@ -66,26 +66,12 @@ contract MixinMooniswap {
internal
returns (uint256 boughtAmount)
{
return _tradeMooniswapInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
}
function _tradeMooniswapInternal(
IEtherTokenV06 weth,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
(IMooniswapPool pool) = abi.decode(bridgeData, (IMooniswapPool));
// Convert WETH to ETH.
uint256 ethValue = 0;
if (sellToken == weth) {
weth.withdraw(sellAmount);
if (sellToken == WETH) {
WETH.withdraw(sellAmount);
ethValue = sellAmount;
} else {
// Grant the pool an allowance.
@@ -96,16 +82,16 @@ contract MixinMooniswap {
}
boughtAmount = pool.swap{value: ethValue}(
sellToken == weth ? IERC20TokenV06(0) : sellToken,
buyToken == weth ? IERC20TokenV06(0) : buyToken,
sellToken == WETH ? IERC20TokenV06(0) : sellToken,
buyToken == WETH ? IERC20TokenV06(0) : buyToken,
sellAmount,
1,
address(0)
);
// Wrap ETH to WETH.
if (buyToken == weth) {
weth.deposit{value:boughtAmount}();
if (buyToken == WETH) {
WETH.deposit{value:boughtAmount}();
}
}
}

View File

@@ -124,35 +124,21 @@ contract MixinUniswap {
)
internal
returns (uint256 boughtAmount)
{
return _tradeUniswapInternal(WETH, sellToken, buyToken, sellAmount, bridgeData);
}
function _tradeUniswapInternal(
IEtherTokenV06 weth,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken,
uint256 sellAmount,
bytes memory bridgeData
)
internal
returns (uint256 boughtAmount)
{
IUniswapExchangeFactory exchangeFactory =
abi.decode(bridgeData, (IUniswapExchangeFactory));
// Get the exchange for the token pair.
IUniswapExchange exchange = _getUniswapExchangeForTokenPair(
weth,
exchangeFactory,
sellToken,
buyToken
);
// Convert from WETH to a token.
if (sellToken == weth) {
if (sellToken == WETH) {
// Unwrap the WETH.
weth.withdraw(sellAmount);
WETH.withdraw(sellAmount);
// Buy as much of `buyToken` token with ETH as possible
boughtAmount = exchange.ethToTokenTransferInput{ value: sellAmount }(
// Minimum buy amount.
@@ -164,7 +150,7 @@ contract MixinUniswap {
);
// Convert from a token to WETH.
} else if (buyToken == weth) {
} else if (buyToken == WETH) {
// Grant the exchange an allowance.
sellToken.approveIfBelow(
address(exchange),
@@ -180,7 +166,7 @@ contract MixinUniswap {
block.timestamp
);
// Wrap the ETH.
weth.deposit{ value: boughtAmount }();
WETH.deposit{ value: boughtAmount }();
// Convert from one token to another.
} else {
// Grant the exchange an allowance.
@@ -214,7 +200,6 @@ contract MixinUniswap {
/// @param buyToken The address of the token we are converting to.
/// @return exchange The uniswap exchange.
function _getUniswapExchangeForTokenPair(
IEtherTokenV06 weth,
IUniswapExchangeFactory exchangeFactory,
IERC20TokenV06 sellToken,
IERC20TokenV06 buyToken
@@ -224,7 +209,7 @@ contract MixinUniswap {
returns (IUniswapExchange exchange)
{
// Whichever isn't WETH is the exchange token.
exchange = sellToken == weth
exchange = sellToken == WETH
? exchangeFactory.getExchange(buyToken)
: exchangeFactory.getExchange(sellToken);
require(address(exchange) != address(0), "MixinUniswap/NO_EXCHANGE");

View File

@@ -1,6 +1,6 @@
{
"name": "@0x/contracts-zero-ex",
"version": "0.26.0",
"version": "0.27.0",
"engines": {
"node": ">=6.12"
},
@@ -43,7 +43,7 @@
"config": {
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IERC20Transformer,IOwnableFeature,ISimpleFunctionRegistryFeature,ITransformERC20Feature,FillQuoteTransformer,PayTakerTransformer,PositiveSlippageFeeTransformer,WethTransformer,OwnableFeature,SimpleFunctionRegistryFeature,TransformERC20Feature,AffiliateFeeTransformer,MetaTransactionsFeature,LogMetadataTransformer,BridgeAdapter,LiquidityProviderFeature,ILiquidityProviderFeature,NativeOrdersFeature,INativeOrdersFeature,FeeCollectorController,FeeCollector,CurveLiquidityProvider,BatchFillNativeOrdersFeature,IBatchFillNativeOrdersFeature,MultiplexFeature,IMultiplexFeature,OtcOrdersFeature,IOtcOrdersFeature",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinBooster|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|BatchFillNativeOrdersFeature|BootstrapFeature|BridgeAdapter|BridgeProtocols|CurveLiquidityProvider|FeeCollector|FeeCollectorController|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinProtocolFees|FixinReentrancyGuard|FixinTokenSpender|FlashWallet|FullMigration|IBatchFillNativeOrdersFeature|IBootstrapFeature|IBridgeAdapter|IERC20Bridge|IERC20Transformer|IFeature|IFlashWallet|ILiquidityProvider|ILiquidityProviderFeature|ILiquidityProviderSandbox|IMetaTransactionsFeature|IMooniswapPool|IMultiplexFeature|INativeOrdersEvents|INativeOrdersFeature|IOtcOrdersFeature|IOwnableFeature|IPancakeSwapFeature|ISimpleFunctionRegistryFeature|IStaking|ITestSimpleFunctionRegistryFeature|ITokenSpenderFeature|ITransformERC20Feature|IUniswapFeature|IUniswapV2Pair|IUniswapV3Feature|IUniswapV3Pool|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibFeeCollector|LibLiquidityProviderRichErrors|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibNativeOrder|LibNativeOrdersRichErrors|LibNativeOrdersStorage|LibOtcOrdersStorage|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignature|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|LiquidityProviderFeature|LiquidityProviderSandbox|LogMetadataTransformer|MetaTransactionsFeature|MixinBalancer|MixinBalancerV2|MixinBancor|MixinClipper|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinCurveV2|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|MixinLido|MixinMStable|MixinMakerPSM|MixinMooniswap|MixinNerve|MixinOasis|MixinShell|MixinUniswap|MixinUniswapV2|MixinUniswapV3|MixinZeroExBridge|MooniswapLiquidityProvider|MultiplexFeature|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
},
"repository": {
"type": "git",
@@ -56,12 +56,12 @@
"homepage": "https://github.com/0xProject/protocol/tree/main/contracts/zero-ex",
"devDependencies": {
"@0x/abi-gen": "^5.6.0",
"@0x/contract-addresses": "^6.4.0",
"@0x/contracts-erc20": "^3.3.13",
"@0x/contract-addresses": "^6.5.0",
"@0x/contracts-erc20": "^3.3.14",
"@0x/contracts-gen": "^2.0.38",
"@0x/contracts-test-utils": "^5.4.5",
"@0x/contracts-test-utils": "^5.4.6",
"@0x/dev-utils": "^4.2.7",
"@0x/order-utils": "^10.4.26",
"@0x/order-utils": "^10.4.27",
"@0x/sol-compiler": "^4.7.3",
"@0x/ts-doc-gen": "^0.0.28",
"@0x/tslint-config": "^4.1.4",
@@ -83,7 +83,7 @@
},
"dependencies": {
"@0x/base-contract": "^6.4.0",
"@0x/protocol-utils": "^1.7.2",
"@0x/protocol-utils": "^1.8.0",
"@0x/subproviders": "^6.5.3",
"@0x/types": "^3.3.3",
"@0x/typescript-typings": "^5.2.0",

View File

@@ -82,7 +82,7 @@ import * as MetaTransactionsFeature from '../test/generated-artifacts/MetaTransa
import * as MixinBalancer from '../test/generated-artifacts/MixinBalancer.json';
import * as MixinBalancerV2 from '../test/generated-artifacts/MixinBalancerV2.json';
import * as MixinBancor from '../test/generated-artifacts/MixinBancor.json';
import * as MixinBooster from '../test/generated-artifacts/MixinBooster.json';
import * as MixinClipper from '../test/generated-artifacts/MixinClipper.json';
import * as MixinCoFiX from '../test/generated-artifacts/MixinCoFiX.json';
import * as MixinCryptoCom from '../test/generated-artifacts/MixinCryptoCom.json';
import * as MixinCurve from '../test/generated-artifacts/MixinCurve.json';
@@ -258,7 +258,7 @@ export const artifacts = {
MixinBalancer: MixinBalancer as ContractArtifact,
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
MixinBancor: MixinBancor as ContractArtifact,
MixinBooster: MixinBooster as ContractArtifact,
MixinClipper: MixinClipper as ContractArtifact,
MixinCoFiX: MixinCoFiX as ContractArtifact,
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
MixinCurve: MixinCurve as ContractArtifact,

View File

@@ -80,7 +80,7 @@ export * from '../test/generated-wrappers/meta_transactions_feature';
export * from '../test/generated-wrappers/mixin_balancer';
export * from '../test/generated-wrappers/mixin_balancer_v2';
export * from '../test/generated-wrappers/mixin_bancor';
export * from '../test/generated-wrappers/mixin_booster';
export * from '../test/generated-wrappers/mixin_clipper';
export * from '../test/generated-wrappers/mixin_co_fi_x';
export * from '../test/generated-wrappers/mixin_crypto_com';
export * from '../test/generated-wrappers/mixin_curve';

View File

@@ -113,7 +113,7 @@
"test/generated-artifacts/MixinBalancer.json",
"test/generated-artifacts/MixinBalancerV2.json",
"test/generated-artifacts/MixinBancor.json",
"test/generated-artifacts/MixinBooster.json",
"test/generated-artifacts/MixinClipper.json",
"test/generated-artifacts/MixinCoFiX.json",
"test/generated-artifacts/MixinCryptoCom.json",
"test/generated-artifacts/MixinCurve.json",

View File

@@ -1,12 +1,96 @@
[
{
"version": "7.0.0",
"version": "16.24.0",
"changes": [
{
"note": "SwapRevertSampler. Refactored sampler to use exchange functions. Remove gas schedule.",
"pr": 245
"note": "Add `Clipper` as a custom liquidity source",
"pr": 299
},
{
"note": "Added `Curve` `Tricrypto2` and `ESD` v2",
"pr": 302
}
]
],
"timestamp": 1628225642
},
{
"version": "16.23.1",
"changes": [
{
"note": "Fix fill amount rounding error when covnerting fills to orders.",
"pr": 296
}
],
"timestamp": 1627572227
},
{
"version": "16.23.0",
"changes": [
{
"note": "ACryptoS",
"pr": 284
}
],
"timestamp": 1626473497
},
{
"version": "16.22.0",
"changes": [
{
"note": "IronSwap",
"pr": 281
}
],
"timestamp": 1626214787
},
{
"version": "16.21.0",
"changes": [
{
"note": "JetSwap",
"pr": 280
}
],
"timestamp": 1625904026
},
{
"version": "16.20.0",
"changes": [
{
"note": "ShibaSwap",
"pr": 276
}
],
"timestamp": 1625607277
},
{
"version": "16.19.1",
"changes": [
{
"note": "Fix LiquidityProvider fallback",
"pr": 272
}
],
"timestamp": 1625544188
},
{
"version": "16.19.0",
"changes": [
{
"note": "Add LiquidityProvider to Polygon sources",
"pr": 270
}
],
"timestamp": 1625190486
},
{
"version": "6.18.3",
"changes": [
{
"note": "Polygon Balance V2"
}
],
"timestamp": 1624987208
},
{
"timestamp": 1624562704,

View File

@@ -5,6 +5,43 @@ Edit the package's CHANGELOG.json file only.
CHANGELOG
## v16.24.0 - _August 6, 2021_
* Add `Clipper` as a custom liquidity source (#299)
* Added `Curve` `Tricrypto2` and `ESD` v2 (#302)
## v16.23.1 - _July 29, 2021_
* Fix fill amount rounding error when covnerting fills to orders. (#296)
## v16.23.0 - _July 16, 2021_
* ACryptoS (#284)
## v16.22.0 - _July 13, 2021_
* IronSwap (#281)
## v16.21.0 - _July 10, 2021_
* JetSwap (#280)
## v16.20.0 - _July 6, 2021_
* ShibaSwap (#276)
## v16.19.1 - _July 6, 2021_
* Fix LiquidityProvider fallback (#272)
## v16.19.0 - _July 2, 2021_
* Add LiquidityProvider to Polygon sources (#270)
## v6.18.3 - _June 29, 2021_
* Polygon Balance V2
## v6.18.2 - _June 24, 2021_
* Dependencies updated

View File

@@ -0,0 +1,144 @@
// 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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
contract ApproximateBuys {
/// @dev Information computing buy quotes for sources that do not have native
/// buy quote support.
struct ApproximateBuyQuoteOpts {
// Arbitrary maker token data to pass to `getSellQuoteCallback`.
bytes makerTokenData;
// Arbitrary taker token data to pass to `getSellQuoteCallback`.
bytes takerTokenData;
// Callback to retrieve a sell quote.
function (bytes memory, bytes memory, uint256)
internal
view
returns (uint256) getSellQuoteCallback;
}
uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
/// @dev Maximum approximate (positive) error rate when approximating a buy quote.
uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
/// @dev Maximum iterations to perform when approximating a buy quote.
uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 5;
function _sampleApproximateBuys(
ApproximateBuyQuoteOpts memory opts,
uint256[] memory makerTokenAmounts
)
internal
view
returns (uint256[] memory takerTokenAmounts)
{
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
if (makerTokenAmounts.length == 0) {
return takerTokenAmounts;
}
uint256 sellAmount = opts.getSellQuoteCallback(
opts.makerTokenData,
opts.takerTokenData,
makerTokenAmounts[0]
);
if (sellAmount == 0) {
return takerTokenAmounts;
}
uint256 buyAmount = opts.getSellQuoteCallback(
opts.takerTokenData,
opts.makerTokenData,
sellAmount
);
if (buyAmount == 0) {
return takerTokenAmounts;
}
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
sellAmount = _safeGetPartialAmountCeil(
makerTokenAmounts[i],
buyAmount,
sellAmount
);
if (sellAmount == 0) {
break;
}
sellAmount = _safeGetPartialAmountCeil(
(ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
ONE_HUNDED_PERCENT_BPS,
sellAmount
);
if (sellAmount == 0) {
break;
}
uint256 _buyAmount = opts.getSellQuoteCallback(
opts.takerTokenData,
opts.makerTokenData,
sellAmount
);
if (_buyAmount == 0) {
break;
}
// We re-use buyAmount next iteration, only assign if it is
// non zero
buyAmount = _buyAmount;
// If we've reached our goal, exit early
if (buyAmount >= makerTokenAmounts[i]) {
uint256 eps =
(buyAmount - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
makerTokenAmounts[i];
if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
break;
}
}
}
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
// if we hit a max iteration limit
// We scale the sell amount to get the approximate target
takerTokenAmounts[i] = _safeGetPartialAmountCeil(
makerTokenAmounts[i],
buyAmount,
sellAmount
);
}
}
function _safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
view
returns (uint256 partialAmount)
{
if (numerator == 0 || target == 0 || denominator == 0) return 0;
uint256 c = numerator * target;
if (c / numerator != target) return 0;
return (c + (denominator - 1)) / denominator;
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,29 +20,26 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBalancer.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IBalancer.sol";
contract BalancerSampler is
MixinBalancer,
SwapRevertSampler
{
function sampleSwapFromBalancer(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeBalancer(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
contract BalancerSampler {
/// @dev Base gas limit for Balancer calls.
uint256 constant private BALANCER_CALL_GAS = 300e3; // 300k
// Balancer math constants
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BConst.sol
uint256 constant private BONE = 10 ** 18;
uint256 constant private MAX_IN_RATIO = BONE / 2;
uint256 constant private MAX_OUT_RATIO = (BONE / 3) + 1 wei;
struct BalancerState {
uint256 takerTokenBalance;
uint256 makerTokenBalance;
uint256 takerTokenWeight;
uint256 makerTokenWeight;
uint256 swapFee;
}
/// @dev Sample sell quotes from Balancer.
@@ -50,7 +47,6 @@ contract BalancerSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBalancer(
@@ -60,17 +56,52 @@ contract BalancerSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(poolAddress),
getSwapQuoteCallback: this.sampleSwapFromBalancer
}),
takerTokenAmounts
);
IBalancer pool = IBalancer(poolAddress);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
return makerTokenAmounts;
}
BalancerState memory poolState;
poolState.takerTokenBalance = pool.getBalance(takerToken);
poolState.makerTokenBalance = pool.getBalance(makerToken);
poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
poolState.swapFee = pool.getSwapFee();
for (uint256 i = 0; i < numSamples; i++) {
// Handles this revert scenario:
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L443
if (takerTokenAmounts[i] > _bmul(poolState.takerTokenBalance, MAX_IN_RATIO)) {
break;
}
try
pool.calcOutGivenIn
{gas: BALANCER_CALL_GAS}
(
poolState.takerTokenBalance,
poolState.takerTokenWeight,
poolState.makerTokenBalance,
poolState.makerTokenWeight,
takerTokenAmounts[i],
poolState.swapFee
)
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from Balancer.
@@ -78,7 +109,6 @@ contract BalancerSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBalancer(
@@ -88,18 +118,74 @@ contract BalancerSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(poolAddress),
buyTokenData: abi.encode(poolAddress),
getSwapQuoteCallback: this.sampleSwapFromBalancer
}),
makerTokenAmounts
);
IBalancer pool = IBalancer(poolAddress);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
if (!pool.isBound(takerToken) || !pool.isBound(makerToken)) {
return takerTokenAmounts;
}
BalancerState memory poolState;
poolState.takerTokenBalance = pool.getBalance(takerToken);
poolState.makerTokenBalance = pool.getBalance(makerToken);
poolState.takerTokenWeight = pool.getDenormalizedWeight(takerToken);
poolState.makerTokenWeight = pool.getDenormalizedWeight(makerToken);
poolState.swapFee = pool.getSwapFee();
for (uint256 i = 0; i < numSamples; i++) {
// Handles this revert scenario:
// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BPool.sol#L505
if (makerTokenAmounts[i] > _bmul(poolState.makerTokenBalance, MAX_OUT_RATIO)) {
break;
}
try
pool.calcInGivenOut
{gas: BALANCER_CALL_GAS}
(
poolState.takerTokenBalance,
poolState.takerTokenWeight,
poolState.makerTokenBalance,
poolState.makerTokenWeight,
makerTokenAmounts[i],
poolState.swapFee
)
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Hacked version of Balancer's `bmul` function, returning 0 instead
/// of reverting.
/// https://github.com/balancer-labs/balancer-core/blob/master/contracts/BNum.sol#L63-L73
/// @param a The first operand.
/// @param b The second operand.
/// @param c The result of the multiplication, or 0 if `bmul` would've reverted.
function _bmul(uint256 a, uint256 b)
private
pure
returns (uint256 c)
{
uint c0 = a * b;
if (a != 0 && c0 / a != b) {
return 0;
}
uint c1 = c0 + (BONE / 2);
if (c1 < c0) {
return 0;
}
uint c2 = c1 / BONE;
return c2;
}
}

View File

@@ -20,29 +20,44 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBalancerV2.sol";
import "./SwapRevertSampler.sol";
import "./SamplerUtils.sol";
contract BalancerV2Sampler is
MixinBalancerV2,
SwapRevertSampler
{
/// @dev Minimal Balancer V2 Vault interface
/// for documentation refer to https://github.com/balancer-labs/balancer-core-v2/blob/master/contracts/vault/interfaces/IVault.sol
interface IBalancerV2Vault {
enum SwapKind { GIVEN_IN, GIVEN_OUT }
function sampleSwapFromBalancerV2(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeBalancerV2(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
struct BatchSwapStep {
bytes32 poolId;
uint256 assetInIndex;
uint256 assetOutIndex;
uint256 amount;
bytes userData;
}
struct FundManagement {
address sender;
bool fromInternalBalance;
address payable recipient;
bool toInternalBalance;
}
function queryBatchSwap(
SwapKind kind,
BatchSwapStep[] calldata swaps,
IAsset[] calldata assets,
FundManagement calldata funds
) external returns (int256[] memory assetDeltas);
}
interface IAsset {
// solhint-disable-previous-line no-empty-blocks
}
contract BalancerV2Sampler is SamplerUtils {
struct BalancerV2PoolInfo {
bytes32 poolId;
address vault;
}
/// @dev Sample sell quotes from Balancer V2.
@@ -50,27 +65,48 @@ contract BalancerV2Sampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBalancerV2(
BalancerV2BridgeData memory poolInfo,
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(poolInfo),
getSwapQuoteCallback: this.sampleSwapFromBalancerV2
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
IBalancerV2Vault.FundManagement memory swapFunds =
_createSwapFunds();
for (uint256 i = 0; i < numSamples; i++) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
_createSwapSteps(poolInfo, takerTokenAmounts[i]);
try
// For sells we specify the takerToken which is what the vault will receive from the trade
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_IN, swapSteps, swapAssets, swapFunds)
// amounts represent pool balance deltas from the swap (incoming balance, outgoing balance)
returns (int256[] memory amounts) {
// Outgoing balance is negative so we need to flip the sign
int256 amountOutFromPool = amounts[1] * -1;
if (amountOutFromPool <= 0) {
break;
}
makerTokenAmounts[i] = uint256(amountOutFromPool);
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from Balancer V2.
@@ -78,27 +114,76 @@ contract BalancerV2Sampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBalancerV2(
BalancerV2BridgeData memory poolInfo,
BalancerV2PoolInfo memory poolInfo,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(poolInfo),
buyTokenData: abi.encode(poolInfo),
getSwapQuoteCallback: this.sampleSwapFromBalancerV2
}),
makerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
IBalancerV2Vault vault = IBalancerV2Vault(poolInfo.vault);
IAsset[] memory swapAssets = new IAsset[](2);
swapAssets[0] = IAsset(takerToken);
swapAssets[1] = IAsset(makerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
IBalancerV2Vault.FundManagement memory swapFunds =
_createSwapFunds();
for (uint256 i = 0; i < numSamples; i++) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
_createSwapSteps(poolInfo, makerTokenAmounts[i]);
try
// For buys we specify the makerToken which is what taker will receive from the trade
vault.queryBatchSwap(IBalancerV2Vault.SwapKind.GIVEN_OUT, swapSteps, swapAssets, swapFunds)
returns (int256[] memory amounts) {
int256 amountIntoPool = amounts[0];
if (amountIntoPool <= 0) {
break;
}
takerTokenAmounts[i] = uint256(amountIntoPool);
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
function _createSwapSteps(
BalancerV2PoolInfo memory poolInfo,
uint256 amount
) private pure returns (IBalancerV2Vault.BatchSwapStep[] memory) {
IBalancerV2Vault.BatchSwapStep[] memory swapSteps =
new IBalancerV2Vault.BatchSwapStep[](1);
swapSteps[0] = IBalancerV2Vault.BatchSwapStep({
poolId: poolInfo.poolId,
assetInIndex: 0,
assetOutIndex: 1,
amount: amount,
userData: ""
});
return swapSteps;
}
function _createSwapFunds()
private
view
returns (IBalancerV2Vault.FundManagement memory)
{
return
IBalancerV2Vault.FundManagement({
sender: address(this),
fromInternalBalance: false,
recipient: payable(address(this)),
toInternalBalance: false
});
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,21 +20,11 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBancor.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IBancor.sol";
contract CompilerHack {}
contract BancorSampler is
CompilerHack,
MixinBancor,
SwapRevertSampler
{
constructor(IEtherTokenV06 weth)
public
MixinBancor(weth)
{ }
contract BancorSampler is CompilerHack {
/// @dev Base gas limit for Bancor calls.
uint256 constant private BANCOR_CALL_GAS = 300e3; // 300k
@@ -44,23 +34,6 @@ contract BancorSampler is
address[][] paths;
}
function sampleSwapFromBancor(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeBancorInternal(
_getNativeWrappedToken(),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from Bancor.
/// @param opts BancorSamplerOpts The Bancor registry contract address and paths
/// @param takerToken Address of the taker token (what to sell).
@@ -68,7 +41,6 @@ contract BancorSampler is
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return bancorNetwork the Bancor Network address
/// @return path the selected conversion path from bancor
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBancor(
@@ -78,30 +50,34 @@ contract BancorSampler is
uint256[] memory takerTokenAmounts
)
public
returns (
address bancorNetwork,
address[] memory path,
uint256[] memory gasUsed,
uint256[] memory makerTokenAmounts
)
view
returns (address bancorNetwork, address[] memory path, uint256[] memory makerTokenAmounts)
{
if (opts.paths.length == 0) {
return (bancorNetwork, path, gasUsed, makerTokenAmounts);
return (bancorNetwork, path, makerTokenAmounts);
}
(bancorNetwork, path) = _findBestPath(opts, takerToken, makerToken, takerTokenAmounts);
makerTokenAmounts = new uint256[](takerTokenAmounts.length);
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(bancorNetwork, path),
getSwapQuoteCallback: this.sampleSwapFromBancor
}),
takerTokenAmounts
);
return (bancorNetwork, path, gasUsed, makerTokenAmounts);
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
try
IBancorNetwork(bancorNetwork)
.rateByPath
{gas: BANCOR_CALL_GAS}
(path, takerTokenAmounts[i])
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch {
// Swallow failures, leaving all results as zero.
break;
}
}
return (bancorNetwork, path, makerTokenAmounts);
}
/// @dev Sample buy quotes from Bancor. Unimplemented
@@ -111,7 +87,6 @@ contract BancorSampler is
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return bancorNetwork the Bancor Network address
/// @return path the selected conversion path from bancor
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBancor(
@@ -122,7 +97,7 @@ contract BancorSampler is
)
public
view
returns (address bancorNetwork, address[] memory path, uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
returns (address bancorNetwork, address[] memory path, uint256[] memory takerTokenAmounts)
{
}

View File

@@ -1,104 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinBooster.sol";
import "./SwapRevertSampler.sol";
contract BoosterSampler is
MixinBooster,
SwapRevertSampler
{
function sampleSwapFromBooster(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeBooster(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from Booster.
/// @param pool Address of the Booster Pool.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromBooster(
address pool,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromBooster
}),
takerTokenAmounts
);
}
/// @dev Sample buy quotes from Booster.
/// @param pool Address of the Booster pool.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromBooster(
address pool,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(pool),
buyTokenData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromBooster
}),
makerTokenAmounts
);
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,99 +20,142 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinCurve.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/ICurve.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract CurveSampler is
MixinCurve,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
constructor(IEtherTokenV06 weth)
public
MixinCurve(weth)
{ }
function sampleSwapFromCurve(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeCurveInternal(
_getNativeWrappedToken(),
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
/// @dev Information for sampling from curve sources.
struct CurveInfo {
address poolAddress;
bytes4 sellQuoteFunctionSelector;
bytes4 buyQuoteFunctionSelector;
}
/// @dev Base gas limit for Curve calls. Some Curves have multiple tokens
/// So a reasonable ceil is 150k per token. Biggest Curve has 4 tokens.
uint256 constant private CURVE_CALL_GAS = 2000e3; // Was 600k for Curve but SnowSwap is using 1500k+
/// @dev Sample sell quotes from Curve.
/// @param curveInfo Curve information specific to this token pair.
/// @param takerToken The taker token to sell.
/// @param makerToken The maker token to buy.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromCurve(
CurveBridgeData memory curveInfo,
address takerToken,
address makerToken,
CurveInfo memory curveInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(curveInfo),
getSwapQuoteCallback: this.sampleSwapFromCurve
}),
takerTokenAmounts
);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
abi.encodeWithSelector(
curveInfo.sellQuoteFunctionSelector,
fromTokenIdx,
toTokenIdx,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Curve.
/// @param curveInfo Curve information specific to this token pair.
/// @param takerToken The taker token to sell.
/// @param makerToken The maker token to buy.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromCurve(
CurveBridgeData memory curveInfo,
address takerToken,
address makerToken,
CurveInfo memory curveInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(curveInfo),
buyTokenData: abi.encode(
CurveBridgeData({
curveAddress: curveInfo.curveAddress,
exchangeFunctionSelector: curveInfo.exchangeFunctionSelector,
fromCoinIdx: curveInfo.toCoinIdx,
toCoinIdx: curveInfo.fromCoinIdx
})
),
getSwapQuoteCallback: this.sampleSwapFromCurve
}),
makerTokenAmounts
);
if (curveInfo.buyQuoteFunctionSelector == bytes4(0)) {
// Buys not supported on this curve, so approximate it.
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(toTokenIdx, curveInfo),
takerTokenData: abi.encode(fromTokenIdx, curveInfo),
getSellQuoteCallback: _sampleSellForApproximateBuyFromCurve
}),
makerTokenAmounts
);
}
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
curveInfo.poolAddress.staticcall.gas(CURVE_CALL_GAS)(
abi.encodeWithSelector(
curveInfo.buyQuoteFunctionSelector,
fromTokenIdx,
toTokenIdx,
makerTokenAmounts[i]
));
uint256 sellAmount = 0;
if (didSucceed) {
sellAmount = abi.decode(resultData, (uint256));
}
takerTokenAmounts[i] = sellAmount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
}
}
function _sampleSellForApproximateBuyFromCurve(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(int128 takerTokenIdx, CurveInfo memory curveInfo) =
abi.decode(takerTokenData, (int128, CurveInfo));
(int128 makerTokenIdx) =
abi.decode(makerTokenData, (int128));
(bool success, bytes memory resultData) =
address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromCurve.selector,
curveInfo,
takerTokenIdx,
makerTokenIdx,
_toSingleValueArray(sellAmount)
));
if (!success) {
return 0;
}
// solhint-disable-next-line indent
return abi.decode(resultData, (uint256[]))[0];
}
}

View File

@@ -1,112 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinCurveV2.sol";
import "./SwapRevertSampler.sol";
contract CurveV2Sampler is
MixinCurveV2,
SwapRevertSampler
{
function sampleSwapFromCurveV2(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeCurveV2(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from Curve.
/// @param curveInfo Curve information specific to this token pair.
/// @param takerToken The taker token to sell.
/// @param makerToken The maker token to buy.
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromCurveV2(
CurveBridgeDataV2 memory curveInfo,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(curveInfo),
getSwapQuoteCallback: this.sampleSwapFromCurveV2
}),
takerTokenAmounts
);
}
/// @dev Sample buy quotes from Curve.
/// @param curveInfo Curve information specific to this token pair.
/// @param takerToken The taker token to sell.
/// @param makerToken The maker token to buy.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromCurveV2(
CurveBridgeDataV2 memory curveInfo,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(curveInfo),
buyTokenData: abi.encode(
CurveBridgeDataV2({
curveAddress: curveInfo.curveAddress,
exchangeFunctionSelector: curveInfo.exchangeFunctionSelector,
fromCoinIdx: curveInfo.toCoinIdx,
toCoinIdx: curveInfo.fromCoinIdx
})
),
getSwapQuoteCallback: this.sampleSwapFromCurveV2
}),
makerTokenAmounts
);
}
}

View File

@@ -20,40 +20,35 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinDodo.sol";
import "./SwapRevertSampler.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
interface IDODOZoo {
function getDODO(address baseToken, address quoteToken) external view returns (address);
}
interface IDODOHelper {
function querySellQuoteToken(address dodo, uint256 amount) external view returns (uint256);
}
interface IDODO {
function querySellBaseToken(uint256 amount) external view returns (uint256);
function _TRADE_ALLOWED_() external view returns (bool);
}
contract DODOSampler is
MixinDodo,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
/// @dev Gas limit for DODO calls.
uint256 constant private DODO_CALL_GAS = 300e3; // 300k
struct DODOSamplerOpts {
address registry;
address helper;
}
function sampleSwapFromDodo(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeDodo(
IERC20TokenV06(sellToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from DODO.
/// @param opts DODOSamplerOpts DODO Registry and helper addresses
/// @param takerToken Address of the taker token (what to sell).
@@ -61,7 +56,6 @@ contract DODOSampler is
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromDODO(
@@ -71,13 +65,13 @@ contract DODOSampler is
uint256[] memory takerTokenAmounts
)
public
returns (
bool sellBase,
address pool,
uint256[] memory gasUsed,
uint256[] memory makerTokenAmounts
)
view
returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
address baseToken;
// If pool exists we have the correct order of Base/Quote
@@ -88,21 +82,29 @@ contract DODOSampler is
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
// No pool either direction
if (address(pool) == address(0)) {
return (sellBase, pool, gasUsed, makerTokenAmounts);
return (sellBase, pool, makerTokenAmounts);
}
baseToken = makerToken;
sellBase = false;
}
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(opts.helper, pool, sellBase),
getSwapQuoteCallback: this.sampleSwapFromDodo
}),
takerTokenAmounts
);
// DODO Pool has been disabled
if (!IDODO(pool)._TRADE_ALLOWED_()) {
return (sellBase, pool, makerTokenAmounts);
}
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = _sampleSellForApproximateBuyFromDODO(
abi.encode(takerToken, pool, baseToken, opts.helper), // taker token data
abi.encode(makerToken, pool, baseToken, opts.helper), // maker token data
takerTokenAmounts[i]
);
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from DODO.
@@ -112,7 +114,6 @@ contract DODOSampler is
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromDODO(
@@ -122,13 +123,13 @@ contract DODOSampler is
uint256[] memory makerTokenAmounts
)
public
returns (
bool sellBase,
address pool,
uint256[] memory gasUsed,
uint256[] memory takerTokenAmounts
)
view
returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
// Pool is BASE/QUOTE
// Look up the pool from the taker/maker combination
pool = IDODOZoo(opts.registry).getDODO(takerToken, makerToken);
@@ -142,21 +143,69 @@ contract DODOSampler is
pool = IDODOZoo(opts.registry).getDODO(makerToken, takerToken);
// No pool either direction
if (address(pool) == address(0)) {
return (sellBase, pool, gasUsed, takerTokenAmounts);
return (sellBase, pool, takerTokenAmounts);
}
baseToken = makerToken;
sellBase = false;
}
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(opts.helper, pool, sellBase),
buyTokenData: abi.encode(opts.helper, pool, !sellBase),
getSwapQuoteCallback: this.sampleSwapFromDodo
// DODO Pool has been disabled
if (!IDODO(pool)._TRADE_ALLOWED_()) {
return (sellBase, pool, takerTokenAmounts);
}
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool, baseToken, opts.helper),
takerTokenData: abi.encode(takerToken, pool, baseToken, opts.helper),
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODO
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromDODO(
bytes memory takerTokenData,
bytes memory /* makerTokenData */,
uint256 sellAmount
)
private
view
returns (uint256)
{
(address takerToken, address pool, address baseToken, address helper) = abi.decode(
takerTokenData,
(address, address, address, address)
);
// We will get called to sell both the taker token and also to sell the maker token
if (takerToken == baseToken) {
// If base token then use the original query on the pool
try
IDODO(pool).querySellBaseToken
{gas: DODO_CALL_GAS}
(sellAmount)
returns (uint256 amount)
{
return amount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
} else {
// If quote token then use helper, this is less accurate
try
IDODOHelper(helper).querySellQuoteToken
{gas: DODO_CALL_GAS}
(pool, sellAmount)
returns (uint256 amount)
{
return amount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
}
}

View File

@@ -20,8 +20,8 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinDodoV2.sol";
import "./SwapRevertSampler.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
interface IDODOV2Registry {
function getDODOPool(address baseToken, address quoteToken)
@@ -30,30 +30,26 @@ interface IDODOV2Registry {
returns (address[] memory machines);
}
interface IDODOV2Pool {
function querySellBase(address trader, uint256 payBaseAmount)
external
view
returns (uint256 receiveQuoteAmount, uint256 mtFee);
function querySellQuote(address trader, uint256 payQuoteAmount)
external
view
returns (uint256 receiveBaseAmount, uint256 mtFee);
}
contract DODOV2Sampler is
MixinDodoV2,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
/// @dev Gas limit for DODO V2 calls.
uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k
function sampleSwapFromDodoV2(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeDodoV2(
IERC20TokenV06(sellToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from DODO V2.
/// @param registry Address of the registry to look up.
/// @param offset offset index for the pool in the registry.
@@ -62,7 +58,6 @@ contract DODOV2Sampler is
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromDODOV2(
@@ -73,27 +68,31 @@ contract DODOV2Sampler is
uint256[] memory takerTokenAmounts
)
public
returns (
bool sellBase,
address pool,
uint256[] memory gasUsed,
uint256[] memory makerTokenAmounts
)
view
returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
if (pool == address(0)) {
return (sellBase, pool, gasUsed, makerTokenAmounts);
return (sellBase, pool, makerTokenAmounts);
}
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(pool, sellBase),
getSwapQuoteCallback: this.sampleSwapFromDodoV2
}),
takerTokenAmounts
);
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2(
abi.encode(takerToken, pool, sellBase), // taker token data
abi.encode(makerToken, pool, sellBase), // maker token data
takerTokenAmounts[i]
);
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from DODO.
@@ -104,7 +103,6 @@ contract DODOV2Sampler is
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return sellBase whether the bridge needs to sell the base token
/// @return pool the DODO pool address
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromDODOV2(
@@ -115,30 +113,68 @@ contract DODOV2Sampler is
uint256[] memory makerTokenAmounts
)
public
returns (
bool sellBase,
address pool,
uint256[] memory gasUsed,
uint256[] memory takerTokenAmounts
)
view
returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
(pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken);
if (pool == address(0)) {
return (sellBase, pool, gasUsed, takerTokenAmounts);
return (sellBase, pool, takerTokenAmounts);
}
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(pool, sellBase),
buyTokenData: abi.encode(pool, !sellBase),
getSwapQuoteCallback: this.sampleSwapFromDodoV2
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool, !sellBase),
takerTokenData: abi.encode(takerToken, pool, sellBase),
getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromDODOV2(
bytes memory takerTokenData,
bytes memory /* makerTokenData */,
uint256 sellAmount
)
private
view
returns (uint256)
{
(address takerToken, address pool, bool sellBase) = abi.decode(
takerTokenData,
(address, address, bool)
);
// We will get called to sell both the taker token and also to sell the maker token
// since we use approximate buy for sell and buy functions
if (sellBase) {
try
IDODOV2Pool(pool).querySellBase
{ gas: DODO_V2_CALL_GAS }
(address(0), sellAmount)
returns (uint256 amount, uint256)
{
return amount;
} catch {
return 0;
}
} else {
try
IDODOV2Pool(pool).querySellQuote
{ gas: DODO_V2_CALL_GAS }
(address(0), sellAmount)
returns (uint256 amount, uint256)
{
return amount;
} catch {
return 0;
}
}
}
function _getNextDODOV2Pool(
address registry,
uint256 offset,

View File

@@ -1,26 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6;
contract DelegateHackedERC20 {
address private constant HACKED = 0xDEf1000000000000000000000000000000DE7d37;
receive() external payable {}
fallback() payable external {
(bool success, bytes memory resultData) =
HACKED.delegatecall(msg.data);
if (!success) {
assembly { revert(add(resultData, 32), mload(resultData)) }
}
assembly { return(add(resultData, 32), mload(resultData)) }
}
/// @dev Hack to get around schema validation
function _garbage()
external
view
returns (uint256)
{
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -23,9 +23,7 @@ pragma experimental ABIEncoderV2;
import "./BalancerSampler.sol";
import "./BalancerV2Sampler.sol";
import "./BancorSampler.sol";
import "./BoosterSampler.sol";
import "./CurveSampler.sol";
import "./CurveV2Sampler.sol";
import "./DODOSampler.sol";
import "./DODOV2Sampler.sol";
import "./Eth2DaiSampler.sol";
@@ -34,25 +32,24 @@ import "./KyberDmmSampler.sol";
import "./LidoSampler.sol";
import "./LiquidityProviderSampler.sol";
import "./MakerPSMSampler.sol";
import "./MultiBridgeSampler.sol";
import "./MStableSampler.sol";
import "./MooniswapSampler.sol";
import "./NativeOrderSampler.sol";
import "./ShellSampler.sol";
import "./SmoothySampler.sol";
import "./TwoHopSampler.sol";
import "./UniswapSampler.sol";
import "./UniswapV2Sampler.sol";
import "./UniswapV3Sampler.sol";
import "./UtilitySampler.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
contract ERC20BridgeSampler is
BalancerSampler,
BalancerV2Sampler,
BancorSampler,
BoosterSampler,
CurveSampler,
CurveV2Sampler,
DODOSampler,
DODOV2Sampler,
Eth2DaiSampler,
@@ -63,8 +60,10 @@ contract ERC20BridgeSampler is
MakerPSMSampler,
MStableSampler,
MooniswapSampler,
MultiBridgeSampler,
NativeOrderSampler,
ShellSampler,
SmoothySampler,
TwoHopSampler,
UniswapSampler,
UniswapV2Sampler,
@@ -77,22 +76,11 @@ contract ERC20BridgeSampler is
bool success;
}
constructor(IEtherTokenV06 weth)
public
BancorSampler(weth)
CurveSampler(weth)
KyberSampler(weth)
MooniswapSampler(weth)
LidoSampler(weth)
UniswapSampler(weth)
{ }
/// @dev Call multiple public functions on this contract in a single transaction.
/// @param callDatas ABI-encoded call data for each function call.
/// @return callResults ABI-encoded results data for each call.
function batchCall(bytes[] calldata callDatas)
external
payable
returns (CallResults[] memory callResults)
{
callResults = new CallResults[](callDatas.length);

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,38 +20,21 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinOasis.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IEth2Dai.sol";
import "./SamplerUtils.sol";
contract Eth2DaiSampler is
MixinOasis,
SwapRevertSampler
SamplerUtils
{
function sampleSwapFromOasis(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeOasis(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Base gas limit for Eth2Dai calls.
uint256 constant private ETH2DAI_CALL_GAS = 1000e3; // 1m
/// @dev Sample sell quotes from Eth2Dai/Oasis.
/// @param router Address of the Eth2Dai/Oasis contract
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromEth2Dai(
@@ -61,17 +44,29 @@ contract Eth2DaiSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromOasis
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IEth2Dai(router).getBuyAmount
{gas: ETH2DAI_CALL_GAS}
(makerToken, takerToken, takerTokenAmounts[i])
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from Eth2Dai/Oasis.
@@ -79,7 +74,6 @@ contract Eth2DaiSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Maker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromEth2Dai(
@@ -89,17 +83,28 @@ contract Eth2DaiSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(router),
buyTokenData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromOasis
}),
makerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IEth2Dai(router).getPayAmount
{gas: ETH2DAI_CALL_GAS}
(takerToken, makerToken, makerTokenAmounts[i])
returns (uint256 amount)
{
takerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
}

View File

@@ -1,30 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6;
contract GasOverhead {
uint256 private _overhead = 2;
// Overhead incurred from updating the overhead storage slot
uint256 constant SSTORE_OVERHEAD = 20000;
function addOverhead(uint256 gas, uint256 gasBefore)
external
{
uint256 callOverhead = gasBefore - gasleft();
// Add additional est overhead of performing this update
_overhead += gas + callOverhead + SSTORE_OVERHEAD;
}
function clearOverhead()
external
{
_overhead = 2;
}
function overhead()
external
view
returns (uint256)
{
return _overhead;
}
}

View File

@@ -1,323 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.6;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
import "./GasOverhead.sol";
contract HackedERC20 {
using LibERC20TokenV06 for IERC20TokenV06;
struct ShadowedAmount {
bool isShadowed;
uint256 lastTrueAmount;
uint256 shadowedAmount;
}
struct Storage {
mapping(address=>ShadowedAmount) shadowedBalances;
mapping(address=>mapping(address=>ShadowedAmount)) shadowedAllowances;
// When enabled the HackedERC20 token will shadow and track balances
// when disabled (default) it will call the original implementation
bool enabled;
}
bytes32 private constant STORAGE_SLOT = 0x64fd48372774b9637ace5c8c7a951f04ea13c793935207f2eada5382a0ec82cb;
GasOverhead private constant GAS_OVERHEAD = GasOverhead(0xDeF1000000000000000000000000000000001337);
// HackedERC20 also has the overhead of being Delegated to from the replaced token
// USDT->DelegateHackedERC20->HackedERC20
uint256 private constant DELEGATE_CALL_OVERHEAD = 5000;
receive() external payable {}
fallback() payable external {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
function balanceOf(address owner)
external
/* view */
returns (uint256 balance)
{
if (!_isEnabled()) {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
(ShadowedAmount memory sBal,) = _getSyncedBalance(owner);
return sBal.shadowedAmount;
}
function allowance(address owner, address spender)
external
/* view */
returns (uint256 allowance_)
{
if (!_isEnabled()) {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
(ShadowedAmount memory sBal,) = _getSyncedAllowance(owner, spender);
return sBal.shadowedAmount;
}
function transferFrom(address from, address to, uint256 amount)
public
returns (bool success)
{
if (!_isEnabled()) {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
_updateAllowance(from, amount);
success = _transferFromInternal(from, to, amount);
}
function transfer(address to, uint256 amount)
external
returns (bool success)
{
if (!_isEnabled()) {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
success = _transferFromInternal(msg.sender, to, amount);
}
function approve(address spender, uint256 amount)
external
returns (bool)
{
if (!_isEnabled()) {
bytes memory r = _forwardCallToImpl();
assembly { return(add(r, 32), mload(r)) }
}
(
ShadowedAmount memory sAllowance,
) = _getSyncedAllowance(msg.sender, spender);
sAllowance.shadowedAmount = amount;
_writeSyncedAllowance(msg.sender, spender, sAllowance);
return true;
}
function _setBalance(address owner, uint256 amount)
public
{
(ShadowedAmount memory sBal,) = _getSyncedBalance(owner);
sBal.shadowedAmount = amount;
_writeSyncedBalance(owner, sBal);
}
function _getSyncedAllowance(address owner, address spender)
private
/* view */
returns (ShadowedAmount memory sAllowance, uint256 gasOverhead)
{
uint256 trueAmount = abi.decode(
_forwardCallToImpl(abi.encodeWithSelector(
IERC20TokenV06.allowance.selector,
owner,
spender
)),
(uint256)
);
// We only want to measure the cost of the underlying token storage lookup
// Not including the excess overhead of our shadow lookup
uint256 gasBefore = gasleft();
sAllowance = _getStorage().shadowedAllowances[owner][spender];
_syncShadowedAmount(sAllowance, trueAmount);
gasOverhead = gasBefore - gasleft();
}
function _getSyncedBalance(address owner)
private
returns (ShadowedAmount memory sBal, uint256 gasOverhead)
{
uint256 trueAmount = abi.decode(
_forwardCallToImpl(abi.encodeWithSelector(
IERC20TokenV06.balanceOf.selector,
owner
)),
(uint256)
);
// We only want to measure the cost of the underlying token storage lookup
// Not including the excess overhead of our shadow lookup
uint256 gasBefore = gasleft();
sBal = _getStorage().shadowedBalances[owner];
_syncShadowedAmount(sBal, trueAmount);
gasOverhead = gasBefore - gasleft();
}
function _syncShadowedAmount(ShadowedAmount memory sAmount, uint256 trueAmount)
private
pure
{
if (!sAmount.isShadowed) {
sAmount.isShadowed = true;
sAmount.shadowedAmount = trueAmount;
} else {
// Detect balance changes that can occur from outside of ERC20
// functions.
if (sAmount.lastTrueAmount > trueAmount) {
sAmount.shadowedAmount = _sub(
sAmount.lastTrueAmount,
sAmount.lastTrueAmount - trueAmount,
'HackedERC20/SHADOW_ADJUSTMENT_UNDERFLOW'
);
} else if (sAmount.lastTrueAmount < trueAmount) {
sAmount.shadowedAmount = _add(
sAmount.lastTrueAmount,
trueAmount - sAmount.lastTrueAmount,
'HackedERC20/SHADOW_ADJUSTMENT_OVERFLOW'
);
}
}
sAmount.lastTrueAmount = trueAmount;
}
function _writeSyncedBalance(address owner, ShadowedAmount memory sBal)
private
{
_getStorage().shadowedBalances[owner] = sBal;
}
function _writeSyncedAllowance(
address owner,
address spender,
ShadowedAmount memory sAllowance
)
private
{
_getStorage().shadowedAllowances[owner][spender] = sAllowance;
}
function _getStorage() private pure returns (Storage storage st) {
bytes32 slot = STORAGE_SLOT;
assembly { st_slot := slot }
}
function _getOriginalImplementationAddress()
private
view
returns (address impl)
{
return address(uint160(address(this)) + 1);
}
function _forwardCallToImpl()
private
returns (bytes memory resultData)
{
bool success;
(success, resultData) =
_getOriginalImplementationAddress().delegatecall(msg.data);
if (!success) {
assembly { revert(add(resultData, 32), mload(resultData)) }
}
}
function _forwardCallToImpl(bytes memory callData)
private
returns (bytes memory resultData)
{
bool success;
(success, resultData) =
_getOriginalImplementationAddress().delegatecall(callData);
if (!success) {
assembly { revert(add(resultData, 32), mload(resultData)) }
}
}
function _transferFromInternal(address from, address to, uint256 amount)
internal
returns (bool)
{
ShadowedAmount memory sFromBal;
ShadowedAmount memory sToBal;
uint256 gasOverhead;
uint256 _gasOverhead;
(sFromBal, _gasOverhead) = _getSyncedBalance(from);
gasOverhead += _gasOverhead;
sFromBal.shadowedAmount = _sub(
sFromBal.shadowedAmount,
amount,
'HackedERC20/BALANCE_UNDERFLOW'
);
_writeSyncedBalance(from, sFromBal);
(sToBal, _gasOverhead) = _getSyncedBalance(to);
gasOverhead += _gasOverhead;
sToBal.shadowedAmount = _add(
sToBal.shadowedAmount,
amount,
'HackedERC20/BALANCE_OVERFLOW'
);
_writeSyncedBalance(to, sToBal);
// Update the global gas overhead from a transfer call
try
GAS_OVERHEAD.addOverhead(gasOverhead + DELEGATE_CALL_OVERHEAD, gasleft())
{ } catch { }
return true;
}
function _updateAllowance(address from, uint256 amount)
internal
{
(ShadowedAmount memory sAllowance, uint256 gasOverhead) = _getSyncedAllowance(from, msg.sender);
if (from != msg.sender && sAllowance.shadowedAmount != uint256(-1)) {
sAllowance.shadowedAmount = _sub(
sAllowance.shadowedAmount,
amount,
'HackedERC20/ALLOWANCE_UNDERFLOW'
);
_writeSyncedAllowance(from, msg.sender, sAllowance);
}
uint256 gasBefore = gasleft();
// Assume a NON MAX_UINT results in allowance update SSTORE
_writeSyncedAllowance(from, msg.sender, sAllowance);
gasOverhead += gasBefore - gasleft();
// Update the global gas overhead from a allowance check
try
GAS_OVERHEAD.addOverhead(gasOverhead + DELEGATE_CALL_OVERHEAD, gasleft())
{ } catch { }
}
function _isEnabled()
internal
returns (bool)
{
return _getStorage().enabled;
}
function _setEnabled(bool enabled)
public
{
_getStorage().enabled = enabled;
}
function _add(uint256 a, uint256 b, string memory errMsg)
private
pure
returns (uint256 c)
{
c = a + b;
require(c >= a, errMsg);
}
function _sub(uint256 a, uint256 b, string memory errMsg)
private
pure
returns (uint256 c)
{
c = a - b;
require(c <= a, errMsg);
}
}

View File

@@ -20,46 +20,49 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinKyberDmm.sol";
import "./SwapRevertSampler.sol";
interface IKyberDmmFactory {
interface IKyberDmmPool {
function getPoolAtIndex(address token0, address token1, uint256 index)
function totalSupply()
external
view
returns (address);
returns (uint256);
}
interface IKyberDmmFactory {
function getPools(address token0, address token1)
external
view
returns (address[] memory _tokenPools);
}
interface IKyberDmmRouter {
function factory() external view returns (address);
function getAmountsOut(uint256 amountIn, address[] calldata pools, address[] calldata path)
external
view
returns (uint256[] memory amounts);
function getAmountsIn(uint256 amountOut, address[] calldata pools, address[] calldata path)
external
view
returns (uint256[] memory amounts);
}
contract KyberDmmSampler is
MixinKyberDmm,
SwapRevertSampler
contract KyberDmmSampler
{
/// @dev Gas limit for KyberDmm calls.
uint256 constant private KYBER_DMM_CALL_GAS = 150e3; // 150k
function sampleSwapFromKyberDmm(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeKyberDmm(
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from KyberDmm.
/// @param router Router to look up tokens and amounts
/// @param path Token route. Should be takerToken -> makerToken
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return pools The pool addresses involved in the multi path trade
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromKyberDmm(
@@ -68,26 +71,32 @@ contract KyberDmmSampler is
uint256[] memory takerTokenAmounts
)
public
returns (
address[] memory pools,
uint256[] memory gasUsed,
uint256[] memory makerTokenAmounts
)
view
returns (address[] memory pools, uint256[] memory makerTokenAmounts)
{
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
pools = _getKyberDmmPools(router, path);
if (pools.length == 0) {
return (pools, gasUsed, makerTokenAmounts);
return (pools, makerTokenAmounts);
}
for (uint256 i = 0; i < numSamples; i++) {
try
IKyberDmmRouter(router).getAmountsOut
{gas: KYBER_DMM_CALL_GAS}
(takerTokenAmounts[i], pools, path)
returns (uint256[] memory amounts)
{
makerTokenAmounts[i] = amounts[path.length - 1];
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: path[0],
buyToken: path[path.length - 1],
bridgeData: abi.encode(router, pools, path),
getSwapQuoteCallback: this.sampleSwapFromKyberDmm
}),
takerTokenAmounts
);
}
/// @dev Sample buy quotes from KyberDmm.
@@ -95,7 +104,6 @@ contract KyberDmmSampler is
/// @param path Token route. Should be takerToken -> makerToken.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return pools The pool addresses involved in the multi path trade
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromKyberDmm(
@@ -104,32 +112,32 @@ contract KyberDmmSampler is
uint256[] memory makerTokenAmounts
)
public
returns (
address[] memory pools,
uint256[] memory gasUsed,
uint256[] memory takerTokenAmounts
)
view
returns (address[] memory pools, uint256[] memory takerTokenAmounts)
{
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
pools = _getKyberDmmPools(router, path);
if (pools.length == 0) {
return (pools, gasUsed, takerTokenAmounts);
return (pools, takerTokenAmounts);
}
address[] memory reversedPath = new address[](path.length);
for (uint256 i = 0; i < path.length; ++i) {
reversedPath[i] = path[path.length - i - 1];
for (uint256 i = 0; i < numSamples; i++) {
try
IKyberDmmRouter(router).getAmountsIn
{gas: KYBER_DMM_CALL_GAS}
(makerTokenAmounts[i], pools, path)
returns (uint256[] memory amounts)
{
takerTokenAmounts[i] = amounts[0];
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
address[] memory reversedPools = _getKyberDmmPools(router, reversedPath);
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: path[0],
buyToken: path[path.length - 1],
sellTokenData: abi.encode(router, pools, path),
buyTokenData: abi.encode(router, reversedPools, reversedPath),
getSwapQuoteCallback: this.sampleSwapFromKyberDmm
}),
makerTokenAmounts
);
}
function _getKyberDmmPools(
@@ -140,17 +148,26 @@ contract KyberDmmSampler is
view
returns (address[] memory pools)
{
pools = new address[](path.length - 1);
IKyberDmmFactory factory = IKyberDmmFactory(IKyberDmmRouter(router).factory());
pools = new address[](path.length - 1);
for (uint256 i = 0; i < pools.length; i++) {
// Currently only supporting the first pool found at the index
// find the best pool
address[] memory allPools;
try
factory.getPoolAtIndex
factory.getPools
{gas: KYBER_DMM_CALL_GAS}
(path[i], path[i + 1], 0)
returns (address pool)
(path[i], path[i + 1])
returns (address[] memory allPools)
{
pools[i] = pool;
uint256 maxSupply = 0;
require(allPools.length >= 1, "KyberDMMSampler/NO_POOLS_FOUND");
for (uint256 j = 0; j < allPools.length; j++) {
uint256 totalSupply = IKyberDmmPool(allPools[j]).totalSupply();
if (totalSupply > maxSupply) {
maxSupply = totalSupply;
pools[i] = allPools[j];
}
}
} catch (bytes memory) {
return new address[](0);
}

View File

@@ -21,22 +21,18 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./interfaces/IKyberNetwork.sol";
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinKyber.sol";
import "./SwapRevertSampler.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract KyberSampler is
MixinKyber,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
/// @dev Gas limit for Kyber calls.
uint256 constant private KYBER_CALL_GAS = 500e3; // 500k
/// @dev Kyber ETH pseudo-address.
address constant internal KYBER_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
constructor(IEtherTokenV06 weth)
public
MixinKyber(weth)
{ }
struct KyberSamplerOpts {
uint256 reserveOffset;
@@ -46,26 +42,6 @@ contract KyberSampler is
bytes hint;
}
function sampleSwapFromKyber(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeKyberInternal(
// these are Immutable in MixinKyber, since they are only set in constructor they must be passed in
IERC20TokenV06(KYBER_ETH_ADDRESS),
_getNativeWrappedToken(),
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Sample sell quotes from Kyber.
/// @param opts KyberSamplerOpts The nth reserve
/// @param takerToken Address of the taker token (what to sell).
@@ -73,7 +49,6 @@ contract KyberSampler is
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return reserveId The id of the reserve found at reserveOffset
/// @return hint The hint for the selected reserve
/// @return gasUsed Gas consumed per sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token amount.
function sampleSellsFromKyberNetwork(
KyberSamplerOpts memory opts,
@@ -82,29 +57,32 @@ contract KyberSampler is
uint256[] memory takerTokenAmounts
)
public
returns (
bytes32 reserveId,
bytes memory hint,
uint256[] memory gasUsed,
uint256[] memory makerTokenAmounts
)
view
returns (bytes32 reserveId, bytes memory hint, uint256[] memory makerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
reserveId = _getNextReserveId(opts, takerToken, makerToken);
if (reserveId == 0x0) {
return (reserveId, hint, gasUsed, makerTokenAmounts);
return (reserveId, hint, makerTokenAmounts);
}
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
hint = opts.hint;
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(opts.networkProxy, hint),
getSwapQuoteCallback: this.sampleSwapFromKyber
}),
takerTokenAmounts
);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
uint256 value = this.sampleSellFromKyberNetwork(
opts,
takerToken,
makerToken,
takerTokenAmounts[i]
);
makerTokenAmounts[i] = value;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Kyber.
@@ -114,7 +92,6 @@ contract KyberSampler is
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return reserveId The id of the reserve found at reserveOffset
/// @return hint The hint for the selected reserve
/// @return gasUsed Gas consumed for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token amount.
function sampleBuysFromKyberNetwork(
KyberSamplerOpts memory opts,
@@ -123,30 +100,27 @@ contract KyberSampler is
uint256[] memory makerTokenAmounts
)
public
returns (
bytes32 reserveId,
bytes memory hint,
uint256[] memory gasUsed,
uint256[] memory takerTokenAmounts
)
view
returns (bytes32 reserveId, bytes memory hint, uint256[] memory takerTokenAmounts)
{
_assertValidPair(makerToken, takerToken);
reserveId = _getNextReserveId(opts, takerToken, makerToken);
if (reserveId == 0x0) {
return (reserveId, hint, gasUsed, takerTokenAmounts);
return (reserveId, hint, takerTokenAmounts);
}
opts.hint = this.encodeKyberHint(opts, reserveId, takerToken, makerToken);
hint = opts.hint;
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(opts.networkProxy, hint),
buyTokenData: abi.encode(opts.networkProxy, hint),
getSwapQuoteCallback: this.sampleSwapFromKyber
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, opts),
takerTokenData: abi.encode(takerToken, opts),
getSellQuoteCallback: _sampleSellForApproximateBuyFromKyber
}),
makerTokenAmounts
);
return (reserveId, hint, takerTokenAmounts);
}
function encodeKyberHint(
@@ -227,6 +201,73 @@ contract KyberSampler is
}
}
function _sampleSellForApproximateBuyFromKyber(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256)
{
(address makerToken, KyberSamplerOpts memory opts) =
abi.decode(makerTokenData, (address, KyberSamplerOpts));
(address takerToken, ) =
abi.decode(takerTokenData, (address, KyberSamplerOpts));
try
this.sampleSellFromKyberNetwork
(opts, takerToken, makerToken, sellAmount)
returns (uint256 amount)
{
return amount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
function sampleSellFromKyberNetwork(
KyberSamplerOpts memory opts,
address takerToken,
address makerToken,
uint256 takerTokenAmount
)
public
view
returns (uint256 makerTokenAmount)
{
// If there is no hint do not continue
if (opts.hint.length == 0) {
return 0;
}
try
IKyberNetworkProxy(opts.networkProxy).getExpectedRateAfterFee
{gas: KYBER_CALL_GAS}
(
takerToken == opts.weth ? KYBER_ETH_ADDRESS : takerToken,
makerToken == opts.weth ? KYBER_ETH_ADDRESS : makerToken,
takerTokenAmount,
0, // fee
opts.hint
)
returns (uint256 rate)
{
uint256 makerTokenDecimals = _getTokenDecimals(makerToken);
uint256 takerTokenDecimals = _getTokenDecimals(takerToken);
makerTokenAmount =
rate *
takerTokenAmount *
10 ** makerTokenDecimals /
10 ** takerTokenDecimals /
10 ** 18;
return makerTokenAmount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
function _getNextReserveId(
KyberSamplerOpts memory opts,
address takerToken,

View File

@@ -20,84 +20,72 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinLido.sol";
import "./SwapRevertSampler.sol";
import "./SamplerUtils.sol";
contract LidoSampler is
MixinLido,
SwapRevertSampler
{
constructor(IEtherTokenV06 weth)
public
MixinLido(weth)
{ }
function sampleSwapFromLido(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeLidoInternal(
_getNativeWrappedToken(),
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
contract LidoSampler is SamplerUtils {
struct LidoInfo {
address stEthToken;
address wethToken;
}
/// @dev Sample sell quotes from Lido
/// @param lido Address of the Lido contract
/// @param lidoInfo Info regarding a specific Lido deployment
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed for each sample amount
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromLido(
address lido,
LidoInfo memory lidoInfo,
address takerToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
pure
returns (uint256[] memory)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(lido),
getSwapQuoteCallback: this.sampleSwapFromLido
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
// Return 0 values if not selling WETH for stETH
uint256 numSamples = takerTokenAmounts.length;
uint256[] memory makerTokenAmounts = new uint256[](numSamples);
return makerTokenAmounts;
}
// Minting stETH is always 1:1 therefore we can just return the same amounts back
return takerTokenAmounts;
}
/// @dev Sample buy quotes from Lido.
/// @param lido Address of the Lido contract
/// @param lidoInfo Info regarding a specific Lido deployment
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed for each sample amount
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromLido(
address lido,
LidoInfo memory lidoInfo,
address takerToken,
address makerToken,
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
pure
returns (uint256[] memory)
{
// 1:1 rate so we can perform an WETH sell
return this.sampleSellsFromLido(lido, takerToken, makerToken, makerTokenAmounts);
_assertValidPair(makerToken, takerToken);
if (takerToken != lidoInfo.wethToken || makerToken != address(lidoInfo.stEthToken)) {
// Return 0 values if not buying stETH for WETH
uint256 numSamples = makerTokenAmounts.length;
uint256[] memory takerTokenAmounts = new uint256[](numSamples);
return takerTokenAmounts;
}
// Minting stETH is always 1:1 therefore we can just return the same amounts back
return makerTokenAmounts;
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,38 +20,24 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinZeroExBridge.sol";
import "./SwapRevertSampler.sol";
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
import "@0x/contracts-zero-ex/contracts/src/vendor/ILiquidityProvider.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract LiquidityProviderSampler is
MixinZeroExBridge,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
function sampleSwapFromLiquidityProvider(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeZeroExBridge(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Default gas limit for liquidity provider calls.
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
/// @dev Sample sell quotes from an arbitrary on-chain liquidity provider.
/// @param providerAddress Address of the liquidity provider.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromLiquidityProvider(
@@ -61,18 +47,34 @@ contract LiquidityProviderSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
bytes memory lpData;
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(providerAddress, lpData),
getSwapQuoteCallback: this.sampleSwapFromLiquidityProvider
}),
takerTokenAmounts
);
// Initialize array of maker token amounts.
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
ILiquidityProvider(providerAddress).getSellQuote
{gas: DEFAULT_CALL_GAS}
(
IERC20TokenV06(takerToken),
IERC20TokenV06(makerToken),
takerTokenAmounts[i]
)
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from an arbitrary on-chain liquidity provider.
@@ -80,7 +82,6 @@ contract LiquidityProviderSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromLiquidityProvider(
@@ -90,18 +91,42 @@ contract LiquidityProviderSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
bytes memory lpData;
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(providerAddress, lpData),
buyTokenData: abi.encode(providerAddress, lpData),
getSwapQuoteCallback: this.sampleSwapFromLiquidityProvider
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, providerAddress),
takerTokenData: abi.encode(takerToken, providerAddress),
getSellQuoteCallback: _sampleSellForApproximateBuyFromLiquidityProvider
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromLiquidityProvider(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(address takerToken, address providerAddress) =
abi.decode(takerTokenData, (address, address));
(address makerToken) =
abi.decode(makerTokenData, (address));
try
this.sampleSellsFromLiquidityProvider
{gas: DEFAULT_CALL_GAS}
(providerAddress, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts)
{
return amounts[0];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
}

View File

@@ -20,38 +20,23 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMStable.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IMStable.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
contract MStableSampler is
MixinMStable,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
function sampleSwapFromMStable(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeMStable(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Default gas limit for mStable calls.
uint256 constant private DEFAULT_CALL_GAS = 800e3; // 800k
/// @dev Sample sell quotes from the mStable contract
/// @param router Address of the mStable contract
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromMStable(
@@ -61,17 +46,31 @@ contract MStableSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromMStable
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
// Initialize array of maker token amounts.
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IMStable(router).getSwapOutput
{gas: DEFAULT_CALL_GAS}
(takerToken, makerToken, takerTokenAmounts[i])
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from MStable contract
@@ -79,7 +78,6 @@ contract MStableSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromMStable(
@@ -89,17 +87,41 @@ contract MStableSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(router),
buyTokenData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromMStable
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, router),
takerTokenData: abi.encode(takerToken, router),
getSellQuoteCallback: _sampleSellForApproximateBuyFromMStable
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromMStable(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(address takerToken, address router) =
abi.decode(takerTokenData, (address, address));
(address makerToken) =
abi.decode(makerTokenData, (address));
try
this.sampleSellsFromMStable
(router, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts)
{
return amounts[0];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
}

View File

@@ -20,13 +20,69 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMakerPSM.sol";
import "./SwapRevertSampler.sol";
import "./SamplerUtils.sol";
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
interface IPSM {
// @dev Get the fee for selling USDC to DAI in PSM
// @return tin toll in [wad]
function tin() external view returns (uint256);
// @dev Get the fee for selling DAI to USDC in PSM
// @return tout toll out [wad]
function tout() external view returns (uint256);
// @dev Get the address of the PSM state Vat
// @return address of the Vat
function vat() external view returns (address);
// @dev Get the address of the underlying vault powering PSM
// @return address of gemJoin contract
function gemJoin() external view returns (address);
// @dev Get the address of DAI
// @return address of DAI contract
function dai() external view returns (address);
// @dev Sell USDC for DAI
// @param usr The address of the account trading USDC for DAI.
// @param gemAmt The amount of USDC to sell in USDC base units
function sellGem(
address usr,
uint256 gemAmt
) external;
// @dev Buy USDC for DAI
// @param usr The address of the account trading DAI for USDC
// @param gemAmt The amount of USDC to buy in USDC base units
function buyGem(
address usr,
uint256 gemAmt
) external;
}
interface IVAT {
// @dev Get a collateral type by identifier
// @param ilkIdentifier bytes32 identifier. Example: ethers.utils.formatBytes32String("PSM-USDC-A")
// @return ilk
// @return ilk.Art Total Normalised Debt in wad
// @return ilk.rate Accumulated Rates in ray
// @return ilk.spot Price with Safety Margin in ray
// @return ilk.line Debt Ceiling in rad
// @return ilk.dust Urn Debt Floor in rad
function ilks(
bytes32 ilkIdentifier
) external view returns (
uint256 Art,
uint256 rate,
uint256 spot,
uint256 line,
uint256 dust
);
}
contract MakerPSMSampler is
MixinMakerPSM,
SwapRevertSampler
SamplerUtils
{
using LibSafeMathV06 for uint256;
/// @dev Information about which PSM module to use
struct MakerPsmInfo {
@@ -35,22 +91,18 @@ contract MakerPSMSampler is
address gemTokenAddress;
}
function sampleSwapFromMakerPsm(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeMakerPsm(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Gas limit for MakerPsm calls.
uint256 constant private MAKER_PSM_CALL_GAS = 300e3; // 300k
// Maker units
// wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)
uint256 constant private WAD = 10 ** 18;
// ray: fixed point decimal with 27 decimals (for precise quantites, e.g. ratios)
uint256 constant private RAY = 10 ** 27;
// rad: fixed point decimal with 45 decimals (result of integer multiplication with a wad and a ray)
uint256 constant private RAD = 10 ** 45;
// See https://github.com/makerdao/dss/blob/master/DEVELOPING.m
/// @dev Sample sell quotes from Maker PSM
function sampleSellsFromMakerPsm(
@@ -60,22 +112,29 @@ contract MakerPSMSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(
MakerPsmBridgeData({
psmAddress: psmInfo.psmAddress,
gemTokenAddres: psmInfo.gemTokenAddress
})
),
getSwapQuoteCallback: this.sampleSwapFromMakerPsm
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
IPSM psm = IPSM(psmInfo.psmAddress);
IVAT vat = IVAT(psm.vat());
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
if (makerToken != psm.dai() && takerToken != psm.dai()) {
return makerTokenAmounts;
}
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = _samplePSMSell(psmInfo, makerToken, takerToken, takerTokenAmounts[i], psm, vat);
if (buyAmount == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
function sampleBuysFromMakerPsm(
@@ -85,21 +144,124 @@ contract MakerPSMSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
MakerPsmBridgeData memory data = MakerPsmBridgeData({
psmAddress: psmInfo.psmAddress,
gemTokenAddres: psmInfo.gemTokenAddress
});
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(data),
buyTokenData: abi.encode(data),
getSwapQuoteCallback: this.sampleSwapFromMakerPsm
}),
makerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
IPSM psm = IPSM(psmInfo.psmAddress);
IVAT vat = IVAT(psm.vat());
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
if (makerToken != psm.dai() && takerToken != psm.dai()) {
return takerTokenAmounts;
}
for (uint256 i = 0; i < numSamples; i++) {
uint256 sellAmount = _samplePSMBuy(psmInfo, makerToken, takerToken, makerTokenAmounts[i], psm, vat);
if (sellAmount == 0) {
break;
}
takerTokenAmounts[i] = sellAmount;
}
}
function _samplePSMSell(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 takerTokenAmount, IPSM psm, IVAT vat)
private
view
returns (uint256)
{
(uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
uint256 gemTokenBaseUnit = uint256(1e6);
if (takerToken == psmInfo.gemTokenAddress) {
// Simulate sellGem
// Selling USDC to the PSM, increasing the total debt
// Convert USDC 6 decimals to 18 decimals [wad]
uint256 takerTokenAmountInWad = takerTokenAmount.safeMul(1e12);
uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
// PSM is too full to fit
if (newTotalDebtInRad >= debtCeilingInRad) {
return 0;
}
uint256 feeInWad = takerTokenAmountInWad.safeMul(psm.tin()).safeDiv(WAD);
uint256 makerTokenAmountInWad = takerTokenAmountInWad.safeSub(feeInWad);
return makerTokenAmountInWad;
} else if (makerToken == psmInfo.gemTokenAddress) {
// Simulate buyGem
// Buying USDC from the PSM, decreasing the total debt
// Selling DAI for USDC, already in 18 decimals [wad]
uint256 takerTokenAmountInWad = takerTokenAmount;
if (takerTokenAmountInWad > totalDebtInWad) {
return 0;
}
uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
// PSM is empty, not enough USDC to buy from it
if (newTotalDebtInRad <= debtFloorInRad) {
return 0;
}
uint256 feeDivisorInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
uint256 makerTokenAmountInGemTokenBaseUnits = takerTokenAmountInWad.safeMul(gemTokenBaseUnit).safeDiv(feeDivisorInWad);
return makerTokenAmountInGemTokenBaseUnits;
}
return 0;
}
function _samplePSMBuy(MakerPsmInfo memory psmInfo, address makerToken, address takerToken, uint256 makerTokenAmount, IPSM psm, IVAT vat)
private
view
returns (uint256)
{
(uint256 totalDebtInWad,,, uint256 debtCeilingInRad, uint256 debtFloorInRad) = vat.ilks(psmInfo.ilkIdentifier);
if (takerToken == psmInfo.gemTokenAddress) {
// Simulate sellGem
// Selling USDC to the PSM, increasing the total debt
uint256 makerTokenAmountInWad = makerTokenAmount;
uint256 feeDivisorInWad = WAD.safeSub(psm.tin()); // eg. 0.999 * 10 ** 18 with 0.1% tin;
uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(WAD).safeDiv(feeDivisorInWad);
uint256 newTotalDebtInRad = totalDebtInWad.safeAdd(takerTokenAmountInWad).safeMul(RAY);
// PSM is too full to fit
if (newTotalDebtInRad >= debtCeilingInRad) {
return 0;
}
uint256 takerTokenAmountInGemInGemBaseUnits = (takerTokenAmountInWad.safeDiv(1e12)).safeAdd(1); // Add 1 to deal with cut off decimals converting to lower decimals
return takerTokenAmountInGemInGemBaseUnits;
} else if (makerToken == psmInfo.gemTokenAddress) {
// Simulate buyGem
// Buying USDC from the PSM, decreasing the total debt
uint256 makerTokenAmountInWad = makerTokenAmount.safeMul(1e12);
uint256 feeMultiplierInWad = WAD.safeAdd(psm.tout()); // eg. 1.001 * 10 ** 18 with 0.1% tout;
uint256 takerTokenAmountInWad = makerTokenAmountInWad.safeMul(feeMultiplierInWad).safeDiv(WAD);
if (takerTokenAmountInWad > totalDebtInWad) {
return 0;
}
uint256 newTotalDebtInRad = totalDebtInWad.safeSub(takerTokenAmountInWad).safeMul(RAY);
// PSM is empty, not enough USDC to buy
if (newTotalDebtInRad <= debtFloorInRad) {
return 0;
}
return takerTokenAmountInWad;
}
return 0;
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,40 +20,17 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinMooniswap.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IMooniswap.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
interface IMooniswapRegistry {
function pools(address token1, address token2) external view returns(address);
}
contract MooniswapSampler is
MixinMooniswap,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
constructor(IEtherTokenV06 weth)
public
MixinMooniswap(weth)
{ }
function sampleSwapFromMooniswap(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeMooniswapInternal(
_getNativeWrappedToken(),
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Gas limit for Mooniswap calls.
uint256 constant private MOONISWAP_CALL_GAS = 150e3; // 150k
/// @dev Sample sell quotes from Mooniswap.
/// @param registry Address of the Mooniswap Registry.
@@ -61,7 +38,6 @@ contract MooniswapSampler is
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return pool The contract address for the pool
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromMooniswap(
@@ -71,31 +47,77 @@ contract MooniswapSampler is
uint256[] memory takerTokenAmounts
)
public
returns (address pool, uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (IMooniswap pool, uint256[] memory makerTokenAmounts)
{
pool = _getMooniswapPool(registry, takerToken, makerToken);
if (address(pool) == address(0)) {
return (pool, gasUsed, makerTokenAmounts);
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
uint256 buyAmount = sampleSingleSellFromMooniswapPool(
registry,
takerToken,
makerToken,
takerTokenAmounts[i]
);
makerTokenAmounts[i] = buyAmount;
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromMooniswap
}),
takerTokenAmounts
pool = IMooniswap(
IMooniswapRegistry(registry).pools(takerToken, makerToken)
);
}
function sampleSingleSellFromMooniswapPool(
address registry,
address mooniswapTakerToken,
address mooniswapMakerToken,
uint256 takerTokenAmount
)
public
view
returns (uint256)
{
// Find the pool for the pair.
IMooniswap pool = IMooniswap(
IMooniswapRegistry(registry).pools(mooniswapTakerToken, mooniswapMakerToken)
);
// If there is no pool then return early
if (address(pool) == address(0)) {
return 0;
}
uint256 poolBalance = mooniswapTakerToken == address(0)
? address(pool).balance
: IERC20TokenV06(mooniswapTakerToken).balanceOf(address(pool));
// If the pool balance is smaller than the sell amount
// don't sample to avoid multiplication overflow in buys
if (poolBalance < takerTokenAmount) {
return 0;
}
try
pool.getReturn
{gas: MOONISWAP_CALL_GAS}
(mooniswapTakerToken, mooniswapMakerToken, takerTokenAmount)
returns (uint256 amount)
{
return amount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
/// @dev Sample buy quotes from Mooniswap.
/// @param registry Address of the Mooniswap Registry.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return pool The contract address for the pool
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromMooniswap(
@@ -105,42 +127,43 @@ contract MooniswapSampler is
uint256[] memory makerTokenAmounts
)
public
returns (address pool, uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (IMooniswap pool, uint256[] memory takerTokenAmounts)
{
pool = _getMooniswapPool(registry, takerToken, makerToken);
if (address(pool) == address(0)) {
return (pool, gasUsed, takerTokenAmounts);
}
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(pool),
buyTokenData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromMooniswap
takerTokenAmounts = _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(registry, makerToken),
takerTokenData: abi.encode(registry, takerToken),
getSellQuoteCallback: _sampleSellForApproximateBuyFromMooniswap
}),
makerTokenAmounts
);
pool = IMooniswap(
IMooniswapRegistry(registry).pools(takerToken, makerToken)
);
}
function _getMooniswapPool(
address registry,
address takerToken,
address makerToken
function _sampleSellForApproximateBuyFromMooniswap(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
internal
returns (address pool)
private
view
returns (uint256 buyAmount)
{
// WETH is actually ETH in these pools and represented as address(0)
address _takerToken = takerToken == address(_getNativeWrappedToken()) ? address(0) : takerToken;
address _makerToken = makerToken == address(_getNativeWrappedToken()) ? address(0) : makerToken;
try
IMooniswapRegistry(registry).pools{gas: 300e3}(_takerToken, _makerToken)
returns (address _pool)
{
pool = _pool;
} catch { }
(address registry, address mooniswapTakerToken) = abi.decode(takerTokenData, (address, address));
(address _registry, address mooniswapMakerToken) = abi.decode(makerTokenData, (address, address));
return sampleSingleSellFromMooniswapPool(
registry,
mooniswapTakerToken,
mooniswapMakerToken,
sellAmount
);
}
}

View File

@@ -0,0 +1,82 @@
// 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;
pragma experimental ABIEncoderV2;
import "./interfaces/IMultiBridge.sol";
contract MultiBridgeSampler {
/// @dev Default gas limit for multibridge calls.
uint256 constant private DEFAULT_CALL_GAS = 400e3; // 400k
/// @dev Sample sell quotes from MultiBridge.
/// @param multibridge Address of the MultiBridge contract.
/// @param takerToken Address of the taker token (what to sell).
/// @param intermediateToken The address of the intermediate token to
/// use in an indirect route.
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromMultiBridge(
address multibridge,
address takerToken,
address intermediateToken,
address makerToken,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
// Initialize array of maker token amounts.
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
// If no address provided, return all zeros.
if (multibridge == address(0)) {
return makerTokenAmounts;
}
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
multibridge.staticcall.gas(DEFAULT_CALL_GAS)(
abi.encodeWithSelector(
IMultiBridge(0).getSellQuote.selector,
takerToken,
intermediateToken,
makerToken,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
// Exit early if the amount is too high for the source to serve
if (buyAmount == 0) {
break;
}
makerTokenAmounts[i] = buyAmount;
}
}
}

View File

@@ -0,0 +1,58 @@
// 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;
pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
contract SamplerUtils {
/// @dev Overridable way to get token decimals.
/// @param tokenAddress Address of the token.
/// @return decimals The decimal places for the token.
function _getTokenDecimals(address tokenAddress)
virtual
internal
view
returns (uint8 decimals)
{
return LibERC20TokenV06.compatDecimals(IERC20TokenV06(tokenAddress));
}
function _toSingleValueArray(uint256 v)
internal
pure
returns (uint256[] memory arr)
{
arr = new uint256[](1);
arr[0] = v;
}
/// @dev Assert that the tokens in a trade pair are valid.
/// @param makerToken Address of the maker token.
/// @param takerToken Address of the taker token.
function _assertValidPair(address makerToken, address takerToken)
internal
pure
{
require(makerToken != takerToken, "ERC20BridgeSampler/INVALID_TOKEN_PAIR");
}
}

View File

@@ -20,41 +20,28 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinShell.sol";
import "./SwapRevertSampler.sol";
import "./ApproximateBuys.sol";
import "./interfaces/IShell.sol";
import "./SamplerUtils.sol";
contract ShellSampler is
MixinShell,
SwapRevertSampler
SamplerUtils,
ApproximateBuys
{
struct ShellInfo {
address poolAddress;
}
function sampleSwapFromShell(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeShell(
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Default gas limit for Shell calls.
uint256 constant private DEFAULT_CALL_GAS = 300e3; // 300k
/// @dev Sample sell quotes from the Shell pool contract
/// @param pool Address of the Shell pool contract
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromShell(
@@ -64,17 +51,26 @@ contract ShellSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromShell
}),
takerTokenAmounts
);
// Initialize array of maker token amounts.
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IShell(pool).viewOriginSwap
{gas: DEFAULT_CALL_GAS}
(takerToken, makerToken, takerTokenAmounts[i])
returns (uint256 amount)
{
makerTokenAmounts[i] = amount;
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from Shell pool contract
@@ -82,7 +78,6 @@ contract ShellSampler is
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromShell(
@@ -92,17 +87,40 @@ contract ShellSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(pool),
buyTokenData: abi.encode(pool),
getSwapQuoteCallback: this.sampleSwapFromShell
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(makerToken, pool),
takerTokenData: abi.encode(takerToken, pool),
getSellQuoteCallback: _sampleSellForApproximateBuyFromShell
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromShell(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(address takerToken, address pool) = abi.decode(takerTokenData, (address, address));
(address makerToken) = abi.decode(makerTokenData, (address));
try
this.sampleSellsFromShell
(pool, takerToken, makerToken, _toSingleValueArray(sellAmount))
returns (uint256[] memory amounts)
{
return amounts[0];
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
return 0;
}
}
}

View File

@@ -0,0 +1,156 @@
// 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;
pragma experimental ABIEncoderV2;
// import "./interfaces/ISmoothy.sol";
import "./ApproximateBuys.sol";
import "./SamplerUtils.sol";
import "./interfaces/ISmoothy.sol";
contract SmoothySampler is
SamplerUtils,
ApproximateBuys
{
/// @dev Information for sampling from smoothy sources.
struct SmoothyInfo {
address poolAddress;
bytes4 sellQuoteFunctionSelector;
bytes4 buyQuoteFunctionSelector;
}
/// @dev Base gas limit for Smoothy calls.
uint256 constant private SMOOTHY_CALL_GAS = 600e3;
/// @dev Sample sell quotes from Smoothy.
/// @param smoothyInfo Smoothy information specific to this token pair.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromSmoothy(
SmoothyInfo memory smoothyInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory takerTokenAmounts
)
public
view
returns (uint256[] memory makerTokenAmounts)
{
// Basically a Curve fork
// Smoothy only keep a percentage of its tokens available in reserve
uint256 poolReserveMakerAmount = ISmoothy(smoothyInfo.poolAddress).getBalance(uint256(toTokenIdx)) -
ISmoothy(smoothyInfo.poolAddress)._yBalances(uint256(toTokenIdx));
(, , , uint256 decimals) = ISmoothy(smoothyInfo.poolAddress).getTokenStats(uint256(toTokenIdx));
poolReserveMakerAmount = poolReserveMakerAmount/(10**(18-decimals));
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
(bool didSucceed, bytes memory resultData) =
smoothyInfo.poolAddress.staticcall.gas(SMOOTHY_CALL_GAS)(
abi.encodeWithSelector(
smoothyInfo.sellQuoteFunctionSelector,
fromTokenIdx,
toTokenIdx,
takerTokenAmounts[i]
));
uint256 buyAmount = 0;
if (didSucceed) {
buyAmount = abi.decode(resultData, (uint256));
}
// Make sure the quoted buyAmount is available in the pool reserve
if (buyAmount >= poolReserveMakerAmount) {
// Assign pool reserve amount for all higher samples to break early
for (uint256 j = i; j < numSamples; j++) {
makerTokenAmounts[j] = poolReserveMakerAmount;
}
break;
} else {
makerTokenAmounts[i] = buyAmount;
}
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Smoothy.
/// @param smoothyInfo Smoothy information specific to this token pair.
/// @param fromTokenIdx Index of the taker token (what to sell).
/// @param toTokenIdx Index of the maker token (what to buy).
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromSmoothy(
SmoothyInfo memory smoothyInfo,
int128 fromTokenIdx,
int128 toTokenIdx,
uint256[] memory makerTokenAmounts
)
public
view
returns (uint256[] memory takerTokenAmounts)
{
// Buys not supported so approximate it.
return _sampleApproximateBuys(
ApproximateBuyQuoteOpts({
makerTokenData: abi.encode(toTokenIdx, smoothyInfo),
takerTokenData: abi.encode(fromTokenIdx, smoothyInfo),
getSellQuoteCallback: _sampleSellForApproximateBuyFromSmoothy
}),
makerTokenAmounts
);
}
function _sampleSellForApproximateBuyFromSmoothy(
bytes memory takerTokenData,
bytes memory makerTokenData,
uint256 sellAmount
)
private
view
returns (uint256 buyAmount)
{
(int128 takerTokenIdx, SmoothyInfo memory smoothyInfo) =
abi.decode(takerTokenData, (int128, SmoothyInfo));
(int128 makerTokenIdx) =
abi.decode(makerTokenData, (int128));
(bool success, bytes memory resultData) =
address(this).staticcall(abi.encodeWithSelector(
this.sampleSellsFromSmoothy.selector,
smoothyInfo,
takerTokenIdx,
makerTokenIdx,
_toSingleValueArray(sellAmount)
));
if (!success) {
return 0;
}
// solhint-disable-next-line indent
return abi.decode(resultData, (uint256[]))[0];
}
}

View File

@@ -1,412 +0,0 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./GasOverhead.sol";
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
interface IHackedERC20 {
function _setBalance(address owner, uint256 amount) external;
function _setEnabled(bool enabled) external;
}
contract SwapRevertSampler {
using LibRichErrorsV06 for bytes;
/// @dev Fixed address to register and read Gas overhead introduced by Swap revert sampling
GasOverhead private constant GAS_OVERHEAD = GasOverhead(0xDeF1000000000000000000000000000000001337);
/// @dev Maximum approximate (positive) error rate when approximating a buy quote.
uint256 private constant APPROXIMATE_BUY_TARGET_EPSILON_BPS = 0.0005e4;
/// @dev Maximum iterations to perform when approximating a buy quote.
uint256 private constant APPROXIMATE_BUY_MAX_ITERATIONS = 3;
uint256 private constant ONE_HUNDED_PERCENT_BPS = 1e4;
/// @dev Upper limit of gas to give to a single Swap call
uint256 private constant CALL_STIPEND = 2e6;
// solhint-disable no-empty-blocks
/// @dev Payable fallback to receive ETH from Kyber/WETH.
receive ()
external
payable
{ }
struct SwapRevertSamplerQuoteOpts {
// Address of the token which is being sold
address sellToken;
// Address of the token which is wanted
address buyToken;
// Data required for the bridge to execute the swap
bytes bridgeData;
// Callback to retrieve a swap quote.
function (address sellToken, address buyToken, bytes memory bridgeData, uint256 sellAmount)
external
returns (uint256)
getSwapQuoteCallback;
}
struct SwapRevertSamplerBuyQuoteOpts {
// Address of the token which is being sold
address sellToken;
// Address of the token which is wanted
address buyToken;
// Data required for the bridge to execute the SELL_TOKEN->BUY_TOKEN swap
bytes sellTokenData;
// Data required for the bridge to execute the BUY_TOKEN->SELL_TOKEN swap
bytes buyTokenData;
// Callback to retrieve a swap quote.
function (address sellToken, address buyToken, bytes memory bridgeData, uint256 sellAmount)
external
returns (uint256)
getSwapQuoteCallback;
}
function _callRevert(
bytes4 selector,
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 amountIn
)
external
{
// Clear any registered overhead
try
GAS_OVERHEAD.clearOverhead()
{ } catch { }
// Measure the gas
uint256 gasUsed = gasleft();
// Perform the sell
(bool success, bytes memory data) = address(this).call(
abi.encodeWithSelector(selector, sellToken, buyToken, bridgeData, amountIn)
);
gasUsed = gasUsed - gasleft();
// Remove any registered gas overhead
try
GAS_OVERHEAD.overhead()
returns (uint256 gasOverhead)
{
gasUsed = gasUsed - gasOverhead;
} catch { }
if (!success) {
data.rrevert();
}
// Revert with the amount bought
_revertSingleSwapSample(abi.decode(data, (uint256)), gasUsed);
}
/// @dev Mints the sell token, then performs the swap, then reverts with the amount out.
/// The SwapRevertSamplerQuoteOpts has been unrolled here as our ABI encoder cannot support
/// encoding the function
function _mintCallRevert(
bytes4 selector,
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256[] memory amountsIn
)
external
{
// We assume the amounts are ascending and that
// the underlying call can handle selling a specific amount
uint256 amountIn = amountsIn[amountsIn.length - 1];
if (sellToken == address(_getNativeWrappedToken())) {
try
IEtherTokenV06(payable(sellToken)).deposit{ value: amountIn }()
{ } catch { }
} else {
IHackedERC20 hackedSellToken = IHackedERC20(payable(sellToken));
// Enable sell token to be tracked and shadowed
try
hackedSellToken._setEnabled(true)
{ } catch { }
// Mint enough to sell
try
hackedSellToken._setBalance(address(this), amountIn)
{ } catch { }
}
// Burn any excess ETH to avoid balance issues for sources which use ETH directly
address(0).transfer(address(this).balance);
uint256[] memory amountsOut = new uint256[](amountsIn.length);
uint256[] memory gasUsed = new uint256[](amountsIn.length);
for (uint256 i = 0; i < amountsIn.length; i++) {
try
this._callRevert{gas: CALL_STIPEND}(
selector,
sellToken,
buyToken,
bridgeData,
amountsIn[i]
)
{
require(false, "Swap Sample should have reverted");
} catch (bytes memory reason) {
// Parse the reverted sample data
(amountsOut[i], gasUsed[i]) = _parseRevertedSingleSwapSample(reason);
// If we detect the amount out is 0 then we return early
// rather than continue performing excess work
// Some sources (Balancer) display issues, especially with small amounts
// Where the amountsOut can range, e.g 448,0,0,0,2476,3048,0,4279,4941,0,0,7133,
if (amountsOut[i] == 0) {
break;
}
}
}
// Revert the entire sampling
_revertSwapSample(amountsOut, gasUsed);
}
function _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts memory opts,
uint256[] memory amountsIn
)
internal
returns (uint256[] memory gasUsed, uint256[] memory amountsOut)
{
try
this._mintCallRevert(
opts.getSwapQuoteCallback.selector,
opts.sellToken,
opts.buyToken,
opts.bridgeData,
amountsIn
)
{
require(false, "Swap Sample should have reverted");
} catch (bytes memory reason) {
// Parse the reverted sample datas
(amountsOut, gasUsed) = abi.decode(reason, (uint256[], uint256[]));
}
}
function _getNativeWrappedToken()
internal
view
returns (IEtherTokenV06)
{
uint256 chainId;
assembly {
chainId := chainid()
}
address token;
if (chainId == 1) {
// Ethereum Mainnet
token = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
} else if (chainId == 3) {
// Ropsten
token = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
} else if (chainId == 4) {
// Rinkeby
token = 0xc778417E063141139Fce010982780140Aa0cD5Ab;
} else if (chainId == 42) {
// Kovan
token = 0xd0A1E359811322d97991E03f863a0C30C2cF029C;
} else if (chainId == 56) {
// BSC
token = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c;
} else if (chainId == 137) {
// Polygon
token = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
} else if (chainId == 1337) {
// 0x Ganache
token = 0x0B1ba0af832d7C05fD64161E0Db78E85978E8082;
}
if (token == address(0)) {
revert("No native wrapped token");
}
return IEtherTokenV06(token);
}
function _revertSingleSwapSample(
uint256 amount,
uint256 gasUsed
)
internal
{
// Revert it so there is no state change
assembly {
mstore(0, amount)
mstore(32, gasUsed)
revert(0, 64)
}
}
function _revertSwapSample(
uint256[] memory amounts,
uint256[] memory gasUsed
)
internal
{
bytes memory data = abi.encode(amounts, gasUsed);
// Revert it so there is no state change
assembly {
revert(add(data, 32), mload(data))
}
}
/// @dev Parses the reverted swap sample data. If no amount
/// is decoded, 0 is returned.
/// @param reason the string which contains the possible
/// sample amount
/// @return the decoded sample amount or 0
/// @return the gas used in the sample
function _parseRevertedSingleSwapSample(
bytes memory reason
)
internal
pure
returns (uint256, uint256)
{
if (reason.length != 64) {
return (0,0);
}
return abi.decode(reason, (uint256, uint256));
}
function _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts memory opts,
uint256[] memory makerTokenAmounts
)
internal
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
{
if (makerTokenAmounts.length == 0) {
return (gasUsed, takerTokenAmounts);
}
takerTokenAmounts = new uint256[](makerTokenAmounts.length);
gasUsed = new uint256[](makerTokenAmounts.length);
uint256[] memory sellAmounts = new uint256[](1);
sellAmounts[0] = makerTokenAmounts[0];
SwapRevertSamplerQuoteOpts memory sellOpts = SwapRevertSamplerQuoteOpts({
sellToken: opts.sellToken,
buyToken: opts.buyToken,
bridgeData: opts.sellTokenData,
getSwapQuoteCallback: opts.getSwapQuoteCallback
});
SwapRevertSamplerQuoteOpts memory buyOpts = SwapRevertSamplerQuoteOpts({
sellToken: opts.buyToken,
buyToken: opts.sellToken,
bridgeData: opts.buyTokenData,
getSwapQuoteCallback: opts.getSwapQuoteCallback
});
// Inverted, perform a sell of the token the user wants to buy
(, sellAmounts) = _sampleSwapQuotesRevert(buyOpts, sellAmounts);
if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
return (gasUsed, takerTokenAmounts);
}
uint256[] memory buyAmounts;
// Sell of the token the user wishes to dispose, see how much we buy
(, buyAmounts) = _sampleSwapQuotesRevert(sellOpts, sellAmounts);
if (buyAmounts.length == 0 || buyAmounts[0] == 0) {
return (gasUsed, takerTokenAmounts);
}
for (uint256 i = 0; i < makerTokenAmounts.length; i++) {
uint256[] memory _gasUsed;
for (uint256 iter = 0; iter < APPROXIMATE_BUY_MAX_ITERATIONS; iter++) {
// adjustedSellAmount = previousSellAmount * (target/actual) * JUMP_MULTIPLIER
sellAmounts[0] = _safeGetPartialAmountCeil(
makerTokenAmounts[i],
buyAmounts[0],
sellAmounts[0]
);
if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
break;
}
sellAmounts[0] = _safeGetPartialAmountCeil(
(ONE_HUNDED_PERCENT_BPS + APPROXIMATE_BUY_TARGET_EPSILON_BPS),
ONE_HUNDED_PERCENT_BPS,
sellAmounts[0]
);
if (sellAmounts.length == 0 || sellAmounts[0] == 0) {
break;
}
uint256[] memory _buyAmounts;
(_gasUsed, _buyAmounts) = _sampleSwapQuotesRevert(sellOpts, sellAmounts);
if (_buyAmounts.length == 0 || _buyAmounts[0] == 0) {
break;
}
// We re-use buyAmount next iteration, only assign if it is
// non zero
buyAmounts = _buyAmounts;
// If we've reached our goal, exit early
if (buyAmounts[0] >= makerTokenAmounts[i]) {
uint256 eps =
(buyAmounts[0] - makerTokenAmounts[i]) * ONE_HUNDED_PERCENT_BPS /
makerTokenAmounts[i];
if (eps <= APPROXIMATE_BUY_TARGET_EPSILON_BPS) {
break;
}
}
}
// We've encountered reverts, so bail
if (_gasUsed.length == 0 || _gasUsed[0] == 0) {
return (gasUsed, takerTokenAmounts);
}
if (buyAmounts.length > 0) {
gasUsed[i] = _gasUsed[0];
// We do our best to close in on the requested amount, but we can either over buy or under buy and exit
// if we hit a max iteration limit
// We scale the sell amount to get the approximate target
takerTokenAmounts[i] = _safeGetPartialAmountCeil(
makerTokenAmounts[i],
buyAmounts[0],
sellAmounts[0]
);
}
}
}
function _safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
)
internal
view
returns (uint256 partialAmount)
{
if (numerator == 0 || target == 0 || denominator == 0) return 0;
uint256 c = numerator * target;
if (c / numerator != target) return 0;
return (c + (denominator - 1)) / denominator;
}
}

View File

@@ -40,11 +40,10 @@ contract TwoHopSampler {
returns (
HopInfo memory firstHop,
HopInfo memory secondHop,
uint256 intermediateAssetAmount,
uint256 buyAmount
)
{
intermediateAssetAmount = 0;
uint256 intermediateAssetAmount = 0;
for (uint256 i = 0; i != firstHopCalls.length; ++i) {
firstHopCalls[i].writeUint256(firstHopCalls[i].length - 32, sellAmount);
(bool didSucceed, bytes memory returnData) = address(this).call(firstHopCalls[i]);
@@ -58,7 +57,7 @@ contract TwoHopSampler {
}
}
if (intermediateAssetAmount == 0) {
return (firstHop, secondHop, intermediateAssetAmount, buyAmount);
return (firstHop, secondHop, buyAmount);
}
for (uint256 j = 0; j != secondHopCalls.length; ++j) {
secondHopCalls[j].writeUint256(secondHopCalls[j].length - 32, intermediateAssetAmount);

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -20,43 +20,32 @@
pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswap.sol";
import "./SwapRevertSampler.sol";
import "./interfaces/IUniswapExchangeQuotes.sol";
import "./SamplerUtils.sol";
interface IUniswapExchangeFactory {
/// @dev Get the exchange for a token.
/// @param tokenAddress The address of the token contract.
function getExchange(address tokenAddress)
external
view
returns (address);
}
contract UniswapSampler is
MixinUniswap,
SwapRevertSampler
SamplerUtils
{
constructor(IEtherTokenV06 weth)
public
MixinUniswap(weth)
{ }
function sampleSwapFromUniswap(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeUniswapInternal(
_getNativeWrappedToken(),
IERC20TokenV06(sellToken),
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Gas limit for Uniswap calls.
uint256 constant private UNISWAP_CALL_GAS = 150e3; // 150k
/// @dev Sample sell quotes from Uniswap.
/// @param router Address of the Uniswap Router
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromUniswap(
@@ -66,24 +55,59 @@ contract UniswapSampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
bridgeData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromUniswap
}),
takerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
for (uint256 i = 0; i < numSamples; i++) {
bool didSucceed = true;
if (makerToken == address(0)) {
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthInputPrice.selector,
takerTokenAmounts[i]
);
} else if (takerToken == address(0)) {
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenInputPrice.selector,
takerTokenAmounts[i]
);
} else {
uint256 ethBought;
(ethBought, didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthInputPrice.selector,
takerTokenAmounts[i]
);
if (ethBought != 0) {
(makerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenInputPrice.selector,
ethBought
);
} else {
makerTokenAmounts[i] = 0;
}
}
// Break early if amounts are 0
if (!didSucceed || makerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Sample buy quotes from Uniswap.
/// @param takerToken Address of the taker token (what to sell).
/// @param makerToken Address of the maker token (what to buy).
/// @param makerTokenAmounts Maker token sell amount for each sample.
/// @return gasUsed gas consumed in each sample sell
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswap(
@@ -93,18 +117,98 @@ contract UniswapSampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: takerToken,
buyToken: makerToken,
sellTokenData: abi.encode(router),
buyTokenData: abi.encode(router),
getSwapQuoteCallback: this.sampleSwapFromUniswap
}),
makerTokenAmounts
);
_assertValidPair(makerToken, takerToken);
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
IUniswapExchangeQuotes takerTokenExchange = takerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, takerToken);
IUniswapExchangeQuotes makerTokenExchange = makerToken == address(0) ?
IUniswapExchangeQuotes(0) : _getUniswapExchange(router, makerToken);
for (uint256 i = 0; i < numSamples; i++) {
bool didSucceed = true;
if (makerToken == address(0)) {
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthOutputPrice.selector,
makerTokenAmounts[i]
);
} else if (takerToken == address(0)) {
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenOutputPrice.selector,
makerTokenAmounts[i]
);
} else {
uint256 ethSold;
(ethSold, didSucceed) = _callUniswapExchangePriceFunction(
address(makerTokenExchange),
makerTokenExchange.getEthToTokenOutputPrice.selector,
makerTokenAmounts[i]
);
if (ethSold != 0) {
(takerTokenAmounts[i], didSucceed) = _callUniswapExchangePriceFunction(
address(takerTokenExchange),
takerTokenExchange.getTokenToEthOutputPrice.selector,
ethSold
);
} else {
takerTokenAmounts[i] = 0;
}
}
// Break early if amounts are 0
if (!didSucceed || takerTokenAmounts[i] == 0) {
break;
}
}
}
/// @dev Gracefully calls a Uniswap pricing function.
/// @param uniswapExchangeAddress Address of an `IUniswapExchangeQuotes` exchange.
/// @param functionSelector Selector of the target function.
/// @param inputAmount Quantity parameter particular to the pricing function.
/// @return outputAmount The returned amount from the function call. Will be
/// zero if the call fails or if `uniswapExchangeAddress` is zero.
function _callUniswapExchangePriceFunction(
address uniswapExchangeAddress,
bytes4 functionSelector,
uint256 inputAmount
)
private
view
returns (uint256 outputAmount, bool didSucceed)
{
if (uniswapExchangeAddress == address(0)) {
return (outputAmount, didSucceed);
}
bytes memory resultData;
(didSucceed, resultData) =
uniswapExchangeAddress.staticcall.gas(UNISWAP_CALL_GAS)(
abi.encodeWithSelector(
functionSelector,
inputAmount
));
if (didSucceed) {
outputAmount = abi.decode(resultData, (uint256));
}
}
/// @dev Retrive an existing Uniswap exchange contract.
/// Throws if the exchange does not exist.
/// @param router Address of the Uniswap router.
/// @param tokenAddress Address of the token contract.
/// @return exchange `IUniswapExchangeQuotes` for the token.
function _getUniswapExchange(address router, address tokenAddress)
private
view
returns (IUniswapExchangeQuotes exchange)
{
exchange = IUniswapExchangeQuotes(
address(IUniswapExchangeFactory(router)
.getExchange(tokenAddress))
);
}
}

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright 2021 ZeroEx Intl.
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.
@@ -21,36 +21,17 @@ pragma solidity ^0.6;
pragma experimental ABIEncoderV2;
import "./interfaces/IUniswapV2Router01.sol";
import "@0x/contracts-zero-ex/contracts/src/transformers/bridges/mixins/MixinUniswapV2.sol";
import "./SwapRevertSampler.sol";
contract UniswapV2Sampler is
MixinUniswapV2,
SwapRevertSampler
contract UniswapV2Sampler
{
function sampleSwapFromUniswapV2(
address sellToken,
address buyToken,
bytes memory bridgeData,
uint256 takerTokenAmount
)
external
returns (uint256)
{
return _tradeUniswapV2(
IERC20TokenV06(buyToken),
takerTokenAmount,
bridgeData
);
}
/// @dev Gas limit for UniswapV2 calls.
uint256 constant private UNISWAPV2_CALL_GAS = 150e3; // 150k
/// @dev Sample sell quotes from UniswapV2.
/// @param router Router to look up tokens and amounts
/// @param path Token route. Should be takerToken -> makerToken
/// @param takerTokenAmounts Taker token sell amount for each sample.
/// @return gasUsed gas consumed for each sample amount
/// @return makerTokenAmounts Maker amounts bought at each taker token
/// amount.
function sampleSellsFromUniswapV2(
@@ -59,24 +40,34 @@ contract UniswapV2Sampler is
uint256[] memory takerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory makerTokenAmounts)
view
returns (uint256[] memory makerTokenAmounts)
{
(gasUsed, makerTokenAmounts) = _sampleSwapQuotesRevert(
SwapRevertSamplerQuoteOpts({
sellToken: path[0],
buyToken: path[path.length - 1],
bridgeData: abi.encode(router, path),
getSwapQuoteCallback: this.sampleSwapFromUniswapV2
}),
takerTokenAmounts
);
uint256 numSamples = takerTokenAmounts.length;
makerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IUniswapV2Router01(router).getAmountsOut
{gas: UNISWAPV2_CALL_GAS}
(takerTokenAmounts[i], path)
returns (uint256[] memory amounts)
{
makerTokenAmounts[i] = amounts[path.length - 1];
// Break early if there are 0 amounts
if (makerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
}
/// @dev Sample buy quotes from UniswapV2.
/// @param router Router to look up tokens and amounts
/// @param path Token route. Should be takerToken -> makerToken.
/// @param makerTokenAmounts Maker token buy amount for each sample.
/// @return gasUsed gas consumed for each sample amount
/// @return takerTokenAmounts Taker amounts sold at each maker token
/// amount.
function sampleBuysFromUniswapV2(
@@ -85,22 +76,27 @@ contract UniswapV2Sampler is
uint256[] memory makerTokenAmounts
)
public
returns (uint256[] memory gasUsed, uint256[] memory takerTokenAmounts)
view
returns (uint256[] memory takerTokenAmounts)
{
address[] memory reversedPath = new address[](path.length);
for (uint256 i = 0; i < path.length; ++i) {
reversedPath[i] = path[path.length - i - 1];
uint256 numSamples = makerTokenAmounts.length;
takerTokenAmounts = new uint256[](numSamples);
for (uint256 i = 0; i < numSamples; i++) {
try
IUniswapV2Router01(router).getAmountsIn
{gas: UNISWAPV2_CALL_GAS}
(makerTokenAmounts[i], path)
returns (uint256[] memory amounts)
{
takerTokenAmounts[i] = amounts[0];
// Break early if there are 0 amounts
if (takerTokenAmounts[i] == 0) {
break;
}
} catch (bytes memory) {
// Swallow failures, leaving all results as zero.
break;
}
}
(gasUsed, takerTokenAmounts) = _sampleSwapApproximateBuys(
SwapRevertSamplerBuyQuoteOpts({
sellToken: path[0],
buyToken: path[path.length - 1],
sellTokenData: abi.encode(router, path),
buyTokenData: abi.encode(router, reversedPath),
getSwapQuoteCallback: this.sampleSwapFromUniswapV2
}),
makerTokenAmounts
);
}
}

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