Compare commits
59 Commits
@0x/contra
...
@0x/contra
Author | SHA1 | Date | |
---|---|---|---|
|
aae46bef84 | ||
|
a0bb36954e | ||
|
4f32f3174f | ||
|
b84107d142 | ||
|
b46eeadc64 | ||
|
692231c2ea | ||
|
3a1becc3e4 | ||
|
3653e2d7f9 | ||
|
65def38d98 | ||
|
859c36cb10 | ||
|
3e34386812 | ||
|
a35af11981 | ||
|
59eabec71e | ||
|
bf4c7e7d50 | ||
|
fd098ca4df | ||
|
c360f8d8fd | ||
|
b358559421 | ||
|
df5aad8e8e | ||
|
8dbf79db59 | ||
|
1839608e84 | ||
|
6e3e795b8b | ||
|
d9c410a7e3 | ||
|
609727afe8 | ||
|
8a5c74c0b4 | ||
|
cd93f3b07e | ||
|
8397b12de6 | ||
|
3f85acec3a | ||
|
d6a9e3d600 | ||
|
361569ac2f | ||
|
719664c145 | ||
|
f800d6c24c | ||
|
a9a81bcafb | ||
|
4280307a15 | ||
|
7b57d3ae51 | ||
|
8a8a5bbda0 | ||
|
76987c8db1 | ||
|
6f8971cc42 | ||
|
71ab882143 | ||
|
5a4961c8d9 | ||
|
4c4fb99d87 | ||
|
872abf09e9 | ||
|
f10bfe7d04 | ||
|
a74d8deff3 | ||
|
835ee4e8de | ||
|
63ec42303f | ||
|
f789aebddc | ||
|
efd83be779 | ||
|
603bc1d51c | ||
|
32a930a7fc | ||
|
f464bf68d7 | ||
|
ebdc4fb509 | ||
|
7580719586 | ||
|
aba9db2be7 | ||
|
a6680411c8 | ||
|
0d0e87de94 | ||
|
ccf2000c09 | ||
|
3eb2e0f56a | ||
|
d07c7d5b69 | ||
|
adf6684c29 |
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "3.7.19",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "3.7.18",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "3.7.17",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "3.7.16",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "3.7.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "3.7.14",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.7.19 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.18 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.17 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.16 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.15 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.14 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "3.7.14",
|
||||
"version": "3.7.19",
|
||||
"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.0",
|
||||
"@0x/contract-wrappers": "^13.17.4",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@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.32",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/contracts-erc1155": "^2.1.37",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "1.1.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "1.1.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "1.1.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "1.1.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "1.1.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "1.1.32",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.1.37 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.36 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.35 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.34 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.33 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.32 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-broker",
|
||||
"version": "1.1.32",
|
||||
"version": "1.1.37",
|
||||
"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.14",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/contracts-exchange": "^3.2.33",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@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.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"ethereum-types": "^3.5.0"
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "3.1.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "3.1.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "3.1.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "3.1.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "3.1.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "3.1.33",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.38 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.37 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.36 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.35 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.34 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.33 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "3.1.33",
|
||||
"version": "3.1.38",
|
||||
"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.14",
|
||||
"@0x/contracts-dev-utils": "^1.3.31",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@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.3.0",
|
||||
"@0x/contracts-exchange": "^3.2.33",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/json-schemas": "^6.1.3",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "1.3.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "1.3.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "1.3.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "1.3.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "1.3.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "1.3.31",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.3.36 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.35 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.34 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.33 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.32 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.31 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "1.3.31",
|
||||
"version": "1.3.36",
|
||||
"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.14",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "2.1.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "2.1.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "2.1.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "2.1.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "2.1.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "2.1.32",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.1.37 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.36 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.35 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.34 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.33 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.32 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "2.1.32",
|
||||
"version": "2.1.37",
|
||||
"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.11",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@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.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"@0x/web3-wrapper": "^7.5.3",
|
||||
"lodash": "^4.17.11"
|
||||
|
@@ -1,4 +1,50 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "3.3.16",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "3.3.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add ethers as a dependency",
|
||||
"pr": 305
|
||||
}
|
||||
],
|
||||
"timestamp": 1628665757
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "3.3.14",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "3.3.13",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "3.3.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "3.3.11",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.3.16 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.15 - _August 11, 2021_
|
||||
|
||||
* Add ethers as a dependency (#305)
|
||||
|
||||
## v3.3.14 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.13 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.12 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.11 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -28,7 +28,7 @@ library LibERC20TokenV06 {
|
||||
bytes constant private DECIMALS_CALL_DATA = hex"313ce567";
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).approve()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param allowance The allowance to set.
|
||||
@@ -49,7 +49,7 @@ library LibERC20TokenV06 {
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).approve()` and sets the allowance to the
|
||||
/// maximum if the current approval is not already >= an amount.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param spender The address that receives an allowance.
|
||||
/// @param amount The minimum allowance needed.
|
||||
@@ -66,7 +66,7 @@ library LibERC20TokenV06 {
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).transfer()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param to The address that receives the tokens
|
||||
/// @param amount Number of tokens to transfer.
|
||||
@@ -86,7 +86,7 @@ library LibERC20TokenV06 {
|
||||
}
|
||||
|
||||
/// @dev Calls `IERC20TokenV06(token).transferFrom()`.
|
||||
/// Reverts if the result fails `isSuccessfulResult()` or the call reverts.
|
||||
/// Reverts if the return data is invalid or the call reverts.
|
||||
/// @param token The address of the token contract.
|
||||
/// @param from The owner of the tokens.
|
||||
/// @param to The address that receives the tokens
|
||||
@@ -168,27 +168,6 @@ library LibERC20TokenV06 {
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Check if the data returned by a non-static call to an ERC20 token
|
||||
/// is a successful result. Supported functions are `transfer()`,
|
||||
/// `transferFrom()`, and `approve()`.
|
||||
/// @param resultData The raw data returned by a non-static call to the ERC20 token.
|
||||
/// @return isSuccessful Whether the result data indicates success.
|
||||
function isSuccessfulResult(bytes memory resultData)
|
||||
internal
|
||||
pure
|
||||
returns (bool isSuccessful)
|
||||
{
|
||||
if (resultData.length == 0) {
|
||||
return true;
|
||||
}
|
||||
if (resultData.length >= 32) {
|
||||
uint256 result = LibBytesV06.readUint256(resultData, 0);
|
||||
if (result == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes a call on address `target` with calldata `callData`
|
||||
/// and asserts that either nothing was returned or a single boolean
|
||||
/// was returned equal to `true`.
|
||||
@@ -201,9 +180,31 @@ library LibERC20TokenV06 {
|
||||
private
|
||||
{
|
||||
(bool didSucceed, bytes memory resultData) = target.call(callData);
|
||||
if (didSucceed && isSuccessfulResult(resultData)) {
|
||||
// Revert if the call reverted.
|
||||
if (!didSucceed) {
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
// If we get back 0 returndata, this may be a non-standard ERC-20 that
|
||||
// does not return a boolean. Check that it at least contains code.
|
||||
if (resultData.length == 0) {
|
||||
uint256 size;
|
||||
assembly { size := extcodesize(target) }
|
||||
require(size > 0, "invalid token address, contains no code");
|
||||
return;
|
||||
}
|
||||
// If we get back at least 32 bytes, we know the target address
|
||||
// contains code, and we assume it is a token that returned a boolean
|
||||
// success value, which must be true.
|
||||
if (resultData.length >= 32) {
|
||||
uint256 result = LibBytesV06.readUint256(resultData, 0);
|
||||
if (result == 1) {
|
||||
return;
|
||||
} else {
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
||||
// If 0 < returndatasize < 32, the target is a contract, but not a
|
||||
// valid token.
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc20",
|
||||
"version": "3.3.11",
|
||||
"version": "3.3.16",
|
||||
"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.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
@@ -82,7 +82,8 @@
|
||||
"typescript": "4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.0"
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"ethers": "~4.0.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "3.1.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "3.1.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "3.1.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "3.1.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "3.1.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "3.1.32",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.1.37 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.36 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.35 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.34 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.33 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.32 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "3.1.32",
|
||||
"version": "3.1.37",
|
||||
"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.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "4.2.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "4.2.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "4.2.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "4.2.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "4.2.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "4.2.33",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.2.38 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.37 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.36 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.35 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.34 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.33 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-forwarder",
|
||||
"version": "4.2.33",
|
||||
"version": "4.2.38",
|
||||
"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.14",
|
||||
"@0x/contracts-dev-utils": "^1.3.31",
|
||||
"@0x/contracts-erc1155": "^2.1.32",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/contracts-exchange": "^3.2.33",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-erc1155": "^2.1.37",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/ts-doc-gen": "^0.0.28",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "4.3.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "4.3.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "4.3.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "4.3.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "4.3.33",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "4.3.32",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.3.37 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.36 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.35 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.34 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.33 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.32 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-libs",
|
||||
"version": "4.3.32",
|
||||
"version": "4.3.37",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -81,9 +81,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "3.2.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "3.2.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "3.2.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "3.2.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "3.2.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "3.2.33",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v3.2.38 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.37 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.36 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.35 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.34 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.33 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange",
|
||||
"version": "3.2.33",
|
||||
"version": "3.2.38",
|
||||
"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.14",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-multisig": "^4.1.33",
|
||||
"@0x/contracts-staking": "^2.0.40",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-multisig": "^4.1.38",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@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.31",
|
||||
"@0x/contracts-erc1155": "^2.1.32",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-erc1155": "^2.1.37",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"lodash": "^4.17.11"
|
||||
},
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "6.2.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "6.2.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "6.2.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "6.2.29",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "6.2.28",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "6.2.27",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v6.2.32 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.31 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.30 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.29 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.28 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.27 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-extensions",
|
||||
"version": "6.2.27",
|
||||
"version": "6.2.32",
|
||||
"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.14",
|
||||
"@0x/contracts-dev-utils": "^1.3.31",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/contracts-exchange": "^3.2.33",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@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.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"ethereum-types": "^3.5.0"
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-integrations",
|
||||
"version": "2.7.48",
|
||||
"version": "2.7.64",
|
||||
"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.3.0",
|
||||
"@0x/contract-wrappers": "^13.17.0",
|
||||
"@0x/contracts-broker": "^1.1.32",
|
||||
"@0x/contracts-coordinator": "^3.1.33",
|
||||
"@0x/contracts-dev-utils": "^1.3.31",
|
||||
"@0x/contracts-exchange-forwarder": "^4.2.33",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-extensions": "^6.2.27",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/contract-wrappers": "^13.17.4",
|
||||
"@0x/contracts-broker": "^1.1.37",
|
||||
"@0x/contracts-coordinator": "^3.1.38",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-exchange-forwarder": "^4.2.38",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-extensions": "^6.2.32",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/coordinator-server": "^1.0.5",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/migrations": "^8.0.9",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/protocol-utils": "^1.7.0",
|
||||
"@0x/migrations": "^8.1.1",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/protocol-utils": "^1.8.2",
|
||||
"@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.17.1",
|
||||
"@0x/asset-swapper": "^16.25.0",
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.14",
|
||||
"@0x/contracts-erc1155": "^2.1.32",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-erc721": "^3.1.32",
|
||||
"@0x/contracts-exchange": "^3.2.33",
|
||||
"@0x/contracts-multisig": "^4.1.33",
|
||||
"@0x/contracts-staking": "^2.0.40",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-zero-ex": "^0.25.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc1155": "^2.1.37",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-erc721": "^3.1.37",
|
||||
"@0x/contracts-exchange": "^3.2.38",
|
||||
"@0x/contracts-multisig": "^4.1.38",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-zero-ex": "^0.28.0",
|
||||
"@0x/subproviders": "^6.5.3",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "4.1.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "4.1.37",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "4.1.36",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "4.1.35",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "4.1.34",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "4.1.33",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.1.38 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.37 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.36 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.35 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.34 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.33 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-multisig",
|
||||
"version": "4.1.33",
|
||||
"version": "4.1.38",
|
||||
"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.14",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "2.0.45",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "2.0.44",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "2.0.43",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "2.0.42",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "2.0.41",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "2.0.40",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v2.0.45 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.44 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.43 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.42 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.41 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.40 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-staking",
|
||||
"version": "2.0.40",
|
||||
"version": "2.0.45",
|
||||
"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.14",
|
||||
"@0x/contracts-dev-utils": "^1.3.31",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contracts-exchange-libs": "^4.3.32",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-dev-utils": "^1.3.36",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-exchange-libs": "^4.3.37",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-utils": "^4.7.11",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@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.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"ethereum-types": "^3.5.0",
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "5.4.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "5.4.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "5.4.6",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "5.4.5",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "5.4.4",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "5.4.3",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v5.4.8 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.7 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.6 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.5 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.4 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.3 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.3",
|
||||
"version": "5.4.8",
|
||||
"engines": {
|
||||
"node": ">=6.12"
|
||||
},
|
||||
@@ -44,10 +44,10 @@
|
||||
"dependencies": {
|
||||
"@0x/assert": "^3.0.27",
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"@0x/contract-addresses": "^6.3.0",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/json-schemas": "^6.1.3",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-coverage": "^4.0.37",
|
||||
"@0x/sol-profiler": "^4.1.27",
|
||||
"@0x/sol-trace": "^3.0.37",
|
||||
|
@@ -40,6 +40,6 @@ export function verifyEventsFromLogs<TEventArgs>(
|
||||
const _logs = filterLogsToArguments<TEventArgs>(logs, eventName);
|
||||
expect(_logs.length, `Number of ${eventName} events emitted`).to.eq(expectedEvents.length);
|
||||
_logs.forEach((log, index) => {
|
||||
expect(log, `${eventName} event ${index}`).to.deep.equal(expectedEvents[index]);
|
||||
expect(log, `${eventName} event ${index}`).to.deep.equal({ ...log, ...expectedEvents[index] });
|
||||
});
|
||||
}
|
||||
|
@@ -1,4 +1,50 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "1.3.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "1.3.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added proposal 1 params and test",
|
||||
"pr": 298
|
||||
}
|
||||
],
|
||||
"timestamp": 1628225642
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "1.2.3",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "1.2.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "1.2.1",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v1.3.2 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.1 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.0 - _August 6, 2021_
|
||||
|
||||
* Added proposal 1 params and test (#298)
|
||||
|
||||
## v1.2.3 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.2 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.2.1 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -21,13 +21,10 @@ pragma solidity ^0.6.12;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "./IStaking.sol";
|
||||
|
||||
|
||||
contract DefaultPoolOperator {
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
|
||||
// Immutables
|
||||
IStaking public immutable stakingProxy;
|
||||
IERC20TokenV06 public immutable weth;
|
||||
@@ -57,7 +54,7 @@ contract DefaultPoolOperator {
|
||||
function returnStakingRewards()
|
||||
external
|
||||
{
|
||||
uint256 wethBalance = weth.compatBalanceOf(address(this));
|
||||
weth.compatTransfer(address(stakingProxy), wethBalance);
|
||||
uint256 wethBalance = weth.balanceOf(address(this));
|
||||
weth.transfer(address(stakingProxy), wethBalance);
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-treasury",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.2",
|
||||
"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.3.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.14",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.19",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-staking": "^2.0.40",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-staking": "^2.0.45",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@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.0",
|
||||
"@0x/protocol-utils": "^1.8.2",
|
||||
"@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
@@ -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,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,4 +1,49 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1629079369,
|
||||
"version": "4.7.16",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "4.7.15",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1628225642,
|
||||
"version": "4.7.14",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1624356181,
|
||||
"version": "4.7.13",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "4.7.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1622609597,
|
||||
"version": "4.7.11",
|
||||
|
@@ -5,6 +5,26 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v4.7.16 - _August 16, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.15 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.14 - _August 6, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.13 - _June 22, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.12 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.11 - _June 2, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.7.11",
|
||||
"version": "4.7.16",
|
||||
"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.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@0x/types": "^3.3.3",
|
||||
|
@@ -1,4 +1,63 @@
|
||||
[
|
||||
{
|
||||
"version": "0.28.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Transfer output tokens in TransformERC20Feature",
|
||||
"pr": 279
|
||||
},
|
||||
{
|
||||
"note": "Add support for takerToken=0xeee... in OtcOrdersFeature",
|
||||
"pr": 287
|
||||
},
|
||||
{
|
||||
"note": "Add support for OTC orders in MultiplexFeature",
|
||||
"pr": 287
|
||||
},
|
||||
{
|
||||
"note": "Multiplex v2: Refactor into multiple files, add ETH support, and other miscellanea",
|
||||
"pr": 263
|
||||
}
|
||||
],
|
||||
"timestamp": 1629079369
|
||||
},
|
||||
{
|
||||
"timestamp": 1628665757,
|
||||
"version": "0.27.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.27.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add `Clipper` as a custom liquidity source"
|
||||
}
|
||||
],
|
||||
"timestamp": 1628225642
|
||||
},
|
||||
{
|
||||
"version": "0.26.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add Lido stETH deposit integration",
|
||||
"pr": 260
|
||||
}
|
||||
],
|
||||
"timestamp": 1624356181
|
||||
},
|
||||
{
|
||||
"timestamp": 1623382456,
|
||||
"version": "0.25.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.25.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,29 @@ Edit the package's CHANGELOG.json file only.
|
||||
|
||||
CHANGELOG
|
||||
|
||||
## v0.28.0 - _August 16, 2021_
|
||||
|
||||
* Transfer output tokens in TransformERC20Feature (#279)
|
||||
* Add support for takerToken=0xeee... in OtcOrdersFeature (#287)
|
||||
* Add support for OTC orders in MultiplexFeature (#287)
|
||||
* Multiplex v2: Refactor into multiple files, add ETH support, and other miscellanea (#263)
|
||||
|
||||
## v0.27.1 - _August 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## 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)
|
||||
|
||||
## v0.25.1 - _June 11, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.25.0 - _June 2, 2021_
|
||||
|
||||
* Add OtcOrdersFeature (#244)
|
||||
|
@@ -88,23 +88,6 @@ library LibNativeOrdersRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function OrderNotSignedByTakerError(
|
||||
bytes32 orderHash,
|
||||
address signer,
|
||||
address taker
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
bytes4(keccak256("OrderNotSignedByTakerError(bytes32,address,address)")),
|
||||
orderHash,
|
||||
signer,
|
||||
taker
|
||||
);
|
||||
}
|
||||
|
||||
function InvalidSignerError(
|
||||
address maker,
|
||||
address signer
|
||||
|
@@ -48,7 +48,7 @@ contract BatchFillNativeOrdersFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "BatchFill";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||
|
||||
constructor(address zeroExAddress)
|
||||
public
|
||||
@@ -170,6 +170,8 @@ contract BatchFillNativeOrdersFeature is
|
||||
orders[i],
|
||||
signatures[i],
|
||||
takerTokenFillAmounts[i],
|
||||
msg.sender,
|
||||
false,
|
||||
msg.sender
|
||||
)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
|
@@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MetaTransactions";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 1);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
|
||||
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
||||
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
|
||||
"MetaTransactionData("
|
||||
@@ -415,7 +415,9 @@ contract MetaTransactionsFeature is
|
||||
outputToken: args.outputToken,
|
||||
inputTokenAmount: args.inputTokenAmount,
|
||||
minOutputTokenAmount: args.minOutputTokenAmount,
|
||||
transformations: args.transformations
|
||||
transformations: args.transformations,
|
||||
useSelfBalance: false,
|
||||
recipient: state.mtx.signer
|
||||
})
|
||||
),
|
||||
state.mtx.value
|
||||
@@ -498,7 +500,9 @@ contract MetaTransactionsFeature is
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
state.mtx.signer // taker is mtx signer
|
||||
state.mtx.signer, // taker is mtx signer
|
||||
false,
|
||||
state.mtx.signer
|
||||
),
|
||||
state.mtx.value
|
||||
);
|
||||
|
@@ -1,820 +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.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../external/ILiquidityProviderSandbox.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../fixins/FixinEIP712.sol";
|
||||
import "../fixins/FixinTokenSpender.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../transformers/LibERC20Transformer.sol";
|
||||
import "../vendor/ILiquidityProvider.sol";
|
||||
import "../vendor/IUniswapV2Pair.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IMultiplexFeature.sol";
|
||||
import "./interfaces/INativeOrdersFeature.sol";
|
||||
import "./interfaces/ITransformERC20Feature.sol";
|
||||
import "./interfaces/IUniswapV3Feature.sol";
|
||||
import "./libs/LibNativeOrder.sol";
|
||||
|
||||
|
||||
/// @dev This feature enables efficient batch and multi-hop trades
|
||||
/// using different liquidity sources.
|
||||
contract MultiplexFeature is
|
||||
IFeature,
|
||||
IMultiplexFeature,
|
||||
FixinCommon,
|
||||
FixinEIP712,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibERC20Transformer for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint128;
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MultiplexFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||
|
||||
/// @dev The WETH token contract.
|
||||
IEtherTokenV06 private immutable weth;
|
||||
/// @dev The sandbox contract address.
|
||||
ILiquidityProviderSandbox public immutable sandbox;
|
||||
// address of the UniswapV2Factory contract.
|
||||
address private constant UNISWAP_FACTORY = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;
|
||||
// address of the (Sushiswap) UniswapV2Factory contract.
|
||||
address private constant SUSHISWAP_FACTORY = 0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac;
|
||||
// Init code hash of the UniswapV2Pair contract.
|
||||
uint256 private constant UNISWAP_PAIR_INIT_CODE_HASH = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;
|
||||
// Init code hash of the (Sushiswap) UniswapV2Pair contract.
|
||||
uint256 private constant SUSHISWAP_PAIR_INIT_CODE_HASH = 0xe18a34eb0e04b04f7a0ac29a6e80748dca96319b42c54d679cb821dca90c6303;
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
IEtherTokenV06 weth_,
|
||||
ILiquidityProviderSandbox sandbox_
|
||||
)
|
||||
public
|
||||
FixinEIP712(zeroExAddress)
|
||||
{
|
||||
weth = weth_;
|
||||
sandbox = sandbox_;
|
||||
}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
/// @return success `LibMigrate.SUCCESS` on success.
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.batchFill.selector);
|
||||
_registerFeatureFunction(this.multiHopFill.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Executes a batch of fills selling `fillData.inputToken`
|
||||
/// for `fillData.outputToken` in sequence. Refer to the
|
||||
/// internal variant `_batchFill` for the allowed nested
|
||||
/// operations.
|
||||
/// @param fillData Encodes the input/output tokens, the sell
|
||||
/// amount, and the nested operations for this batch fill.
|
||||
/// @param minBuyAmount The minimum amount of `fillData.outputToken`
|
||||
/// to buy. Reverts if this amount is not met.
|
||||
/// @return outputTokenAmount The amount of the output token bought.
|
||||
function batchFill(
|
||||
BatchFillData memory fillData,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
override
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
// Cache the sender's balance of the output token.
|
||||
outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender);
|
||||
// Cache the contract's ETH balance prior to this call.
|
||||
uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value);
|
||||
|
||||
// Perform the batch fill.
|
||||
_batchFill(fillData);
|
||||
|
||||
// The `outputTokenAmount` returned by `_batchFill` may not
|
||||
// be fully accurate (e.g. due to some janky token).
|
||||
outputTokenAmount = fillData.outputToken.getTokenBalanceOf(msg.sender)
|
||||
.safeSub(outputTokenAmount);
|
||||
require(
|
||||
outputTokenAmount >= minBuyAmount,
|
||||
"MultiplexFeature::batchFill/UNDERBOUGHT"
|
||||
);
|
||||
|
||||
uint256 ethBalanceAfter = address(this).balance;
|
||||
require(
|
||||
ethBalanceAfter >= ethBalanceBefore,
|
||||
"MultiplexFeature::batchFill/OVERSPENT_ETH"
|
||||
);
|
||||
// Refund ETH
|
||||
if (ethBalanceAfter > ethBalanceBefore) {
|
||||
_transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Executes a sequence of fills "hopping" through the
|
||||
/// path of tokens given by `fillData.tokens`. Refer to the
|
||||
/// internal variant `_multiHopFill` for the allowed nested
|
||||
/// operations.
|
||||
/// @param fillData Encodes the path of tokens, the sell amount,
|
||||
/// and the nested operations for this multi-hop fill.
|
||||
/// @param minBuyAmount The minimum amount of the output token
|
||||
/// to buy. Reverts if this amount is not met.
|
||||
/// @return outputTokenAmount The amount of the output token bought.
|
||||
function multiHopFill(
|
||||
MultiHopFillData memory fillData,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
payable
|
||||
override
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
IERC20TokenV06 outputToken = IERC20TokenV06(fillData.tokens[fillData.tokens.length - 1]);
|
||||
// Cache the sender's balance of the output token.
|
||||
outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender);
|
||||
// Cache the contract's ETH balance prior to this call.
|
||||
uint256 ethBalanceBefore = address(this).balance.safeSub(msg.value);
|
||||
|
||||
// Perform the multi-hop fill. Pass in `msg.value` as the maximum
|
||||
// allowable amount of ETH for the wrapped calls to consume.
|
||||
_multiHopFill(fillData, msg.value);
|
||||
|
||||
// The `outputTokenAmount` returned by `_multiHopFill` may not
|
||||
// be fully accurate (e.g. due to some janky token).
|
||||
outputTokenAmount = outputToken.getTokenBalanceOf(msg.sender)
|
||||
.safeSub(outputTokenAmount);
|
||||
require(
|
||||
outputTokenAmount >= minBuyAmount,
|
||||
"MultiplexFeature::multiHopFill/UNDERBOUGHT"
|
||||
);
|
||||
|
||||
uint256 ethBalanceAfter = address(this).balance;
|
||||
require(
|
||||
ethBalanceAfter >= ethBalanceBefore,
|
||||
"MultiplexFeature::multiHopFill/OVERSPENT_ETH"
|
||||
);
|
||||
// Refund ETH
|
||||
if (ethBalanceAfter > ethBalanceBefore) {
|
||||
_transferEth(msg.sender, ethBalanceAfter - ethBalanceBefore);
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to FQT. If `fillData.sellAmount` is set to `type(uint256).max`,
|
||||
// this is effectively a batch fill. Otherwise it can be set to perform a
|
||||
// market sell of some amount. Note that the `outputTokenAmount` returned
|
||||
// by this function could theoretically be inaccurate if `msg.sender` has
|
||||
// set a token allowance on an external contract that gets called during
|
||||
// the execution of this function.
|
||||
function _batchFill(BatchFillData memory fillData)
|
||||
internal
|
||||
returns (uint256 outputTokenAmount, uint256 remainingEth)
|
||||
{
|
||||
// Track the remaining ETH allocated to this call.
|
||||
remainingEth = msg.value;
|
||||
// Track the amount of input token sold.
|
||||
uint256 soldAmount;
|
||||
for (uint256 i = 0; i != fillData.calls.length; i++) {
|
||||
// Check if we've hit our target.
|
||||
if (soldAmount >= fillData.sellAmount) { break; }
|
||||
WrappedBatchCall memory wrappedCall = fillData.calls[i];
|
||||
// Compute the fill amount.
|
||||
uint256 inputTokenAmount = LibSafeMathV06.min256(
|
||||
wrappedCall.sellAmount,
|
||||
fillData.sellAmount.safeSub(soldAmount)
|
||||
);
|
||||
if (wrappedCall.selector == INativeOrdersFeature._fillRfqOrder.selector) {
|
||||
// Decode the RFQ order and signature.
|
||||
(
|
||||
LibNativeOrder.RfqOrder memory order,
|
||||
LibSignature.Signature memory signature
|
||||
) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(LibNativeOrder.RfqOrder, LibSignature.Signature)
|
||||
);
|
||||
if (order.expiry <= uint64(block.timestamp)) {
|
||||
bytes32 orderHash = _getEIP712Hash(
|
||||
LibNativeOrder.getRfqOrderStructHash(order)
|
||||
);
|
||||
emit ExpiredRfqOrder(
|
||||
orderHash,
|
||||
order.maker,
|
||||
order.expiry
|
||||
);
|
||||
continue;
|
||||
}
|
||||
require(
|
||||
order.takerToken == fillData.inputToken &&
|
||||
order.makerToken == fillData.outputToken,
|
||||
"MultiplexFeature::_batchFill/RFQ_ORDER_INVALID_TOKENS"
|
||||
);
|
||||
// Try filling the RFQ order. Swallows reverts.
|
||||
try
|
||||
INativeOrdersFeature(address(this))._fillRfqOrder
|
||||
(
|
||||
order,
|
||||
signature,
|
||||
inputTokenAmount.safeDowncastToUint128(),
|
||||
msg.sender
|
||||
)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(takerTokenFilledAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(makerTokenFilledAmount);
|
||||
} catch {}
|
||||
} else if (wrappedCall.selector == this._sellToUniswap.selector) {
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
require(
|
||||
tokens.length >= 2 &&
|
||||
tokens[0] == address(fillData.inputToken) &&
|
||||
tokens[tokens.length - 1] == address(fillData.outputToken),
|
||||
"MultiplexFeature::_batchFill/UNISWAP_INVALID_TOKENS"
|
||||
);
|
||||
// Perform the Uniswap/Sushiswap trade.
|
||||
uint256 outputTokenAmount_ = _sellToUniswap(
|
||||
tokens,
|
||||
inputTokenAmount,
|
||||
isSushi,
|
||||
address(0),
|
||||
msg.sender
|
||||
);
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
} else if (wrappedCall.selector == IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector) {
|
||||
(bool success, bytes memory resultData) = address(this).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
|
||||
wrappedCall.data,
|
||||
inputTokenAmount,
|
||||
0,
|
||||
msg.sender
|
||||
)
|
||||
);
|
||||
if (success) {
|
||||
uint256 outputTokenAmount_ = abi.decode(resultData, (uint256));
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
}
|
||||
} else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) {
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
if (fillData.inputToken.isTokenETH()) {
|
||||
inputTokenAmount = LibSafeMathV06.min256(
|
||||
inputTokenAmount,
|
||||
remainingEth
|
||||
);
|
||||
// Transfer the input ETH to the provider.
|
||||
_transferEth(payable(provider), inputTokenAmount);
|
||||
// Count that ETH as spent.
|
||||
remainingEth -= inputTokenAmount;
|
||||
} else {
|
||||
// Transfer input ERC20 tokens to the provider.
|
||||
_transferERC20TokensFrom(
|
||||
fillData.inputToken,
|
||||
msg.sender,
|
||||
provider,
|
||||
inputTokenAmount
|
||||
);
|
||||
}
|
||||
// Perform the PLP trade.
|
||||
uint256 outputTokenAmount_ = _sellToLiquidityProvider(
|
||||
fillData.inputToken,
|
||||
fillData.outputToken,
|
||||
inputTokenAmount,
|
||||
ILiquidityProvider(provider),
|
||||
msg.sender,
|
||||
auxiliaryData
|
||||
);
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
args.taker = msg.sender;
|
||||
args.inputToken = fillData.inputToken;
|
||||
args.outputToken = fillData.outputToken;
|
||||
args.inputTokenAmount = inputTokenAmount;
|
||||
args.minOutputTokenAmount = 0;
|
||||
uint256 ethValue;
|
||||
(args.transformations, ethValue) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(ITransformERC20Feature.Transformation[], uint256)
|
||||
);
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(
|
||||
ethValue,
|
||||
remainingEth
|
||||
);
|
||||
if (ethValue > 0) {
|
||||
require(
|
||||
args.inputToken.isTokenETH(),
|
||||
"MultiplexFeature::_batchFill/ETH_TRANSFORM_ONLY"
|
||||
);
|
||||
}
|
||||
try ITransformERC20Feature(address(this))._transformERC20
|
||||
{value: ethValue}
|
||||
(args)
|
||||
returns (uint256 outputTokenAmount_)
|
||||
{
|
||||
remainingEth -= ethValue;
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
} catch {}
|
||||
} else if (wrappedCall.selector == this._multiHopFill.selector) {
|
||||
MultiHopFillData memory multiHopFillData;
|
||||
uint256 ethValue;
|
||||
(
|
||||
multiHopFillData.tokens,
|
||||
multiHopFillData.calls,
|
||||
ethValue
|
||||
) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address[], WrappedMultiHopCall[], uint256)
|
||||
);
|
||||
multiHopFillData.sellAmount = inputTokenAmount;
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(
|
||||
ethValue,
|
||||
remainingEth
|
||||
);
|
||||
// Subtract the ethValue allocated to the nested multi-hop fill.
|
||||
remainingEth -= ethValue;
|
||||
(uint256 outputTokenAmount_, uint256 leftoverEth) =
|
||||
_multiHopFill(multiHopFillData, ethValue);
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
// Add back any ETH that wasn't used by the nested multi-hop fill.
|
||||
remainingEth += leftoverEth;
|
||||
} else {
|
||||
revert("MultiplexFeature::_batchFill/UNRECOGNIZED_SELECTOR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal variant of `multiHopFill`. This function can be nested within
|
||||
// a `_batchFill`.
|
||||
// This function executes a sequence of fills "hopping" through the
|
||||
// path of tokens given by `fillData.tokens`. The nested operations that
|
||||
// can be used as "hops" are:
|
||||
// - WETH.deposit (wraps ETH)
|
||||
// - WETH.withdraw (unwraps WETH)
|
||||
// - _sellToUniswap (executes a Uniswap/Sushiswap swap)
|
||||
// - _sellToLiquidityProvider (executes a PLP swap)
|
||||
// - _transformERC20 (executes arbitrary ERC20 Transformations)
|
||||
// This function optimizes the number of ERC20 transfers performed
|
||||
// by having each hop transfer its output tokens directly to the
|
||||
// target address of the next hop. Note that the `outputTokenAmount` returned
|
||||
// by this function could theoretically be inaccurate if `msg.sender` has
|
||||
// set a token allowance on an external contract that gets called during
|
||||
// the execution of this function.
|
||||
function _multiHopFill(MultiHopFillData memory fillData, uint256 totalEth)
|
||||
public
|
||||
returns (uint256 outputTokenAmount, uint256 remainingEth)
|
||||
{
|
||||
// There should be one call/hop between every two tokens
|
||||
// in the path.
|
||||
// tokens[0]––calls[0]––>tokens[1]––...––calls[n-1]––>tokens[n]
|
||||
require(
|
||||
fillData.tokens.length == fillData.calls.length + 1,
|
||||
"MultiplexFeature::_multiHopFill/MISMATCHED_ARRAY_LENGTHS"
|
||||
);
|
||||
// Track the remaining ETH allocated to this call.
|
||||
remainingEth = totalEth;
|
||||
// This variable is used as the input and output amounts of
|
||||
// each hop. After the final hop, this will contain the output
|
||||
// amount of the multi-hop fill.
|
||||
outputTokenAmount = fillData.sellAmount;
|
||||
// This variable is used to cache the address to target in the
|
||||
// next hop. See `_computeHopRecipient` for details.
|
||||
address nextTarget;
|
||||
for (uint256 i = 0; i != fillData.calls.length; i++) {
|
||||
WrappedMultiHopCall memory wrappedCall = fillData.calls[i];
|
||||
if (wrappedCall.selector == this._sellToUniswap.selector) {
|
||||
// If the next hop supports a "transfer then execute" pattern,
|
||||
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
|
||||
// for details.
|
||||
address recipient = _computeHopRecipient(fillData.calls, i);
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
// Perform the Uniswap/Sushiswap trade.
|
||||
outputTokenAmount = _sellToUniswap(
|
||||
tokens,
|
||||
outputTokenAmount,
|
||||
isSushi,
|
||||
nextTarget,
|
||||
recipient
|
||||
);
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = recipient == msg.sender ? address(0) : recipient;
|
||||
} else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) {
|
||||
// If the next hop supports a "transfer then execute" pattern,
|
||||
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
|
||||
// for details.
|
||||
address recipient = _computeHopRecipient(fillData.calls, i);
|
||||
// If `nextTarget` was not set in the previous hop, then we
|
||||
// need to send in the input ETH/tokens to the liquidity provider
|
||||
// contract before executing the trade.
|
||||
if (nextTarget == address(0)) {
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
// Transfer input ETH or ERC20 tokens to the liquidity
|
||||
// provider contract.
|
||||
if (IERC20TokenV06(fillData.tokens[i]).isTokenETH()) {
|
||||
outputTokenAmount = LibSafeMathV06.min256(
|
||||
outputTokenAmount,
|
||||
remainingEth
|
||||
);
|
||||
_transferEth(payable(provider), outputTokenAmount);
|
||||
remainingEth -= outputTokenAmount;
|
||||
} else {
|
||||
_transferERC20TokensFrom(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
msg.sender,
|
||||
provider,
|
||||
outputTokenAmount
|
||||
);
|
||||
}
|
||||
outputTokenAmount = _sellToLiquidityProvider(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
IERC20TokenV06(fillData.tokens[i + 1]),
|
||||
outputTokenAmount,
|
||||
ILiquidityProvider(provider),
|
||||
recipient,
|
||||
auxiliaryData
|
||||
);
|
||||
} else {
|
||||
(, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
// Tokens and ETH have already been transferred to
|
||||
// the liquidity provider contract in the previous hop.
|
||||
outputTokenAmount = _sellToLiquidityProvider(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
IERC20TokenV06(fillData.tokens[i + 1]),
|
||||
outputTokenAmount,
|
||||
ILiquidityProvider(nextTarget),
|
||||
recipient,
|
||||
auxiliaryData
|
||||
);
|
||||
}
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = recipient == msg.sender ? address(0) : recipient;
|
||||
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
args.inputToken = IERC20TokenV06(fillData.tokens[i]);
|
||||
args.outputToken = IERC20TokenV06(fillData.tokens[i + 1]);
|
||||
args.minOutputTokenAmount = 0;
|
||||
args.taker = payable(_computeHopRecipient(fillData.calls, i));
|
||||
if (nextTarget != address(0)) {
|
||||
// If `nextTarget` was set in the previous hop, then the input
|
||||
// token was already sent to the FlashWallet. Setting
|
||||
// `inputTokenAmount` to 0 indicates that no tokens need to
|
||||
// be pulled into the FlashWallet before executing the
|
||||
// transformations.
|
||||
args.inputTokenAmount = 0;
|
||||
} else if (
|
||||
args.taker != msg.sender &&
|
||||
!args.inputToken.isTokenETH()
|
||||
) {
|
||||
address flashWallet = address(
|
||||
ITransformERC20Feature(address(this)).getTransformWallet()
|
||||
);
|
||||
// The input token has _not_ already been sent to the
|
||||
// FlashWallet. We also want PayTakerTransformer to
|
||||
// send the output token to some address other than
|
||||
// msg.sender, so we must transfer the input token
|
||||
// to the FlashWallet here.
|
||||
_transferERC20TokensFrom(
|
||||
args.inputToken,
|
||||
msg.sender,
|
||||
flashWallet,
|
||||
outputTokenAmount
|
||||
);
|
||||
args.inputTokenAmount = 0;
|
||||
} else {
|
||||
// Otherwise, either:
|
||||
// (1) args.taker == msg.sender, in which case
|
||||
// `_transformERC20` will pull the input token
|
||||
// into the FlashWallet, or
|
||||
// (2) args.inputToken == ETH_TOKEN_ADDRESS, in which
|
||||
// case ETH is attached to the call and no token
|
||||
// transfer occurs.
|
||||
args.inputTokenAmount = outputTokenAmount;
|
||||
}
|
||||
uint256 ethValue;
|
||||
(args.transformations, ethValue) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(ITransformERC20Feature.Transformation[], uint256)
|
||||
);
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(ethValue, remainingEth);
|
||||
if (ethValue > 0) {
|
||||
require(
|
||||
args.inputToken.isTokenETH(),
|
||||
"MultiplexFeature::_multiHopFill/ETH_TRANSFORM_ONLY"
|
||||
);
|
||||
}
|
||||
// Call `_transformERC20`.
|
||||
outputTokenAmount = ITransformERC20Feature(address(this))
|
||||
._transformERC20{value: ethValue}(args);
|
||||
// Decrement the remaining ETH.
|
||||
remainingEth -= ethValue;
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = args.taker == msg.sender ? address(0) : args.taker;
|
||||
} else if (wrappedCall.selector == IEtherTokenV06.deposit.selector) {
|
||||
require(
|
||||
i == 0,
|
||||
"MultiplexFeature::_multiHopFill/DEPOSIT_FIRST_HOP_ONLY"
|
||||
);
|
||||
uint256 ethValue = LibSafeMathV06.min256(outputTokenAmount, remainingEth);
|
||||
// Wrap ETH.
|
||||
weth.deposit{value: ethValue}();
|
||||
nextTarget = _computeHopRecipient(fillData.calls, i);
|
||||
weth.transfer(nextTarget, ethValue);
|
||||
remainingEth -= ethValue;
|
||||
} else if (wrappedCall.selector == IEtherTokenV06.withdraw.selector) {
|
||||
require(
|
||||
i == fillData.calls.length - 1,
|
||||
"MultiplexFeature::_multiHopFill/WITHDRAW_LAST_HOP_ONLY"
|
||||
);
|
||||
// Unwrap WETH and send to `msg.sender`.
|
||||
weth.withdraw(outputTokenAmount);
|
||||
_transferEth(msg.sender, outputTokenAmount);
|
||||
nextTarget = address(0);
|
||||
} else {
|
||||
revert("MultiplexFeature::_multiHopFill/UNRECOGNIZED_SELECTOR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to the UniswapFeature, but with a couple of differences:
|
||||
// - Does not perform the transfer in if `pairAddress` is given,
|
||||
// which indicates that the transfer in was already performed
|
||||
// in the previous hop of a multi-hop fill.
|
||||
// - Does not include a minBuyAmount check (which is performed in
|
||||
// either `batchFill` or `multiHopFill`).
|
||||
// - Takes a `recipient` address parameter, so the output of the
|
||||
// final `swap` call can be sent to an address other than `msg.sender`.
|
||||
function _sellToUniswap(
|
||||
address[] memory tokens,
|
||||
uint256 sellAmount,
|
||||
bool isSushi,
|
||||
address pairAddress,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
require(tokens.length > 1, "MultiplexFeature::_sellToUniswap/InvalidTokensLength");
|
||||
|
||||
if (pairAddress == address(0)) {
|
||||
pairAddress = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
|
||||
_transferERC20TokensFrom(
|
||||
IERC20TokenV06(tokens[0]),
|
||||
msg.sender,
|
||||
pairAddress,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < tokens.length - 1; i++) {
|
||||
(address inputToken, address outputToken) = (tokens[i], tokens[i + 1]);
|
||||
outputTokenAmount = _computeUniswapOutputAmount(
|
||||
pairAddress,
|
||||
inputToken,
|
||||
outputToken,
|
||||
sellAmount
|
||||
);
|
||||
(uint256 amount0Out, uint256 amount1Out) = inputToken < outputToken
|
||||
? (uint256(0), outputTokenAmount)
|
||||
: (outputTokenAmount, uint256(0));
|
||||
address to = i < tokens.length - 2
|
||||
? _computeUniswapPairAddress(outputToken, tokens[i + 2], isSushi)
|
||||
: recipient;
|
||||
IUniswapV2Pair(pairAddress).swap(
|
||||
amount0Out,
|
||||
amount1Out,
|
||||
to,
|
||||
new bytes(0)
|
||||
);
|
||||
pairAddress = to;
|
||||
sellAmount = outputTokenAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the LiquidityProviderFeature, but without the transfer in
|
||||
// (which is potentially done in the previous hop of a multi-hop fill)
|
||||
// and without the minBuyAmount check (which is performed at the top, i.e.
|
||||
// in either `batchFill` or `multiHopFill`).
|
||||
function _sellToLiquidityProvider(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
ILiquidityProvider provider,
|
||||
address recipient,
|
||||
bytes memory auxiliaryData
|
||||
)
|
||||
public
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
uint256 balanceBefore = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient);
|
||||
if (IERC20TokenV06(inputToken).isTokenETH()) {
|
||||
sandbox.executeSellEthForToken(
|
||||
provider,
|
||||
outputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
} else if (IERC20TokenV06(outputToken).isTokenETH()) {
|
||||
sandbox.executeSellTokenForEth(
|
||||
provider,
|
||||
inputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
} else {
|
||||
sandbox.executeSellTokenForToken(
|
||||
provider,
|
||||
inputToken,
|
||||
outputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
}
|
||||
outputTokenAmount = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient)
|
||||
.safeSub(balanceBefore);
|
||||
emit LiquidityProviderSwap(
|
||||
address(inputToken),
|
||||
address(outputToken),
|
||||
inputTokenAmount,
|
||||
outputTokenAmount,
|
||||
address(provider),
|
||||
recipient
|
||||
);
|
||||
return outputTokenAmount;
|
||||
}
|
||||
|
||||
function _transferEth(address payable recipient, uint256 amount)
|
||||
private
|
||||
{
|
||||
(bool success,) = recipient.call{value: amount}("");
|
||||
require(success, "MultiplexFeature::_transferEth/TRANSFER_FALIED");
|
||||
}
|
||||
|
||||
// Some liquidity sources (e.g. Uniswap, Sushiswap, and PLP) can be passed
|
||||
// a `recipient` parameter so the boguht tokens are transferred to the
|
||||
// `recipient` address rather than `msg.sender`.
|
||||
// Some liquidity sources (also Uniswap, Sushiswap, and PLP incidentally)
|
||||
// support a "transfer then execute" pattern, where the token being sold
|
||||
// can be transferred into the contract before calling a swap function to
|
||||
// execute the trade.
|
||||
// If the current hop in a multi-hop fill satisfies the first condition,
|
||||
// and the next hop satisfies the second condition, the tokens bought
|
||||
// in the current hop can be directly sent to the target contract of
|
||||
// the next hop to save a transfer.
|
||||
function _computeHopRecipient(
|
||||
WrappedMultiHopCall[] memory calls,
|
||||
uint256 i
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (address recipient)
|
||||
{
|
||||
recipient = msg.sender;
|
||||
if (i < calls.length - 1) {
|
||||
WrappedMultiHopCall memory nextCall = calls[i + 1];
|
||||
if (nextCall.selector == this._sellToUniswap.selector) {
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
nextCall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
recipient = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
|
||||
} else if (nextCall.selector == this._sellToLiquidityProvider.selector) {
|
||||
(recipient,) = abi.decode(
|
||||
nextCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
} else if (nextCall.selector == IEtherTokenV06.withdraw.selector) {
|
||||
recipient = address(this);
|
||||
} else if (nextCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
recipient = address(
|
||||
ITransformERC20Feature(address(this)).getTransformWallet()
|
||||
);
|
||||
}
|
||||
}
|
||||
require(
|
||||
recipient != address(0),
|
||||
"MultiplexFeature::_computeHopRecipient/RECIPIENT_IS_NULL"
|
||||
);
|
||||
}
|
||||
|
||||
// Computes the the amount of output token that would be bought
|
||||
// from Uniswap/Sushiswap given the input amount.
|
||||
function _computeUniswapOutputAmount(
|
||||
address pairAddress,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 outputAmount)
|
||||
{
|
||||
require(
|
||||
inputAmount > 0,
|
||||
"MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_INPUT_AMOUNT"
|
||||
);
|
||||
(uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairAddress).getReserves();
|
||||
require(
|
||||
reserve0 > 0 && reserve1 > 0,
|
||||
'MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_LIQUIDITY'
|
||||
);
|
||||
(uint256 inputReserve, uint256 outputReserve) = inputToken < outputToken
|
||||
? (reserve0, reserve1)
|
||||
: (reserve1, reserve0);
|
||||
uint256 inputAmountWithFee = inputAmount.safeMul(997);
|
||||
uint256 numerator = inputAmountWithFee.safeMul(outputReserve);
|
||||
uint256 denominator = inputReserve.safeMul(1000).safeAdd(inputAmountWithFee);
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
// Computes the Uniswap/Sushiswap pair contract address for the
|
||||
// given tokens.
|
||||
function _computeUniswapPairAddress(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
bool isSushi
|
||||
)
|
||||
private
|
||||
pure
|
||||
returns (address pairAddress)
|
||||
{
|
||||
(address token0, address token1) = tokenA < tokenB
|
||||
? (tokenA, tokenB)
|
||||
: (tokenB, tokenA);
|
||||
if (isSushi) {
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
SUSHISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
SUSHISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
} else {
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
UNISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
UNISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
@@ -34,7 +34,7 @@ contract NativeOrdersFeature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "LimitOrders";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 0);
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
|
@@ -47,21 +47,12 @@ contract OtcOrdersFeature is
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibSafeMathV06 for uint128;
|
||||
|
||||
/// @dev Options for handling ETH/WETH conversion
|
||||
/// @param LeaveAsWeth Neither unwrap nor wrap.
|
||||
/// @param WrapEth Wrap attached ETH.
|
||||
/// @param UnwrapWeth Unwrap WETH before transferring
|
||||
/// to taker.
|
||||
enum WethOptions {
|
||||
LeaveAsWeth,
|
||||
WrapEth,
|
||||
UnwrapWeth
|
||||
}
|
||||
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "OtcOrders";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||
/// @dev ETH pseudo-token address.
|
||||
address constant private ETH_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
|
||||
/// @dev The WETH token contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
@@ -80,8 +71,12 @@ contract OtcOrdersFeature is
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.fillOtcOrder.selector);
|
||||
_registerFeatureFunction(this.fillOtcOrderForEth.selector);
|
||||
_registerFeatureFunction(this.fillOtcOrderWithEth.selector);
|
||||
_registerFeatureFunction(this.fillTakerSignedOtcOrderForEth.selector);
|
||||
_registerFeatureFunction(this.fillTakerSignedOtcOrder.selector);
|
||||
_registerFeatureFunction(this.batchFillTakerSignedOtcOrders.selector);
|
||||
_registerFeatureFunction(this._fillOtcOrder.selector);
|
||||
_registerFeatureFunction(this.getOtcOrderInfo.selector);
|
||||
_registerFeatureFunction(this.getOtcOrderHash.selector);
|
||||
_registerFeatureFunction(this.lastOtcTxOriginNonce.selector);
|
||||
@@ -93,36 +88,90 @@ contract OtcOrdersFeature is
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @param unwrapWeth Whether or not to unwrap bought WETH into ETH
|
||||
/// before transferring it to the taker. Should be set to false
|
||||
/// if the maker token is not WETH.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
uint128 takerTokenFillAmount,
|
||||
bool unwrapWeth
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
if (!_isSenderValidTaker(order.taker)) {
|
||||
bytes32 orderHash = getOtcOrderHash(order);
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
|
||||
orderHash,
|
||||
msg.sender,
|
||||
order.taker
|
||||
).rrevert();
|
||||
}
|
||||
LibSignature.Signature memory nullSignature;
|
||||
return _fillOtcOrderPrivate(
|
||||
order,
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
nullSignature,
|
||||
msg.sender
|
||||
);
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
takerTokenFillAmount,
|
||||
unwrapWeth ? WethOptions.UnwrapWeth : WethOptions.LeaveAsWeth
|
||||
msg.sender,
|
||||
msg.sender
|
||||
);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
msg.sender,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
makerTokenFilledAmount,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// Unwraps bought WETH into ETH. before sending it to
|
||||
/// the taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrderForEth(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
require(
|
||||
order.makerToken == WETH,
|
||||
"OtcOrdersFeature::fillOtcOrderForEth/MAKER_TOKEN_NOT_WETH"
|
||||
);
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
msg.sender
|
||||
);
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
takerTokenFillAmount,
|
||||
msg.sender,
|
||||
address(this)
|
||||
);
|
||||
// Unwrap WETH
|
||||
WETH.withdraw(makerTokenFilledAmount);
|
||||
// Transfer ETH to taker
|
||||
_transferEth(msg.sender, makerTokenFilledAmount);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
msg.sender,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
makerTokenFilledAmount,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -141,21 +190,47 @@ contract OtcOrdersFeature is
|
||||
payable
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
if (!_isSenderValidTaker(order.taker)) {
|
||||
bytes32 orderHash = getOtcOrderHash(order);
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
|
||||
orderHash,
|
||||
msg.sender,
|
||||
order.taker
|
||||
).rrevert();
|
||||
if (order.takerToken == WETH) {
|
||||
// Wrap ETH
|
||||
WETH.deposit{value: msg.value}();
|
||||
} else {
|
||||
require(
|
||||
address(order.takerToken) == ETH_TOKEN_ADDRESS,
|
||||
"OtcOrdersFeature::fillOtcOrderWithEth/INVALID_TAKER_TOKEN"
|
||||
);
|
||||
}
|
||||
LibSignature.Signature memory nullSignature;
|
||||
return _fillOtcOrderPrivate(
|
||||
order,
|
||||
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
nullSignature,
|
||||
msg.sender
|
||||
);
|
||||
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
msg.value.safeDowncastToUint128(),
|
||||
WethOptions.WrapEth
|
||||
address(this),
|
||||
msg.sender
|
||||
);
|
||||
if (takerTokenFilledAmount < msg.value) {
|
||||
uint256 refundAmount = msg.value - uint256(takerTokenFilledAmount);
|
||||
if (order.takerToken == WETH) {
|
||||
WETH.withdraw(refundAmount);
|
||||
}
|
||||
// Refund unused ETH
|
||||
_transferEth(msg.sender, refundAmount);
|
||||
}
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
msg.sender,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
makerTokenFilledAmount,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,112 +239,28 @@ contract OtcOrdersFeature is
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
/// @param unwrapWeth Whether or not to unwrap bought WETH into ETH
|
||||
/// before transferring it to the taker. Should be set to false
|
||||
/// if the maker token is not WETH.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillTakerSignedOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
LibSignature.Signature memory takerSignature,
|
||||
bool unwrapWeth
|
||||
LibSignature.Signature memory takerSignature
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
return _fillOtcOrderPrivate(
|
||||
order,
|
||||
makerSignature,
|
||||
takerSignature,
|
||||
order.takerAmount,
|
||||
unwrapWeth ? WethOptions.UnwrapWeth : WethOptions.LeaveAsWeth
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fill an OTC order. Private variant.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
/// Ignored if msg.sender == order.taker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to
|
||||
/// fill this order with.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _fillOtcOrderPrivate(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
LibSignature.Signature memory takerSignature,
|
||||
uint128 takerTokenFillAmount,
|
||||
WethOptions wethOptions
|
||||
)
|
||||
private
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
|
||||
// Must be fillable.
|
||||
if (orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableError(
|
||||
orderInfo.orderHash,
|
||||
uint8(orderInfo.status)
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
address taker = msg.sender;
|
||||
{
|
||||
LibNativeOrdersStorage.Storage storage stor =
|
||||
LibNativeOrdersStorage.getStorage();
|
||||
|
||||
// Must be fillable by the tx.origin.
|
||||
if (
|
||||
order.txOrigin != tx.origin &&
|
||||
!stor.originRegistry[order.txOrigin][tx.origin]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByOriginError(
|
||||
orderInfo.orderHash,
|
||||
tx.origin,
|
||||
order.txOrigin
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// Maker signature must be valid for the order.
|
||||
address makerSigner = LibSignature.getSignerOfHash(orderInfo.orderHash, makerSignature);
|
||||
if (
|
||||
makerSigner != order.maker &&
|
||||
!stor.orderSignerRegistry[order.maker][makerSigner]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
|
||||
orderInfo.orderHash,
|
||||
makerSigner,
|
||||
order.maker
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// If msg.sender is not the taker, validate the taker signature.
|
||||
if (!_isSenderValidTaker(order.taker)) {
|
||||
address takerSigner = LibSignature.getSignerOfHash(orderInfo.orderHash, takerSignature);
|
||||
if (
|
||||
takerSigner != order.taker &&
|
||||
!stor.orderSignerRegistry[order.taker][takerSigner]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotSignedByTakerError(
|
||||
orderInfo.orderHash,
|
||||
takerSigner,
|
||||
order.taker
|
||||
).rrevert();
|
||||
}
|
||||
taker = order.taker;
|
||||
}
|
||||
}
|
||||
|
||||
// Settle between the maker and taker.
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
address taker = LibSignature.getSignerOfHash(orderInfo.orderHash, takerSignature);
|
||||
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
taker
|
||||
);
|
||||
_settleOtcOrder(
|
||||
order,
|
||||
order.takerAmount,
|
||||
taker,
|
||||
takerTokenFillAmount,
|
||||
wethOptions
|
||||
taker
|
||||
);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
@@ -278,22 +269,227 @@ contract OtcOrdersFeature is
|
||||
taker,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
takerTokenFilledAmount,
|
||||
makerTokenFilledAmount
|
||||
order.makerAmount,
|
||||
order.takerAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fully fill an OTC order. "Meta-transaction" variant,
|
||||
/// requires order to be signed by both maker and taker.
|
||||
/// Unwraps bought WETH into ETH. before sending it to
|
||||
/// the taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
function fillTakerSignedOtcOrderForEth(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
LibSignature.Signature memory takerSignature
|
||||
)
|
||||
public
|
||||
override
|
||||
{
|
||||
require(
|
||||
order.makerToken == WETH,
|
||||
"OtcOrdersFeature::fillTakerSignedOtcOrder/MAKER_TOKEN_NOT_WETH"
|
||||
);
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
address taker = LibSignature.getSignerOfHash(orderInfo.orderHash, takerSignature);
|
||||
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
taker
|
||||
);
|
||||
_settleOtcOrder(
|
||||
order,
|
||||
order.takerAmount,
|
||||
taker,
|
||||
address(this)
|
||||
);
|
||||
// Unwrap WETH
|
||||
WETH.withdraw(order.makerAmount);
|
||||
// Transfer ETH to taker
|
||||
_transferEth(taker, order.makerAmount);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
taker,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
order.makerAmount,
|
||||
order.takerAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fills multiple taker-signed OTC orders.
|
||||
/// @param orders Array of OTC orders.
|
||||
/// @param makerSignatures Array of maker signatures for each order.
|
||||
/// @param takerSignatures Array of taker signatures for each order.
|
||||
/// @param unwrapWeth Array of booleans representing whether or not
|
||||
/// to unwrap bought WETH into ETH for each order. Should be set
|
||||
/// to false if the maker token is not WETH.
|
||||
/// @return successes Array of booleans representing whether or not
|
||||
/// each order in `orders` was filled successfully.
|
||||
function batchFillTakerSignedOtcOrders(
|
||||
LibNativeOrder.OtcOrder[] memory orders,
|
||||
LibSignature.Signature[] memory makerSignatures,
|
||||
LibSignature.Signature[] memory takerSignatures,
|
||||
bool[] memory unwrapWeth
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (bool[] memory successes)
|
||||
{
|
||||
require(
|
||||
orders.length == makerSignatures.length &&
|
||||
orders.length == takerSignatures.length &&
|
||||
orders.length == unwrapWeth.length,
|
||||
"OtcOrdersFeature::batchFillTakerSignedOtcOrders/MISMATCHED_ARRAY_LENGTHS"
|
||||
);
|
||||
successes = new bool[](orders.length);
|
||||
for (uint256 i = 0; i != orders.length; i++) {
|
||||
bytes4 fnSelector = unwrapWeth[i]
|
||||
? this.fillTakerSignedOtcOrderForEth.selector
|
||||
: this.fillTakerSignedOtcOrder.selector;
|
||||
// Swallow reverts
|
||||
(successes[i], ) = _implementation.delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
fnSelector,
|
||||
orders[i],
|
||||
makerSignatures[i],
|
||||
takerSignatures[i]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// Internal variant.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @param taker The address to fill the order in the context of.
|
||||
/// @param useSelfBalance Whether to use the Exchange Proxy's balance
|
||||
/// of input tokens.
|
||||
/// @param recipient The recipient of the bought maker tokens.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker,
|
||||
bool useSelfBalance,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
override
|
||||
onlySelf
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
taker
|
||||
);
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
takerTokenFillAmount,
|
||||
useSelfBalance ? address(this) : taker,
|
||||
recipient
|
||||
);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
taker,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
makerTokenFilledAmount,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Validates an OTC order, reverting if the order cannot be
|
||||
/// filled by the given taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param orderInfo Info on the order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param taker The order taker.
|
||||
function _validateOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
address taker
|
||||
)
|
||||
private
|
||||
view
|
||||
{
|
||||
// Must be fillable.
|
||||
if (orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableError(
|
||||
orderInfo.orderHash,
|
||||
uint8(orderInfo.status)
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// Must be a valid taker for the order.
|
||||
if (order.taker != address(0) && order.taker != taker) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
|
||||
orderInfo.orderHash,
|
||||
taker,
|
||||
order.taker
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
LibNativeOrdersStorage.Storage storage stor =
|
||||
LibNativeOrdersStorage.getStorage();
|
||||
|
||||
// Must be fillable by the tx.origin.
|
||||
if (
|
||||
order.txOrigin != tx.origin &&
|
||||
!stor.originRegistry[order.txOrigin][tx.origin]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByOriginError(
|
||||
orderInfo.orderHash,
|
||||
tx.origin,
|
||||
order.txOrigin
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// Maker signature must be valid for the order.
|
||||
address makerSigner = LibSignature.getSignerOfHash(orderInfo.orderHash, makerSignature);
|
||||
if (
|
||||
makerSigner != order.maker &&
|
||||
!stor.orderSignerRegistry[order.maker][makerSigner]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
|
||||
orderInfo.orderHash,
|
||||
makerSigner,
|
||||
order.maker
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Settle the trade between an OTC order's maker and taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @param payer The address holding the taker tokens.
|
||||
/// @param recipient The recipient of the maker tokens.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _settleOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
address taker,
|
||||
uint128 takerTokenFillAmount,
|
||||
WethOptions wethOptions
|
||||
address payer,
|
||||
address recipient
|
||||
)
|
||||
private
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
@@ -326,57 +522,34 @@ contract OtcOrdersFeature is
|
||||
));
|
||||
}
|
||||
|
||||
if (wethOptions == WethOptions.WrapEth) {
|
||||
require(
|
||||
order.takerToken == WETH,
|
||||
"OtcOrdersFeature/INVALID_WRAP_ETH"
|
||||
);
|
||||
// Wrap ETH
|
||||
WETH.deposit{value: takerTokenFilledAmount}();
|
||||
// Transfer WETH to maker
|
||||
WETH.transfer(order.maker, takerTokenFilledAmount);
|
||||
if (takerTokenFilledAmount < msg.value) {
|
||||
// Refund unused ETH
|
||||
_transferEth(
|
||||
msg.sender,
|
||||
msg.value - uint256(takerTokenFilledAmount)
|
||||
);
|
||||
if (payer == address(this)) {
|
||||
if (address(order.takerToken) == ETH_TOKEN_ADDRESS) {
|
||||
// Transfer ETH to the maker.
|
||||
payable(order.maker).transfer(takerTokenFilledAmount);
|
||||
} else {
|
||||
// Transfer this -> maker.
|
||||
_transferERC20Tokens(
|
||||
order.takerToken,
|
||||
order.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Transfer taker -> maker
|
||||
_transferERC20TokensFrom(
|
||||
order.takerToken,
|
||||
taker,
|
||||
payer,
|
||||
order.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
if (wethOptions == WethOptions.UnwrapWeth) {
|
||||
require(
|
||||
order.makerToken == WETH,
|
||||
"OtcOrdersFeature/INVALID_UNWRAP_WETH"
|
||||
);
|
||||
// Transfer maker tokens in
|
||||
_transferERC20TokensFrom(
|
||||
order.makerToken,
|
||||
order.maker,
|
||||
address(this),
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
// Unwrap WETH
|
||||
WETH.withdraw(makerTokenFilledAmount);
|
||||
// Transfer ETH to taker
|
||||
_transferEth(taker, makerTokenFilledAmount);
|
||||
} else {
|
||||
// Transfer maker -> taker.
|
||||
_transferERC20TokensFrom(
|
||||
order.makerToken,
|
||||
order.maker,
|
||||
taker,
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
// Transfer maker -> recipient.
|
||||
_transferERC20TokensFrom(
|
||||
order.makerToken,
|
||||
order.maker,
|
||||
recipient,
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Get the order info for an OTC order.
|
||||
@@ -461,12 +634,4 @@ contract OtcOrdersFeature is
|
||||
revertData.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
function _isSenderValidTaker(address orderTaker)
|
||||
private
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return orderTaker == address(0) || orderTaker == msg.sender;
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../errors/LibTransformERC20RichErrors.sol";
|
||||
@@ -51,16 +52,14 @@ contract TransformERC20Feature is
|
||||
struct TransformERC20PrivateState {
|
||||
IFlashWallet wallet;
|
||||
address transformerDeployer;
|
||||
uint256 takerOutputTokenBalanceBefore;
|
||||
uint256 takerOutputTokenBalanceAfter;
|
||||
uint256 recipientOutputTokenBalanceBefore;
|
||||
uint256 recipientOutputTokenBalanceAfter;
|
||||
}
|
||||
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "TransformERC20";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 3, 1);
|
||||
|
||||
constructor() public {}
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 4, 0);
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
@@ -76,7 +75,7 @@ contract TransformERC20Feature is
|
||||
_registerFeatureFunction(this.setTransformerDeployer.selector);
|
||||
_registerFeatureFunction(this.setQuoteSigner.selector);
|
||||
_registerFeatureFunction(this.getQuoteSigner.selector);
|
||||
_registerFeatureFunction(this.transformERC20.selector);
|
||||
_registerFeatureFunction(this.transformERC20Staging.selector);
|
||||
_registerFeatureFunction(this._transformERC20.selector);
|
||||
if (this.getTransformWallet() == IFlashWallet(address(0))) {
|
||||
// Create the transform wallet if it doesn't exist.
|
||||
@@ -146,6 +145,44 @@ contract TransformERC20Feature is
|
||||
LibTransformERC20Storage.getStorage().wallet = wallet;
|
||||
}
|
||||
|
||||
/// @dev Wrapper for `transformERC20`. This selector will be temporarily
|
||||
/// registered to the Exchange Proxy so that we can migrate 0x API
|
||||
/// with no downtime. Once 0x API has been updated to point to this
|
||||
/// function, we can safely re-register `transformERC20`, point
|
||||
/// 0x API back to `transformERC20`, and deregister this function.
|
||||
/// @param inputToken The token being provided by the sender.
|
||||
/// If `0xeee...`, ETH is implied and should be provided with the call.`
|
||||
/// @param outputToken The token to be acquired by the sender.
|
||||
/// `0xeee...` implies ETH.
|
||||
/// @param inputTokenAmount The amount of `inputToken` to take from the sender.
|
||||
/// If set to `uint256(-1)`, the entire spendable balance of the taker
|
||||
/// will be solt.
|
||||
/// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
|
||||
/// must receive for the entire transformation to succeed. If set to zero,
|
||||
/// the minimum output token transfer will not be asserted.
|
||||
/// @param transformations The transformations to execute on the token balance(s)
|
||||
/// in sequence.
|
||||
/// @return outputTokenAmount The amount of `outputToken` received by the sender.
|
||||
function transformERC20Staging(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 minOutputTokenAmount,
|
||||
Transformation[] memory transformations
|
||||
)
|
||||
public
|
||||
payable
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
return transformERC20(
|
||||
inputToken,
|
||||
outputToken,
|
||||
inputTokenAmount,
|
||||
minOutputTokenAmount,
|
||||
transformations
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a series of transformations to convert an ERC20 `inputToken`
|
||||
/// to an ERC20 `outputToken`.
|
||||
/// @param inputToken The token being provided by the sender.
|
||||
@@ -180,7 +217,9 @@ contract TransformERC20Feature is
|
||||
outputToken: outputToken,
|
||||
inputTokenAmount: inputTokenAmount,
|
||||
minOutputTokenAmount: minOutputTokenAmount,
|
||||
transformations: transformations
|
||||
transformations: transformations,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -208,7 +247,7 @@ contract TransformERC20Feature is
|
||||
{
|
||||
// If the input token amount is -1 and we are not selling ETH,
|
||||
// transform the taker's entire spendable balance.
|
||||
if (args.inputTokenAmount == uint256(-1)) {
|
||||
if (!args.useSelfBalance && args.inputTokenAmount == uint256(-1)) {
|
||||
if (LibERC20Transformer.isTokenETH(args.inputToken)) {
|
||||
// We can't pull more ETH from the taker, so we just set the
|
||||
// input token amount to the value attached to the call.
|
||||
@@ -225,17 +264,12 @@ contract TransformERC20Feature is
|
||||
state.wallet = getTransformWallet();
|
||||
state.transformerDeployer = getTransformerDeployer();
|
||||
|
||||
// Remember the initial output token balance of the taker.
|
||||
state.takerOutputTokenBalanceBefore =
|
||||
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.taker);
|
||||
// Remember the initial output token balance of the recipient.
|
||||
state.recipientOutputTokenBalanceBefore =
|
||||
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.recipient);
|
||||
|
||||
// Pull input tokens from the taker to the wallet and transfer attached ETH.
|
||||
_transferInputTokensAndAttachedEth(
|
||||
args.inputToken,
|
||||
args.taker,
|
||||
address(state.wallet),
|
||||
args.inputTokenAmount
|
||||
);
|
||||
_transferInputTokensAndAttachedEth(args, address(state.wallet));
|
||||
|
||||
{
|
||||
// Perform transformations.
|
||||
@@ -244,22 +278,29 @@ contract TransformERC20Feature is
|
||||
state.wallet,
|
||||
args.transformations[i],
|
||||
state.transformerDeployer,
|
||||
args.taker
|
||||
args.recipient
|
||||
);
|
||||
}
|
||||
// Transfer output tokens from wallet to recipient
|
||||
outputTokenAmount = _executeOutputTokenTransfer(
|
||||
args.outputToken,
|
||||
state.wallet,
|
||||
args.recipient
|
||||
);
|
||||
}
|
||||
|
||||
// Compute how much output token has been transferred to the taker.
|
||||
state.takerOutputTokenBalanceAfter =
|
||||
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.taker);
|
||||
if (state.takerOutputTokenBalanceAfter < state.takerOutputTokenBalanceBefore) {
|
||||
// Compute how much output token has been transferred to the recipient.
|
||||
state.recipientOutputTokenBalanceAfter =
|
||||
LibERC20Transformer.getTokenBalanceOf(args.outputToken, args.recipient);
|
||||
if (state.recipientOutputTokenBalanceAfter < state.recipientOutputTokenBalanceBefore) {
|
||||
LibTransformERC20RichErrors.NegativeTransformERC20OutputError(
|
||||
address(args.outputToken),
|
||||
state.takerOutputTokenBalanceBefore - state.takerOutputTokenBalanceAfter
|
||||
state.recipientOutputTokenBalanceBefore - state.recipientOutputTokenBalanceAfter
|
||||
).rrevert();
|
||||
}
|
||||
outputTokenAmount = state.takerOutputTokenBalanceAfter.safeSub(
|
||||
state.takerOutputTokenBalanceBefore
|
||||
outputTokenAmount = LibSafeMathV06.min256(
|
||||
outputTokenAmount,
|
||||
state.recipientOutputTokenBalanceAfter.safeSub(state.recipientOutputTokenBalanceBefore)
|
||||
);
|
||||
// Ensure enough output token has been sent to the taker.
|
||||
if (outputTokenAmount < args.minOutputTokenAmount) {
|
||||
@@ -292,38 +333,49 @@ contract TransformERC20Feature is
|
||||
return LibTransformERC20Storage.getStorage().wallet;
|
||||
}
|
||||
|
||||
/// @dev Transfer input tokens from the taker and any attached ETH to `to`
|
||||
/// @param inputToken The token to pull from the taker.
|
||||
/// @param from The from (taker) address.
|
||||
/// @dev Transfer input tokens and any attached ETH to `to`
|
||||
/// @param args A `TransformERC20Args` struct.
|
||||
/// @param to The recipient of tokens and ETH.
|
||||
/// @param amount Amount of `inputToken` tokens to transfer.
|
||||
function _transferInputTokensAndAttachedEth(
|
||||
IERC20TokenV06 inputToken,
|
||||
address from,
|
||||
address payable to,
|
||||
uint256 amount
|
||||
TransformERC20Args memory args,
|
||||
address payable to
|
||||
)
|
||||
private
|
||||
{
|
||||
if (
|
||||
LibERC20Transformer.isTokenETH(args.inputToken) &&
|
||||
msg.value < args.inputTokenAmount
|
||||
) {
|
||||
// Token is ETH, so the caller must attach enough ETH to the call.
|
||||
LibTransformERC20RichErrors.InsufficientEthAttachedError(
|
||||
msg.value,
|
||||
args.inputTokenAmount
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// Transfer any attached ETH.
|
||||
if (msg.value != 0) {
|
||||
to.transfer(msg.value);
|
||||
}
|
||||
|
||||
// Transfer input tokens.
|
||||
if (!LibERC20Transformer.isTokenETH(inputToken) && amount != 0) {
|
||||
// Token is not ETH, so pull ERC20 tokens.
|
||||
_transferERC20TokensFrom(
|
||||
inputToken,
|
||||
from,
|
||||
to,
|
||||
amount
|
||||
);
|
||||
} else if (msg.value < amount) {
|
||||
// Token is ETH, so the caller must attach enough ETH to the call.
|
||||
LibTransformERC20RichErrors.InsufficientEthAttachedError(
|
||||
msg.value,
|
||||
amount
|
||||
).rrevert();
|
||||
if (!LibERC20Transformer.isTokenETH(args.inputToken)) {
|
||||
if (args.useSelfBalance) {
|
||||
// Use EP balance input token.
|
||||
_transferERC20Tokens(
|
||||
args.inputToken,
|
||||
to,
|
||||
args.inputTokenAmount
|
||||
);
|
||||
} else {
|
||||
// Pull ERC20 tokens from taker.
|
||||
_transferERC20TokensFrom(
|
||||
args.inputToken,
|
||||
args.taker,
|
||||
to,
|
||||
args.inputTokenAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,12 +383,12 @@ contract TransformERC20Feature is
|
||||
/// @param wallet The wallet instance.
|
||||
/// @param transformation The transformation.
|
||||
/// @param transformerDeployer The address of the transformer deployer.
|
||||
/// @param taker The taker address.
|
||||
/// @param recipient The recipient address.
|
||||
function _executeTransformation(
|
||||
IFlashWallet wallet,
|
||||
Transformation memory transformation,
|
||||
address transformerDeployer,
|
||||
address payable taker
|
||||
address payable recipient
|
||||
)
|
||||
private
|
||||
{
|
||||
@@ -354,7 +406,7 @@ contract TransformERC20Feature is
|
||||
IERC20Transformer.transform.selector,
|
||||
IERC20Transformer.TransformContext({
|
||||
sender: msg.sender,
|
||||
taker: taker,
|
||||
recipient: recipient,
|
||||
data: transformation.data
|
||||
})
|
||||
)
|
||||
@@ -370,4 +422,52 @@ contract TransformERC20Feature is
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
function _executeOutputTokenTransfer(
|
||||
IERC20TokenV06 outputToken,
|
||||
IFlashWallet wallet,
|
||||
address payable recipient
|
||||
)
|
||||
private
|
||||
returns (uint256 transferAmount)
|
||||
{
|
||||
transferAmount =
|
||||
LibERC20Transformer.getTokenBalanceOf(outputToken, address(wallet));
|
||||
if (LibERC20Transformer.isTokenETH(outputToken)) {
|
||||
wallet.executeCall(
|
||||
recipient,
|
||||
"",
|
||||
transferAmount
|
||||
);
|
||||
} else {
|
||||
bytes memory resultData = wallet.executeCall(
|
||||
payable(address(outputToken)),
|
||||
abi.encodeWithSelector(
|
||||
IERC20TokenV06.transfer.selector,
|
||||
recipient,
|
||||
transferAmount
|
||||
),
|
||||
0
|
||||
);
|
||||
if (resultData.length == 0) {
|
||||
// If we get back 0 returndata, this may be a non-standard ERC-20 that
|
||||
// does not return a boolean. Check that it at least contains code.
|
||||
uint256 size;
|
||||
assembly { size := extcodesize(outputToken) }
|
||||
require(size > 0, "invalid token address, contains no code");
|
||||
} else if (resultData.length >= 32) {
|
||||
// If we get back at least 32 bytes, we know the target address
|
||||
// contains code, and we assume it is a token that returned a boolean
|
||||
// success value, which must be true.
|
||||
uint256 result = LibBytesV06.readUint256(resultData, 0);
|
||||
if (result != 1) {
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
} else {
|
||||
// If 0 < returndatasize < 32, the target is a contract, but not a
|
||||
// valid token.
|
||||
LibRichErrorsV06.rrevert(resultData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -40,7 +40,7 @@ contract UniswapV3Feature is
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "UniswapV3Feature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||
/// @dev WETH contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
/// @dev UniswapV3 Factory contract address prepended with '0xff' and left-aligned.
|
||||
@@ -88,6 +88,7 @@ contract UniswapV3Feature is
|
||||
_registerFeatureFunction(this.sellEthForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForEthToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this._sellHeldTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.uniswapV3SwapCallback.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
@@ -175,6 +176,33 @@ contract UniswapV3Feature is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// Private variant, uses tokens held by `address(this)`.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for sender.
|
||||
/// @return buyAmount Amount of the last token in the path bought.
|
||||
function _sellHeldTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
override
|
||||
onlySelf
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
buyAmount = _swap(
|
||||
encodedPath,
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
address(this),
|
||||
_normalizeRecipient(recipient)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev The UniswapV3 pool swap callback which pays the funds requested
|
||||
/// by the caller/pool to the pool. Can only be called by a valid
|
||||
/// UniswapV3 pool.
|
||||
|
@@ -24,9 +24,21 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
interface IMultiplexFeature {
|
||||
// Identifies the type of subcall.
|
||||
enum MultiplexSubcall {
|
||||
Invalid,
|
||||
RFQ,
|
||||
OTC,
|
||||
UniswapV2,
|
||||
UniswapV3,
|
||||
LiquidityProvider,
|
||||
TransformERC20,
|
||||
BatchSell,
|
||||
MultiHopSell
|
||||
}
|
||||
|
||||
// Parameters for `batchFill`.
|
||||
struct BatchFillData {
|
||||
// Parameters for a batch sell.
|
||||
struct BatchSellParams {
|
||||
// The token being sold.
|
||||
IERC20TokenV06 inputToken;
|
||||
// The token being bought.
|
||||
@@ -34,84 +46,182 @@ interface IMultiplexFeature {
|
||||
// The amount of `inputToken` to sell.
|
||||
uint256 sellAmount;
|
||||
// The nested calls to perform.
|
||||
WrappedBatchCall[] calls;
|
||||
BatchSellSubcall[] calls;
|
||||
// Whether to use the Exchange Proxy's balance
|
||||
// of input tokens.
|
||||
bool useSelfBalance;
|
||||
// The recipient of the bought output tokens.
|
||||
address recipient;
|
||||
}
|
||||
|
||||
// Represents a call nested within a `batchFill`.
|
||||
struct WrappedBatchCall {
|
||||
// The selector of the function to call.
|
||||
bytes4 selector;
|
||||
// Amount of `inputToken` to sell.
|
||||
// Represents a constituent call of a batch sell.
|
||||
struct BatchSellSubcall {
|
||||
// The function to call.
|
||||
MultiplexSubcall id;
|
||||
// Amount of input token to sell. If the highest bit is 1,
|
||||
// this value represents a proportion of the total
|
||||
// `sellAmount` of the batch sell. See `_normalizeSellAmount`
|
||||
// for details.
|
||||
uint256 sellAmount;
|
||||
// ABI-encoded parameters needed to perform the call.
|
||||
bytes data;
|
||||
}
|
||||
|
||||
// Parameters for `multiHopFill`.
|
||||
struct MultiHopFillData {
|
||||
// Parameters for a multi-hop sell.
|
||||
struct MultiHopSellParams {
|
||||
// The sell path, i.e.
|
||||
// tokens = [inputToken, hopToken1, ..., hopTokenN, outputToken]
|
||||
address[] tokens;
|
||||
// The amount of `tokens[0]` to sell.
|
||||
uint256 sellAmount;
|
||||
// The nested calls to perform.
|
||||
WrappedMultiHopCall[] calls;
|
||||
MultiHopSellSubcall[] calls;
|
||||
// Whether to use the Exchange Proxy's balance
|
||||
// of input tokens.
|
||||
bool useSelfBalance;
|
||||
// The recipient of the bought output tokens.
|
||||
address recipient;
|
||||
}
|
||||
|
||||
// Represents a call nested within a `multiHopFill`.
|
||||
struct WrappedMultiHopCall {
|
||||
// The selector of the function to call.
|
||||
bytes4 selector;
|
||||
// Represents a constituent call of a multi-hop sell.
|
||||
struct MultiHopSellSubcall {
|
||||
// The function to call.
|
||||
MultiplexSubcall id;
|
||||
// ABI-encoded parameters needed to perform the call.
|
||||
bytes data;
|
||||
}
|
||||
|
||||
event LiquidityProviderSwap(
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount,
|
||||
address provider,
|
||||
address recipient
|
||||
);
|
||||
struct BatchSellState {
|
||||
// Tracks the amount of input token sold.
|
||||
uint256 soldAmount;
|
||||
// Tracks the amount of output token bought.
|
||||
uint256 boughtAmount;
|
||||
}
|
||||
|
||||
event ExpiredRfqOrder(
|
||||
bytes32 orderHash,
|
||||
address maker,
|
||||
uint64 expiry
|
||||
);
|
||||
struct MultiHopSellState {
|
||||
// This variable is used for the input and output amounts of
|
||||
// each hop. After the final hop, this will contain the output
|
||||
// amount of the multi-hop sell.
|
||||
uint256 outputTokenAmount;
|
||||
// For each hop in a multi-hop sell, `from` is the
|
||||
// address that holds the input tokens of the hop,
|
||||
// `to` is the address that receives the output tokens
|
||||
// of the hop.
|
||||
// See `_computeHopTarget` for details.
|
||||
address from;
|
||||
address to;
|
||||
// The index of the current hop in the multi-hop chain.
|
||||
uint256 hopIndex;
|
||||
}
|
||||
|
||||
/// @dev Executes a batch of fills selling `fillData.inputToken`
|
||||
/// for `fillData.outputToken` in sequence. Refer to the
|
||||
/// internal variant `_batchFill` for the allowed nested
|
||||
/// operations.
|
||||
/// @param fillData Encodes the input/output tokens, the sell
|
||||
/// amount, and the nested operations for this batch fill.
|
||||
/// @param minBuyAmount The minimum amount of `fillData.outputToken`
|
||||
/// to buy. Reverts if this amount is not met.
|
||||
/// @return outputTokenAmount The amount of the output token bought.
|
||||
function batchFill(
|
||||
BatchFillData calldata fillData,
|
||||
/// @dev Sells attached ETH for `outputToken` using the provided
|
||||
/// calls.
|
||||
/// @param outputToken The token to buy.
|
||||
/// @param calls The calls to use to sell the attached ETH.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken` that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function multiplexBatchSellEthForToken(
|
||||
IERC20TokenV06 outputToken,
|
||||
BatchSellSubcall[] calldata calls,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 outputTokenAmount);
|
||||
returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Executes a sequence of fills "hopping" through the
|
||||
/// path of tokens given by `fillData.tokens`. Refer to the
|
||||
/// internal variant `_multiHopFill` for the allowed nested
|
||||
/// operations.
|
||||
/// @param fillData Encodes the path of tokens, the sell amount,
|
||||
/// and the nested operations for this multi-hop fill.
|
||||
/// @param minBuyAmount The minimum amount of the output token
|
||||
/// to buy. Reverts if this amount is not met.
|
||||
/// @return outputTokenAmount The amount of the output token bought.
|
||||
function multiHopFill(
|
||||
MultiHopFillData calldata fillData,
|
||||
/// @dev Sells `sellAmount` of the given `inputToken` for ETH
|
||||
/// using the provided calls.
|
||||
/// @param inputToken The token to sell.
|
||||
/// @param calls The calls to use to sell the input tokens.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of ETH that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of ETH bought.
|
||||
function multiplexBatchSellTokenForEth(
|
||||
IERC20TokenV06 inputToken,
|
||||
BatchSellSubcall[] calldata calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Sells `sellAmount` of the given `inputToken` for
|
||||
/// `outputToken` using the provided calls.
|
||||
/// @param inputToken The token to sell.
|
||||
/// @param outputToken The token to buy.
|
||||
/// @param calls The calls to use to sell the input tokens.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken`
|
||||
/// that must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function multiplexBatchSellTokenForToken(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
BatchSellSubcall[] calldata calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Sells attached ETH via the given sequence of tokens
|
||||
/// and calls. `tokens[0]` must be WETH.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function multiplexMultiHopSellEthForToken(
|
||||
address[] calldata tokens,
|
||||
MultiHopSellSubcall[] calldata calls,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 outputTokenAmount);
|
||||
returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// for ETH via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` must be WETH.
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param minBuyAmount The minimum amount of ETH that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of ETH bought.
|
||||
function multiplexMultiHopSellTokenForEth(
|
||||
address[] calldata tokens,
|
||||
MultiHopSellSubcall[] calldata calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount);
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function multiplexMultiHopSellTokenForToken(
|
||||
address[] calldata tokens,
|
||||
MultiHopSellSubcall[] calldata calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
external
|
||||
returns (uint256 boughtAmount);
|
||||
}
|
||||
|
@@ -126,13 +126,18 @@ interface INativeOrdersFeature is
|
||||
/// @param signature The order signature.
|
||||
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
|
||||
/// @param taker The order taker.
|
||||
/// @param useSelfBalance Whether to use the ExchangeProxy's transient
|
||||
/// balance of taker tokens to fill the order.
|
||||
/// @param recipient The recipient of the maker tokens.
|
||||
/// @return takerTokenFilledAmount How much maker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _fillRfqOrder(
|
||||
LibNativeOrder.RfqOrder calldata order,
|
||||
LibSignature.Signature calldata signature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker
|
||||
address taker,
|
||||
bool useSelfBalance,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
|
||||
|
@@ -31,16 +31,16 @@ interface IOtcOrdersFeature {
|
||||
/// @param orderHash The canonical hash of the order.
|
||||
/// @param maker The maker of the order.
|
||||
/// @param taker The taker of the order.
|
||||
/// @param takerTokenFilledAmount How much taker token was filled.
|
||||
/// @param makerTokenFilledAmount How much maker token was filled.
|
||||
/// @param takerTokenFilledAmount How much taker token was filled.
|
||||
event OtcOrderFilled(
|
||||
bytes32 orderHash,
|
||||
address maker,
|
||||
address taker,
|
||||
address makerToken,
|
||||
address takerToken,
|
||||
uint128 takerTokenFilledAmount,
|
||||
uint128 makerTokenFilledAmount
|
||||
uint128 makerTokenFilledAmount,
|
||||
uint128 takerTokenFilledAmount
|
||||
);
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
@@ -48,15 +48,29 @@ interface IOtcOrdersFeature {
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @param unwrapWeth Whether or not to unwrap bought WETH into ETH
|
||||
/// before transferring it to the taker. Should be set to false
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
uint128 takerTokenFillAmount,
|
||||
bool unwrapWeth
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
external
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// Unwraps bought WETH into ETH before sending it to
|
||||
/// the taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrderForEth(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
external
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
|
||||
@@ -80,16 +94,64 @@ interface IOtcOrdersFeature {
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
/// @param unwrapWeth Whether or not to unwrap bought WETH into ETH
|
||||
/// before transferring it to the taker. Should be set to false
|
||||
/// if the maker token is not WETH.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillTakerSignedOtcOrder(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
LibSignature.Signature calldata takerSignature,
|
||||
bool unwrapWeth
|
||||
LibSignature.Signature calldata takerSignature
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Fully fill an OTC order. "Meta-transaction" variant,
|
||||
/// requires order to be signed by both maker and taker.
|
||||
/// Unwraps bought WETH into ETH before sending it to
|
||||
/// the taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
function fillTakerSignedOtcOrderForEth(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
LibSignature.Signature calldata takerSignature
|
||||
)
|
||||
external;
|
||||
|
||||
/// @dev Fills multiple taker-signed OTC orders.
|
||||
/// @param orders Array of OTC orders.
|
||||
/// @param makerSignatures Array of maker signatures for each order.
|
||||
/// @param takerSignatures Array of taker signatures for each order.
|
||||
/// @param unwrapWeth Array of booleans representing whether or not
|
||||
/// to unwrap bought WETH into ETH for each order. Should be set
|
||||
/// to false if the maker token is not WETH.
|
||||
/// @return successes Array of booleans representing whether or not
|
||||
/// each order in `orders` was filled successfully.
|
||||
function batchFillTakerSignedOtcOrders(
|
||||
LibNativeOrder.OtcOrder[] calldata orders,
|
||||
LibSignature.Signature[] calldata makerSignatures,
|
||||
LibSignature.Signature[] calldata takerSignatures,
|
||||
bool[] calldata unwrapWeth
|
||||
)
|
||||
external
|
||||
returns (bool[] memory successes);
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// Internal variant.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerTokenFillAmount Maximum taker token amount to fill this
|
||||
/// order with.
|
||||
/// @param taker The address to fill the order in the context of.
|
||||
/// @param useSelfBalance Whether to use the Exchange Proxy's balance
|
||||
/// of input tokens.
|
||||
/// @param recipient The recipient of the bought maker tokens.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker,
|
||||
bool useSelfBalance,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
|
||||
|
@@ -59,6 +59,10 @@ interface ITransformERC20Feature {
|
||||
// The transformations to execute on the token balance(s)
|
||||
// in sequence.
|
||||
Transformation[] transformations;
|
||||
// Whether to use the Exchange Proxy's balance of `inputToken`.
|
||||
bool useSelfBalance;
|
||||
// The recipient of the bought `outputToken`.
|
||||
address payable recipient;
|
||||
}
|
||||
|
||||
/// @dev Raised upon a successful `transformERC20`.
|
||||
|
@@ -70,6 +70,22 @@ interface IUniswapV3Feature {
|
||||
external
|
||||
returns (uint256 buyAmount);
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// Private variant, uses tokens held by `address(this)`.
|
||||
/// @param encodedPath Uniswap-encoded path.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for sender.
|
||||
/// @return buyAmount Amount of the last token in the path bought.
|
||||
function _sellHeldTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
returns (uint256 buyAmount);
|
||||
|
||||
/// @dev The UniswapV3 pool swap callback which pays the funds requested
|
||||
/// by the caller/pool to the pool. Can only be called by a valid
|
||||
/// UniswapV3 pool.
|
||||
|
@@ -0,0 +1,742 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../external/ILiquidityProviderSandbox.sol";
|
||||
import "../../fixins/FixinCommon.sol";
|
||||
import "../../fixins/FixinEIP712.sol";
|
||||
import "../../migrations/LibMigrate.sol";
|
||||
import "../interfaces/IFeature.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
import "./MultiplexLiquidityProvider.sol";
|
||||
import "./MultiplexOtc.sol";
|
||||
import "./MultiplexRfq.sol";
|
||||
import "./MultiplexTransformERC20.sol";
|
||||
import "./MultiplexUniswapV2.sol";
|
||||
import "./MultiplexUniswapV3.sol";
|
||||
|
||||
|
||||
/// @dev This feature enables efficient batch and multi-hop trades
|
||||
/// using different liquidity sources.
|
||||
contract MultiplexFeature is
|
||||
IFeature,
|
||||
IMultiplexFeature,
|
||||
FixinCommon,
|
||||
MultiplexLiquidityProvider,
|
||||
MultiplexOtc,
|
||||
MultiplexRfq,
|
||||
MultiplexTransformERC20,
|
||||
MultiplexUniswapV2,
|
||||
MultiplexUniswapV3
|
||||
{
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MultiplexFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(2, 0, 0);
|
||||
/// @dev The highest bit of a uint256 value.
|
||||
uint256 private constant HIGH_BIT = 2 ** 255;
|
||||
/// @dev Mask of the lower 255 bits of a uint256 value.
|
||||
uint256 private constant LOWER_255_BITS = HIGH_BIT - 1;
|
||||
|
||||
/// @dev The WETH token contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
constructor(
|
||||
address zeroExAddress,
|
||||
IEtherTokenV06 weth,
|
||||
ILiquidityProviderSandbox sandbox,
|
||||
address uniswapFactory,
|
||||
address sushiswapFactory,
|
||||
bytes32 uniswapPairInitCodeHash,
|
||||
bytes32 sushiswapPairInitCodeHash
|
||||
)
|
||||
public
|
||||
FixinEIP712(zeroExAddress)
|
||||
MultiplexLiquidityProvider(sandbox)
|
||||
MultiplexUniswapV2(
|
||||
uniswapFactory,
|
||||
sushiswapFactory,
|
||||
uniswapPairInitCodeHash,
|
||||
sushiswapPairInitCodeHash
|
||||
)
|
||||
{
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
/// @dev Initialize and register this feature.
|
||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||
/// @return success `LibMigrate.SUCCESS` on success.
|
||||
function migrate()
|
||||
external
|
||||
returns (bytes4 success)
|
||||
{
|
||||
_registerFeatureFunction(this.multiplexBatchSellEthForToken.selector);
|
||||
_registerFeatureFunction(this.multiplexBatchSellTokenForEth.selector);
|
||||
_registerFeatureFunction(this.multiplexBatchSellTokenForToken.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellEthForToken.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellTokenForEth.selector);
|
||||
_registerFeatureFunction(this.multiplexMultiHopSellTokenForToken.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Sells attached ETH for `outputToken` using the provided
|
||||
/// calls.
|
||||
/// @param outputToken The token to buy.
|
||||
/// @param calls The calls to use to sell the attached ETH.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken` that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function multiplexBatchSellEthForToken(
|
||||
IERC20TokenV06 outputToken,
|
||||
BatchSellSubcall[] memory calls,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
payable
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Wrap ETH.
|
||||
WETH.deposit{value: msg.value}();
|
||||
// WETH is now held by this contract,
|
||||
// so `useSelfBalance` is true.
|
||||
return _multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: WETH,
|
||||
outputToken: outputToken,
|
||||
sellAmount: msg.value,
|
||||
calls: calls,
|
||||
useSelfBalance: true,
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Sells `sellAmount` of the given `inputToken` for ETH
|
||||
/// using the provided calls.
|
||||
/// @param inputToken The token to sell.
|
||||
/// @param calls The calls to use to sell the input tokens.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of ETH that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of ETH bought.
|
||||
function multiplexBatchSellTokenForEth(
|
||||
IERC20TokenV06 inputToken,
|
||||
BatchSellSubcall[] memory calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// The outputToken is implicitly WETH. The `recipient`
|
||||
// of the WETH is set to this contract, since we
|
||||
// must unwrap the WETH and transfer the resulting ETH.
|
||||
boughtAmount = _multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: WETH,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this)
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
// Unwrap WETH.
|
||||
WETH.withdraw(boughtAmount);
|
||||
// Transfer ETH to `msg.sender`.
|
||||
_transferEth(msg.sender, boughtAmount);
|
||||
}
|
||||
|
||||
/// @dev Sells `sellAmount` of the given `inputToken` for
|
||||
/// `outputToken` using the provided calls.
|
||||
/// @param inputToken The token to sell.
|
||||
/// @param outputToken The token to buy.
|
||||
/// @param calls The calls to use to sell the input tokens.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken`
|
||||
/// that must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function multiplexBatchSellTokenForToken(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
BatchSellSubcall[] memory calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
return _multiplexBatchSell(
|
||||
BatchSellParams({
|
||||
inputToken: inputToken,
|
||||
outputToken: outputToken,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a batch sell and checks that at least
|
||||
/// `minBuyAmount` of `outputToken` was bought.
|
||||
/// @param params Batch sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of `outputToken` that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of `outputToken` bought.
|
||||
function _multiplexBatchSell(
|
||||
BatchSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
private
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Cache the recipient's initial balance of the output token.
|
||||
uint256 balanceBefore = params.outputToken.balanceOf(params.recipient);
|
||||
// Execute the batch sell.
|
||||
BatchSellState memory state = _executeBatchSell(params);
|
||||
// Compute the change in balance of the output token.
|
||||
uint256 balanceDelta = params.outputToken.balanceOf(params.recipient)
|
||||
.safeSub(balanceBefore);
|
||||
// Use the minimum of the balanceDelta and the returned bought
|
||||
// amount in case of weird tokens and whatnot.
|
||||
boughtAmount = LibSafeMathV06.min256(balanceDelta, state.boughtAmount);
|
||||
// Enforce `minBuyAmount`.
|
||||
require(
|
||||
boughtAmount >= minBuyAmount,
|
||||
"MultiplexFeature::_multiplexBatchSell/UNDERBOUGHT"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Sells attached ETH via the given sequence of tokens
|
||||
/// and calls. `tokens[0]` must be WETH.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function multiplexMultiHopSellEthForToken(
|
||||
address[] memory tokens,
|
||||
MultiHopSellSubcall[] memory calls,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
payable
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// First token must be WETH.
|
||||
require(
|
||||
tokens[0] == address(WETH),
|
||||
"MultiplexFeature::multiplexMultiHopSellEthForToken/NOT_WETH"
|
||||
);
|
||||
// Wrap ETH.
|
||||
WETH.deposit{value: msg.value}();
|
||||
// WETH is now held by this contract,
|
||||
// so `useSelfBalance` is true.
|
||||
return _multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: msg.value,
|
||||
calls: calls,
|
||||
useSelfBalance: true,
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// for ETH via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` must be WETH.
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of ETH that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of ETH bought.
|
||||
function multiplexMultiHopSellTokenForEth(
|
||||
address[] memory tokens,
|
||||
MultiHopSellSubcall[] memory calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Last token must be WETH.
|
||||
require(
|
||||
tokens[tokens.length - 1] == address(WETH),
|
||||
"MultiplexFeature::multiplexMultiHopSellTokenForEth/NOT_WETH"
|
||||
);
|
||||
// The `recipient of the WETH is set to this contract, since
|
||||
// we must unwrap the WETH and transfer the resulting ETH.
|
||||
boughtAmount = _multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: address(this)
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
// Unwrap WETH.
|
||||
WETH.withdraw(boughtAmount);
|
||||
// Transfer ETH to `msg.sender`.
|
||||
_transferEth(msg.sender, boughtAmount);
|
||||
}
|
||||
|
||||
/// @dev Sells `sellAmount` of the input token (`tokens[0]`)
|
||||
/// via the given sequence of tokens and calls.
|
||||
/// The last token in `tokens` is the output token that
|
||||
/// will ultimately be sent to `msg.sender`
|
||||
/// @param tokens The sequence of tokens to use for the sell,
|
||||
/// i.e. `tokens[i]` will be sold for `tokens[i+1]` via
|
||||
/// `calls[i]`.
|
||||
/// @param calls The sequence of calls to use for the sell.
|
||||
/// @param sellAmount The amount of `inputToken` to sell.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function multiplexMultiHopSellTokenForToken(
|
||||
address[] memory tokens,
|
||||
MultiHopSellSubcall[] memory calls,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
return _multiplexMultiHopSell(
|
||||
MultiHopSellParams({
|
||||
tokens: tokens,
|
||||
sellAmount: sellAmount,
|
||||
calls: calls,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender
|
||||
}),
|
||||
minBuyAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Executes a multi-hop sell and checks that at least
|
||||
/// `minBuyAmount` of output tokens were bought.
|
||||
/// @param params Multi-hop sell parameters.
|
||||
/// @param minBuyAmount The minimum amount of output tokens that
|
||||
/// must be bought for this function to not revert.
|
||||
/// @return boughtAmount The amount of output tokens bought.
|
||||
function _multiplexMultiHopSell(
|
||||
MultiHopSellParams memory params,
|
||||
uint256 minBuyAmount
|
||||
)
|
||||
private
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// There should be one call/hop between every two tokens
|
||||
// in the path.
|
||||
// tokens[0]––calls[0]––>tokens[1]––...––calls[n-1]––>tokens[n]
|
||||
require(
|
||||
params.tokens.length == params.calls.length + 1,
|
||||
"MultiplexFeature::_multiplexMultiHopSell/MISMATCHED_ARRAY_LENGTHS"
|
||||
);
|
||||
// The output token is the last token in the path.
|
||||
IERC20TokenV06 outputToken = IERC20TokenV06(
|
||||
params.tokens[params.tokens.length - 1]
|
||||
);
|
||||
// Cache the recipient's balance of the output token.
|
||||
uint256 balanceBefore = outputToken.balanceOf(params.recipient);
|
||||
// Execute the multi-hop sell.
|
||||
MultiHopSellState memory state = _executeMultiHopSell(params);
|
||||
// Compute the change in balance of the output token.
|
||||
uint256 balanceDelta = outputToken.balanceOf(params.recipient)
|
||||
.safeSub(balanceBefore);
|
||||
// Use the minimum of the balanceDelta and the returned bought
|
||||
// amount in case of weird tokens and whatnot.
|
||||
boughtAmount = LibSafeMathV06.min256(balanceDelta, state.outputTokenAmount);
|
||||
// Enforce `minBuyAmount`.
|
||||
require(
|
||||
boughtAmount >= minBuyAmount,
|
||||
"MultiplexFeature::_multiplexMultiHopSell/UNDERBOUGHT"
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Iterates through the constituent calls of a batch
|
||||
/// sell and executes each one, until the full amount
|
||||
// has been sold.
|
||||
/// @param params Batch sell parameters.
|
||||
/// @return state A struct containing the amounts of `inputToken`
|
||||
/// sold and `outputToken` bought.
|
||||
function _executeBatchSell(BatchSellParams memory params)
|
||||
private
|
||||
returns (BatchSellState memory state)
|
||||
{
|
||||
// Iterate through the calls and execute each one
|
||||
// until the full amount has been sold.
|
||||
for (uint256 i = 0; i != params.calls.length; i++) {
|
||||
// Check if we've hit our target.
|
||||
if (state.soldAmount >= params.sellAmount) { break; }
|
||||
BatchSellSubcall memory subcall = params.calls[i];
|
||||
// Compute the input token amount.
|
||||
uint256 inputTokenAmount = _normalizeSellAmount(
|
||||
subcall.sellAmount,
|
||||
params.sellAmount,
|
||||
state.soldAmount
|
||||
);
|
||||
if (subcall.id == MultiplexSubcall.RFQ) {
|
||||
_batchSellRfqOrder(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.OTC) {
|
||||
_batchSellOtcOrder(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV2) {
|
||||
_batchSellUniswapV2(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV3) {
|
||||
_batchSellUniswapV3(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.LiquidityProvider) {
|
||||
_batchSellLiquidityProvider(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.TransformERC20) {
|
||||
_batchSellTransformERC20(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.MultiHopSell) {
|
||||
_nestedMultiHopSell(
|
||||
state,
|
||||
params,
|
||||
subcall.data,
|
||||
inputTokenAmount
|
||||
);
|
||||
} else {
|
||||
revert("MultiplexFeature::_executeBatchSell/INVALID_SUBCALL");
|
||||
}
|
||||
}
|
||||
require(
|
||||
state.soldAmount == params.sellAmount,
|
||||
"MultiplexFeature::_executeBatchSell/INCORRECT_AMOUNT_SOLD"
|
||||
);
|
||||
}
|
||||
|
||||
// This function executes a sequence of fills "hopping" through the
|
||||
// path of tokens given by `params.tokens`.
|
||||
function _executeMultiHopSell(MultiHopSellParams memory params)
|
||||
private
|
||||
returns (MultiHopSellState memory state)
|
||||
{
|
||||
// This variable is used for the input and output amounts of
|
||||
// each hop. After the final hop, this will contain the output
|
||||
// amount of the multi-hop fill.
|
||||
state.outputTokenAmount = params.sellAmount;
|
||||
// The first call may expect the input tokens to be held by
|
||||
// `msg.sender`, `address(this)`, or some other address.
|
||||
// Compute the expected address and transfer the input tokens
|
||||
// there if necessary.
|
||||
state.from = _computeHopTarget(params, 0);
|
||||
// If the input tokens are currently held by `msg.sender` but
|
||||
// the first hop expects them elsewhere, perform a `transferFrom`.
|
||||
if (!params.useSelfBalance && state.from != msg.sender) {
|
||||
_transferERC20TokensFrom(
|
||||
IERC20TokenV06(params.tokens[0]),
|
||||
msg.sender,
|
||||
state.from,
|
||||
params.sellAmount
|
||||
);
|
||||
}
|
||||
// If the input tokens are currently held by `address(this)` but
|
||||
// the first hop expects them elsewhere, perform a `transfer`.
|
||||
if (params.useSelfBalance && state.from != address(this)) {
|
||||
_transferERC20Tokens(
|
||||
IERC20TokenV06(params.tokens[0]),
|
||||
state.from,
|
||||
params.sellAmount
|
||||
);
|
||||
}
|
||||
// Iterate through the calls and execute each one.
|
||||
for (state.hopIndex = 0; state.hopIndex != params.calls.length; state.hopIndex++) {
|
||||
MultiHopSellSubcall memory subcall = params.calls[state.hopIndex];
|
||||
// Compute the recipient of the tokens that will be
|
||||
// bought by the current hop.
|
||||
state.to = _computeHopTarget(params, state.hopIndex + 1);
|
||||
|
||||
if (subcall.id == MultiplexSubcall.UniswapV2) {
|
||||
_multiHopSellUniswapV2(
|
||||
state,
|
||||
params,
|
||||
subcall.data
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.UniswapV3) {
|
||||
_multiHopSellUniswapV3(state, subcall.data);
|
||||
} else if (subcall.id == MultiplexSubcall.LiquidityProvider) {
|
||||
_multiHopSellLiquidityProvider(
|
||||
state,
|
||||
params,
|
||||
subcall.data
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.BatchSell) {
|
||||
_nestedBatchSell(
|
||||
state,
|
||||
params,
|
||||
subcall.data
|
||||
);
|
||||
} else {
|
||||
revert("MultiplexFeature::_executeMultiHopSell/INVALID_SUBCALL");
|
||||
}
|
||||
// The recipient of the current hop will be the source
|
||||
// of tokens for the next hop.
|
||||
state.from = state.to;
|
||||
}
|
||||
}
|
||||
|
||||
function _nestedMultiHopSell(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory data,
|
||||
uint256 sellAmount
|
||||
)
|
||||
private
|
||||
{
|
||||
MultiHopSellParams memory multiHopParams;
|
||||
// Decode the tokens and calls for the nested
|
||||
// multi-hop sell.
|
||||
(
|
||||
multiHopParams.tokens,
|
||||
multiHopParams.calls
|
||||
) = abi.decode(
|
||||
data,
|
||||
(address[], MultiHopSellSubcall[])
|
||||
);
|
||||
multiHopParams.sellAmount = sellAmount;
|
||||
// If the batch sell is using input tokens held by
|
||||
// `address(this)`, then so should the nested
|
||||
// multi-hop sell.
|
||||
multiHopParams.useSelfBalance = params.useSelfBalance;
|
||||
// Likewise, the recipient of the multi-hop sell is
|
||||
// equal to the recipient of its containing batch sell.
|
||||
multiHopParams.recipient = params.recipient;
|
||||
// Execute the nested multi-hop sell.
|
||||
uint256 outputTokenAmount =
|
||||
_executeMultiHopSell(multiHopParams).outputTokenAmount;
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(sellAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(outputTokenAmount);
|
||||
}
|
||||
|
||||
function _nestedBatchSell(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory data
|
||||
)
|
||||
private
|
||||
{
|
||||
BatchSellParams memory batchSellParams;
|
||||
// Decode the calls for the nested batch sell.
|
||||
batchSellParams.calls = abi.decode(
|
||||
data,
|
||||
(BatchSellSubcall[])
|
||||
);
|
||||
// The input and output tokens of the batch
|
||||
// sell are the current and next tokens in
|
||||
// `params.tokens`, respectively.
|
||||
batchSellParams.inputToken = IERC20TokenV06(
|
||||
params.tokens[state.hopIndex]
|
||||
);
|
||||
batchSellParams.outputToken = IERC20TokenV06(
|
||||
params.tokens[state.hopIndex + 1]
|
||||
);
|
||||
// The `sellAmount` for the batch sell is the
|
||||
// `outputTokenAmount` from the previous hop.
|
||||
batchSellParams.sellAmount = state.outputTokenAmount;
|
||||
// If the nested batch sell is the first hop
|
||||
// and `useSelfBalance` for the containing multi-
|
||||
// hop sell is false, the nested batch sell should
|
||||
// pull tokens from `msg.sender` (so `batchSellParams.useSelfBalance`
|
||||
// should be false). Otherwise `batchSellParams.useSelfBalance`
|
||||
// should be true.
|
||||
batchSellParams.useSelfBalance = state.hopIndex > 0 || params.useSelfBalance;
|
||||
// `state.to` has been populated with the address
|
||||
// that should receive the output tokens of the
|
||||
// batch sell.
|
||||
batchSellParams.recipient = state.to;
|
||||
// Execute the nested batch sell.
|
||||
state.outputTokenAmount =
|
||||
_executeBatchSell(batchSellParams).boughtAmount;
|
||||
}
|
||||
|
||||
// Transfers some amount of ETH to the given recipient and
|
||||
// reverts if the transfer fails.
|
||||
function _transferEth(address payable recipient, uint256 amount)
|
||||
private
|
||||
{
|
||||
(bool success,) = recipient.call{value: amount}("");
|
||||
require(success, "MultiplexFeature::_transferEth/TRANSFER_FAILED");
|
||||
}
|
||||
|
||||
// This function computes the "target" address of hop index `i` within
|
||||
// a multi-hop sell.
|
||||
// If `i == 0`, the target is the address which should hold the input
|
||||
// tokens prior to executing `calls[0]`. Otherwise, it is the address
|
||||
// that should receive `tokens[i]` upon executing `calls[i-1]`.
|
||||
function _computeHopTarget(
|
||||
MultiHopSellParams memory params,
|
||||
uint256 i
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (address target)
|
||||
{
|
||||
if (i == params.calls.length) {
|
||||
// The last call should send the output tokens to the
|
||||
// multi-hop sell recipient.
|
||||
target = params.recipient;
|
||||
} else {
|
||||
MultiHopSellSubcall memory subcall = params.calls[i];
|
||||
if (subcall.id == MultiplexSubcall.UniswapV2) {
|
||||
// UniswapV2 (and Sushiswap) allow tokens to be
|
||||
// transferred into the pair contract before `swap`
|
||||
// is called, so we compute the pair contract's address.
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
subcall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
target = _computeUniswapPairAddress(
|
||||
tokens[0],
|
||||
tokens[1],
|
||||
isSushi
|
||||
);
|
||||
} else if (subcall.id == MultiplexSubcall.LiquidityProvider) {
|
||||
// Similar to UniswapV2, LiquidityProvider contracts
|
||||
// allow tokens to be transferred in before the swap
|
||||
// is executed, so we the target is the address encoded
|
||||
// in the subcall data.
|
||||
(target,) = abi.decode(
|
||||
subcall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
} else if (
|
||||
subcall.id == MultiplexSubcall.UniswapV3 ||
|
||||
subcall.id == MultiplexSubcall.BatchSell
|
||||
) {
|
||||
// UniswapV3 uses a callback to pull in the tokens being
|
||||
// sold to it. The callback implemented in `UniswapV3Feature`
|
||||
// can either:
|
||||
// - call `transferFrom` to move tokens from `msg.sender` to the
|
||||
// UniswapV3 pool, or
|
||||
// - call `transfer` to move tokens from `address(this)` to the
|
||||
// UniswapV3 pool.
|
||||
// A nested batch sell is similar, in that it can either:
|
||||
// - use tokens from `msg.sender`, or
|
||||
// - use tokens held by `address(this)`.
|
||||
|
||||
// Suppose UniswapV3/BatchSell is the first call in the multi-hop
|
||||
// path. The input tokens are either held by `msg.sender`,
|
||||
// or in the case of `multiplexMultiHopSellEthForToken` WETH is
|
||||
// held by `address(this)`. The target is set accordingly.
|
||||
|
||||
// If this is _not_ the first call in the multi-hop path, we
|
||||
// are dealing with an "intermediate" token in the multi-hop path,
|
||||
// which `msg.sender` may not have an allowance set for. Thus
|
||||
// target must be set to `address(this)` for `i > 0`.
|
||||
if (i == 0 && !params.useSelfBalance) {
|
||||
target = msg.sender;
|
||||
} else {
|
||||
target = address(this);
|
||||
}
|
||||
} else {
|
||||
revert("MultiplexFeature::_computeHopTarget/INVALID_SUBCALL");
|
||||
}
|
||||
}
|
||||
require(
|
||||
target != address(0),
|
||||
"MultiplexFeature::_computeHopTarget/TARGET_IS_NULL"
|
||||
);
|
||||
}
|
||||
|
||||
// If `rawAmount` encodes a proportion of `totalSellAmount`, this function
|
||||
// converts it to an absolute quantity. Caps the normalized amount to
|
||||
// the remaining sell amount (`totalSellAmount - soldAmount`).
|
||||
function _normalizeSellAmount(
|
||||
uint256 rawAmount,
|
||||
uint256 totalSellAmount,
|
||||
uint256 soldAmount
|
||||
)
|
||||
private
|
||||
pure
|
||||
returns (uint256 normalized)
|
||||
{
|
||||
if ((rawAmount & HIGH_BIT) == HIGH_BIT) {
|
||||
// If the high bit of `rawAmount` is set then the lower 255 bits
|
||||
// specify a fraction of `totalSellAmount`.
|
||||
return LibSafeMathV06.min256(
|
||||
totalSellAmount
|
||||
* LibSafeMathV06.min256(rawAmount & LOWER_255_BITS, 1e18)
|
||||
/ 1e18,
|
||||
totalSellAmount.safeSub(soldAmount)
|
||||
);
|
||||
} else {
|
||||
return LibSafeMathV06.min256(
|
||||
rawAmount,
|
||||
totalSellAmount.safeSub(soldAmount)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,202 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../external/ILiquidityProviderSandbox.sol";
|
||||
import "../../fixins/FixinCommon.sol";
|
||||
import "../../fixins/FixinTokenSpender.sol";
|
||||
import "../../vendor/ILiquidityProvider.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexLiquidityProvider is
|
||||
FixinCommon,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
// Same event fired by LiquidityProviderFeature
|
||||
event LiquidityProviderSwap(
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
uint256 outputTokenAmount,
|
||||
address provider,
|
||||
address recipient
|
||||
);
|
||||
|
||||
/// @dev The sandbox contract address.
|
||||
ILiquidityProviderSandbox private immutable SANDBOX;
|
||||
|
||||
constructor(ILiquidityProviderSandbox sandbox)
|
||||
internal
|
||||
{
|
||||
SANDBOX = sandbox;
|
||||
}
|
||||
|
||||
// A payable external function that we can delegatecall to
|
||||
// swallow reverts and roll back the input token transfer.
|
||||
function _batchSellLiquidityProviderExternal(
|
||||
IMultiplexFeature.BatchSellParams calldata params,
|
||||
bytes calldata wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Revert if not a delegatecall.
|
||||
require(
|
||||
address(this) != _implementation,
|
||||
"MultiplexLiquidityProvider::_batchSellLiquidityProviderExternal/ONLY_DELEGATECALL"
|
||||
);
|
||||
|
||||
// Decode the provider address and auxiliary data.
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCallData,
|
||||
(address, bytes)
|
||||
);
|
||||
|
||||
if (params.useSelfBalance) {
|
||||
// If `useSelfBalance` is true, use the input tokens
|
||||
// held by `address(this)`.
|
||||
_transferERC20Tokens(
|
||||
params.inputToken,
|
||||
provider,
|
||||
sellAmount
|
||||
);
|
||||
} else {
|
||||
// Otherwise, transfer the input tokens from `msg.sender`.
|
||||
_transferERC20TokensFrom(
|
||||
params.inputToken,
|
||||
msg.sender,
|
||||
provider,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
// Cache the recipient's balance of the output token.
|
||||
uint256 balanceBefore = params.outputToken
|
||||
.balanceOf(params.recipient);
|
||||
// Execute the swap.
|
||||
SANDBOX.executeSellTokenForToken(
|
||||
ILiquidityProvider(provider),
|
||||
params.inputToken,
|
||||
params.outputToken,
|
||||
params.recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
// Compute amount of output token received by the
|
||||
// recipient.
|
||||
boughtAmount = params.outputToken
|
||||
.balanceOf(params.recipient)
|
||||
.safeSub(balanceBefore);
|
||||
|
||||
emit LiquidityProviderSwap(
|
||||
address(params.inputToken),
|
||||
address(params.outputToken),
|
||||
sellAmount,
|
||||
boughtAmount,
|
||||
provider,
|
||||
params.recipient
|
||||
);
|
||||
}
|
||||
|
||||
function _batchSellLiquidityProvider(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
// Swallow reverts
|
||||
(bool success, bytes memory resultData) = _implementation.delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
this._batchSellLiquidityProviderExternal.selector,
|
||||
params,
|
||||
wrappedCallData,
|
||||
sellAmount
|
||||
)
|
||||
);
|
||||
if (success) {
|
||||
// Decode the output token amount on success.
|
||||
uint256 boughtAmount = abi.decode(resultData, (uint256));
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(sellAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(boughtAmount);
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called after tokens have already been transferred
|
||||
// into the liquidity provider contract (in the previous hop).
|
||||
function _multiHopSellLiquidityProvider(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory wrappedCallData
|
||||
)
|
||||
internal
|
||||
{
|
||||
IERC20TokenV06 inputToken = IERC20TokenV06(params.tokens[state.hopIndex]);
|
||||
IERC20TokenV06 outputToken = IERC20TokenV06(params.tokens[state.hopIndex + 1]);
|
||||
// Decode the provider address and auxiliary data.
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCallData,
|
||||
(address, bytes)
|
||||
);
|
||||
// Cache the recipient's balance of the output token.
|
||||
uint256 balanceBefore = outputToken
|
||||
.balanceOf(state.to);
|
||||
// Execute the swap.
|
||||
SANDBOX.executeSellTokenForToken(
|
||||
ILiquidityProvider(provider),
|
||||
inputToken,
|
||||
outputToken,
|
||||
state.to,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
// The previous `ouputTokenAmount` was effectively the
|
||||
// input amount for this call. Cache the value before
|
||||
// overwriting it with the new output token amount so
|
||||
// that both the input and ouput amounts can be in the
|
||||
// `LiquidityProviderSwap` event.
|
||||
uint256 sellAmount = state.outputTokenAmount;
|
||||
// Compute amount of output token received by the
|
||||
// recipient.
|
||||
state.outputTokenAmount = outputToken
|
||||
.balanceOf(state.to)
|
||||
.safeSub(balanceBefore);
|
||||
|
||||
emit LiquidityProviderSwap(
|
||||
address(inputToken),
|
||||
address(outputToken),
|
||||
sellAmount,
|
||||
state.outputTokenAmount,
|
||||
provider,
|
||||
state.to
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../fixins/FixinEIP712.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
import "../interfaces/IOtcOrdersFeature.sol";
|
||||
import "../libs/LibNativeOrder.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexOtc is
|
||||
FixinEIP712
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
event ExpiredOtcOrder(
|
||||
bytes32 orderHash,
|
||||
address maker,
|
||||
uint64 expiry
|
||||
);
|
||||
|
||||
function _batchSellOtcOrder(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
// Decode the Otc order and signature.
|
||||
(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory signature
|
||||
) = abi.decode(
|
||||
wrappedCallData,
|
||||
(LibNativeOrder.OtcOrder, LibSignature.Signature)
|
||||
);
|
||||
// Validate tokens.
|
||||
require(
|
||||
order.takerToken == params.inputToken &&
|
||||
order.makerToken == params.outputToken,
|
||||
"MultiplexOtc::_batchSellOtcOrder/OTC_ORDER_INVALID_TOKENS"
|
||||
);
|
||||
// Pre-emptively check if the order is expired.
|
||||
uint64 expiry = uint64(order.expiryAndNonce >> 192);
|
||||
if (expiry <= uint64(block.timestamp)) {
|
||||
bytes32 orderHash = _getEIP712Hash(
|
||||
LibNativeOrder.getOtcOrderStructHash(order)
|
||||
);
|
||||
emit ExpiredOtcOrder(
|
||||
orderHash,
|
||||
order.maker,
|
||||
expiry
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Try filling the Otc order. Swallows reverts.
|
||||
try
|
||||
IOtcOrdersFeature(address(this))._fillOtcOrder
|
||||
(
|
||||
order,
|
||||
signature,
|
||||
sellAmount.safeDowncastToUint128(),
|
||||
msg.sender,
|
||||
params.useSelfBalance,
|
||||
params.recipient
|
||||
)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(takerTokenFilledAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(makerTokenFilledAmount);
|
||||
} catch {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../fixins/FixinEIP712.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
import "../interfaces/INativeOrdersFeature.sol";
|
||||
import "../libs/LibNativeOrder.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexRfq is
|
||||
FixinEIP712
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
event ExpiredRfqOrder(
|
||||
bytes32 orderHash,
|
||||
address maker,
|
||||
uint64 expiry
|
||||
);
|
||||
|
||||
function _batchSellRfqOrder(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
// Decode the RFQ order and signature.
|
||||
(
|
||||
LibNativeOrder.RfqOrder memory order,
|
||||
LibSignature.Signature memory signature
|
||||
) = abi.decode(
|
||||
wrappedCallData,
|
||||
(LibNativeOrder.RfqOrder, LibSignature.Signature)
|
||||
);
|
||||
// Pre-emptively check if the order is expired.
|
||||
if (order.expiry <= uint64(block.timestamp)) {
|
||||
bytes32 orderHash = _getEIP712Hash(
|
||||
LibNativeOrder.getRfqOrderStructHash(order)
|
||||
);
|
||||
emit ExpiredRfqOrder(
|
||||
orderHash,
|
||||
order.maker,
|
||||
order.expiry
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Validate tokens.
|
||||
require(
|
||||
order.takerToken == params.inputToken &&
|
||||
order.makerToken == params.outputToken,
|
||||
"MultiplexRfq::_batchSellRfqOrder/RFQ_ORDER_INVALID_TOKENS"
|
||||
);
|
||||
// Try filling the RFQ order. Swallows reverts.
|
||||
try
|
||||
INativeOrdersFeature(address(this))._fillRfqOrder
|
||||
(
|
||||
order,
|
||||
signature,
|
||||
sellAmount.safeDowncastToUint128(),
|
||||
msg.sender,
|
||||
params.useSelfBalance,
|
||||
params.recipient
|
||||
)
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(takerTokenFilledAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(makerTokenFilledAmount);
|
||||
} catch {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
import "../interfaces/ITransformERC20Feature.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexTransformERC20 {
|
||||
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
function _batchSellTransformERC20(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
// We want the TransformedERC20 event to have
|
||||
// `msg.sender` as the taker.
|
||||
args.taker = msg.sender;
|
||||
args.inputToken = params.inputToken;
|
||||
args.outputToken = params.outputToken;
|
||||
args.inputTokenAmount = sellAmount;
|
||||
args.minOutputTokenAmount = 0;
|
||||
args.useSelfBalance = params.useSelfBalance;
|
||||
args.recipient = payable(params.recipient);
|
||||
(args.transformations) = abi.decode(
|
||||
wrappedCallData,
|
||||
(ITransformERC20Feature.Transformation[])
|
||||
);
|
||||
// Execute the transformations and swallow reverts.
|
||||
try ITransformERC20Feature(address(this))._transformERC20
|
||||
(args)
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(sellAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(outputTokenAmount);
|
||||
} catch {}
|
||||
}
|
||||
}
|
@@ -0,0 +1,290 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../fixins/FixinCommon.sol";
|
||||
import "../../fixins/FixinTokenSpender.sol";
|
||||
import "../../vendor/IUniswapV2Pair.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexUniswapV2 is
|
||||
FixinCommon,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
// address of the UniswapV2Factory contract.
|
||||
address private immutable UNISWAP_FACTORY;
|
||||
// address of the (Sushiswap) UniswapV2Factory contract.
|
||||
address private immutable SUSHISWAP_FACTORY;
|
||||
// Init code hash of the UniswapV2Pair contract.
|
||||
bytes32 private immutable UNISWAP_PAIR_INIT_CODE_HASH;
|
||||
// Init code hash of the (Sushiswap) UniswapV2Pair contract.
|
||||
bytes32 private immutable SUSHISWAP_PAIR_INIT_CODE_HASH;
|
||||
|
||||
constructor(
|
||||
address uniswapFactory,
|
||||
address sushiswapFactory,
|
||||
bytes32 uniswapPairInitCodeHash,
|
||||
bytes32 sushiswapPairInitCodeHash
|
||||
)
|
||||
internal
|
||||
{
|
||||
UNISWAP_FACTORY = uniswapFactory;
|
||||
SUSHISWAP_FACTORY = sushiswapFactory;
|
||||
UNISWAP_PAIR_INIT_CODE_HASH = uniswapPairInitCodeHash;
|
||||
SUSHISWAP_PAIR_INIT_CODE_HASH = sushiswapPairInitCodeHash;
|
||||
}
|
||||
|
||||
// A payable external function that we can delegatecall to
|
||||
// swallow reverts and roll back the input token transfer.
|
||||
function _batchSellUniswapV2External(
|
||||
IMultiplexFeature.BatchSellParams calldata params,
|
||||
bytes calldata wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Revert is not a delegatecall.
|
||||
require(
|
||||
address(this) != _implementation,
|
||||
"MultiplexLiquidityProvider::_batchSellUniswapV2External/ONLY_DELEGATECALL"
|
||||
);
|
||||
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
wrappedCallData,
|
||||
(address[], bool)
|
||||
);
|
||||
// Validate tokens
|
||||
require(
|
||||
tokens.length >= 2 &&
|
||||
tokens[0] == address(params.inputToken) &&
|
||||
tokens[tokens.length - 1] == address(params.outputToken),
|
||||
"MultiplexUniswapV2::_batchSellUniswapV2/INVALID_TOKENS"
|
||||
);
|
||||
// Compute the address of the first Uniswap pair
|
||||
// contract that will execute a swap.
|
||||
address firstPairAddress = _computeUniswapPairAddress(
|
||||
tokens[0],
|
||||
tokens[1],
|
||||
isSushi
|
||||
);
|
||||
// `_sellToUniswapV2` assumes the input tokens have been
|
||||
// transferred into the pair contract before it is called,
|
||||
// so we transfer the tokens in now (either from `msg.sender`
|
||||
// or using the Exchange Proxy's balance).
|
||||
if (params.useSelfBalance) {
|
||||
_transferERC20Tokens(
|
||||
IERC20TokenV06(tokens[0]),
|
||||
firstPairAddress,
|
||||
sellAmount
|
||||
);
|
||||
} else {
|
||||
_transferERC20TokensFrom(
|
||||
IERC20TokenV06(tokens[0]),
|
||||
msg.sender,
|
||||
firstPairAddress,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
// Execute the Uniswap/Sushiswap trade.
|
||||
return _sellToUniswapV2(
|
||||
tokens,
|
||||
sellAmount,
|
||||
isSushi,
|
||||
firstPairAddress,
|
||||
params.recipient
|
||||
);
|
||||
}
|
||||
|
||||
function _batchSellUniswapV2(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
// Swallow reverts
|
||||
(bool success, bytes memory resultData) = _implementation.delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
this._batchSellUniswapV2External.selector,
|
||||
params,
|
||||
wrappedCallData,
|
||||
sellAmount
|
||||
)
|
||||
);
|
||||
if (success) {
|
||||
// Decode the output token amount on success.
|
||||
uint256 boughtAmount = abi.decode(resultData, (uint256));
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(sellAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(boughtAmount);
|
||||
}
|
||||
}
|
||||
|
||||
function _multiHopSellUniswapV2(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
IMultiplexFeature.MultiHopSellParams memory params,
|
||||
bytes memory wrappedCallData
|
||||
)
|
||||
internal
|
||||
{
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
wrappedCallData,
|
||||
(address[], bool)
|
||||
);
|
||||
// Validate the tokens
|
||||
require(
|
||||
tokens.length >= 2 &&
|
||||
tokens[0] == params.tokens[state.hopIndex] &&
|
||||
tokens[tokens.length - 1] == params.tokens[state.hopIndex + 1],
|
||||
"MultiplexUniswapV2::_multiHopSellUniswapV2/INVALID_TOKENS"
|
||||
);
|
||||
// Execute the Uniswap/Sushiswap trade.
|
||||
state.outputTokenAmount = _sellToUniswapV2(
|
||||
tokens,
|
||||
state.outputTokenAmount,
|
||||
isSushi,
|
||||
state.from,
|
||||
state.to
|
||||
);
|
||||
}
|
||||
|
||||
function _sellToUniswapV2(
|
||||
address[] memory tokens,
|
||||
uint256 sellAmount,
|
||||
bool isSushi,
|
||||
address pairAddress,
|
||||
address recipient
|
||||
)
|
||||
private
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
// Iterate through `tokens` perform a swap against the Uniswap
|
||||
// pair contract for each `(tokens[i], tokens[i+1])`.
|
||||
for (uint256 i = 0; i < tokens.length - 1; i++) {
|
||||
(address inputToken, address outputToken) = (tokens[i], tokens[i + 1]);
|
||||
// Compute the output token amount
|
||||
outputTokenAmount = _computeUniswapOutputAmount(
|
||||
pairAddress,
|
||||
inputToken,
|
||||
outputToken,
|
||||
sellAmount
|
||||
);
|
||||
(uint256 amount0Out, uint256 amount1Out) = inputToken < outputToken
|
||||
? (uint256(0), outputTokenAmount)
|
||||
: (outputTokenAmount, uint256(0));
|
||||
// The Uniswap pair contract will transfer the output tokens to
|
||||
// the next pair contract if there is one, otherwise transfer to
|
||||
// `recipient`.
|
||||
address to = i < tokens.length - 2
|
||||
? _computeUniswapPairAddress(outputToken, tokens[i + 2], isSushi)
|
||||
: recipient;
|
||||
// Execute the swap.
|
||||
IUniswapV2Pair(pairAddress).swap(
|
||||
amount0Out,
|
||||
amount1Out,
|
||||
to,
|
||||
new bytes(0)
|
||||
);
|
||||
// To avoid recomputing the pair address of the next pair, store
|
||||
// `to` in `pairAddress`.
|
||||
pairAddress = to;
|
||||
// The outputTokenAmount
|
||||
sellAmount = outputTokenAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the Uniswap/Sushiswap pair contract address for the
|
||||
// given tokens.
|
||||
function _computeUniswapPairAddress(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
bool isSushi
|
||||
)
|
||||
internal
|
||||
view
|
||||
returns (address pairAddress)
|
||||
{
|
||||
// Tokens are lexicographically sorted in the Uniswap contract.
|
||||
(address token0, address token1) = tokenA < tokenB
|
||||
? (tokenA, tokenB)
|
||||
: (tokenB, tokenA);
|
||||
if (isSushi) {
|
||||
// Use the Sushiswap factory address and codehash
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
SUSHISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
SUSHISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
} else {
|
||||
// Use the Uniswap factory address and codehash
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
UNISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
UNISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the the amount of output token that would be bought
|
||||
// from Uniswap/Sushiswap given the input amount.
|
||||
function _computeUniswapOutputAmount(
|
||||
address pairAddress,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 outputAmount)
|
||||
{
|
||||
// Input amount should be non-zero.
|
||||
require(
|
||||
inputAmount > 0,
|
||||
"MultiplexUniswapV2::_computeUniswapOutputAmount/INSUFFICIENT_INPUT_AMOUNT"
|
||||
);
|
||||
// Query the reserves of the pair contract.
|
||||
(uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairAddress).getReserves();
|
||||
// Reserves must be non-zero.
|
||||
require(
|
||||
reserve0 > 0 && reserve1 > 0,
|
||||
'MultiplexUniswapV2::_computeUniswapOutputAmount/INSUFFICIENT_LIQUIDITY'
|
||||
);
|
||||
// Tokens are lexicographically sorted in the Uniswap contract.
|
||||
(uint256 inputReserve, uint256 outputReserve) = inputToken < outputToken
|
||||
? (reserve0, reserve1)
|
||||
: (reserve1, reserve0);
|
||||
// Compute the output amount.
|
||||
uint256 inputAmountWithFee = inputAmount.safeMul(997);
|
||||
uint256 numerator = inputAmountWithFee.safeMul(outputReserve);
|
||||
uint256 denominator = inputReserve.safeMul(1000).safeAdd(inputAmountWithFee);
|
||||
return numerator / denominator;
|
||||
}
|
||||
}
|
@@ -0,0 +1,123 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../../fixins/FixinTokenSpender.sol";
|
||||
import "../interfaces/IMultiplexFeature.sol";
|
||||
import "../interfaces/IUniswapV3Feature.sol";
|
||||
|
||||
|
||||
abstract contract MultiplexUniswapV3 is
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
function _batchSellUniswapV3(
|
||||
IMultiplexFeature.BatchSellState memory state,
|
||||
IMultiplexFeature.BatchSellParams memory params,
|
||||
bytes memory wrappedCallData,
|
||||
uint256 sellAmount
|
||||
)
|
||||
internal
|
||||
{
|
||||
bool success;
|
||||
bytes memory resultData;
|
||||
if (params.useSelfBalance) {
|
||||
// If the tokens are held by `address(this)`, we call
|
||||
// the `onlySelf` variant `_sellHeldTokenForTokenToUniswapV3`,
|
||||
// which uses the Exchange Proxy's balance of input token.
|
||||
(success, resultData) = address(this).call(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature._sellHeldTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
sellAmount,
|
||||
0,
|
||||
params.recipient
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we self-delegatecall the normal variant
|
||||
// `sellTokenForTokenToUniswapV3`, which pulls the input token
|
||||
// from `msg.sender`.
|
||||
(success, resultData) = address(this).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
sellAmount,
|
||||
0,
|
||||
params.recipient
|
||||
)
|
||||
);
|
||||
}
|
||||
if (success) {
|
||||
// Decode the output token amount on success.
|
||||
uint256 outputTokenAmount = abi.decode(resultData, (uint256));
|
||||
// Increment the sold and bought amounts.
|
||||
state.soldAmount = state.soldAmount.safeAdd(sellAmount);
|
||||
state.boughtAmount = state.boughtAmount.safeAdd(outputTokenAmount);
|
||||
}
|
||||
}
|
||||
|
||||
function _multiHopSellUniswapV3(
|
||||
IMultiplexFeature.MultiHopSellState memory state,
|
||||
bytes memory wrappedCallData
|
||||
)
|
||||
internal
|
||||
{
|
||||
bool success;
|
||||
bytes memory resultData;
|
||||
if (state.from == address(this)) {
|
||||
// If the tokens are held by `address(this)`, we call
|
||||
// the `onlySelf` variant `_sellHeldTokenForTokenToUniswapV3`,
|
||||
// which uses the Exchange Proxy's balance of input token.
|
||||
(success, resultData) = address(this).call(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature._sellHeldTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
state.outputTokenAmount,
|
||||
0,
|
||||
state.to
|
||||
)
|
||||
);
|
||||
} else {
|
||||
// Otherwise, we self-delegatecall the normal variant
|
||||
// `sellTokenForTokenToUniswapV3`, which pulls the input token
|
||||
// from `msg.sender`.
|
||||
(success, resultData) = address(this).delegatecall(
|
||||
abi.encodeWithSelector(
|
||||
IUniswapV3Feature.sellTokenForTokenToUniswapV3.selector,
|
||||
wrappedCallData,
|
||||
state.outputTokenAmount,
|
||||
0,
|
||||
state.to
|
||||
)
|
||||
);
|
||||
}
|
||||
if (success) {
|
||||
// Decode the output token amount on success.
|
||||
state.outputTokenAmount = abi.decode(resultData, (uint256));
|
||||
} else {
|
||||
revert("MultiplexUniswapV3::_multiHopSellUniswapV3/SWAP_FAILED");
|
||||
}
|
||||
}
|
||||
}
|
@@ -52,8 +52,10 @@ abstract contract NativeOrdersSettlement is
|
||||
bytes32 orderHash;
|
||||
// Maker of the order.
|
||||
address maker;
|
||||
// Taker of the order.
|
||||
address taker;
|
||||
// The address holding the taker tokens.
|
||||
address payer;
|
||||
// Recipient of the maker tokens.
|
||||
address recipient;
|
||||
// Maker token.
|
||||
IERC20TokenV06 makerToken;
|
||||
// Taker token.
|
||||
@@ -82,6 +84,22 @@ abstract contract NativeOrdersSettlement is
|
||||
address sender;
|
||||
}
|
||||
|
||||
/// @dev Params for `_fillRfqOrderPrivate()`
|
||||
struct FillRfqOrderPrivateParams {
|
||||
LibNativeOrder.RfqOrder order;
|
||||
// The order signature.
|
||||
LibSignature.Signature signature;
|
||||
// Maximum taker token to fill this order with.
|
||||
uint128 takerTokenFillAmount;
|
||||
// The order taker.
|
||||
address taker;
|
||||
// Whether to use the Exchange Proxy's balance
|
||||
// of taker tokens.
|
||||
bool useSelfBalance;
|
||||
// The recipient of the maker tokens.
|
||||
address recipient;
|
||||
}
|
||||
|
||||
// @dev Fill results returned by `_fillLimitOrderPrivate()` and
|
||||
/// `_fillRfqOrderPrivate()`.
|
||||
struct FillNativeOrderResults {
|
||||
@@ -154,12 +172,14 @@ abstract contract NativeOrdersSettlement is
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
FillNativeOrderResults memory results =
|
||||
_fillRfqOrderPrivate(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
msg.sender
|
||||
);
|
||||
_fillRfqOrderPrivate(FillRfqOrderPrivateParams({
|
||||
order: order,
|
||||
signature: signature,
|
||||
takerTokenFillAmount: takerTokenFillAmount,
|
||||
taker: msg.sender,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender
|
||||
}));
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = (
|
||||
results.takerTokenFilledAmount,
|
||||
results.makerTokenFilledAmount
|
||||
@@ -220,12 +240,14 @@ abstract contract NativeOrdersSettlement is
|
||||
returns (uint128 makerTokenFilledAmount)
|
||||
{
|
||||
FillNativeOrderResults memory results =
|
||||
_fillRfqOrderPrivate(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
msg.sender
|
||||
);
|
||||
_fillRfqOrderPrivate(FillRfqOrderPrivateParams({
|
||||
order: order,
|
||||
signature: signature,
|
||||
takerTokenFillAmount: takerTokenFillAmount,
|
||||
taker: msg.sender,
|
||||
useSelfBalance: false,
|
||||
recipient: msg.sender
|
||||
}));
|
||||
// Must have filled exactly the amount requested.
|
||||
if (results.takerTokenFilledAmount < takerTokenFillAmount) {
|
||||
LibNativeOrdersRichErrors.FillOrKillFailedError(
|
||||
@@ -260,33 +282,36 @@ abstract contract NativeOrdersSettlement is
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
FillNativeOrderResults memory results =
|
||||
_fillLimitOrderPrivate(FillLimitOrderPrivateParams({
|
||||
order: order,
|
||||
signature: signature,
|
||||
takerTokenFillAmount: takerTokenFillAmount,
|
||||
taker: taker,
|
||||
sender: sender
|
||||
}));
|
||||
_fillLimitOrderPrivate(FillLimitOrderPrivateParams(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
taker,
|
||||
sender
|
||||
));
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = (
|
||||
results.takerTokenFilledAmount,
|
||||
results.makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fill an RFQ order. Internal variant. ETH protocol fees can be
|
||||
/// attached to this call. Any unspent ETH will be refunded to
|
||||
/// `msg.sender` (not `sender`).
|
||||
/// @dev Fill an RFQ order. Internal variant.
|
||||
/// @param order The RFQ order.
|
||||
/// @param signature The order signature.
|
||||
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
|
||||
/// @param taker The order taker.
|
||||
/// @param useSelfBalance Whether to use the ExchangeProxy's transient
|
||||
/// balance of taker tokens to fill the order.
|
||||
/// @param recipient The recipient of the maker tokens.
|
||||
/// @return takerTokenFilledAmount How much maker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function _fillRfqOrder(
|
||||
LibNativeOrder.RfqOrder memory order,
|
||||
LibSignature.Signature memory signature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker
|
||||
address taker,
|
||||
bool useSelfBalance,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
virtual
|
||||
@@ -294,12 +319,14 @@ abstract contract NativeOrdersSettlement is
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
FillNativeOrderResults memory results =
|
||||
_fillRfqOrderPrivate(
|
||||
_fillRfqOrderPrivate(FillRfqOrderPrivateParams(
|
||||
order,
|
||||
signature,
|
||||
takerTokenFillAmount,
|
||||
taker
|
||||
);
|
||||
taker,
|
||||
useSelfBalance,
|
||||
recipient
|
||||
));
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = (
|
||||
results.takerTokenFilledAmount,
|
||||
results.makerTokenFilledAmount
|
||||
@@ -387,7 +414,8 @@ abstract contract NativeOrdersSettlement is
|
||||
SettleOrderInfo({
|
||||
orderHash: orderInfo.orderHash,
|
||||
maker: params.order.maker,
|
||||
taker: params.taker,
|
||||
payer: params.taker,
|
||||
recipient: params.taker,
|
||||
makerToken: IERC20TokenV06(params.order.makerToken),
|
||||
takerToken: IERC20TokenV06(params.order.takerToken),
|
||||
makerAmount: params.order.makerAmount,
|
||||
@@ -427,22 +455,14 @@ abstract contract NativeOrdersSettlement is
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fill an RFQ order. Private variant. Does not refund protocol fees.
|
||||
/// @param order The RFQ order.
|
||||
/// @param signature The order signature.
|
||||
/// @param takerTokenFillAmount Maximum taker token to fill this order with.
|
||||
/// @param taker The order taker.
|
||||
/// @dev Fill an RFQ order. Private variant.
|
||||
/// @param params Function params.
|
||||
/// @return results Results of the fill.
|
||||
function _fillRfqOrderPrivate(
|
||||
LibNativeOrder.RfqOrder memory order,
|
||||
LibSignature.Signature memory signature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker
|
||||
)
|
||||
function _fillRfqOrderPrivate(FillRfqOrderPrivateParams memory params)
|
||||
private
|
||||
returns (FillNativeOrderResults memory results)
|
||||
{
|
||||
LibNativeOrder.OrderInfo memory orderInfo = getRfqOrderInfo(order);
|
||||
LibNativeOrder.OrderInfo memory orderInfo = getRfqOrderInfo(params.order);
|
||||
|
||||
// Must be fillable.
|
||||
if (orderInfo.status != LibNativeOrder.OrderStatus.FILLABLE) {
|
||||
@@ -457,32 +477,41 @@ abstract contract NativeOrdersSettlement is
|
||||
LibNativeOrdersStorage.getStorage();
|
||||
|
||||
// Must be fillable by the tx.origin.
|
||||
if (order.txOrigin != tx.origin && !stor.originRegistry[order.txOrigin][tx.origin]) {
|
||||
if (
|
||||
params.order.txOrigin != tx.origin &&
|
||||
!stor.originRegistry[params.order.txOrigin][tx.origin]
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByOriginError(
|
||||
orderInfo.orderHash,
|
||||
tx.origin,
|
||||
order.txOrigin
|
||||
params.order.txOrigin
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
// Must be fillable by the taker.
|
||||
if (order.taker != address(0) && order.taker != taker) {
|
||||
if (params.order.taker != address(0) && params.order.taker != params.taker) {
|
||||
LibNativeOrdersRichErrors.OrderNotFillableByTakerError(
|
||||
orderInfo.orderHash,
|
||||
taker,
|
||||
order.taker
|
||||
params.taker,
|
||||
params.order.taker
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
// Signature must be valid for the order.
|
||||
{
|
||||
address signer = LibSignature.getSignerOfHash(orderInfo.orderHash, signature);
|
||||
if (signer != order.maker && !isValidOrderSigner(order.maker, signer)) {
|
||||
address signer = LibSignature.getSignerOfHash(
|
||||
orderInfo.orderHash,
|
||||
params.signature
|
||||
);
|
||||
if (
|
||||
signer != params.order.maker &&
|
||||
!isValidOrderSigner(params.order.maker, signer)
|
||||
) {
|
||||
LibNativeOrdersRichErrors.OrderNotSignedByMakerError(
|
||||
orderInfo.orderHash,
|
||||
signer,
|
||||
order.maker
|
||||
params.order.maker
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
@@ -491,26 +520,27 @@ abstract contract NativeOrdersSettlement is
|
||||
(results.takerTokenFilledAmount, results.makerTokenFilledAmount) = _settleOrder(
|
||||
SettleOrderInfo({
|
||||
orderHash: orderInfo.orderHash,
|
||||
maker: order.maker,
|
||||
taker: taker,
|
||||
makerToken: IERC20TokenV06(order.makerToken),
|
||||
takerToken: IERC20TokenV06(order.takerToken),
|
||||
makerAmount: order.makerAmount,
|
||||
takerAmount: order.takerAmount,
|
||||
takerTokenFillAmount: takerTokenFillAmount,
|
||||
maker: params.order.maker,
|
||||
payer: params.useSelfBalance ? address(this) : params.taker,
|
||||
recipient: params.recipient,
|
||||
makerToken: IERC20TokenV06(params.order.makerToken),
|
||||
takerToken: IERC20TokenV06(params.order.takerToken),
|
||||
makerAmount: params.order.makerAmount,
|
||||
takerAmount: params.order.takerAmount,
|
||||
takerTokenFillAmount: params.takerTokenFillAmount,
|
||||
takerTokenFilledAmount: orderInfo.takerTokenFilledAmount
|
||||
})
|
||||
);
|
||||
|
||||
emit RfqOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
taker,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
params.order.maker,
|
||||
params.taker,
|
||||
address(params.order.makerToken),
|
||||
address(params.order.takerToken),
|
||||
results.takerTokenFilledAmount,
|
||||
results.makerTokenFilledAmount,
|
||||
order.pool
|
||||
params.order.pool
|
||||
);
|
||||
}
|
||||
|
||||
@@ -549,19 +579,28 @@ abstract contract NativeOrdersSettlement is
|
||||
// function if the order is cancelled.
|
||||
settleInfo.takerTokenFilledAmount.safeAdd128(takerTokenFilledAmount);
|
||||
|
||||
// Transfer taker -> maker.
|
||||
_transferERC20TokensFrom(
|
||||
settleInfo.takerToken,
|
||||
settleInfo.taker,
|
||||
settleInfo.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
if (settleInfo.payer == address(this)) {
|
||||
// Transfer this -> maker.
|
||||
_transferERC20Tokens(
|
||||
settleInfo.takerToken,
|
||||
settleInfo.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
} else {
|
||||
// Transfer taker -> maker.
|
||||
_transferERC20TokensFrom(
|
||||
settleInfo.takerToken,
|
||||
settleInfo.payer,
|
||||
settleInfo.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
// Transfer maker -> taker.
|
||||
// Transfer maker -> recipient.
|
||||
_transferERC20TokensFrom(
|
||||
settleInfo.makerToken,
|
||||
settleInfo.maker,
|
||||
settleInfo.taker,
|
||||
settleInfo.recipient,
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
@@ -137,8 +137,8 @@ contract FillQuoteTransformer is
|
||||
/// @dev Mask of the lower 255 bits of a uint256 value.
|
||||
uint256 private constant LOWER_255_BITS = HIGH_BIT - 1;
|
||||
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||
/// protocol fees will be sent to the taker.
|
||||
address private constant REFUND_RECEIVER_TAKER = address(1);
|
||||
/// protocol fees will be sent to the transform recipient.
|
||||
address private constant REFUND_RECEIVER_RECIPIENT = address(1);
|
||||
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||
/// protocol fees will be sent to the sender.
|
||||
address private constant REFUND_RECEIVER_SENDER = address(2);
|
||||
@@ -272,8 +272,8 @@ contract FillQuoteTransformer is
|
||||
// Refund unspent protocol fees.
|
||||
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
|
||||
bool transferSuccess;
|
||||
if (data.refundReceiver == REFUND_RECEIVER_TAKER) {
|
||||
(transferSuccess,) = context.taker.call{value: state.ethRemaining}("");
|
||||
if (data.refundReceiver == REFUND_RECEIVER_RECIPIENT) {
|
||||
(transferSuccess,) = context.recipient.call{value: state.ethRemaining}("");
|
||||
} else if (data.refundReceiver == REFUND_RECEIVER_SENDER) {
|
||||
(transferSuccess,) = context.sender.call{value: state.ethRemaining}("");
|
||||
} else {
|
||||
|
@@ -30,9 +30,9 @@ interface IERC20Transformer {
|
||||
struct TransformContext {
|
||||
// The caller of `TransformERC20.transformERC20()`.
|
||||
address payable sender;
|
||||
// taker The taker address, which may be distinct from `sender` in the case
|
||||
// The recipient address, which may be distinct from `sender` e.g. in
|
||||
// meta-transactions.
|
||||
address payable taker;
|
||||
address payable recipient;
|
||||
// Arbitrary data to pass to the transformer.
|
||||
bytes data;
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ contract LogMetadataTransformer is
|
||||
override
|
||||
returns (bytes4 success)
|
||||
{
|
||||
emit TransformerMetadata(context.sender, context.taker, context.data);
|
||||
emit TransformerMetadata(context.sender, context.recipient, context.data);
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@@ -75,7 +75,7 @@ contract PayTakerTransformer is
|
||||
amount = data.tokens[i].getTokenBalanceOf(address(this));
|
||||
}
|
||||
if (amount != 0) {
|
||||
data.tokens[i].transformerTransfer(context.taker, amount);
|
||||
data.tokens[i].transformerTransfer(context.recipient, amount);
|
||||
}
|
||||
}
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
|
@@ -25,13 +25,16 @@ import "./BridgeProtocols.sol";
|
||||
import "./mixins/MixinBalancer.sol";
|
||||
import "./mixins/MixinBalancerV2.sol";
|
||||
import "./mixins/MixinBancor.sol";
|
||||
import "./mixins/MixinClipper.sol";
|
||||
import "./mixins/MixinCoFiX.sol";
|
||||
import "./mixins/MixinCurve.sol";
|
||||
import "./mixins/MixinCurveV2.sol";
|
||||
import "./mixins/MixinCryptoCom.sol";
|
||||
import "./mixins/MixinDodo.sol";
|
||||
import "./mixins/MixinDodoV2.sol";
|
||||
import "./mixins/MixinKyber.sol";
|
||||
import "./mixins/MixinKyberDmm.sol";
|
||||
import "./mixins/MixinLido.sol";
|
||||
import "./mixins/MixinMakerPSM.sol";
|
||||
import "./mixins/MixinMooniswap.sol";
|
||||
import "./mixins/MixinMStable.sol";
|
||||
@@ -48,13 +51,16 @@ contract BridgeAdapter is
|
||||
MixinBalancer,
|
||||
MixinBalancerV2,
|
||||
MixinBancor,
|
||||
MixinClipper,
|
||||
MixinCoFiX,
|
||||
MixinCurve,
|
||||
MixinCurveV2,
|
||||
MixinCryptoCom,
|
||||
MixinDodo,
|
||||
MixinDodoV2,
|
||||
MixinKyber,
|
||||
MixinKyberDmm,
|
||||
MixinLido,
|
||||
MixinMakerPSM,
|
||||
MixinMooniswap,
|
||||
MixinMStable,
|
||||
@@ -71,12 +77,15 @@ contract BridgeAdapter is
|
||||
MixinBalancer()
|
||||
MixinBalancerV2()
|
||||
MixinBancor(weth)
|
||||
MixinClipper(weth)
|
||||
MixinCoFiX()
|
||||
MixinCurve(weth)
|
||||
MixinCurveV2()
|
||||
MixinCryptoCom()
|
||||
MixinDodo()
|
||||
MixinDodoV2()
|
||||
MixinKyber(weth)
|
||||
MixinLido(weth)
|
||||
MixinMakerPSM()
|
||||
MixinMooniswap(weth)
|
||||
MixinMStable()
|
||||
@@ -107,6 +116,13 @@ contract BridgeAdapter is
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CURVEV2) {
|
||||
boughtAmount = _tradeCurveV2(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.UNISWAPV3) {
|
||||
boughtAmount = _tradeUniswapV3(
|
||||
sellToken,
|
||||
@@ -225,6 +241,20 @@ contract BridgeAdapter is
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.LIDO) {
|
||||
boughtAmount = _tradeLido(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else if (protocolId == BridgeProtocols.CLIPPER) {
|
||||
boughtAmount = _tradeClipper(
|
||||
sellToken,
|
||||
buyToken,
|
||||
sellAmount,
|
||||
order.bridgeData
|
||||
);
|
||||
} else {
|
||||
boughtAmount = _tradeZeroExBridge(
|
||||
sellToken,
|
||||
|
@@ -47,4 +47,7 @@ library BridgeProtocols {
|
||||
uint128 internal constant BALANCERV2 = 17;
|
||||
uint128 internal constant UNISWAPV3 = 18;
|
||||
uint128 internal constant KYBERDMM = 19;
|
||||
uint128 internal constant CURVEV2 = 20;
|
||||
uint128 internal constant LIDO = 21;
|
||||
uint128 internal constant CLIPPER = 22;
|
||||
}
|
||||
|
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
// 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-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
|
||||
contract MixinCurveV2 {
|
||||
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
struct CurveBridgeDataV2 {
|
||||
address curveAddress;
|
||||
bytes4 exchangeFunctionSelector;
|
||||
int128 fromCoinIdx;
|
||||
int128 toCoinIdx;
|
||||
}
|
||||
|
||||
function _tradeCurveV2(
|
||||
IERC20TokenV06 sellToken,
|
||||
IERC20TokenV06 buyToken,
|
||||
uint256 sellAmount,
|
||||
bytes memory bridgeData
|
||||
)
|
||||
internal
|
||||
returns (uint256 boughtAmount)
|
||||
{
|
||||
// Decode the bridge data to get the Curve metadata.
|
||||
CurveBridgeDataV2 memory data = abi.decode(bridgeData, (CurveBridgeDataV2));
|
||||
sellToken.approveIfBelow(data.curveAddress, sellAmount);
|
||||
|
||||
uint256 beforeBalance = buyToken.balanceOf(address(this));
|
||||
(bool success, bytes memory resultData) =
|
||||
data.curveAddress.call(abi.encodeWithSelector(
|
||||
data.exchangeFunctionSelector,
|
||||
data.fromCoinIdx,
|
||||
data.toCoinIdx,
|
||||
// dx
|
||||
sellAmount,
|
||||
// min dy
|
||||
1
|
||||
));
|
||||
if (!success) {
|
||||
resultData.rrevert();
|
||||
}
|
||||
|
||||
return buyToken.balanceOf(address(this)).safeSub(beforeBalance);
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@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";
|
||||
|
||||
|
||||
/// @dev Minimal interface for minting StETH
|
||||
interface ILido {
|
||||
/// @dev Adds eth to the pool
|
||||
/// @param _referral optional address for referrals
|
||||
/// @return StETH Amount of shares generated
|
||||
function submit(address _referral) external payable returns (uint256 StETH);
|
||||
/// @dev Retrieve the current pooled ETH representation of the shares amount
|
||||
/// @param _sharesAmount amount of shares
|
||||
/// @return amount of pooled ETH represented by the shares amount
|
||||
function getPooledEthByShares(uint256 _sharesAmount) external view returns (uint256);
|
||||
}
|
||||
|
||||
|
||||
contract MixinLido {
|
||||
using LibERC20TokenV06 for IERC20TokenV06;
|
||||
using LibERC20TokenV06 for IEtherTokenV06;
|
||||
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
|
||||
constructor(IEtherTokenV06 weth)
|
||||
public
|
||||
{
|
||||
WETH = weth;
|
||||
}
|
||||
|
||||
function _tradeLido(
|
||||
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);
|
||||
boughtAmount = lido.getPooledEthByShares(lido.submit{ value: sellAmount}(address(0)));
|
||||
} else {
|
||||
revert("MixinLido/UNSUPPORTED_TOKEN_PAIR");
|
||||
}
|
||||
}
|
||||
}
|
@@ -33,7 +33,7 @@ contract TestFillQuoteTransformerHost is
|
||||
TestMintableERC20Token inputToken,
|
||||
uint256 inputTokenAmount,
|
||||
address payable sender,
|
||||
address payable taker,
|
||||
address payable recipient,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
@@ -47,7 +47,7 @@ contract TestFillQuoteTransformerHost is
|
||||
transformer,
|
||||
IERC20Transformer.TransformContext({
|
||||
sender: sender,
|
||||
taker: taker,
|
||||
recipient: recipient,
|
||||
data: data
|
||||
})
|
||||
);
|
||||
|
@@ -46,16 +46,6 @@ contract TestLiquidityProvider {
|
||||
uint256 inputTokenBalance
|
||||
);
|
||||
|
||||
IERC20TokenV06 public immutable xAsset;
|
||||
IERC20TokenV06 public immutable yAsset;
|
||||
|
||||
constructor(IERC20TokenV06 xAsset_, IERC20TokenV06 yAsset_)
|
||||
public
|
||||
{
|
||||
xAsset = xAsset_;
|
||||
yAsset = yAsset_;
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
|
||||
/// @dev Trades `inputToken` for `outputToken`. The amount of `inputToken`
|
||||
@@ -83,6 +73,8 @@ contract TestLiquidityProvider {
|
||||
minBuyAmount,
|
||||
IERC20TokenV06(inputToken).balanceOf(address(this))
|
||||
);
|
||||
uint256 outputTokenBalance = IERC20TokenV06(outputToken).balanceOf(address(this));
|
||||
IERC20TokenV06(outputToken).transfer(recipient, outputTokenBalance);
|
||||
}
|
||||
|
||||
/// @dev Trades ETH for token. ETH must be sent to the contract prior to
|
||||
@@ -106,6 +98,8 @@ contract TestLiquidityProvider {
|
||||
minBuyAmount,
|
||||
address(this).balance
|
||||
);
|
||||
uint256 outputTokenBalance = IERC20TokenV06(outputToken).balanceOf(address(this));
|
||||
IERC20TokenV06(outputToken).transfer(recipient, outputTokenBalance);
|
||||
}
|
||||
|
||||
/// @dev Trades token for ETH. The token must be sent to the contract prior
|
||||
@@ -129,5 +123,6 @@ contract TestLiquidityProvider {
|
||||
minBuyAmount,
|
||||
IERC20TokenV06(inputToken).balanceOf(address(this))
|
||||
);
|
||||
recipient.transfer(address(this).balance);
|
||||
}
|
||||
}
|
||||
|
@@ -89,7 +89,9 @@ contract TestMetaTransactionsNativeOrdersFeature is
|
||||
LibNativeOrder.RfqOrder memory order,
|
||||
LibSignature.Signature memory signature,
|
||||
uint128 takerTokenFillAmount,
|
||||
address taker
|
||||
address taker,
|
||||
bool /* useSelfBalance */,
|
||||
address /* recipient */
|
||||
)
|
||||
public
|
||||
override
|
||||
|
@@ -57,7 +57,7 @@ contract TestMintTokenERC20Transformer is
|
||||
address(this),
|
||||
msg.sender,
|
||||
context.sender,
|
||||
context.taker,
|
||||
context.recipient,
|
||||
context.data,
|
||||
LibERC20Transformer.isTokenETH(data.inputToken)
|
||||
? address(this).balance
|
||||
@@ -71,15 +71,22 @@ contract TestMintTokenERC20Transformer is
|
||||
data.inputToken.transfer(address(0), data.burnAmount);
|
||||
}
|
||||
// Mint output tokens.
|
||||
if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
|
||||
context.taker.transfer(data.mintAmount);
|
||||
} else {
|
||||
data.outputToken.mint(
|
||||
context.taker,
|
||||
data.mintAmount
|
||||
);
|
||||
// Burn fees from output.
|
||||
data.outputToken.burn(context.taker, data.feeAmount);
|
||||
if (!LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
|
||||
if (data.feeAmount > data.mintAmount) {
|
||||
data.outputToken.burn(
|
||||
context.recipient,
|
||||
data.feeAmount - data.mintAmount
|
||||
);
|
||||
} else {
|
||||
data.outputToken.mint(
|
||||
address(this),
|
||||
data.mintAmount
|
||||
);
|
||||
data.outputToken.burn(
|
||||
context.recipient,
|
||||
data.feeAmount
|
||||
);
|
||||
}
|
||||
}
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
}
|
||||
|
@@ -22,6 +22,12 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
|
||||
contract TestMintableERC20Token {
|
||||
event Transfer(
|
||||
address token,
|
||||
address from,
|
||||
address to,
|
||||
uint256 value
|
||||
);
|
||||
|
||||
mapping(address => uint256) public balanceOf;
|
||||
mapping(address => mapping(address => uint256)) public allowance;
|
||||
@@ -81,6 +87,7 @@ contract TestMintableERC20Token {
|
||||
require(balanceOf[from] >= amount, "TestMintableERC20Token/INSUFFICIENT_FUNDS");
|
||||
balanceOf[from] -= amount;
|
||||
balanceOf[to] += amount;
|
||||
emit Transfer(address(this), from, to, amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
44
contracts/zero-ex/contracts/test/TestUniswapV2Factory.sol
Normal file
44
contracts/zero-ex/contracts/test/TestUniswapV2Factory.sol
Normal file
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "./TestUniswapV2Pool.sol";
|
||||
|
||||
contract TestUniswapV2Factory {
|
||||
struct CreationParameters {
|
||||
IERC20TokenV06 token0;
|
||||
IERC20TokenV06 token1;
|
||||
}
|
||||
|
||||
event PoolCreated(TestUniswapV2Pool pool);
|
||||
|
||||
bytes32 public immutable POOL_INIT_CODE_HASH;
|
||||
mapping (IERC20TokenV06 => mapping (IERC20TokenV06 => TestUniswapV2Pool)) public getPool;
|
||||
CreationParameters public creationParameters;
|
||||
|
||||
constructor() public {
|
||||
POOL_INIT_CODE_HASH = keccak256(type(TestUniswapV2Pool).creationCode);
|
||||
}
|
||||
|
||||
function createPool(IERC20TokenV06 tokenA, IERC20TokenV06 tokenB)
|
||||
external
|
||||
returns (TestUniswapV2Pool pool)
|
||||
{
|
||||
(IERC20TokenV06 token0, IERC20TokenV06 token1) = tokenA < tokenB
|
||||
? (tokenA, tokenB)
|
||||
: (tokenB, tokenA);
|
||||
require(
|
||||
getPool[token0][token1] == TestUniswapV2Pool(0),
|
||||
"TestUniswapV2Factory/POOL_ALREADY_EXISTS"
|
||||
);
|
||||
creationParameters = CreationParameters({
|
||||
token0: token0,
|
||||
token1: token1
|
||||
});
|
||||
pool = new TestUniswapV2Pool
|
||||
{ salt: keccak256(abi.encodePacked(token0, token1)) }();
|
||||
getPool[token0][token1] = pool;
|
||||
getPool[token1][token0] = pool;
|
||||
emit PoolCreated(pool);
|
||||
}
|
||||
}
|
67
contracts/zero-ex/contracts/test/TestUniswapV2Pool.sol
Normal file
67
contracts/zero-ex/contracts/test/TestUniswapV2Pool.sol
Normal file
@@ -0,0 +1,67 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
pragma solidity ^0.6;
|
||||
pragma experimental ABIEncoderV2;
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
import "../src/vendor/IUniswapV2Pair.sol";
|
||||
|
||||
interface IUniswapV2PoolDeployer {
|
||||
struct CreationParameters {
|
||||
IERC20TokenV06 token0;
|
||||
IERC20TokenV06 token1;
|
||||
}
|
||||
|
||||
function creationParameters() external view returns (CreationParameters memory);
|
||||
}
|
||||
|
||||
contract TestUniswapV2Pool is IUniswapV2Pair {
|
||||
IERC20TokenV06 public immutable token0;
|
||||
IERC20TokenV06 public immutable token1;
|
||||
|
||||
uint112 reserve0;
|
||||
uint112 reserve1;
|
||||
uint32 blockTimestampLast;
|
||||
|
||||
constructor() public {
|
||||
IUniswapV2PoolDeployer.CreationParameters memory params =
|
||||
IUniswapV2PoolDeployer(msg.sender).creationParameters();
|
||||
(token0, token1) = (params.token0, params.token1);
|
||||
}
|
||||
|
||||
function swap(
|
||||
uint256 amount0Out,
|
||||
uint256 amount1Out,
|
||||
address to,
|
||||
bytes calldata /* data */
|
||||
)
|
||||
external
|
||||
override
|
||||
{
|
||||
if (amount0Out > 0) {
|
||||
token0.transfer(to, amount0Out);
|
||||
}
|
||||
if (amount1Out > 0) {
|
||||
token1.transfer(to, amount1Out);
|
||||
}
|
||||
}
|
||||
|
||||
function setReserves(
|
||||
uint112 reserve0_,
|
||||
uint112 reserve1_,
|
||||
uint32 blockTimestampLast_
|
||||
)
|
||||
external
|
||||
{
|
||||
reserve0 = reserve0_;
|
||||
reserve1 = reserve1_;
|
||||
blockTimestampLast = blockTimestampLast_;
|
||||
}
|
||||
|
||||
function getReserves()
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (uint112, uint112, uint32)
|
||||
{
|
||||
return (reserve0, reserve1, blockTimestampLast);
|
||||
}
|
||||
}
|
@@ -26,11 +26,15 @@ import "./TestMintableERC20Token.sol";
|
||||
contract TestWeth is
|
||||
TestMintableERC20Token
|
||||
{
|
||||
event Deposit(address owner, uint256 value);
|
||||
event Withdrawal(address owner, uint256 value);
|
||||
|
||||
function deposit()
|
||||
external
|
||||
payable
|
||||
{
|
||||
this.mint(msg.sender, msg.value);
|
||||
emit Deposit(msg.sender, msg.value);
|
||||
}
|
||||
|
||||
function depositTo(address owner)
|
||||
@@ -38,6 +42,7 @@ contract TestWeth is
|
||||
payable
|
||||
{
|
||||
this.mint(owner, msg.value);
|
||||
emit Deposit(owner, msg.value);
|
||||
}
|
||||
|
||||
function withdraw(uint256 amount)
|
||||
@@ -46,5 +51,6 @@ contract TestWeth is
|
||||
require(balanceOf[msg.sender] >= amount, "TestWeth/INSUFFICIENT_FUNDS");
|
||||
balanceOf[msg.sender] -= amount;
|
||||
msg.sender.transfer(amount);
|
||||
emit Withdrawal(msg.sender, amount);
|
||||
}
|
||||
}
|
||||
|
@@ -53,7 +53,7 @@ contract TestWethTransformerHost is
|
||||
transformer,
|
||||
IERC20Transformer.TransformContext({
|
||||
sender: msg.sender,
|
||||
taker: msg.sender,
|
||||
recipient: msg.sender,
|
||||
data: data
|
||||
})
|
||||
);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-zero-ex",
|
||||
"version": "0.25.0",
|
||||
"version": "0.28.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|MixinCoFiX|MixinCryptoCom|MixinCurve|MixinDodo|MixinDodoV2|MixinKyber|MixinKyberDmm|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|MultiplexLiquidityProvider|MultiplexOtc|MultiplexRfq|MultiplexTransformERC20|MultiplexUniswapV2|MultiplexUniswapV3|NativeOrdersCancellation|NativeOrdersFeature|NativeOrdersInfo|NativeOrdersProtocolFees|NativeOrdersSettlement|OtcOrdersFeature|OwnableFeature|PancakeSwapFeature|PayTakerTransformer|PermissionlessTransformerDeployer|PositiveSlippageFeeTransformer|SimpleFunctionRegistryFeature|TestBridge|TestCallTarget|TestCurve|TestDelegateCaller|TestFeeCollectorController|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFixinProtocolFees|TestFixinTokenSpender|TestFullMigration|TestInitialMigration|TestLibNativeOrder|TestLibSignature|TestLiquidityProvider|TestMetaTransactionsNativeOrdersFeature|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestMooniswap|TestNativeOrdersFeature|TestNoEthRecipient|TestOrderSignerRegistryWithContractWallet|TestPermissionlessTransformerDeployerSuicidal|TestPermissionlessTransformerDeployerTransformer|TestRfqOriginRegistration|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestStaking|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestUniswapV2Factory|TestUniswapV2Pool|TestUniswapV3Factory|TestUniswapV3Feature|TestUniswapV3Pool|TestWeth|TestWethTransformerHost|TestZeroExFeature|TransformERC20Feature|Transformer|TransformerDeployer|UniswapFeature|UniswapV3Feature|WethTransformer|ZeroEx|ZeroExOptimized).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -56,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.3.0",
|
||||
"@0x/contracts-erc20": "^3.3.11",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/contracts-erc20": "^3.3.16",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.3",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.24",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@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.0",
|
||||
"@0x/protocol-utils": "^1.8.2",
|
||||
"@0x/subproviders": "^6.5.3",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
|
@@ -82,13 +82,16 @@ 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 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';
|
||||
import * as MixinCurveV2 from '../test/generated-artifacts/MixinCurveV2.json';
|
||||
import * as MixinDodo from '../test/generated-artifacts/MixinDodo.json';
|
||||
import * as MixinDodoV2 from '../test/generated-artifacts/MixinDodoV2.json';
|
||||
import * as MixinKyber from '../test/generated-artifacts/MixinKyber.json';
|
||||
import * as MixinKyberDmm from '../test/generated-artifacts/MixinKyberDmm.json';
|
||||
import * as MixinLido from '../test/generated-artifacts/MixinLido.json';
|
||||
import * as MixinMakerPSM from '../test/generated-artifacts/MixinMakerPSM.json';
|
||||
import * as MixinMooniswap from '../test/generated-artifacts/MixinMooniswap.json';
|
||||
import * as MixinMStable from '../test/generated-artifacts/MixinMStable.json';
|
||||
@@ -101,6 +104,12 @@ import * as MixinUniswapV3 from '../test/generated-artifacts/MixinUniswapV3.json
|
||||
import * as MixinZeroExBridge from '../test/generated-artifacts/MixinZeroExBridge.json';
|
||||
import * as MooniswapLiquidityProvider from '../test/generated-artifacts/MooniswapLiquidityProvider.json';
|
||||
import * as MultiplexFeature from '../test/generated-artifacts/MultiplexFeature.json';
|
||||
import * as MultiplexLiquidityProvider from '../test/generated-artifacts/MultiplexLiquidityProvider.json';
|
||||
import * as MultiplexOtc from '../test/generated-artifacts/MultiplexOtc.json';
|
||||
import * as MultiplexRfq from '../test/generated-artifacts/MultiplexRfq.json';
|
||||
import * as MultiplexTransformERC20 from '../test/generated-artifacts/MultiplexTransformERC20.json';
|
||||
import * as MultiplexUniswapV2 from '../test/generated-artifacts/MultiplexUniswapV2.json';
|
||||
import * as MultiplexUniswapV3 from '../test/generated-artifacts/MultiplexUniswapV3.json';
|
||||
import * as NativeOrdersCancellation from '../test/generated-artifacts/NativeOrdersCancellation.json';
|
||||
import * as NativeOrdersFeature from '../test/generated-artifacts/NativeOrdersFeature.json';
|
||||
import * as NativeOrdersInfo from '../test/generated-artifacts/NativeOrdersInfo.json';
|
||||
@@ -148,6 +157,8 @@ import * as TestTransformerBase from '../test/generated-artifacts/TestTransforme
|
||||
import * as TestTransformERC20 from '../test/generated-artifacts/TestTransformERC20.json';
|
||||
import * as TestTransformerDeployerTransformer from '../test/generated-artifacts/TestTransformerDeployerTransformer.json';
|
||||
import * as TestTransformerHost from '../test/generated-artifacts/TestTransformerHost.json';
|
||||
import * as TestUniswapV2Factory from '../test/generated-artifacts/TestUniswapV2Factory.json';
|
||||
import * as TestUniswapV2Pool from '../test/generated-artifacts/TestUniswapV2Pool.json';
|
||||
import * as TestUniswapV3Factory from '../test/generated-artifacts/TestUniswapV3Factory.json';
|
||||
import * as TestUniswapV3Feature from '../test/generated-artifacts/TestUniswapV3Feature.json';
|
||||
import * as TestUniswapV3Pool from '../test/generated-artifacts/TestUniswapV3Pool.json';
|
||||
@@ -189,7 +200,6 @@ export const artifacts = {
|
||||
BootstrapFeature: BootstrapFeature as ContractArtifact,
|
||||
LiquidityProviderFeature: LiquidityProviderFeature as ContractArtifact,
|
||||
MetaTransactionsFeature: MetaTransactionsFeature as ContractArtifact,
|
||||
MultiplexFeature: MultiplexFeature as ContractArtifact,
|
||||
NativeOrdersFeature: NativeOrdersFeature as ContractArtifact,
|
||||
OtcOrdersFeature: OtcOrdersFeature as ContractArtifact,
|
||||
OwnableFeature: OwnableFeature as ContractArtifact,
|
||||
@@ -216,6 +226,13 @@ export const artifacts = {
|
||||
IUniswapV3Feature: IUniswapV3Feature as ContractArtifact,
|
||||
LibNativeOrder: LibNativeOrder as ContractArtifact,
|
||||
LibSignature: LibSignature as ContractArtifact,
|
||||
MultiplexFeature: MultiplexFeature as ContractArtifact,
|
||||
MultiplexLiquidityProvider: MultiplexLiquidityProvider as ContractArtifact,
|
||||
MultiplexOtc: MultiplexOtc as ContractArtifact,
|
||||
MultiplexRfq: MultiplexRfq as ContractArtifact,
|
||||
MultiplexTransformERC20: MultiplexTransformERC20 as ContractArtifact,
|
||||
MultiplexUniswapV2: MultiplexUniswapV2 as ContractArtifact,
|
||||
MultiplexUniswapV3: MultiplexUniswapV3 as ContractArtifact,
|
||||
NativeOrdersCancellation: NativeOrdersCancellation as ContractArtifact,
|
||||
NativeOrdersInfo: NativeOrdersInfo as ContractArtifact,
|
||||
NativeOrdersProtocolFees: NativeOrdersProtocolFees as ContractArtifact,
|
||||
@@ -255,13 +272,16 @@ export const artifacts = {
|
||||
MixinBalancer: MixinBalancer as ContractArtifact,
|
||||
MixinBalancerV2: MixinBalancerV2 as ContractArtifact,
|
||||
MixinBancor: MixinBancor as ContractArtifact,
|
||||
MixinClipper: MixinClipper as ContractArtifact,
|
||||
MixinCoFiX: MixinCoFiX as ContractArtifact,
|
||||
MixinCryptoCom: MixinCryptoCom as ContractArtifact,
|
||||
MixinCurve: MixinCurve as ContractArtifact,
|
||||
MixinCurveV2: MixinCurveV2 as ContractArtifact,
|
||||
MixinDodo: MixinDodo as ContractArtifact,
|
||||
MixinDodoV2: MixinDodoV2 as ContractArtifact,
|
||||
MixinKyber: MixinKyber as ContractArtifact,
|
||||
MixinKyberDmm: MixinKyberDmm as ContractArtifact,
|
||||
MixinLido: MixinLido as ContractArtifact,
|
||||
MixinMStable: MixinMStable as ContractArtifact,
|
||||
MixinMakerPSM: MixinMakerPSM as ContractArtifact,
|
||||
MixinMooniswap: MixinMooniswap as ContractArtifact,
|
||||
@@ -314,6 +334,8 @@ export const artifacts = {
|
||||
TestTransformerBase: TestTransformerBase as ContractArtifact,
|
||||
TestTransformerDeployerTransformer: TestTransformerDeployerTransformer as ContractArtifact,
|
||||
TestTransformerHost: TestTransformerHost as ContractArtifact,
|
||||
TestUniswapV2Factory: TestUniswapV2Factory as ContractArtifact,
|
||||
TestUniswapV2Pool: TestUniswapV2Pool as ContractArtifact,
|
||||
TestUniswapV3Factory: TestUniswapV3Factory as ContractArtifact,
|
||||
TestUniswapV3Feature: TestUniswapV3Feature as ContractArtifact,
|
||||
TestUniswapV3Pool: TestUniswapV3Pool as ContractArtifact,
|
||||
|
@@ -72,8 +72,6 @@ blockchainTests('LiquidityProvider feature', env => {
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
token.address,
|
||||
weth.address,
|
||||
);
|
||||
});
|
||||
blockchainTests.resets('Sandbox', () => {
|
||||
|
@@ -354,6 +354,8 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
inputTokenAmount: args.inputTokenAmount,
|
||||
minOutputTokenAmount: args.minOutputTokenAmount,
|
||||
transformations: args.transformations,
|
||||
useSelfBalance: false,
|
||||
recipient: mtx.signer,
|
||||
})
|
||||
.getABIEncodedTransactionData();
|
||||
return expect(tx).to.revertWith(
|
||||
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user