Compare commits
97 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 | ||
|
9bf889aa30 | ||
|
e81c88564e | ||
|
901d400d62 | ||
|
289474e2ce | ||
|
407ca21168 | ||
|
5c68fc24d2 | ||
|
548800e0a9 | ||
|
bde3d6dc6a | ||
|
56550a6acc | ||
|
e51b83accc | ||
|
d5ae971f1c | ||
|
5a2f5f9a42 | ||
|
75a3b70cef | ||
|
803cf65ba1 | ||
|
5d3947b838 | ||
|
4397a59008 | ||
|
966d54c935 | ||
|
234ddb495d | ||
|
a744acc7bc | ||
|
27c624633c | ||
|
7ef75101b4 | ||
|
6f8aace00d | ||
|
6c264b2f18 | ||
|
df055e1958 | ||
|
70d2117470 | ||
|
2c173ccaf3 | ||
|
d2f4a0c5f3 | ||
|
0d6021e5e3 | ||
|
bb04726e7f | ||
|
220ca370c2 | ||
|
63af4e3e98 | ||
|
9754e12d82 | ||
|
d72ebed246 | ||
|
587fc71058 | ||
|
7d34e09a12 | ||
|
7d15baad0f | ||
|
1e6476ada7 | ||
|
1d6ca5f6b5 |
@@ -2,11 +2,11 @@ version: 2.1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
resource_class: large
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: node:12
|
||||
environment:
|
||||
NODE_OPTIONS: '--max-old-space-size=6442'
|
||||
NODE_OPTIONS: '--max-old-space-size=16384'
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- checkout
|
||||
@@ -18,8 +18,8 @@ jobs:
|
||||
name: yarn
|
||||
command: yarn --frozen-lockfile --ignore-engines install || yarn --frozen-lockfile --ignore-engines install
|
||||
- setup_remote_docker
|
||||
- run: yarn build:ci || yarn build:ci || yarn build:ci
|
||||
- run: yarn build:ts || yarn build:ts || yarn build:ts
|
||||
- run: yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci || yarn build:ci
|
||||
- run: yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts || yarn build:ts
|
||||
- save_cache:
|
||||
key: repo-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
|
@@ -3,5 +3,6 @@
|
||||
"tabWidth": 4,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
|
@@ -49,7 +49,6 @@
|
||||
| Package | Version |
|
||||
| ------: | :------ |
|
||||
|
||||
|
||||
<!-- For example:
|
||||
| `0x.js` | 2.0.4 |
|
||||
| `Exchange Contract` | v2 |
|
||||
|
@@ -43,12 +43,12 @@ These packages are all under development. See [/contracts/README.md](/contracts/
|
||||
#### 0x-specific packages
|
||||
|
||||
| Package | Version | Description |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
||||
| -------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| [`@0x/contract-addresses`](/packages/contract-addresses) | [](https://www.npmjs.com/package/@0x/contract-addresses) | A tiny utility library for getting known deployed contract addresses for a particular network. |
|
||||
| [`@0x/contract-wrappers`](/packages/contract-wrappers) | [](https://www.npmjs.com/package/@0x/contract-wrappers) | JS/TS wrappers for interacting with the 0x smart contracts |
|
||||
| [`@0x/order-utils`](/packages/order-utils) | [](https://www.npmjs.com/package/@0x/order-utils) | A set of utilities for generating, parsing, signing and validating 0x orders |
|
||||
| [`@0x/migrations`](/packages/migrations) | [](https://www.npmjs.com/package/@0x/migrations) | Migration tool for deploying 0x smart contracts on private testnets |
|
||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
| [`@0x/contract-artifacts`](/packages/contract-artifacts) | [](https://www.npmjs.com/package/@0x/contract-artifacts) | 0x smart contract compilation artifacts | |
|
||||
|
||||
## Usage
|
||||
|
||||
|
@@ -1,4 +1,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "3.7.13",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "3.7.12",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "3.7.11",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v3.7.13 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.12 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.7.11 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-asset-proxy",
|
||||
"version": "3.7.11",
|
||||
"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.16.1",
|
||||
"@0x/contract-wrappers": "^13.17.4",
|
||||
"@0x/contracts-gen": "^2.0.38",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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.29",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "1.1.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "1.1.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "1.1.29",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v1.1.31 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.30 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.29 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-broker",
|
||||
"version": "1.1.29",
|
||||
"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.11",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/contracts-exchange": "^3.2.30",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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.21",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"ethereum-types": "^3.5.0"
|
||||
|
@@ -7,7 +7,10 @@ export interface GodsUnchainedProperties {
|
||||
quality: BigNumber | number;
|
||||
}
|
||||
|
||||
const propertyDataEncoder = AbiEncoder.create([{ name: 'proto', type: 'uint16' }, { name: 'quality', type: 'uint8' }]);
|
||||
const propertyDataEncoder = AbiEncoder.create([
|
||||
{ name: 'proto', type: 'uint16' },
|
||||
{ name: 'quality', type: 'uint8' },
|
||||
]);
|
||||
const brokerDataEncoder = AbiEncoder.create([
|
||||
{ name: 'godsUnchainedAddress', type: 'address' },
|
||||
{ name: 'validatorAddress', type: 'address' },
|
||||
|
@@ -1,4 +1,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "3.1.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "3.1.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "3.1.30",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v3.1.32 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.31 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.30 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-coordinator",
|
||||
"version": "3.1.30",
|
||||
"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.11",
|
||||
"@0x/contracts-dev-utils": "^1.3.28",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@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.21",
|
||||
"@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.1.0",
|
||||
"@0x/contracts-exchange": "^3.2.30",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "1.3.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "1.3.29",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "1.3.28",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v1.3.30 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.29 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.3.28 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-dev-utils",
|
||||
"version": "1.3.28",
|
||||
"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.11",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@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.0",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "2.1.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "2.1.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "2.1.29",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v2.1.31 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.30 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.1.29 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc1155",
|
||||
"version": "2.1.29",
|
||||
"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.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",
|
||||
@@ -81,7 +81,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@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,77 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "3.3.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "3.3.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "3.3.8",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v3.3.10 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.9 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.3.8 - _May 5, 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.8",
|
||||
"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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "3.1.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "3.1.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "3.1.29",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v3.1.31 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.30 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.1.29 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-erc721",
|
||||
"version": "3.1.29",
|
||||
"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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "4.2.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "4.2.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "4.2.30",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v4.2.32 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.31 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.2.30 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-forwarder",
|
||||
"version": "4.2.30",
|
||||
"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.11",
|
||||
"@0x/contracts-dev-utils": "^1.3.28",
|
||||
"@0x/contracts-erc1155": "^2.1.29",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/contracts-exchange": "^3.2.30",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "4.3.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "4.3.30",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "4.3.29",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v4.3.31 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.30 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.3.29 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange-libs",
|
||||
"version": "4.3.29",
|
||||
"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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "3.2.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "3.2.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "3.2.30",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v3.2.32 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.31 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v3.2.30 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-exchange",
|
||||
"version": "3.2.30",
|
||||
"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.11",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@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.30",
|
||||
"@0x/contracts-staking": "^2.0.37",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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.28",
|
||||
"@0x/contracts-erc1155": "^2.1.29",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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"
|
||||
},
|
||||
|
@@ -13,7 +13,11 @@ export const exchangeDataEncoder = {
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (constants.BATCH_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
data = (exchangeInstance as any)
|
||||
[fnName](orders, orders.map(order => order.takerAssetAmount), orders.map(order => order.signature))
|
||||
[fnName](
|
||||
orders,
|
||||
orders.map(order => order.takerAssetAmount),
|
||||
orders.map(order => order.signature),
|
||||
)
|
||||
.getABIEncodedTransactionData();
|
||||
} else if (constants.MARKET_FILL_FN_NAMES.indexOf(fnName) !== -1) {
|
||||
const fillAsset = /Buy/.test(fnName) ? 'makerAssetAmount' : 'takerAssetAmount';
|
||||
|
@@ -39,7 +39,10 @@ blockchainTests.resets('Reentrancy Tests', env => {
|
||||
// Handle tuples.
|
||||
if (item.type === 'tuple') {
|
||||
const tuple = item as TupleDataItem;
|
||||
return _.zipObject(tuple.components.map(c => c.name), tuple.components.map(createFunctionInputs));
|
||||
return _.zipObject(
|
||||
tuple.components.map(c => c.name),
|
||||
tuple.components.map(createFunctionInputs),
|
||||
);
|
||||
}
|
||||
// Handle strings.
|
||||
if (item.type === 'string') {
|
||||
|
@@ -109,7 +109,11 @@ export class ExchangeWrapper {
|
||||
opts: { makerAssetFillAmount: BigNumber; gas?: number; gasPrice?: BigNumber },
|
||||
): Promise<TransactionReceiptWithDecodedLogs> {
|
||||
return this.exchangeContract
|
||||
.marketBuyOrdersNoThrow(orders, opts.makerAssetFillAmount, orders.map(signedOrder => signedOrder.signature))
|
||||
.marketBuyOrdersNoThrow(
|
||||
orders,
|
||||
opts.makerAssetFillAmount,
|
||||
orders.map(signedOrder => signedOrder.signature),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from, gas: opts.gas });
|
||||
}
|
||||
public async marketSellOrdersFillOrKillAsync(
|
||||
|
@@ -1,4 +1,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "6.2.26",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "6.2.25",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "6.2.24",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v6.2.26 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.25 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v6.2.24 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-extensions",
|
||||
"version": "6.2.24",
|
||||
"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.11",
|
||||
"@0x/contracts-dev-utils": "^1.3.28",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/contracts-exchange": "^3.2.30",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@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.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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.0",
|
||||
"@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.40",
|
||||
"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.1.0",
|
||||
"@0x/contract-wrappers": "^13.16.1",
|
||||
"@0x/contracts-broker": "^1.1.29",
|
||||
"@0x/contracts-coordinator": "^3.1.30",
|
||||
"@0x/contracts-dev-utils": "^1.3.28",
|
||||
"@0x/contracts-exchange-forwarder": "^4.2.30",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@0x/contracts-extensions": "^6.2.24",
|
||||
"@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.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/coordinator-server": "^1.0.5",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/migrations": "^8.0.6",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@0x/protocol-utils": "^1.6.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.10.0",
|
||||
"@0x/asset-swapper": "^16.25.0",
|
||||
"@0x/base-contract": "^6.4.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.11",
|
||||
"@0x/contracts-erc1155": "^2.1.29",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-erc721": "^3.1.29",
|
||||
"@0x/contracts-exchange": "^3.2.30",
|
||||
"@0x/contracts-multisig": "^4.1.30",
|
||||
"@0x/contracts-staking": "^2.0.37",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@0x/contracts-zero-ex": "^0.23.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",
|
||||
|
@@ -534,9 +534,14 @@ blockchainTests.skip('Coordinator Client', env => {
|
||||
const signedOrders = [signedOrder, signedOrderWithDifferentFeeRecipient];
|
||||
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
|
||||
await coordinatorClient
|
||||
.batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
|
||||
from: takerAddress,
|
||||
})
|
||||
.batchFillOrdersAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmounts,
|
||||
signedOrders.map(o => o.signature),
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
)
|
||||
.then(res => {
|
||||
expect(res).to.be.undefined();
|
||||
})
|
||||
@@ -570,9 +575,14 @@ blockchainTests.skip('Coordinator Client', env => {
|
||||
const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
|
||||
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
|
||||
await coordinatorClient
|
||||
.batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
|
||||
from: takerAddress,
|
||||
})
|
||||
.batchFillOrdersAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmounts,
|
||||
signedOrders.map(o => o.signature),
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
)
|
||||
.then(res => {
|
||||
expect(res).to.be.undefined();
|
||||
})
|
||||
@@ -600,9 +610,14 @@ blockchainTests.skip('Coordinator Client', env => {
|
||||
const signedOrders = [signedOrder, signedOrderWithDifferentCoordinatorOperator];
|
||||
const takerAssetFillAmounts = [takerTokenFillAmount, takerTokenFillAmount, takerTokenFillAmount];
|
||||
await coordinatorClient
|
||||
.batchFillOrdersAsync(signedOrders, takerAssetFillAmounts, signedOrders.map(o => o.signature), {
|
||||
from: takerAddress,
|
||||
})
|
||||
.batchFillOrdersAsync(
|
||||
signedOrders,
|
||||
takerAssetFillAmounts,
|
||||
signedOrders.map(o => o.signature),
|
||||
{
|
||||
from: takerAddress,
|
||||
},
|
||||
)
|
||||
.then(res => {
|
||||
expect(res).to.be.undefined();
|
||||
})
|
||||
|
@@ -267,7 +267,11 @@ blockchainTests.resets('Coordinator integration tests', env => {
|
||||
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
|
||||
await balanceStore.updateBalancesAsync();
|
||||
balanceStore.assertEquals(expectedBalances);
|
||||
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||
verifyEvents(
|
||||
txReceipt,
|
||||
orders.map(order => expectedFillEvent(order)),
|
||||
ExchangeEvents.Fill,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should fill the orders if called by approver (eth fee, no refund)`, async () => {
|
||||
await balanceStore.updateBalancesAsync();
|
||||
@@ -280,7 +284,11 @@ blockchainTests.resets('Coordinator integration tests', env => {
|
||||
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
|
||||
await balanceStore.updateBalancesAsync();
|
||||
balanceStore.assertEquals(expectedBalances);
|
||||
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||
verifyEvents(
|
||||
txReceipt,
|
||||
orders.map(order => expectedFillEvent(order)),
|
||||
ExchangeEvents.Fill,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should fill the orders if called by approver (mixed fees, refund)`, async () => {
|
||||
await balanceStore.updateBalancesAsync();
|
||||
@@ -293,7 +301,11 @@ blockchainTests.resets('Coordinator integration tests', env => {
|
||||
expectedBalances.simulateFills(orders, taker.address, txReceipt, deployment, value);
|
||||
await balanceStore.updateBalancesAsync();
|
||||
balanceStore.assertEquals(expectedBalances);
|
||||
verifyEvents(txReceipt, orders.map(order => expectedFillEvent(order)), ExchangeEvents.Fill);
|
||||
verifyEvents(
|
||||
txReceipt,
|
||||
orders.map(order => expectedFillEvent(order)),
|
||||
ExchangeEvents.Fill,
|
||||
);
|
||||
});
|
||||
it(`${fnName} should revert with an invalid approval signature`, async () => {
|
||||
const approvalSignature = hexUtils.concat(
|
||||
@@ -360,7 +372,11 @@ blockchainTests.resets('Coordinator integration tests', env => {
|
||||
.executeTransaction(transaction, maker.address, transaction.signature, [])
|
||||
.awaitTransactionSuccessAsync({ from: maker.address });
|
||||
|
||||
verifyEvents(txReceipt, orders.map(order => expectedCancelEvent(order)), ExchangeEvents.Cancel);
|
||||
verifyEvents(
|
||||
txReceipt,
|
||||
orders.map(order => expectedCancelEvent(order)),
|
||||
ExchangeEvents.Cancel,
|
||||
);
|
||||
});
|
||||
it('cancelOrdersUpTo call should be successful without an approval', async () => {
|
||||
const data = exchangeDataEncoder.encodeOrdersToExchangeData(ExchangeFunctionName.CancelOrdersUpTo, []);
|
||||
|
@@ -186,13 +186,13 @@ blockchainTests.resets('LibAssetData', env => {
|
||||
});
|
||||
|
||||
it('should decode multiasset data', async () => {
|
||||
expect(await devUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData).callAsync()).to.deep.equal(
|
||||
[
|
||||
AssetProxyId.MultiAsset,
|
||||
KNOWN_MULTI_ASSET_ENCODING.amounts,
|
||||
KNOWN_MULTI_ASSET_ENCODING.nestedAssetData,
|
||||
],
|
||||
);
|
||||
expect(
|
||||
await devUtils.decodeMultiAssetData(KNOWN_MULTI_ASSET_ENCODING.assetData).callAsync(),
|
||||
).to.deep.equal([
|
||||
AssetProxyId.MultiAsset,
|
||||
KNOWN_MULTI_ASSET_ENCODING.amounts,
|
||||
KNOWN_MULTI_ASSET_ENCODING.nestedAssetData,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should encode StaticCall data', async () => {
|
||||
|
@@ -278,15 +278,21 @@ blockchainTests.resets('matchOrders integration tests', env => {
|
||||
ExchangeRevertErrors.BatchMatchOrdersErrorCodes.InvalidLengthRightSignatures,
|
||||
);
|
||||
let tx = deployment.exchange
|
||||
.batchMatchOrders(leftOrders, rightOrders, leftOrders.map(order => order.signature), [
|
||||
rightOrders[0].signature,
|
||||
])
|
||||
.batchMatchOrders(
|
||||
leftOrders,
|
||||
rightOrders,
|
||||
leftOrders.map(order => order.signature),
|
||||
[rightOrders[0].signature],
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: matcher.address });
|
||||
await expect(tx).to.revertWith(expectedError);
|
||||
tx = deployment.exchange
|
||||
.batchMatchOrdersWithMaximalFill(leftOrders, rightOrders, leftOrders.map(order => order.signature), [
|
||||
rightOrders[0].signature,
|
||||
])
|
||||
.batchMatchOrdersWithMaximalFill(
|
||||
leftOrders,
|
||||
rightOrders,
|
||||
leftOrders.map(order => order.signature),
|
||||
[rightOrders[0].signature],
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ from: matcher.address });
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
@@ -475,7 +481,10 @@ blockchainTests.resets('matchOrders integration tests', env => {
|
||||
],
|
||||
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
|
||||
matchIndices: [[0, 0], [1, 0]],
|
||||
matchIndices: [
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
],
|
||||
shouldMaximallyFill: false,
|
||||
});
|
||||
});
|
||||
@@ -524,7 +533,10 @@ blockchainTests.resets('matchOrders integration tests', env => {
|
||||
],
|
||||
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT],
|
||||
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
matchIndices: [[0, 0], [0, 1]],
|
||||
matchIndices: [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
],
|
||||
shouldMaximallyFill: false,
|
||||
});
|
||||
});
|
||||
@@ -626,7 +638,11 @@ blockchainTests.resets('matchOrders integration tests', env => {
|
||||
],
|
||||
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
matchIndices: [[0, 0], [0, 1], [1, 1]],
|
||||
matchIndices: [
|
||||
[0, 0],
|
||||
[0, 1],
|
||||
[1, 1],
|
||||
],
|
||||
shouldMaximallyFill: false,
|
||||
});
|
||||
});
|
||||
@@ -801,7 +817,11 @@ blockchainTests.resets('matchOrders integration tests', env => {
|
||||
],
|
||||
leftOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
rightOrdersTakerAssetFilledAmounts: [constants.ZERO_AMOUNT, constants.ZERO_AMOUNT],
|
||||
matchIndices: [[0, 0], [1, 0], [1, 1]],
|
||||
matchIndices: [
|
||||
[0, 0],
|
||||
[1, 0],
|
||||
[1, 1],
|
||||
],
|
||||
shouldMaximallyFill: true,
|
||||
});
|
||||
});
|
||||
|
@@ -106,7 +106,12 @@ blockchainTests.fork.resets('Forwarder mainnet tests', env => {
|
||||
orders[1].takerAssetAmount.dividedToIntegerBy(2),
|
||||
);
|
||||
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
|
||||
.marketSellOrdersWithEth(orders, orders.map(o => o.signature), [], [])
|
||||
.marketSellOrdersWithEth(
|
||||
orders,
|
||||
orders.map(o => o.signature),
|
||||
[],
|
||||
[],
|
||||
)
|
||||
.callAsync({
|
||||
from: takerAddress,
|
||||
value: ethSellAmount,
|
||||
@@ -161,7 +166,13 @@ blockchainTests.fork.resets('Forwarder mainnet tests', env => {
|
||||
orders[1].makerAssetAmount.dividedToIntegerBy(2),
|
||||
);
|
||||
const [wethSpentAmount, makerAssetAcquiredAmount] = await forwarder
|
||||
.marketBuyOrdersWithEth(orders, makerAssetBuyAmount, orders.map(o => o.signature), [], [])
|
||||
.marketBuyOrdersWithEth(
|
||||
orders,
|
||||
makerAssetBuyAmount,
|
||||
orders.map(o => o.signature),
|
||||
[],
|
||||
[],
|
||||
)
|
||||
.callAsync({
|
||||
from: takerAddress,
|
||||
value: ethSellAmount,
|
||||
|
@@ -190,9 +190,14 @@ export function MakerMixin<TBase extends Constructor>(Base: TBase): TBase & Cons
|
||||
rightTakerAssetData,
|
||||
makerFeeAssetData,
|
||||
takerFeeAssetData,
|
||||
] = [leftMakerToken, leftTakerToken, rightMakerToken, rightTakerToken, makerFeeToken, takerFeeToken].map(
|
||||
token => encodeERC20AssetData(token.address),
|
||||
);
|
||||
] = [
|
||||
leftMakerToken,
|
||||
leftTakerToken,
|
||||
rightMakerToken,
|
||||
rightTakerToken,
|
||||
makerFeeToken,
|
||||
takerFeeToken,
|
||||
].map(token => encodeERC20AssetData(token.address));
|
||||
|
||||
// Construct and sign the left order
|
||||
const leftOrder = await this.signOrderAsync({
|
||||
|
@@ -8,7 +8,10 @@ import { Actor, Constructor } from './base';
|
||||
* Useful for BalanceStore.
|
||||
*/
|
||||
export function actorAddressesByName(actors: Actor[]): ObjectMap<string> {
|
||||
return _.zipObject(actors.map(actor => actor.name), actors.map(actor => actor.address));
|
||||
return _.zipObject(
|
||||
actors.map(actor => actor.name),
|
||||
actors.map(actor => actor.address),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -77,19 +77,24 @@ tests('Exchange signature validation fuzz tests', env => {
|
||||
before(async () => {
|
||||
chainId = await env.web3Wrapper.getChainIdAsync();
|
||||
accounts = await env.getAccountAddressesAsync();
|
||||
privateKeys = _.zipObject(accounts, accounts.map((a, i) => constants.TESTRPC_PRIVATE_KEYS[i]));
|
||||
privateKeys = _.zipObject(
|
||||
accounts,
|
||||
accounts.map((a, i) => constants.TESTRPC_PRIVATE_KEYS[i]),
|
||||
);
|
||||
deployment = await DeploymentManager.deployAsync(env, {
|
||||
numErc20TokensToDeploy: 0,
|
||||
numErc721TokensToDeploy: 0,
|
||||
numErc1155TokensToDeploy: 0,
|
||||
});
|
||||
exchange = deployment.exchange;
|
||||
walletContractAddress = (await TestSignatureValidationWalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestSignatureValidationWallet,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
)).address;
|
||||
walletContractAddress = (
|
||||
await TestSignatureValidationWalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestSignatureValidationWallet,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{},
|
||||
)
|
||||
).address;
|
||||
// This just has to be a contract address that doesn't implement the
|
||||
// wallet spec.
|
||||
notWalletContractAddress = exchange.address;
|
||||
@@ -715,7 +720,7 @@ tests('Exchange signature validation fuzz tests', env => {
|
||||
invalidTestTransactionMangledSignature(),
|
||||
];
|
||||
const simulationEnvironment = new SimulationEnvironment(deployment, new BlockchainBalanceStore({}, {}), []);
|
||||
const simulation = new class extends Simulation {
|
||||
const simulation = new (class extends Simulation {
|
||||
// tslint:disable-next-line: prefer-function-over-method
|
||||
protected async *_assertionGenerator(): AsyncIterableIterator<AssertionResult | void> {
|
||||
while (true) {
|
||||
@@ -723,7 +728,7 @@ tests('Exchange signature validation fuzz tests', env => {
|
||||
yield (await action!.next()).value;
|
||||
}
|
||||
}
|
||||
}(simulationEnvironment);
|
||||
})(simulationEnvironment);
|
||||
simulation.resets = true;
|
||||
return simulation.fuzzAsync();
|
||||
});
|
||||
|
@@ -1,4 +1,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "4.1.32",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "4.1.31",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "4.1.30",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v4.1.32 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.31 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.1.30 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-multisig",
|
||||
"version": "4.1.30",
|
||||
"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.11",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@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.0",
|
||||
"@0x/contracts-utils": "^4.7.8",
|
||||
"@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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "2.0.39",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "2.0.38",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "2.0.37",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v2.0.39 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.38 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v2.0.37 - _May 5, 2021_
|
||||
|
||||
* Patch epoch finalization issue (#221)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-staking",
|
||||
"version": "2.0.37",
|
||||
"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.11",
|
||||
"@0x/contracts-dev-utils": "^1.3.28",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@0x/contracts-exchange-libs": "^4.3.29",
|
||||
"@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.8",
|
||||
"@0x/contracts-utils": "^4.7.16",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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.0",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
"@0x/utils": "^6.4.3",
|
||||
"ethereum-types": "^3.5.0",
|
||||
|
@@ -157,11 +157,11 @@ export class FinalizerActor extends BaseActor {
|
||||
const delegators = delegatorsByPoolId[poolId];
|
||||
delegatorBalancesByPoolId[poolId] = {};
|
||||
for (const delegator of delegators) {
|
||||
delegatorBalancesByPoolId[poolId][
|
||||
delegator
|
||||
] = (await this._stakingApiWrapper.stakingContract
|
||||
.getStakeDelegatedToPoolByOwner(delegator, poolId)
|
||||
.callAsync()).currentEpochBalance;
|
||||
delegatorBalancesByPoolId[poolId][delegator] = (
|
||||
await this._stakingApiWrapper.stakingContract
|
||||
.getStakeDelegatedToPoolByOwner(delegator, poolId)
|
||||
.callAsync()
|
||||
).currentEpochBalance;
|
||||
}
|
||||
}
|
||||
return delegatorBalancesByPoolId;
|
||||
@@ -253,7 +253,10 @@ export class FinalizerActor extends BaseActor {
|
||||
const totalFeesCollected = BigNumber.sum(...activePools.map(p => p.feesCollected));
|
||||
const totalWeightedStake = BigNumber.sum(...activePools.map(p => p.weightedStake));
|
||||
if (totalRewards.eq(0) || totalFeesCollected.eq(0) || totalWeightedStake.eq(0)) {
|
||||
return _.zipObject(poolIds, _.times(poolIds.length, () => new BigNumber(0)));
|
||||
return _.zipObject(
|
||||
poolIds,
|
||||
_.times(poolIds.length, () => new BigNumber(0)),
|
||||
);
|
||||
}
|
||||
const rewards = await Promise.all(
|
||||
activePools.map(async pool =>
|
||||
|
@@ -102,13 +102,15 @@ blockchainTests('Migration tests', env => {
|
||||
});
|
||||
|
||||
it('should set the correct initial params', async () => {
|
||||
const stakingProxyContractAddress = (await StakingProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.StakingProxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
stakingContract.address,
|
||||
)).address;
|
||||
const stakingProxyContractAddress = (
|
||||
await StakingProxyContract.deployFrom0xArtifactAsync(
|
||||
artifacts.StakingProxy,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
artifacts,
|
||||
stakingContract.address,
|
||||
)
|
||||
).address;
|
||||
|
||||
const stakingProxyContract = new StakingContract(
|
||||
stakingProxyContractAddress,
|
||||
|
@@ -1,4 +1,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "5.4.2",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "5.4.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "5.4.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v5.4.2 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.1 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v5.4.0 - _May 5, 2021_
|
||||
|
||||
* Set default ganache gas limit to 100e6 (#197)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-test-utils",
|
||||
"version": "5.4.0",
|
||||
"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.1.0",
|
||||
"@0x/contract-addresses": "^6.6.0",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/json-schemas": "^6.1.3",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@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] });
|
||||
});
|
||||
}
|
||||
|
@@ -108,9 +108,7 @@ export async function testWithReferenceFuncAsync(
|
||||
return expect.fail(
|
||||
actualError,
|
||||
expectedError,
|
||||
`${testCaseString}: expected error message '${actualError.message}' to equal '${
|
||||
expectedError.message
|
||||
}'`,
|
||||
`${testCaseString}: expected error message '${actualError.message}' to equal '${expectedError.message}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,87 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "1.2.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Added proposal 0 params and test",
|
||||
"pr": 252
|
||||
}
|
||||
],
|
||||
"timestamp": 1622154125
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "1.1.8",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "1.1.7",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "1.1.6",
|
||||
|
@@ -5,6 +5,42 @@ 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
|
||||
|
||||
## v1.2.0 - _May 27, 2021_
|
||||
|
||||
* Added proposal 0 params and test (#252)
|
||||
|
||||
## v1.1.8 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.7 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v1.1.6 - _May 5, 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.1.6",
|
||||
"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.1.0",
|
||||
"@0x/contracts-asset-proxy": "^3.7.11",
|
||||
"@0x/contracts-erc20": "^3.3.8",
|
||||
"@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.37",
|
||||
"@0x/contracts-test-utils": "^5.4.0",
|
||||
"@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.6.0",
|
||||
"@0x/protocol-utils": "^1.8.2",
|
||||
"@0x/subproviders": "^6.5.3",
|
||||
"@0x/types": "^3.3.3",
|
||||
"@0x/typescript-typings": "^5.2.0",
|
||||
|
57
contracts/treasury/src/proposals.ts
Normal file
57
contracts/treasury/src/proposals.ts
Normal file
File diff suppressed because one or more lines are too long
222
contracts/treasury/test/proposal_test.ts
Normal file
222
contracts/treasury/test/proposal_test.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import { artifacts as erc20Artifacts, ERC20TokenEvents } from '@0x/contracts-erc20';
|
||||
import { StakingContract, StakingProxyContract } from '@0x/contracts-staking';
|
||||
import { blockchainTests, constants, verifyEventsFromLogs } from '@0x/contracts-test-utils';
|
||||
import { BigNumber, hexUtils, logUtils } from '@0x/utils';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { proposals } from '../src/proposals';
|
||||
|
||||
import { artifacts } from './artifacts';
|
||||
import { ZrxTreasuryContract, ZrxTreasuryEvents } from './wrappers';
|
||||
|
||||
const SUBGRAPH_URL = 'https://api.thegraph.com/subgraphs/name/mzhu25/zeroex-staking';
|
||||
const STAKING_PROXY_ADDRESS = '0xa26e80e7dea86279c6d778d702cc413e6cffa777';
|
||||
const TREASURY_ADDRESS = '0x0bb1810061c2f5b2088054ee184e6c79e1591101';
|
||||
const PROPOSER = process.env.PROPOSER || constants.NULL_ADDRESS;
|
||||
const VOTER = '0xba4f44e774158408e2dc6c5cb65bc995f0a89180';
|
||||
const VOTER_OPERATED_POOLS = ['0x0000000000000000000000000000000000000000000000000000000000000017'];
|
||||
blockchainTests.configure({
|
||||
fork: {
|
||||
unlockedAccounts: [PROPOSER, VOTER],
|
||||
},
|
||||
});
|
||||
|
||||
async function querySubgraphAsync(operatorAddress: string): Promise<string[]> {
|
||||
const query = `
|
||||
{
|
||||
stakingActor(id: "${operatorAddress}") {
|
||||
operatedPools {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const response = await fetch(SUBGRAPH_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query,
|
||||
}),
|
||||
});
|
||||
const {
|
||||
data: { stakingActor },
|
||||
} = await response.json();
|
||||
if (stakingActor) {
|
||||
return stakingActor.operatedPools.map((pool: { id: string }) => hexUtils.leftPad(pool.id));
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
blockchainTests.fork.skip('Treasury proposal mainnet fork tests', env => {
|
||||
let staking: StakingContract;
|
||||
let stakingProxy: StakingProxyContract;
|
||||
let treasury: ZrxTreasuryContract;
|
||||
let votingPeriod: BigNumber;
|
||||
|
||||
async function fastForwardToNextEpochAsync(): Promise<void> {
|
||||
const epochEndTime = await staking.getCurrentEpochEarliestEndTimeInSeconds().callAsync();
|
||||
const lastBlockTime = await env.web3Wrapper.getBlockTimestampAsync('latest');
|
||||
const dt = Math.max(0, epochEndTime.minus(lastBlockTime).toNumber());
|
||||
await env.web3Wrapper.increaseTimeAsync(dt);
|
||||
// mine next block
|
||||
await env.web3Wrapper.mineBlockAsync();
|
||||
const lastPoolId = new BigNumber(await staking.lastPoolId().callAsync(), 16);
|
||||
const batchExecuteCalldata = [
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
staking.endEpoch().getABIEncodedTransactionData(),
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
...[...new Array(lastPoolId.toNumber())].map((_x, i) =>
|
||||
staking.finalizePool(hexUtils.leftPad(i + 1)).getABIEncodedTransactionData(),
|
||||
),
|
||||
];
|
||||
await stakingProxy.batchExecute(batchExecuteCalldata).awaitTransactionSuccessAsync();
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
const abis = _.mapValues({ ...artifacts, ...erc20Artifacts }, v => v.compilerOutput.abi);
|
||||
treasury = new ZrxTreasuryContract(TREASURY_ADDRESS, env.provider, env.txDefaults, abis);
|
||||
votingPeriod = await treasury.votingPeriod().callAsync();
|
||||
staking = new StakingContract(STAKING_PROXY_ADDRESS, env.provider, env.txDefaults);
|
||||
stakingProxy = new StakingProxyContract(STAKING_PROXY_ADDRESS, env.provider, env.txDefaults);
|
||||
});
|
||||
|
||||
describe('Proposal 0', () => {
|
||||
it('works', async () => {
|
||||
const proposal = proposals[0];
|
||||
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 = '0xf9347f751a6a1467abc722ec7d80ba2698dd9d6c';
|
||||
verifyEventsFromLogs(
|
||||
executeTx.logs,
|
||||
[
|
||||
{
|
||||
_from: TREASURY_ADDRESS,
|
||||
_to: recipient,
|
||||
_value: new BigNumber(400_000).times('1e18'),
|
||||
},
|
||||
],
|
||||
ERC20TokenEvents.Transfer,
|
||||
);
|
||||
});
|
||||
});
|
||||
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,76 @@
|
||||
[
|
||||
{
|
||||
"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",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "4.7.10",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1621600614,
|
||||
"version": "4.7.9",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1620214333,
|
||||
"version": "4.7.8",
|
||||
|
@@ -5,6 +5,38 @@ 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
|
||||
|
||||
## v4.7.10 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.9 - _May 21, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v4.7.8 - _May 5, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@0x/contracts-utils",
|
||||
"version": "4.7.8",
|
||||
"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.0",
|
||||
"@0x/contracts-test-utils": "^5.4.8",
|
||||
"@0x/dev-utils": "^4.2.7",
|
||||
"@0x/order-utils": "^10.4.21",
|
||||
"@0x/order-utils": "^10.4.28",
|
||||
"@0x/sol-compiler": "^4.7.3",
|
||||
"@0x/tslint-config": "^4.1.4",
|
||||
"@0x/types": "^3.3.3",
|
||||
|
@@ -536,7 +536,11 @@ blockchainTests('LibBytes', env => {
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [10, 0, 11, 'eleven bytes'], [15, 0, 16, 'sixteen bytes']]));
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[10, 0, 11, 'eleven bytes'],
|
||||
[15, 0, 16, 'sixteen bytes'],
|
||||
]));
|
||||
|
||||
describe('copies backward', () =>
|
||||
test([
|
||||
@@ -603,7 +607,11 @@ blockchainTests('LibBytes', env => {
|
||||
]));
|
||||
|
||||
describe('copies forward within one word and one byte overlap', () =>
|
||||
test([[0, 0, 1, 'one byte'], [0, 10, 11, 'eleven bytes'], [0, 15, 16, 'sixteen bytes']]));
|
||||
test([
|
||||
[0, 0, 1, 'one byte'],
|
||||
[0, 10, 11, 'eleven bytes'],
|
||||
[0, 15, 16, 'sixteen bytes'],
|
||||
]));
|
||||
});
|
||||
|
||||
describe('slice', () => {
|
||||
|
@@ -1,4 +1,96 @@
|
||||
[
|
||||
{
|
||||
"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": [
|
||||
{
|
||||
"note": "Add OtcOrdersFeature",
|
||||
"pr": 244
|
||||
},
|
||||
{
|
||||
"note": "Add UniswapV3 VIP feature",
|
||||
"pr": 237
|
||||
}
|
||||
],
|
||||
"timestamp": 1622609597
|
||||
},
|
||||
{
|
||||
"timestamp": 1621944788,
|
||||
"version": "0.24.1",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Dependencies updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.24.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Add special selectors to selector collision test",
|
||||
"pr": 243
|
||||
}
|
||||
],
|
||||
"timestamp": 1621600614
|
||||
},
|
||||
{
|
||||
"version": "0.23.0",
|
||||
"changes": [
|
||||
|
@@ -5,6 +5,42 @@ 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)
|
||||
* Add UniswapV3 VIP feature (#237)
|
||||
|
||||
## v0.24.1 - _May 25, 2021_
|
||||
|
||||
* Dependencies updated
|
||||
|
||||
## v0.24.0 - _May 21, 2021_
|
||||
|
||||
* Add special selectors to selector collision test (#243)
|
||||
|
||||
## v0.23.0 - _May 5, 2021_
|
||||
|
||||
* Added ETH support to `MixinCurve` (#220)
|
||||
|
@@ -26,11 +26,13 @@ import "./features/interfaces/ITokenSpenderFeature.sol";
|
||||
import "./features/interfaces/ITransformERC20Feature.sol";
|
||||
import "./features/interfaces/IMetaTransactionsFeature.sol";
|
||||
import "./features/interfaces/IUniswapFeature.sol";
|
||||
import "./features/interfaces/IUniswapV3Feature.sol";
|
||||
import "./features/interfaces/IPancakeSwapFeature.sol";
|
||||
import "./features/interfaces/ILiquidityProviderFeature.sol";
|
||||
import "./features/interfaces/INativeOrdersFeature.sol";
|
||||
import "./features/interfaces/IBatchFillNativeOrdersFeature.sol";
|
||||
import "./features/interfaces/IMultiplexFeature.sol";
|
||||
import "./features/interfaces/IOtcOrdersFeature.sol";
|
||||
|
||||
|
||||
/// @dev Interface for a fully featured Exchange Proxy.
|
||||
@@ -40,11 +42,13 @@ interface IZeroEx is
|
||||
ITransformERC20Feature,
|
||||
IMetaTransactionsFeature,
|
||||
IUniswapFeature,
|
||||
IUniswapV3Feature,
|
||||
IPancakeSwapFeature,
|
||||
ILiquidityProviderFeature,
|
||||
INativeOrdersFeature,
|
||||
IBatchFillNativeOrdersFeature,
|
||||
IMultiplexFeature
|
||||
IMultiplexFeature,
|
||||
IOtcOrdersFeature
|
||||
{
|
||||
// solhint-disable state-visibility
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -108,7 +108,7 @@ contract LiquidityProviderFeature is
|
||||
|
||||
if (!LibERC20Transformer.isTokenETH(inputToken)) {
|
||||
// Transfer input ERC20 tokens to the provider.
|
||||
_transferERC20Tokens(
|
||||
_transferERC20TokensFrom(
|
||||
inputToken,
|
||||
msg.sender,
|
||||
address(provider),
|
||||
|
@@ -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("
|
||||
@@ -251,7 +251,7 @@ contract MetaTransactionsFeature is
|
||||
|
||||
// Pay the fee to the sender.
|
||||
if (state.mtx.feeAmount > 0) {
|
||||
_transferERC20Tokens(
|
||||
_transferERC20TokensFrom(
|
||||
state.mtx.feeToken,
|
||||
state.mtx.signer,
|
||||
state.sender,
|
||||
@@ -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,803 +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 "./libs/LibNativeOrder.sol";
|
||||
|
||||
|
||||
/// @dev This feature enables efficient batch and multi-hop trades
|
||||
/// using different liquidity sources.
|
||||
contract MultiplexFeature is
|
||||
IFeature,
|
||||
IMultiplexFeature,
|
||||
FixinCommon,
|
||||
FixinEIP712,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibERC20Transformer for IERC20TokenV06;
|
||||
using LibSafeMathV06 for uint128;
|
||||
using LibSafeMathV06 for uint256;
|
||||
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "MultiplexFeature";
|
||||
/// @dev Version of this feature.
|
||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 1);
|
||||
|
||||
/// @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 == this._sellToLiquidityProvider.selector) {
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
if (fillData.inputToken.isTokenETH()) {
|
||||
inputTokenAmount = LibSafeMathV06.min256(
|
||||
inputTokenAmount,
|
||||
remainingEth
|
||||
);
|
||||
// Transfer the input ETH to the provider.
|
||||
_transferEth(payable(provider), inputTokenAmount);
|
||||
// Count that ETH as spent.
|
||||
remainingEth -= inputTokenAmount;
|
||||
} else {
|
||||
// Transfer input ERC20 tokens to the provider.
|
||||
_transferERC20Tokens(
|
||||
fillData.inputToken,
|
||||
msg.sender,
|
||||
provider,
|
||||
inputTokenAmount
|
||||
);
|
||||
}
|
||||
// Perform the PLP trade.
|
||||
uint256 outputTokenAmount_ = _sellToLiquidityProvider(
|
||||
fillData.inputToken,
|
||||
fillData.outputToken,
|
||||
inputTokenAmount,
|
||||
ILiquidityProvider(provider),
|
||||
msg.sender,
|
||||
auxiliaryData
|
||||
);
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
args.taker = msg.sender;
|
||||
args.inputToken = fillData.inputToken;
|
||||
args.outputToken = fillData.outputToken;
|
||||
args.inputTokenAmount = inputTokenAmount;
|
||||
args.minOutputTokenAmount = 0;
|
||||
uint256 ethValue;
|
||||
(args.transformations, ethValue) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(ITransformERC20Feature.Transformation[], uint256)
|
||||
);
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(
|
||||
ethValue,
|
||||
remainingEth
|
||||
);
|
||||
if (ethValue > 0) {
|
||||
require(
|
||||
args.inputToken.isTokenETH(),
|
||||
"MultiplexFeature::_batchFill/ETH_TRANSFORM_ONLY"
|
||||
);
|
||||
}
|
||||
try ITransformERC20Feature(address(this))._transformERC20
|
||||
{value: ethValue}
|
||||
(args)
|
||||
returns (uint256 outputTokenAmount_)
|
||||
{
|
||||
remainingEth -= ethValue;
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
} catch {}
|
||||
} else if (wrappedCall.selector == this._multiHopFill.selector) {
|
||||
MultiHopFillData memory multiHopFillData;
|
||||
uint256 ethValue;
|
||||
(
|
||||
multiHopFillData.tokens,
|
||||
multiHopFillData.calls,
|
||||
ethValue
|
||||
) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address[], WrappedMultiHopCall[], uint256)
|
||||
);
|
||||
multiHopFillData.sellAmount = inputTokenAmount;
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(
|
||||
ethValue,
|
||||
remainingEth
|
||||
);
|
||||
// Subtract the ethValue allocated to the nested multi-hop fill.
|
||||
remainingEth -= ethValue;
|
||||
(uint256 outputTokenAmount_, uint256 leftoverEth) =
|
||||
_multiHopFill(multiHopFillData, ethValue);
|
||||
// Increment the sold and bought amounts.
|
||||
soldAmount = soldAmount.safeAdd(inputTokenAmount);
|
||||
outputTokenAmount = outputTokenAmount.safeAdd(outputTokenAmount_);
|
||||
// Add back any ETH that wasn't used by the nested multi-hop fill.
|
||||
remainingEth += leftoverEth;
|
||||
} else {
|
||||
revert("MultiplexFeature::_batchFill/UNRECOGNIZED_SELECTOR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Internal variant of `multiHopFill`. This function can be nested within
|
||||
// a `_batchFill`.
|
||||
// This function executes a sequence of fills "hopping" through the
|
||||
// path of tokens given by `fillData.tokens`. The nested operations that
|
||||
// can be used as "hops" are:
|
||||
// - WETH.deposit (wraps ETH)
|
||||
// - WETH.withdraw (unwraps WETH)
|
||||
// - _sellToUniswap (executes a Uniswap/Sushiswap swap)
|
||||
// - _sellToLiquidityProvider (executes a PLP swap)
|
||||
// - _transformERC20 (executes arbitrary ERC20 Transformations)
|
||||
// This function optimizes the number of ERC20 transfers performed
|
||||
// by having each hop transfer its output tokens directly to the
|
||||
// target address of the next hop. Note that the `outputTokenAmount` returned
|
||||
// by this function could theoretically be inaccurate if `msg.sender` has
|
||||
// set a token allowance on an external contract that gets called during
|
||||
// the execution of this function.
|
||||
function _multiHopFill(MultiHopFillData memory fillData, uint256 totalEth)
|
||||
public
|
||||
returns (uint256 outputTokenAmount, uint256 remainingEth)
|
||||
{
|
||||
// There should be one call/hop between every two tokens
|
||||
// in the path.
|
||||
// tokens[0]––calls[0]––>tokens[1]––...––calls[n-1]––>tokens[n]
|
||||
require(
|
||||
fillData.tokens.length == fillData.calls.length + 1,
|
||||
"MultiplexFeature::_multiHopFill/MISMATCHED_ARRAY_LENGTHS"
|
||||
);
|
||||
// Track the remaining ETH allocated to this call.
|
||||
remainingEth = totalEth;
|
||||
// This variable is used as the input and output amounts of
|
||||
// each hop. After the final hop, this will contain the output
|
||||
// amount of the multi-hop fill.
|
||||
outputTokenAmount = fillData.sellAmount;
|
||||
// This variable is used to cache the address to target in the
|
||||
// next hop. See `_computeHopRecipient` for details.
|
||||
address nextTarget;
|
||||
for (uint256 i = 0; i != fillData.calls.length; i++) {
|
||||
WrappedMultiHopCall memory wrappedCall = fillData.calls[i];
|
||||
if (wrappedCall.selector == this._sellToUniswap.selector) {
|
||||
// If the next hop supports a "transfer then execute" pattern,
|
||||
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
|
||||
// for details.
|
||||
address recipient = _computeHopRecipient(fillData.calls, i);
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
// Perform the Uniswap/Sushiswap trade.
|
||||
outputTokenAmount = _sellToUniswap(
|
||||
tokens,
|
||||
outputTokenAmount,
|
||||
isSushi,
|
||||
nextTarget,
|
||||
recipient
|
||||
);
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = recipient == msg.sender ? address(0) : recipient;
|
||||
} else if (wrappedCall.selector == this._sellToLiquidityProvider.selector) {
|
||||
// If the next hop supports a "transfer then execute" pattern,
|
||||
// the recipient will not be `msg.sender`. See `_computeHopRecipient`
|
||||
// for details.
|
||||
address recipient = _computeHopRecipient(fillData.calls, i);
|
||||
// If `nextTarget` was not set in the previous hop, then we
|
||||
// need to send in the input ETH/tokens to the liquidity provider
|
||||
// contract before executing the trade.
|
||||
if (nextTarget == address(0)) {
|
||||
(address provider, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
// Transfer input ETH or ERC20 tokens to the liquidity
|
||||
// provider contract.
|
||||
if (IERC20TokenV06(fillData.tokens[i]).isTokenETH()) {
|
||||
outputTokenAmount = LibSafeMathV06.min256(
|
||||
outputTokenAmount,
|
||||
remainingEth
|
||||
);
|
||||
_transferEth(payable(provider), outputTokenAmount);
|
||||
remainingEth -= outputTokenAmount;
|
||||
} else {
|
||||
_transferERC20Tokens(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
msg.sender,
|
||||
provider,
|
||||
outputTokenAmount
|
||||
);
|
||||
}
|
||||
outputTokenAmount = _sellToLiquidityProvider(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
IERC20TokenV06(fillData.tokens[i + 1]),
|
||||
outputTokenAmount,
|
||||
ILiquidityProvider(provider),
|
||||
recipient,
|
||||
auxiliaryData
|
||||
);
|
||||
} else {
|
||||
(, bytes memory auxiliaryData) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
// Tokens and ETH have already been transferred to
|
||||
// the liquidity provider contract in the previous hop.
|
||||
outputTokenAmount = _sellToLiquidityProvider(
|
||||
IERC20TokenV06(fillData.tokens[i]),
|
||||
IERC20TokenV06(fillData.tokens[i + 1]),
|
||||
outputTokenAmount,
|
||||
ILiquidityProvider(nextTarget),
|
||||
recipient,
|
||||
auxiliaryData
|
||||
);
|
||||
}
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = recipient == msg.sender ? address(0) : recipient;
|
||||
} else if (wrappedCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
ITransformERC20Feature.TransformERC20Args memory args;
|
||||
args.inputToken = IERC20TokenV06(fillData.tokens[i]);
|
||||
args.outputToken = IERC20TokenV06(fillData.tokens[i + 1]);
|
||||
args.minOutputTokenAmount = 0;
|
||||
args.taker = payable(_computeHopRecipient(fillData.calls, i));
|
||||
if (nextTarget != address(0)) {
|
||||
// If `nextTarget` was set in the previous hop, then the input
|
||||
// token was already sent to the FlashWallet. Setting
|
||||
// `inputTokenAmount` to 0 indicates that no tokens need to
|
||||
// be pulled into the FlashWallet before executing the
|
||||
// transformations.
|
||||
args.inputTokenAmount = 0;
|
||||
} else if (
|
||||
args.taker != msg.sender &&
|
||||
!args.inputToken.isTokenETH()
|
||||
) {
|
||||
address flashWallet = address(
|
||||
ITransformERC20Feature(address(this)).getTransformWallet()
|
||||
);
|
||||
// The input token has _not_ already been sent to the
|
||||
// FlashWallet. We also want PayTakerTransformer to
|
||||
// send the output token to some address other than
|
||||
// msg.sender, so we must transfer the input token
|
||||
// to the FlashWallet here.
|
||||
_transferERC20Tokens(
|
||||
args.inputToken,
|
||||
msg.sender,
|
||||
flashWallet,
|
||||
outputTokenAmount
|
||||
);
|
||||
args.inputTokenAmount = 0;
|
||||
} else {
|
||||
// Otherwise, either:
|
||||
// (1) args.taker == msg.sender, in which case
|
||||
// `_transformERC20` will pull the input token
|
||||
// into the FlashWallet, or
|
||||
// (2) args.inputToken == ETH_TOKEN_ADDRESS, in which
|
||||
// case ETH is attached to the call and no token
|
||||
// transfer occurs.
|
||||
args.inputTokenAmount = outputTokenAmount;
|
||||
}
|
||||
uint256 ethValue;
|
||||
(args.transformations, ethValue) = abi.decode(
|
||||
wrappedCall.data,
|
||||
(ITransformERC20Feature.Transformation[], uint256)
|
||||
);
|
||||
// Do not spend more than the remaining ETH.
|
||||
ethValue = LibSafeMathV06.min256(ethValue, remainingEth);
|
||||
if (ethValue > 0) {
|
||||
require(
|
||||
args.inputToken.isTokenETH(),
|
||||
"MultiplexFeature::_multiHopFill/ETH_TRANSFORM_ONLY"
|
||||
);
|
||||
}
|
||||
// Call `_transformERC20`.
|
||||
outputTokenAmount = ITransformERC20Feature(address(this))
|
||||
._transformERC20{value: ethValue}(args);
|
||||
// Decrement the remaining ETH.
|
||||
remainingEth -= ethValue;
|
||||
// If the recipient was not `msg.sender`, it must be the target
|
||||
// contract for the next hop.
|
||||
nextTarget = args.taker == msg.sender ? address(0) : args.taker;
|
||||
} else if (wrappedCall.selector == IEtherTokenV06.deposit.selector) {
|
||||
require(
|
||||
i == 0,
|
||||
"MultiplexFeature::_multiHopFill/DEPOSIT_FIRST_HOP_ONLY"
|
||||
);
|
||||
uint256 ethValue = LibSafeMathV06.min256(outputTokenAmount, remainingEth);
|
||||
// Wrap ETH.
|
||||
weth.deposit{value: ethValue}();
|
||||
nextTarget = _computeHopRecipient(fillData.calls, i);
|
||||
weth.transfer(nextTarget, ethValue);
|
||||
remainingEth -= ethValue;
|
||||
} else if (wrappedCall.selector == IEtherTokenV06.withdraw.selector) {
|
||||
require(
|
||||
i == fillData.calls.length - 1,
|
||||
"MultiplexFeature::_multiHopFill/WITHDRAW_LAST_HOP_ONLY"
|
||||
);
|
||||
// Unwrap WETH and send to `msg.sender`.
|
||||
weth.withdraw(outputTokenAmount);
|
||||
_transferEth(msg.sender, outputTokenAmount);
|
||||
nextTarget = address(0);
|
||||
} else {
|
||||
revert("MultiplexFeature::_multiHopFill/UNRECOGNIZED_SELECTOR");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Similar to the UniswapFeature, but with a couple of differences:
|
||||
// - Does not perform the transfer in if `pairAddress` is given,
|
||||
// which indicates that the transfer in was already performed
|
||||
// in the previous hop of a multi-hop fill.
|
||||
// - Does not include a minBuyAmount check (which is performed in
|
||||
// either `batchFill` or `multiHopFill`).
|
||||
// - Takes a `recipient` address parameter, so the output of the
|
||||
// final `swap` call can be sent to an address other than `msg.sender`.
|
||||
function _sellToUniswap(
|
||||
address[] memory tokens,
|
||||
uint256 sellAmount,
|
||||
bool isSushi,
|
||||
address pairAddress,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
require(tokens.length > 1, "MultiplexFeature::_sellToUniswap/InvalidTokensLength");
|
||||
|
||||
if (pairAddress == address(0)) {
|
||||
pairAddress = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
|
||||
_transferERC20Tokens(
|
||||
IERC20TokenV06(tokens[0]),
|
||||
msg.sender,
|
||||
pairAddress,
|
||||
sellAmount
|
||||
);
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < tokens.length - 1; i++) {
|
||||
(address inputToken, address outputToken) = (tokens[i], tokens[i + 1]);
|
||||
outputTokenAmount = _computeUniswapOutputAmount(
|
||||
pairAddress,
|
||||
inputToken,
|
||||
outputToken,
|
||||
sellAmount
|
||||
);
|
||||
(uint256 amount0Out, uint256 amount1Out) = inputToken < outputToken
|
||||
? (uint256(0), outputTokenAmount)
|
||||
: (outputTokenAmount, uint256(0));
|
||||
address to = i < tokens.length - 2
|
||||
? _computeUniswapPairAddress(outputToken, tokens[i + 2], isSushi)
|
||||
: recipient;
|
||||
IUniswapV2Pair(pairAddress).swap(
|
||||
amount0Out,
|
||||
amount1Out,
|
||||
to,
|
||||
new bytes(0)
|
||||
);
|
||||
pairAddress = to;
|
||||
sellAmount = outputTokenAmount;
|
||||
}
|
||||
}
|
||||
|
||||
// Same as the LiquidityProviderFeature, but without the transfer in
|
||||
// (which is potentially done in the previous hop of a multi-hop fill)
|
||||
// and without the minBuyAmount check (which is performed at the top, i.e.
|
||||
// in either `batchFill` or `multiHopFill`).
|
||||
function _sellToLiquidityProvider(
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint256 inputTokenAmount,
|
||||
ILiquidityProvider provider,
|
||||
address recipient,
|
||||
bytes memory auxiliaryData
|
||||
)
|
||||
public
|
||||
returns (uint256 outputTokenAmount)
|
||||
{
|
||||
uint256 balanceBefore = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient);
|
||||
if (IERC20TokenV06(inputToken).isTokenETH()) {
|
||||
sandbox.executeSellEthForToken(
|
||||
provider,
|
||||
outputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
} else if (IERC20TokenV06(outputToken).isTokenETH()) {
|
||||
sandbox.executeSellTokenForEth(
|
||||
provider,
|
||||
inputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
} else {
|
||||
sandbox.executeSellTokenForToken(
|
||||
provider,
|
||||
inputToken,
|
||||
outputToken,
|
||||
recipient,
|
||||
0,
|
||||
auxiliaryData
|
||||
);
|
||||
}
|
||||
outputTokenAmount = IERC20TokenV06(outputToken).getTokenBalanceOf(recipient)
|
||||
.safeSub(balanceBefore);
|
||||
emit LiquidityProviderSwap(
|
||||
address(inputToken),
|
||||
address(outputToken),
|
||||
inputTokenAmount,
|
||||
outputTokenAmount,
|
||||
address(provider),
|
||||
recipient
|
||||
);
|
||||
return outputTokenAmount;
|
||||
}
|
||||
|
||||
function _transferEth(address payable recipient, uint256 amount)
|
||||
private
|
||||
{
|
||||
(bool success,) = recipient.call{value: amount}("");
|
||||
require(success, "MultiplexFeature::_transferEth/TRANSFER_FALIED");
|
||||
}
|
||||
|
||||
// Some liquidity sources (e.g. Uniswap, Sushiswap, and PLP) can be passed
|
||||
// a `recipient` parameter so the boguht tokens are transferred to the
|
||||
// `recipient` address rather than `msg.sender`.
|
||||
// Some liquidity sources (also Uniswap, Sushiswap, and PLP incidentally)
|
||||
// support a "transfer then execute" pattern, where the token being sold
|
||||
// can be transferred into the contract before calling a swap function to
|
||||
// execute the trade.
|
||||
// If the current hop in a multi-hop fill satisfies the first condition,
|
||||
// and the next hop satisfies the second condition, the tokens bought
|
||||
// in the current hop can be directly sent to the target contract of
|
||||
// the next hop to save a transfer.
|
||||
function _computeHopRecipient(
|
||||
WrappedMultiHopCall[] memory calls,
|
||||
uint256 i
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (address recipient)
|
||||
{
|
||||
recipient = msg.sender;
|
||||
if (i < calls.length - 1) {
|
||||
WrappedMultiHopCall memory nextCall = calls[i + 1];
|
||||
if (nextCall.selector == this._sellToUniswap.selector) {
|
||||
(address[] memory tokens, bool isSushi) = abi.decode(
|
||||
nextCall.data,
|
||||
(address[], bool)
|
||||
);
|
||||
recipient = _computeUniswapPairAddress(tokens[0], tokens[1], isSushi);
|
||||
} else if (nextCall.selector == this._sellToLiquidityProvider.selector) {
|
||||
(recipient,) = abi.decode(
|
||||
nextCall.data,
|
||||
(address, bytes)
|
||||
);
|
||||
} else if (nextCall.selector == IEtherTokenV06.withdraw.selector) {
|
||||
recipient = address(this);
|
||||
} else if (nextCall.selector == ITransformERC20Feature._transformERC20.selector) {
|
||||
recipient = address(
|
||||
ITransformERC20Feature(address(this)).getTransformWallet()
|
||||
);
|
||||
}
|
||||
}
|
||||
require(
|
||||
recipient != address(0),
|
||||
"MultiplexFeature::_computeHopRecipient/RECIPIENT_IS_NULL"
|
||||
);
|
||||
}
|
||||
|
||||
// Computes the the amount of output token that would be bought
|
||||
// from Uniswap/Sushiswap given the input amount.
|
||||
function _computeUniswapOutputAmount(
|
||||
address pairAddress,
|
||||
address inputToken,
|
||||
address outputToken,
|
||||
uint256 inputAmount
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (uint256 outputAmount)
|
||||
{
|
||||
require(
|
||||
inputAmount > 0,
|
||||
"MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_INPUT_AMOUNT"
|
||||
);
|
||||
(uint256 reserve0, uint256 reserve1,) = IUniswapV2Pair(pairAddress).getReserves();
|
||||
require(
|
||||
reserve0 > 0 && reserve1 > 0,
|
||||
'MultiplexFeature::_computeUniswapOutputAmount/INSUFFICIENT_LIQUIDITY'
|
||||
);
|
||||
(uint256 inputReserve, uint256 outputReserve) = inputToken < outputToken
|
||||
? (reserve0, reserve1)
|
||||
: (reserve1, reserve0);
|
||||
uint256 inputAmountWithFee = inputAmount.safeMul(997);
|
||||
uint256 numerator = inputAmountWithFee.safeMul(outputReserve);
|
||||
uint256 denominator = inputReserve.safeMul(1000).safeAdd(inputAmountWithFee);
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
// Computes the Uniswap/Sushiswap pair contract address for the
|
||||
// given tokens.
|
||||
function _computeUniswapPairAddress(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
bool isSushi
|
||||
)
|
||||
private
|
||||
pure
|
||||
returns (address pairAddress)
|
||||
{
|
||||
(address token0, address token1) = tokenA < tokenB
|
||||
? (tokenA, tokenB)
|
||||
: (tokenB, tokenA);
|
||||
if (isSushi) {
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
SUSHISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
SUSHISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
} else {
|
||||
return address(uint256(keccak256(abi.encodePacked(
|
||||
hex'ff',
|
||||
UNISWAP_FACTORY,
|
||||
keccak256(abi.encodePacked(token0, token1)),
|
||||
UNISWAP_PAIR_INIT_CODE_HASH
|
||||
))));
|
||||
}
|
||||
}
|
||||
}
|
@@ -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,
|
||||
|
637
contracts/zero-ex/contracts/src/features/OtcOrdersFeature.sol
Normal file
637
contracts/zero-ex/contracts/src/features/OtcOrdersFeature.sol
Normal file
@@ -0,0 +1,637 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibMathV06.sol";
|
||||
import "../errors/LibNativeOrdersRichErrors.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../fixins/FixinEIP712.sol";
|
||||
import "../fixins/FixinTokenSpender.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../storage/LibNativeOrdersStorage.sol";
|
||||
import "../storage/LibOtcOrdersStorage.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IOtcOrdersFeature.sol";
|
||||
import "./libs/LibNativeOrder.sol";
|
||||
import "./libs/LibSignature.sol";
|
||||
|
||||
|
||||
/// @dev Feature for interacting with OTC orders.
|
||||
contract OtcOrdersFeature is
|
||||
IFeature,
|
||||
IOtcOrdersFeature,
|
||||
FixinCommon,
|
||||
FixinEIP712,
|
||||
FixinTokenSpender
|
||||
{
|
||||
using LibSafeMathV06 for uint256;
|
||||
using LibSafeMathV06 for uint128;
|
||||
|
||||
/// @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;
|
||||
|
||||
constructor(address zeroExAddress, IEtherTokenV06 weth)
|
||||
public
|
||||
FixinEIP712(zeroExAddress)
|
||||
{
|
||||
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.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);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// @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 fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
uint128 takerTokenFillAmount
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
msg.sender
|
||||
);
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
takerTokenFillAmount,
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fill an OTC order whose taker token is WETH for up
|
||||
/// to `msg.value`.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrderWithEth(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature
|
||||
)
|
||||
public
|
||||
override
|
||||
payable
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
if (order.takerToken == WETH) {
|
||||
// Wrap ETH
|
||||
WETH.deposit{value: msg.value}();
|
||||
} else {
|
||||
require(
|
||||
address(order.takerToken) == ETH_TOKEN_ADDRESS,
|
||||
"OtcOrdersFeature::fillOtcOrderWithEth/INVALID_TAKER_TOKEN"
|
||||
);
|
||||
}
|
||||
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
msg.sender
|
||||
);
|
||||
|
||||
(takerTokenFilledAmount, makerTokenFilledAmount) = _settleOtcOrder(
|
||||
order,
|
||||
msg.value.safeDowncastToUint128(),
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Fully fill an OTC order. "Meta-transaction" variant,
|
||||
/// requires order to be signed by both maker and taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
function fillTakerSignedOtcOrder(
|
||||
LibNativeOrder.OtcOrder memory order,
|
||||
LibSignature.Signature memory makerSignature,
|
||||
LibSignature.Signature memory takerSignature
|
||||
)
|
||||
public
|
||||
override
|
||||
{
|
||||
LibNativeOrder.OtcOrderInfo memory orderInfo = getOtcOrderInfo(order);
|
||||
address taker = LibSignature.getSignerOfHash(orderInfo.orderHash, takerSignature);
|
||||
|
||||
_validateOtcOrder(
|
||||
order,
|
||||
orderInfo,
|
||||
makerSignature,
|
||||
taker
|
||||
);
|
||||
_settleOtcOrder(
|
||||
order,
|
||||
order.takerAmount,
|
||||
taker,
|
||||
taker
|
||||
);
|
||||
|
||||
emit OtcOrderFilled(
|
||||
orderInfo.orderHash,
|
||||
order.maker,
|
||||
taker,
|
||||
address(order.makerToken),
|
||||
address(order.takerToken),
|
||||
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,
|
||||
uint128 takerTokenFillAmount,
|
||||
address payer,
|
||||
address recipient
|
||||
)
|
||||
private
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount)
|
||||
{
|
||||
{
|
||||
// Unpack nonce fields
|
||||
uint64 nonceBucket = uint64(order.expiryAndNonce >> 128);
|
||||
uint128 nonce = uint128(order.expiryAndNonce);
|
||||
// Update tx origin nonce for the order
|
||||
LibOtcOrdersStorage.getStorage().txOriginNonces
|
||||
[order.txOrigin][nonceBucket] = nonce;
|
||||
}
|
||||
|
||||
if (takerTokenFillAmount == order.takerAmount) {
|
||||
takerTokenFilledAmount = order.takerAmount;
|
||||
makerTokenFilledAmount = order.makerAmount;
|
||||
} else {
|
||||
// Clamp the taker token fill amount to the fillable amount.
|
||||
takerTokenFilledAmount = LibSafeMathV06.min128(
|
||||
takerTokenFillAmount,
|
||||
order.takerAmount
|
||||
);
|
||||
// Compute the maker token amount.
|
||||
// This should never overflow because the values are all clamped to
|
||||
// (2^128-1).
|
||||
makerTokenFilledAmount = uint128(LibMathV06.getPartialAmountFloor(
|
||||
uint256(takerTokenFilledAmount),
|
||||
uint256(order.takerAmount),
|
||||
uint256(order.makerAmount)
|
||||
));
|
||||
}
|
||||
|
||||
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,
|
||||
payer,
|
||||
order.maker,
|
||||
takerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
// Transfer maker -> recipient.
|
||||
_transferERC20TokensFrom(
|
||||
order.makerToken,
|
||||
order.maker,
|
||||
recipient,
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Get the order info for an OTC order.
|
||||
/// @param order The OTC order.
|
||||
/// @return orderInfo Info about the order.
|
||||
function getOtcOrderInfo(LibNativeOrder.OtcOrder memory order)
|
||||
public
|
||||
override
|
||||
view
|
||||
returns (LibNativeOrder.OtcOrderInfo memory orderInfo)
|
||||
{
|
||||
// compute order hash.
|
||||
orderInfo.orderHash = getOtcOrderHash(order);
|
||||
|
||||
LibOtcOrdersStorage.Storage storage stor =
|
||||
LibOtcOrdersStorage.getStorage();
|
||||
|
||||
// Unpack expiry and nonce fields
|
||||
uint64 expiry = uint64(order.expiryAndNonce >> 192);
|
||||
uint64 nonceBucket = uint64(order.expiryAndNonce >> 128);
|
||||
uint128 nonce = uint128(order.expiryAndNonce);
|
||||
|
||||
// check tx origin nonce
|
||||
uint128 lastNonce = stor.txOriginNonces
|
||||
[order.txOrigin]
|
||||
[nonceBucket];
|
||||
if (nonce <= lastNonce) {
|
||||
orderInfo.status = LibNativeOrder.OrderStatus.INVALID;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
// Check for expiration.
|
||||
if (expiry <= uint64(block.timestamp)) {
|
||||
orderInfo.status = LibNativeOrder.OrderStatus.EXPIRED;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
orderInfo.status = LibNativeOrder.OrderStatus.FILLABLE;
|
||||
return orderInfo;
|
||||
}
|
||||
|
||||
/// @dev Get the canonical hash of an OTC order.
|
||||
/// @param order The OTC order.
|
||||
/// @return orderHash The order hash.
|
||||
function getOtcOrderHash(LibNativeOrder.OtcOrder memory order)
|
||||
public
|
||||
override
|
||||
view
|
||||
returns (bytes32 orderHash)
|
||||
{
|
||||
return _getEIP712Hash(
|
||||
LibNativeOrder.getOtcOrderStructHash(order)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Get the last nonce used for a particular
|
||||
/// tx.origin address and nonce bucket.
|
||||
/// @param txOrigin The address.
|
||||
/// @param nonceBucket The nonce bucket index.
|
||||
/// @return lastNonce The last nonce value used.
|
||||
function lastOtcTxOriginNonce(address txOrigin, uint64 nonceBucket)
|
||||
public
|
||||
override
|
||||
view
|
||||
returns (uint128 lastNonce)
|
||||
{
|
||||
LibOtcOrdersStorage.Storage storage stor =
|
||||
LibOtcOrdersStorage.getStorage();
|
||||
return stor.txOriginNonces
|
||||
[txOrigin]
|
||||
[nonceBucket];
|
||||
}
|
||||
|
||||
function _transferEth(address recipient, uint256 amount)
|
||||
private
|
||||
{
|
||||
// Transfer ETH to recipient
|
||||
(bool success, bytes memory revertData) =
|
||||
recipient.call{value: amount}("");
|
||||
// Revert on failure
|
||||
if (!success) {
|
||||
revertData.rrevert();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.
|
||||
_transferERC20Tokens(
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
447
contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol
Normal file
447
contracts/zero-ex/contracts/src/features/UniswapV3Feature.sol
Normal file
@@ -0,0 +1,447 @@
|
||||
// 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 "../vendor/IUniswapV3Pool.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../fixins/FixinTokenSpender.sol";
|
||||
import "./interfaces/IFeature.sol";
|
||||
import "./interfaces/IUniswapV3Feature.sol";
|
||||
|
||||
|
||||
/// @dev VIP uniswap fill functions.
|
||||
contract UniswapV3Feature is
|
||||
IFeature,
|
||||
IUniswapV3Feature,
|
||||
FixinCommon,
|
||||
FixinTokenSpender
|
||||
{
|
||||
/// @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, 1, 0);
|
||||
/// @dev WETH contract.
|
||||
IEtherTokenV06 private immutable WETH;
|
||||
/// @dev UniswapV3 Factory contract address prepended with '0xff' and left-aligned.
|
||||
bytes32 private immutable UNI_FF_FACTORY_ADDRESS;
|
||||
/// @dev UniswapV3 pool init code hash.
|
||||
bytes32 private immutable UNI_POOL_INIT_CODE_HASH;
|
||||
/// @dev Minimum size of an encoded swap path:
|
||||
/// sizeof(address(inputToken) | uint24(fee) | address(outputToken))
|
||||
uint256 private constant SINGLE_HOP_PATH_SIZE = 20 + 3 + 20;
|
||||
/// @dev How many bytes to skip ahead in an encoded path to start at the next hop:
|
||||
/// sizeof(address(inputToken) | uint24(fee))
|
||||
uint256 private constant PATH_SKIP_HOP_SIZE = 20 + 3;
|
||||
/// @dev The size of the swap callback data.
|
||||
uint256 private constant SWAP_CALLBACK_DATA_SIZE = 128;
|
||||
/// @dev Minimum tick price sqrt ratio.
|
||||
uint160 internal constant MIN_PRICE_SQRT_RATIO = 4295128739;
|
||||
/// @dev Minimum tick price sqrt ratio.
|
||||
uint160 internal constant MAX_PRICE_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
|
||||
/// @dev Mask of lower 20 bytes.
|
||||
uint256 private constant ADDRESS_MASK = 0x00ffffffffffffffffffffffffffffffffffffffff;
|
||||
/// @dev Mask of lower 3 bytes.
|
||||
uint256 private constant UINT24_MASK = 0xffffff;
|
||||
|
||||
/// @dev Construct this contract.
|
||||
/// @param weth The WETH contract.
|
||||
/// @param uniFactory The UniswapV3 factory contract.
|
||||
/// @param poolInitCodeHash The UniswapV3 pool init code hash.
|
||||
constructor(
|
||||
IEtherTokenV06 weth,
|
||||
address uniFactory,
|
||||
bytes32 poolInitCodeHash
|
||||
) public {
|
||||
WETH = weth;
|
||||
UNI_FF_FACTORY_ADDRESS = bytes32((uint256(0xff) << 248) | (uint256(uniFactory) << 88));
|
||||
UNI_POOL_INIT_CODE_HASH = poolInitCodeHash;
|
||||
}
|
||||
|
||||
/// @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.sellEthForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForEthToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.sellTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this._sellHeldTokenForTokenToUniswapV3.selector);
|
||||
_registerFeatureFunction(this.uniswapV3SwapCallback.selector);
|
||||
return LibMigrate.MIGRATE_SUCCESS;
|
||||
}
|
||||
|
||||
/// @dev Sell attached ETH directly against uniswap v3.
|
||||
/// @param encodedPath Uniswap-encoded path, where the first token is WETH.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for sender.
|
||||
/// @param minBuyAmount Minimum amount of the last token in the path to buy.
|
||||
/// @return buyAmount Amount of the last token in the path bought.
|
||||
function sellEthForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
payable
|
||||
override
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
// Wrap ETH.
|
||||
WETH.deposit{ value: msg.value }();
|
||||
return _swap(
|
||||
encodedPath,
|
||||
msg.value,
|
||||
minBuyAmount,
|
||||
address(this), // we are payer because we hold the WETH
|
||||
_normalizeRecipient(recipient)
|
||||
);
|
||||
}
|
||||
|
||||
/// @dev Sell a token for ETH directly against uniswap v3.
|
||||
/// @param encodedPath Uniswap-encoded path, where the last token is WETH.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of ETH to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for sender.
|
||||
/// @return buyAmount Amount of ETH bought.
|
||||
function sellTokenForEthToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address payable recipient
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
buyAmount = _swap(
|
||||
encodedPath,
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
msg.sender,
|
||||
address(this) // we are recipient because we need to unwrap WETH
|
||||
);
|
||||
WETH.withdraw(buyAmount);
|
||||
// Transfer ETH to recipient.
|
||||
(bool success, bytes memory revertData) =
|
||||
_normalizeRecipient(recipient).call{ value: buyAmount }("");
|
||||
if (!success) {
|
||||
revertData.rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// @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 sellTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
public
|
||||
override
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
buyAmount = _swap(
|
||||
encodedPath,
|
||||
sellAmount,
|
||||
minBuyAmount,
|
||||
msg.sender,
|
||||
_normalizeRecipient(recipient)
|
||||
);
|
||||
}
|
||||
|
||||
/// @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.
|
||||
/// @param amount0Delta Token0 amount owed.
|
||||
/// @param amount1Delta Token1 amount owed.
|
||||
/// @param data Arbitrary data forwarded from swap() caller. An ABI-encoded
|
||||
/// struct of: inputToken, outputToken, fee, payer
|
||||
function uniswapV3SwapCallback(
|
||||
int256 amount0Delta,
|
||||
int256 amount1Delta,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
override
|
||||
{
|
||||
IERC20TokenV06 token0;
|
||||
IERC20TokenV06 token1;
|
||||
address payer;
|
||||
{
|
||||
uint24 fee;
|
||||
// Decode the data.
|
||||
require(data.length == SWAP_CALLBACK_DATA_SIZE, "UniswapFeature/INVALID_SWAP_CALLBACK_DATA");
|
||||
assembly {
|
||||
let p := add(36, calldataload(68))
|
||||
token0 := calldataload(p)
|
||||
token1 := calldataload(add(p, 32))
|
||||
fee := calldataload(add(p, 64))
|
||||
payer := calldataload(add(p, 96))
|
||||
}
|
||||
(token0, token1) = token0 < token1
|
||||
? (token0, token1)
|
||||
: (token1, token0);
|
||||
// Only a valid pool contract can call this function.
|
||||
require(
|
||||
msg.sender == address(_toPool(token0, fee, token1)),
|
||||
"UniswapV3Feature/INVALID_SWAP_CALLBACK_CALLER"
|
||||
);
|
||||
}
|
||||
// Pay the amount owed to the pool.
|
||||
if (amount0Delta > 0) {
|
||||
_pay(token0, payer, msg.sender, uint256(amount0Delta));
|
||||
} else if (amount1Delta > 0) {
|
||||
_pay(token1, payer, msg.sender, uint256(amount1Delta));
|
||||
} else {
|
||||
revert("UniswapV3Feature/INVALID_SWAP_AMOUNTS");
|
||||
}
|
||||
}
|
||||
|
||||
// Executes successive swaps along an encoded uniswap path.
|
||||
function _swap(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address payer,
|
||||
address recipient
|
||||
)
|
||||
private
|
||||
returns (uint256 buyAmount)
|
||||
{
|
||||
if (sellAmount != 0) {
|
||||
require(sellAmount <= uint256(type(int256).max), "UniswapV3Feature/SELL_AMOUNT_OVERFLOW");
|
||||
|
||||
// Perform a swap for each hop in the path.
|
||||
bytes memory swapCallbackData = new bytes(SWAP_CALLBACK_DATA_SIZE);
|
||||
while (true) {
|
||||
bool isPathMultiHop = _isPathMultiHop(encodedPath);
|
||||
bool zeroForOne;
|
||||
IUniswapV3Pool pool;
|
||||
{
|
||||
(
|
||||
IERC20TokenV06 inputToken,
|
||||
uint24 fee,
|
||||
IERC20TokenV06 outputToken
|
||||
) = _decodeFirstPoolInfoFromPath(encodedPath);
|
||||
pool = _toPool(inputToken, fee, outputToken);
|
||||
zeroForOne = inputToken < outputToken;
|
||||
_updateSwapCallbackData(
|
||||
swapCallbackData,
|
||||
inputToken,
|
||||
outputToken,
|
||||
fee,
|
||||
payer
|
||||
);
|
||||
}
|
||||
(int256 amount0, int256 amount1) = pool.swap(
|
||||
// Intermediate tokens go to this contract.
|
||||
isPathMultiHop ? address(this) : recipient,
|
||||
zeroForOne,
|
||||
int256(sellAmount),
|
||||
zeroForOne
|
||||
? MIN_PRICE_SQRT_RATIO + 1
|
||||
: MAX_PRICE_SQRT_RATIO - 1,
|
||||
swapCallbackData
|
||||
);
|
||||
{
|
||||
int256 _buyAmount = -(zeroForOne ? amount1 : amount0);
|
||||
require(_buyAmount >= 0, "UniswapV3Feature/INVALID_BUY_AMOUNT");
|
||||
buyAmount = uint256(_buyAmount);
|
||||
}
|
||||
if (!isPathMultiHop) {
|
||||
// Done.
|
||||
break;
|
||||
}
|
||||
// Continue with next hop.
|
||||
payer = address(this); // Subsequent hops are paid for by us.
|
||||
sellAmount = buyAmount;
|
||||
// Skip to next hop along path.
|
||||
encodedPath = _shiftHopFromPathInPlace(encodedPath);
|
||||
}
|
||||
}
|
||||
require(minBuyAmount <= buyAmount, "UniswapV3Feature/UNDERBOUGHT");
|
||||
}
|
||||
|
||||
// Pay tokens from `payer` to `to`, using `transferFrom()` if
|
||||
// `payer` != this contract.
|
||||
function _pay(
|
||||
IERC20TokenV06 token,
|
||||
address payer,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
private
|
||||
{
|
||||
if (payer != address(this)) {
|
||||
_transferERC20TokensFrom(token, payer, to, amount);
|
||||
} else {
|
||||
_transferERC20Tokens(token, to, amount);
|
||||
}
|
||||
}
|
||||
|
||||
// Update `swapCallbackData` in place with new values.
|
||||
function _updateSwapCallbackData(
|
||||
bytes memory swapCallbackData,
|
||||
IERC20TokenV06 inputToken,
|
||||
IERC20TokenV06 outputToken,
|
||||
uint24 fee,
|
||||
address payer
|
||||
)
|
||||
private
|
||||
pure
|
||||
{
|
||||
assembly {
|
||||
let p := add(swapCallbackData, 32)
|
||||
mstore(p, inputToken)
|
||||
mstore(add(p, 32), outputToken)
|
||||
mstore(add(p, 64), and(UINT24_MASK, fee))
|
||||
mstore(add(p, 96), and(ADDRESS_MASK, payer))
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the pool address given two tokens and a fee.
|
||||
function _toPool(
|
||||
IERC20TokenV06 inputToken,
|
||||
uint24 fee,
|
||||
IERC20TokenV06 outputToken
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (IUniswapV3Pool pool)
|
||||
{
|
||||
// address(keccak256(abi.encodePacked(
|
||||
// hex"ff",
|
||||
// UNI_FACTORY_ADDRESS,
|
||||
// keccak256(abi.encode(inputToken, outputToken, fee)),
|
||||
// UNI_POOL_INIT_CODE_HASH
|
||||
// )))
|
||||
bytes32 ffFactoryAddress = UNI_FF_FACTORY_ADDRESS;
|
||||
bytes32 poolInitCodeHash = UNI_POOL_INIT_CODE_HASH;
|
||||
(IERC20TokenV06 token0, IERC20TokenV06 token1) = inputToken < outputToken
|
||||
? (inputToken, outputToken)
|
||||
: (outputToken, inputToken);
|
||||
assembly {
|
||||
let s := mload(0x40)
|
||||
let p := s
|
||||
mstore(p, ffFactoryAddress)
|
||||
p := add(p, 21)
|
||||
// Compute the inner hash in-place
|
||||
mstore(p, token0)
|
||||
mstore(add(p, 32), token1)
|
||||
mstore(add(p, 64), and(UINT24_MASK, fee))
|
||||
mstore(p, keccak256(p, 96))
|
||||
p := add(p, 32)
|
||||
mstore(p, poolInitCodeHash)
|
||||
pool := and(ADDRESS_MASK, keccak256(s, 85))
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether or not an encoded uniswap path contains more than one hop.
|
||||
function _isPathMultiHop(bytes memory encodedPath)
|
||||
private
|
||||
pure
|
||||
returns (bool isMultiHop)
|
||||
{
|
||||
return encodedPath.length > SINGLE_HOP_PATH_SIZE;
|
||||
}
|
||||
|
||||
|
||||
// Return the first input token, output token, and fee of an encoded uniswap path.
|
||||
function _decodeFirstPoolInfoFromPath(bytes memory encodedPath)
|
||||
private
|
||||
pure
|
||||
returns (
|
||||
IERC20TokenV06 inputToken,
|
||||
uint24 fee,
|
||||
IERC20TokenV06 outputToken
|
||||
)
|
||||
{
|
||||
require(encodedPath.length >= SINGLE_HOP_PATH_SIZE, "UniswapV3Feature/BAD_PATH_ENCODING");
|
||||
assembly {
|
||||
let p := add(encodedPath, 32)
|
||||
inputToken := shr(96, mload(p))
|
||||
p := add(p, 20)
|
||||
fee := shr(232, mload(p))
|
||||
p := add(p, 3)
|
||||
outputToken := shr(96, mload(p))
|
||||
}
|
||||
}
|
||||
|
||||
// Skip past the first hop of an encoded uniswap path in-place.
|
||||
function _shiftHopFromPathInPlace(bytes memory encodedPath)
|
||||
private
|
||||
pure
|
||||
returns (bytes memory shiftedEncodedPath)
|
||||
{
|
||||
require(encodedPath.length >= PATH_SKIP_HOP_SIZE, "UniswapV3Feature/BAD_PATH_ENCODING");
|
||||
uint256 shiftSize = PATH_SKIP_HOP_SIZE;
|
||||
uint256 newSize = encodedPath.length - shiftSize;
|
||||
assembly {
|
||||
shiftedEncodedPath := add(encodedPath, shiftSize)
|
||||
mstore(shiftedEncodedPath, newSize)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert null address values to msg.sender.
|
||||
function _normalizeRecipient(address recipient)
|
||||
private
|
||||
view
|
||||
returns (address payable normalizedRecipient)
|
||||
{
|
||||
return recipient == address(0) ? msg.sender : payable(recipient);
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -0,0 +1,184 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2021 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../libs/LibNativeOrder.sol";
|
||||
import "../libs/LibSignature.sol";
|
||||
|
||||
|
||||
/// @dev Feature for interacting with OTC orders.
|
||||
interface IOtcOrdersFeature {
|
||||
|
||||
/// @dev Emitted whenever an `OtcOrder` is filled.
|
||||
/// @param orderHash The canonical hash of the order.
|
||||
/// @param maker The maker of the order.
|
||||
/// @param taker The taker of the order.
|
||||
/// @param 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 makerTokenFilledAmount,
|
||||
uint128 takerTokenFilledAmount
|
||||
);
|
||||
|
||||
/// @dev Fill an OTC order for up to `takerTokenFillAmount` taker tokens.
|
||||
/// @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 fillOtcOrder(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
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);
|
||||
|
||||
/// @dev Fill an OTC order whose taker token is WETH for up
|
||||
/// to `msg.value`.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @return takerTokenFilledAmount How much taker token was filled.
|
||||
/// @return makerTokenFilledAmount How much maker token was filled.
|
||||
function fillOtcOrderWithEth(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint128 takerTokenFilledAmount, uint128 makerTokenFilledAmount);
|
||||
|
||||
/// @dev Fully fill an OTC order. "Meta-transaction" variant,
|
||||
/// requires order to be signed by both maker and taker.
|
||||
/// @param order The OTC order.
|
||||
/// @param makerSignature The order signature from the maker.
|
||||
/// @param takerSignature The order signature from the taker.
|
||||
function fillTakerSignedOtcOrder(
|
||||
LibNativeOrder.OtcOrder calldata order,
|
||||
LibSignature.Signature calldata makerSignature,
|
||||
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);
|
||||
|
||||
/// @dev Get the order info for an OTC order.
|
||||
/// @param order The OTC order.
|
||||
/// @return orderInfo Info about the order.
|
||||
function getOtcOrderInfo(LibNativeOrder.OtcOrder calldata order)
|
||||
external
|
||||
view
|
||||
returns (LibNativeOrder.OtcOrderInfo memory orderInfo);
|
||||
|
||||
/// @dev Get the canonical hash of an OTC order.
|
||||
/// @param order The OTC order.
|
||||
/// @return orderHash The order hash.
|
||||
function getOtcOrderHash(LibNativeOrder.OtcOrder calldata order)
|
||||
external
|
||||
view
|
||||
returns (bytes32 orderHash);
|
||||
|
||||
/// @dev Get the last nonce used for a particular
|
||||
/// tx.origin address and nonce bucket.
|
||||
/// @param txOrigin The address.
|
||||
/// @param nonceBucket The nonce bucket index.
|
||||
/// @return lastNonce The last nonce value used.
|
||||
function lastOtcTxOriginNonce(address txOrigin, uint64 nonceBucket)
|
||||
external
|
||||
view
|
||||
returns (uint128 lastNonce);
|
||||
}
|
@@ -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`.
|
||||
|
@@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
/*
|
||||
|
||||
Copyright 2020 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
|
||||
|
||||
/// @dev VIP uniswap v3 fill functions.
|
||||
interface IUniswapV3Feature {
|
||||
|
||||
/// @dev Sell attached ETH directly against uniswap v3.
|
||||
/// @param encodedPath Uniswap-encoded path, where the first token is WETH.
|
||||
/// @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 sellEthForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (uint256 buyAmount);
|
||||
|
||||
/// @dev Sell a token for ETH directly against uniswap v3.
|
||||
/// @param encodedPath Uniswap-encoded path, where the last token is WETH.
|
||||
/// @param sellAmount amount of the first token in the path to sell.
|
||||
/// @param minBuyAmount Minimum amount of ETH to buy.
|
||||
/// @param recipient The recipient of the bought tokens. Can be zero for sender.
|
||||
/// @return buyAmount Amount of ETH bought.
|
||||
function sellTokenForEthToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address payable recipient
|
||||
)
|
||||
external
|
||||
returns (uint256 buyAmount);
|
||||
|
||||
/// @dev Sell a token for another token directly against uniswap v3.
|
||||
/// @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 sellTokenForTokenToUniswapV3(
|
||||
bytes memory encodedPath,
|
||||
uint256 sellAmount,
|
||||
uint256 minBuyAmount,
|
||||
address recipient
|
||||
)
|
||||
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.
|
||||
/// @param amount0Delta Token0 amount owed.
|
||||
/// @param amount1Delta Token1 amount owed.
|
||||
/// @param data Arbitrary data forwarded from swap() caller. An ABI-encoded
|
||||
/// struct of: inputToken, outputToken, fee, payer
|
||||
function uniswapV3SwapCallback(
|
||||
int256 amount0Delta,
|
||||
int256 amount1Delta,
|
||||
bytes calldata data
|
||||
)
|
||||
external;
|
||||
}
|
@@ -69,6 +69,18 @@ library LibNativeOrder {
|
||||
uint256 salt;
|
||||
}
|
||||
|
||||
/// @dev An OTC limit order.
|
||||
struct OtcOrder {
|
||||
IERC20TokenV06 makerToken;
|
||||
IERC20TokenV06 takerToken;
|
||||
uint128 makerAmount;
|
||||
uint128 takerAmount;
|
||||
address maker;
|
||||
address taker;
|
||||
address txOrigin;
|
||||
uint256 expiryAndNonce; // [uint64 expiry, uint64 nonceBucket, uint128 nonce]
|
||||
}
|
||||
|
||||
/// @dev Info on a limit or RFQ order.
|
||||
struct OrderInfo {
|
||||
bytes32 orderHash;
|
||||
@@ -76,6 +88,12 @@ library LibNativeOrder {
|
||||
uint128 takerTokenFilledAmount;
|
||||
}
|
||||
|
||||
/// @dev Info on an OTC order.
|
||||
struct OtcOrderInfo {
|
||||
bytes32 orderHash;
|
||||
OrderStatus status;
|
||||
}
|
||||
|
||||
uint256 private constant UINT_128_MASK = (1 << 128) - 1;
|
||||
uint256 private constant UINT_64_MASK = (1 << 64) - 1;
|
||||
uint256 private constant ADDRESS_MASK = (1 << 160) - 1;
|
||||
@@ -118,6 +136,22 @@ library LibNativeOrder {
|
||||
uint256 private constant _RFQ_ORDER_TYPEHASH =
|
||||
0xe593d3fdfa8b60e5e17a1b2204662ecbe15c23f2084b9ad5bae40359540a7da9;
|
||||
|
||||
// The type hash for OTC orders, which is:
|
||||
// keccak256(abi.encodePacked(
|
||||
// "OtcOrder(",
|
||||
// "address makerToken,",
|
||||
// "address takerToken,",
|
||||
// "uint128 makerAmount,",
|
||||
// "uint128 takerAmount,",
|
||||
// "address maker,",
|
||||
// "address taker,",
|
||||
// "address txOrigin,",
|
||||
// "uint256 expiryAndNonce"
|
||||
// ")"
|
||||
// ))
|
||||
uint256 private constant _OTC_ORDER_TYPEHASH =
|
||||
0x2f754524de756ae72459efbe1ec88c19a745639821de528ac3fb88f9e65e35c8;
|
||||
|
||||
/// @dev Get the struct hash of a limit order.
|
||||
/// @param order The limit order.
|
||||
/// @return structHash The struct hash of the order.
|
||||
@@ -222,6 +256,49 @@ library LibNativeOrder {
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Get the struct hash of an OTC order.
|
||||
/// @param order The OTC order.
|
||||
/// @return structHash The struct hash of the order.
|
||||
function getOtcOrderStructHash(OtcOrder memory order)
|
||||
internal
|
||||
pure
|
||||
returns (bytes32 structHash)
|
||||
{
|
||||
// The struct hash is:
|
||||
// keccak256(abi.encode(
|
||||
// TYPE_HASH,
|
||||
// order.makerToken,
|
||||
// order.takerToken,
|
||||
// order.makerAmount,
|
||||
// order.takerAmount,
|
||||
// order.maker,
|
||||
// order.taker,
|
||||
// order.txOrigin,
|
||||
// order.expiryAndNonce,
|
||||
// ))
|
||||
assembly {
|
||||
let mem := mload(0x40)
|
||||
mstore(mem, _OTC_ORDER_TYPEHASH)
|
||||
// order.makerToken;
|
||||
mstore(add(mem, 0x20), and(ADDRESS_MASK, mload(order)))
|
||||
// order.takerToken;
|
||||
mstore(add(mem, 0x40), and(ADDRESS_MASK, mload(add(order, 0x20))))
|
||||
// order.makerAmount;
|
||||
mstore(add(mem, 0x60), and(UINT_128_MASK, mload(add(order, 0x40))))
|
||||
// order.takerAmount;
|
||||
mstore(add(mem, 0x80), and(UINT_128_MASK, mload(add(order, 0x60))))
|
||||
// order.maker;
|
||||
mstore(add(mem, 0xA0), and(ADDRESS_MASK, mload(add(order, 0x80))))
|
||||
// order.taker;
|
||||
mstore(add(mem, 0xC0), and(ADDRESS_MASK, mload(add(order, 0xA0))))
|
||||
// order.txOrigin;
|
||||
mstore(add(mem, 0xE0), and(ADDRESS_MASK, mload(add(order, 0xC0))))
|
||||
// order.expiryAndNonce;
|
||||
mstore(add(mem, 0x100), mload(add(order, 0xE0)))
|
||||
structHash := keccak256(mem, 0x120)
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Refund any leftover protocol fees in `msg.value` to `msg.sender`.
|
||||
/// @param ethProtocolFeePaid How much ETH was paid in protocol fees.
|
||||
function refundExcessProtocolFeeToSender(uint256 ethProtocolFeePaid)
|
||||
|
@@ -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,
|
||||
@@ -404,7 +432,7 @@ abstract contract NativeOrdersSettlement is
|
||||
params.order.takerAmount,
|
||||
params.order.takerTokenFeeAmount
|
||||
));
|
||||
_transferERC20Tokens(
|
||||
_transferERC20TokensFrom(
|
||||
params.order.takerToken,
|
||||
params.taker,
|
||||
params.order.feeRecipient,
|
||||
@@ -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.
|
||||
_transferERC20Tokens(
|
||||
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.
|
||||
_transferERC20Tokens(
|
||||
// Transfer maker -> recipient.
|
||||
_transferERC20TokensFrom(
|
||||
settleInfo.makerToken,
|
||||
settleInfo.maker,
|
||||
settleInfo.taker,
|
||||
settleInfo.recipient,
|
||||
makerTokenFilledAmount
|
||||
);
|
||||
}
|
||||
|
@@ -35,7 +35,7 @@ abstract contract FixinTokenSpender {
|
||||
/// @param owner The owner of the tokens.
|
||||
/// @param to The recipient of the tokens.
|
||||
/// @param amount The amount of `token` to transfer.
|
||||
function _transferERC20Tokens(
|
||||
function _transferERC20TokensFrom(
|
||||
IERC20TokenV06 token,
|
||||
address owner,
|
||||
address to,
|
||||
@@ -87,6 +87,60 @@ abstract contract FixinTokenSpender {
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Transfers ERC20 tokens from ourselves to `to`.
|
||||
/// @param token The token to spend.
|
||||
/// @param to The recipient of the tokens.
|
||||
/// @param amount The amount of `token` to transfer.
|
||||
function _transferERC20Tokens(
|
||||
IERC20TokenV06 token,
|
||||
address to,
|
||||
uint256 amount
|
||||
)
|
||||
internal
|
||||
{
|
||||
require(address(token) != address(this), "FixinTokenSpender/CANNOT_INVOKE_SELF");
|
||||
|
||||
assembly {
|
||||
let ptr := mload(0x40) // free memory pointer
|
||||
|
||||
// selector for transfer(address,uint256)
|
||||
mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
|
||||
mstore(add(ptr, 0x04), and(to, ADDRESS_MASK))
|
||||
mstore(add(ptr, 0x24), amount)
|
||||
|
||||
let success := call(
|
||||
gas(),
|
||||
and(token, ADDRESS_MASK),
|
||||
0,
|
||||
ptr,
|
||||
0x44,
|
||||
ptr,
|
||||
32
|
||||
)
|
||||
|
||||
let rdsize := returndatasize()
|
||||
|
||||
// Check for ERC20 success. ERC20 tokens should return a boolean,
|
||||
// but some don't. We accept 0-length return data as success, or at
|
||||
// least 32 bytes that starts with a 32-byte boolean true.
|
||||
success := and(
|
||||
success, // call itself succeeded
|
||||
or(
|
||||
iszero(rdsize), // no return data, or
|
||||
and(
|
||||
iszero(lt(rdsize, 32)), // at least 32 bytes
|
||||
eq(mload(ptr), 1) // starts with uint256(1)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if iszero(success) {
|
||||
returndatacopy(ptr, 0, rdsize)
|
||||
revert(ptr, rdsize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Gets the maximum amount of an ERC20 token `token` that can be
|
||||
/// pulled from `owner` by this address.
|
||||
/// @param token The token to spend.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user