├── LICENSE
├── asset-proxy
├── erc1155-proxy.md
├── erc20-bridge-proxy.md
├── erc20-proxy.md
├── erc721-proxy.md
├── multi-asset-proxy.md
└── static-call-proxy.md
├── exchange-proxy
├── deployment.md
├── exchange-proxy.md
├── features
│ ├── meta-transactions.md
│ ├── signature-validator.md
│ ├── token-spender.md
│ └── transform-erc20.md
└── img
│ ├── ep-deployment-deps.png
│ ├── exchange-proxy-bootstrap.png
│ ├── exchange-proxy-call-forwarding.png
│ ├── exchange-proxy-mtxs.png
│ ├── token-spender.png
│ ├── transform-example.png
│ ├── transform-pipeline.png
│ └── zero_ex_migrate.png
├── img
├── 0x_coordinator.png
├── 0x_forwarder.png
├── 0x_v2_trade_erc20_erc20.png
└── 0x_v2_trade_erc20_erc721.png
├── order-types
├── chai-bridge.md
├── dydx.md
├── img
│ └── dydx-settlement.png
├── property-based.md
└── stop-limit.md
├── staking
├── Adjusted_Staking_Percentage_Formula.pdf
├── assets
│ ├── .DS_Store
│ ├── Batch-Calls.png
│ ├── Batch-Calls.xml
│ ├── CobbDouglas.png
│ ├── Epochs.png
│ ├── Epochs.xml
│ ├── ProtocolFee.png
│ ├── ProtocolFee.xml
│ ├── Read-Only Proxy.png
│ ├── Read-Only Proxy.xml
│ ├── Rewards.png
│ ├── Rewards.xml
│ ├── StakeManagementExample2.png
│ ├── StakeManagementExample2.xml
│ ├── Staking Architecture - Basic.png
│ ├── Staking Architecture - Basic.xml
│ ├── Staking Architecture - Catastrophic Failure.png
│ ├── Staking Architecture - Catastrophic Failure.xml
│ ├── Staking Architecture - Read-Only.png
│ ├── Staking Architecture - Read-Only.xml
│ ├── Staking.png
│ ├── Staking.xml
│ └── reward_tracking
│ │ ├── .DS_Store
│ │ ├── Reward-Final.png
│ │ ├── RewardAfterManyEpochs-InPractice.png
│ │ ├── RewardAfterManyEpochs.png
│ │ ├── RewardFromWhatWeStore-Example.png
│ │ ├── RewardFromWhatWeStore.png
│ │ ├── RewardSingleEpoch.png
│ │ └── WhatWeStore.png
└── staking-specification.md
├── v1
└── v1-whitepaper.pdf
├── v2
├── coordinator-specification.md
├── forwarder-specification.md
└── v2-specification.md
└── v3
├── coordinator-specification.md
├── forwarder-specification.md
├── protocol-fees.pdf
├── v3-specification.md
└── zero-ex-governor.md
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/asset-proxy/erc1155-proxy.md:
--------------------------------------------------------------------------------
1 | # ERC1155Proxy
2 |
3 | ## ZEIP-24
4 |
5 | The `ERC1155Proxy` was first proposed in [ZEIP-24](https://github.com/0xProject/ZEIPs/issues/24). Please refer to the ZEIP for information and discussion about how this contract works at a higher level.
6 |
7 | ## Transferring ERC-1155 tokens
8 |
9 | The `ERC1155Proxy` is responsible for transferring [ERC-1155 tokens](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1155.md). Users must first approve this contract by calling the `setApprovalForAll` method on the token that will be exchanged.
10 |
11 | The `ERC1155Proxy` utilizes an ERC-1155 token's `safeBatchTransferFrom` method to transfer multiple tokens at once, if desired. Values of each `tokenId` to be transferred are scaled similarly to the method used in the [`MultiAssetProxy`](../asset-proxy/multi-asset-proxy.md). `values` (`uint256` array) and `tokenIds` (`uint256` array) parameters are encoded within the `assetData` that can be utilized by this contract's `transferFrom` method. Each element of `values` corresponds to an element at the same index of `tokenIds`. The `ERC1155Proxy` will multiply each `values` element by the `amount` passed into `ERC1155Proxy.transferFrom` and then pass the resulting scaled values into `ERC1155Token.safeBathTransferFrom` when performing the transfer.
12 |
13 | ### transferFrom
14 |
15 | This contract may transfer ERC-1155 tokens if its `transferFrom` method is called from an authorized address.
16 |
17 | ```solidity
18 | /// @dev Transfers assets. Either succeeds or throws.
19 | /// @param assetData Byte array encoded for the respective asset proxy.
20 | /// @param from Address to transfer asset from.
21 | /// @param to Address to transfer asset to.
22 | /// @param amount Amount of asset to transfer.
23 | function transferFrom(
24 | bytes assetData,
25 | address from,
26 | address to,
27 | uint256 amount
28 | )
29 | external;
30 | ```
31 |
32 | #### Logic
33 |
34 | Calling `ERC1155Proxy.transferFrom` will perform the following steps:
35 |
36 | 1. Decode the `erc1155TokenAddress`, `tokenIds`, `values`, and `callbackData` from `assetData`
37 | 1. Multiply each element of `values` by `amount`, resulting in a `scaledValues` array
38 | 1. Call `ERC1155Token(erc1155TokenAddress).safeBatchTransferFrom(from, to, tokenIds, scaledValues, callbackData)`
39 | 1. Revert if the call was unsuccessful
40 |
41 | #### Errors
42 |
43 | The `transferFrom` method may revert with the following errors:
44 |
45 | | Error | Condition |
46 | | --------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
47 | | [StandardError("SENDER_NOT_AUTHORIZED")](../v3/v3-specification.md#standard-error) | `msg.sender` has not been authorized |
48 | | [StandardError("FROM_LESS_THAN_TO_REQUIRED")](../v3/v3-specification.md#standard-error) | `assetData` is shorter than 4 bytes |
49 | | [StandardError("UINT256_OVERFLOW")](../v3/v3-specification.md#standard-error) | The multiplication of an element of `assetData.values` and `amount` resulted in an overflow |
50 | | \* | This contract will rethrow any revert data received from an unsuccessful call of `ERC1155Token.safeBatchTransferFrom` |
51 |
52 | ## Encoding assetData
53 |
54 | This contract expects ERC-1155 [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0xa7cb5fb7`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
55 |
56 | ```solidity
57 | /// @dev Function signature for encoding ERC-1155 assetData.
58 | /// @param tokenAddress Address of ERC-1155 token contract.
59 | /// @param tokenIds Array of ids of tokens to be transferred.
60 | /// @param values Array of values that correspond to each token id to be transferred.
61 | /// Note that each value will be multiplied by the amount being filled in the order before transferring.
62 | /// @param callbackData Extra data to be passed to receiver's `onERC1155Received` callback function.
63 | function ERC1155Assets(
64 | address tokenAddress,
65 | uint256[] calldata tokenIds,
66 | uint256[] calldata values,
67 | bytes calldata callbackData
68 | )
69 | external;
70 | ```
71 |
72 | In Solidity, this data can be encoded with:
73 |
74 | ```solidity
75 | bytes memory data = abi.encodeWithSelector(
76 | 0xa7cb5fb7,
77 | erc1155TokenAddress,
78 | tokenIds,
79 | values,
80 | callbackData
81 | );
82 | ```
83 |
84 | NOTE: The `ERC1155Proxy` does not enforce strict length checks for `assetData`, which means that extra data may be appended to this field with any arbitrary encoding. Any extra data will be ignored by the `ERC1155Proxy` but may be used in external contracts interacting with the [`Exchange`](../v3/v3-specification.md#exchange) contract. Relayers that do not desire this behavior should validate the length of all `assetData` fields contained in [orders](../v3/v3-specification.md#orders) before acceptance.
85 |
86 | ## Authorizations
87 |
88 | The `ERC1155Proxy` has the following interface for managing which addresses are allowed to call this contract's `transferFrom` method. These authorization functions can only be called by the contract's `owner` (currently, the [`ZeroExGovernor`](../v3/zero-ex-governor.md) contract).
89 |
90 | ```solidity
91 | contract IAuthorizable {
92 |
93 | /// @dev Gets all authorized addresses.
94 | /// @return Array of authorized addresses.
95 | function getAuthorizedAddresses()
96 | external
97 | view
98 | returns (address[]);
99 |
100 | /// @dev Authorizes an address.
101 | /// @param target Address to authorize.
102 | function addAuthorizedAddress(address target)
103 | external;
104 |
105 | /// @dev Removes authorizion of an address.
106 | /// @param target Address to remove authorization from.
107 | function removeAuthorizedAddress(address target)
108 | external;
109 |
110 | /// @dev Removes authorizion of an address.
111 | /// @param target Address to remove authorization from.
112 | /// @param index Index of target in authorities array.
113 | function removeAuthorizedAddressAtIndex(
114 | address target,
115 | uint256 index
116 | )
117 | external;
118 | }
119 | ```
120 |
121 | The contracts that are currently authorized to call the `ERC20Proxy` contract's `transferFrom` method are:
122 |
123 | - [Exchange 2.0](../v2/v2-specification.md#exchange)
124 | - [Exchange 3.0](../v3/v3-specification.md#exchange)
125 | - [MultiAssetProxy](../asset-proxy/multi-asset-proxy.md)
126 |
--------------------------------------------------------------------------------
/asset-proxy/erc20-bridge-proxy.md:
--------------------------------------------------------------------------------
1 | # ERC20BridgeProxy
2 |
3 | ## ZEIP-39
4 |
5 | The `ERC20BridgeProxy` was first proposed in [ZEIP-47](https://github.com/0xProject/ZEIPs/issues/47). Please refer to the ZEIP for information and discussion about how this contract works at a higher level.
6 |
7 | ## Transfering ERC-20 tokens via an ERC20Bridge contract
8 |
9 | ### transferFrom
10 |
11 | This contract may transfer an ERC-20 token if its `transferFrom` method is called from an authorized address.
12 |
13 | ```solidity
14 | /// @dev Transfers assets. Either succeeds or throws.
15 | /// @param assetData Byte array encoded for the respective asset proxy.
16 | /// @param from Address to transfer asset from.
17 | /// @param to Address to transfer asset to.
18 | /// @param amount Amount of asset to transfer.
19 | function transferFrom(
20 | bytes assetData,
21 | address from,
22 | address to,
23 | uint256 amount
24 | )
25 | external;
26 | ```
27 |
28 | #### Logic
29 |
30 | Calling `ERC20BridgeProxy.transferFrom` will perform the following steps:
31 |
32 | 1. Decode `tokenAddress`, `bridgeAddress`, and `bridgeData` from `assetData`
33 | 1. Query the `to` account's initial balance of the ERC-20 token at `tokenAddress`
34 | 1. Call `IERC20Bridge(bridgeAddress).bridgeTransferFrom(tokenAddress, from, to, amount, bridgeData)`
35 | 1. Revert if the call did not return `0xdc1600f3` (this contract's id)
36 | 1. Revert if the `to` account's new balance of the ERC-20 token is less than its initial balance plus `amount`
37 |
38 | #### Errors
39 |
40 | The `transferFrom` method may revert with the following errors:
41 |
42 | | Error | Condition |
43 | | ---------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44 | | [StandardError("BRIDGE_FAILED")](../v3/v3-specification.md#standard-error) | The `bridgeTransferFrom` call did not return `0xdc1600f3` |
45 | | [StandardError("BRIDGE_UNDERPAY")](../v3/v3-specification.md#standard-error) | The `bridgeTransferFrom` call increase the `to` account's balance of the ERC-20 token by at least `amount` |
46 | | \* | This contract will rethrow any revert data received from an unsuccessful call of `IERC20Bridge(bridgeAddress).bridgeTransferFrom(tokenAddress, from, to, amount, bridgeData)` |
47 |
48 | ## Encoding assetData
49 |
50 | This contract expects ERC20Bridge [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0xdc1600f3`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
51 |
52 | ```solidity
53 | /// @dev Function signature for encoding ERC20Bridge assetData.
54 | /// @param tokenAddress Address of token to transfer.
55 | /// @param bridgeAddress Address of the bridge contract.
56 | /// @param bridgeData Arbitrary data to be passed to the bridge contract.
57 | function ERC20Bridge(
58 | address tokenAddress,
59 | address bridgeAddress,
60 | bytes calldata bridgeData
61 | )
62 | external;
63 | ```
64 |
65 | In Solidity, this data can be encoded with:
66 |
67 | ```solidity
68 | bytes memory data = abi.encodeWithSelector(
69 | 0xdc1600f3,
70 | tokenAddress,
71 | bridgeAddress,
72 | bridgeData
73 | );
74 | ```
75 |
76 | ## Authorizations
77 |
78 | The `ERC20BridgeProxy` has the following interface for managing which addresses are allowed to call this contract's `transferFrom` method. These authorization functions can only be called by the contract's `owner` (currently, the [`ZeroExGovernor`](../v3/zero-ex-governor.md) contract).
79 |
80 | ```solidity
81 | contract IAuthorizable {
82 |
83 | /// @dev Gets all authorized addresses.
84 | /// @return Array of authorized addresses.
85 | function getAuthorizedAddresses()
86 | external
87 | view
88 | returns (address[]);
89 |
90 | /// @dev Authorizes an address.
91 | /// @param target Address to authorize.
92 | function addAuthorizedAddress(address target)
93 | external;
94 |
95 | /// @dev Removes authorizion of an address.
96 | /// @param target Address to remove authorization from.
97 | function removeAuthorizedAddress(address target)
98 | external;
99 |
100 | /// @dev Removes authorizion of an address.
101 | /// @param target Address to remove authorization from.
102 | /// @param index Index of target in authorities array.
103 | function removeAuthorizedAddressAtIndex(
104 | address target,
105 | uint256 index
106 | )
107 | external;
108 | }
109 | ```
110 |
111 | The contracts that are currently authorized to call the `ERC20BridgeProxy` contract's `transferFrom` method are:
112 |
113 | - [Exchange 3.0](../v3/v3-specification.md#exchange)
114 | - [MultiAssetProxy](../asset-proxy/multi-asset-proxy.md)
115 |
116 | ## Writing an ERC20Bridge contract
117 |
118 | `ERC20Bridge` contracts can be permissionlessly deployed and connected to the `ERC20BridgeProxy`. An bridge should have the following interface:
119 |
120 | ```solidity
121 | contract IERC20Bridge {
122 |
123 | // @dev Result of a successful bridge call.
124 | bytes4 constant internal BRIDGE_SUCCESS = 0xdc1600f3;
125 |
126 | /// @dev Transfers `amount` of the ERC20 `tokenAddress` from `from` to `to`.
127 | /// @param tokenAddress The address of the ERC20 token to transfer.
128 | /// @param from Address to transfer asset from.
129 | /// @param to Address to transfer asset to.
130 | /// @param amount Amount of asset to transfer.
131 | /// @param bridgeData Arbitrary asset data needed by the bridge contract.
132 | /// @return success The magic bytes `0x37708e9b` if successful.
133 | function bridgeTransferFrom(
134 | address tokenAddress,
135 | address from,
136 | address to,
137 | uint256 amount,
138 | bytes calldata bridgeData
139 | )
140 | external
141 | returns (bytes4 success);
142 | }
143 | ```
144 |
145 | ### ERC20Bridge design patterns
146 |
147 | The `bridgeTransferFrom` function is required to transfer some amount of an ERC-20 token to the `to` address, but it is agnostic to where it acquires those tokens. An `ERC20Bridge` contract may already be holding the tokens or may be authorized to transfer the tokens on behalf of the `from` address.
148 |
149 | One common pattern can be used to bridge liquidity with on-chain decentralized exchanges. The bridge contract is set to the `makerAddress` of the 0x order and will purchase the required tokens just in time before transferring them to the `takerAddress` address. This takes advantage of the fact that the taker-to-maker transfer in an order occurs before the maker-to-taker transfer. After the taker's tokens are transferred to the bridge contract, it can sell those tokens in an on-chain trade and then transfer the newly purchased ERC-20 token to the taker.
150 |
151 | Example implementations of bridge contracts can be found [here](https://github.com/0xProject/0x-monorepo/tree/development/contracts/asset-proxy/contracts/src/bridges).
152 |
--------------------------------------------------------------------------------
/asset-proxy/erc20-proxy.md:
--------------------------------------------------------------------------------
1 | # ERC20Proxy
2 |
3 | ## Transferring ERC-20 tokens
4 |
5 | The `ERC20Proxy` is responsible for transferring [ERC-20 tokens](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md). Users must first approve this contract by calling the `approve` method on the token that will be exchanged. It is recommended that users approve a value of 2^256 -1. This minimizes the amount of times `approve` must be called, and also [increases efficiency](https://github.com/ethereum/EIPs/issues/717) for many ERC-20 tokens.
6 |
7 | ### transferFrom
8 |
9 | This contract may transfer an ERC-20 token if its `transferFrom` method is called from an authorized address.
10 |
11 | ```solidity
12 | /// @dev Transfers assets. Either succeeds or throws.
13 | /// @param assetData Byte array encoded for the respective asset proxy.
14 | /// @param from Address to transfer asset from.
15 | /// @param to Address to transfer asset to.
16 | /// @param amount Amount of asset to transfer.
17 | function transferFrom(
18 | bytes assetData,
19 | address from,
20 | address to,
21 | uint256 amount
22 | )
23 | external;
24 | ```
25 |
26 | #### Logic
27 |
28 | Calling `ERC20Proxy.transferFrom` will perform the following steps:
29 |
30 | 1. Decode `erc20TokenAddress` from `assetData`
31 | 1. Call `ERC20Token(erc20TokenAddress).transferFrom(from, to, amount)`
32 | 1. Revert if the call was unsuccessful
33 | 1. Revert if the call was successful but returned 0
34 |
35 | Note that this implementation will correctly handle edge cases where the ERC-20 token contract does not return a value or does not throw upon failure.
36 |
37 | #### Errors
38 |
39 | The `transferFrom` method may revert with the following errors:
40 |
41 | | Error | Condition |
42 | | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
43 | | [StandardError("SENDER_NOT_AUTHORIZED")](../v3/v3-specification.md#standard-error) | `msg.sender` has not been authorized |
44 | | [StandardError("TRANSFER_FAILED")](../v3/v3-specification.md#standard-error) | The `ERC20Token.transferFrom` call failed for any reason (likely insufficient balance/allowance) |
45 |
46 | ## Encoding assetData
47 |
48 | This contract expects ERC-20 [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0xf47261b0`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
49 |
50 | ```solidity
51 | /// @dev Function signature for encoding ERC-20 assetData.
52 | /// @param tokenAddress Address of ERC20Token contract.
53 | function ERC20Token(address tokenAddress)
54 | external;
55 | ```
56 |
57 | In Solidity, this data can be encoded with:
58 |
59 | ```solidity
60 | bytes memory data = abi.encodeWithSelector(
61 | 0xf47261b0,
62 | erc20TokenAddress
63 | );
64 | ```
65 |
66 | NOTE: The `ERC20Proxy` does not enforce strict length checks for `assetData`, which means that extra data may be appended to this field with any arbitrary encoding. Any extra data will be ignored by the `ERC20Proxy` but may be used in external contracts interacting with the [`Exchange`](../v3/v3-specification.md#exchange) contract. Relayers that do not desire this behavior should validate the length of all `assetData` fields contained in [orders](../v3/v3-specification.md#orders) before acceptance.
67 |
68 | ## Authorizations
69 |
70 | The `ERC20Proxy` has the following interface for managing which addresses are allowed to call this contract's `transferFrom` method. These authorization functions can only be called by the contract's `owner` (currently, the [`ZeroExGovernor`](../v3/zero-ex-governor.md) contract).
71 |
72 | ```solidity
73 | contract IAuthorizable {
74 |
75 | /// @dev Gets all authorized addresses.
76 | /// @return Array of authorized addresses.
77 | function getAuthorizedAddresses()
78 | external
79 | view
80 | returns (address[]);
81 |
82 | /// @dev Authorizes an address.
83 | /// @param target Address to authorize.
84 | function addAuthorizedAddress(address target)
85 | external;
86 |
87 | /// @dev Removes authorizion of an address.
88 | /// @param target Address to remove authorization from.
89 | function removeAuthorizedAddress(address target)
90 | external;
91 |
92 | /// @dev Removes authorizion of an address.
93 | /// @param target Address to remove authorization from.
94 | /// @param index Index of target in authorities array.
95 | function removeAuthorizedAddressAtIndex(
96 | address target,
97 | uint256 index
98 | )
99 | external;
100 | }
101 | ```
102 |
103 | The contracts that are currently authorized to call the `ERC20Proxy` contract's `transferFrom` method are:
104 |
105 | - [Exchange 2.0](../v2/v2-specification.md#exchange)
106 | - [Exchange 3.0](../v3/v3-specification.md#exchange)
107 | - [MultiAssetProxy](../asset-proxy/multi-asset-proxy.md)
108 |
--------------------------------------------------------------------------------
/asset-proxy/erc721-proxy.md:
--------------------------------------------------------------------------------
1 | # ERC721Proxy
2 |
3 | ## Transferring ERC-721 tokens
4 |
5 | The `ERC721Proxy` is responsible for transferring [ERC-721 tokens](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md). Users must first approve this contract by calling the `approve` or `setApprovalForAll` methods on the token that will be exchanged. `setApprovalForAll` is highly recommended, because it allows the user to approve multiple `tokenIds` with a single transaction.
6 |
7 | ### transferFrom
8 |
9 | This contract may transfer an ERC-721 token if its `transferFrom` method is called from an authorized address.
10 |
11 | ```solidity
12 | /// @dev Transfers assets. Either succeeds or throws.
13 | /// @param assetData Byte array encoded for the respective asset proxy.
14 | /// @param from Address to transfer asset from.
15 | /// @param to Address to transfer asset to.
16 | /// @param amount Amount of asset to transfer.
17 | function transferFrom(
18 | bytes assetData,
19 | address from,
20 | address to,
21 | uint256 amount
22 | )
23 | external;
24 | ```
25 |
26 | #### Logic
27 |
28 | Calling `ERC721Proxy.transferFrom` will perform the following steps:
29 |
30 | 1. Decode `erc721TokenAddress` and `tokenId` from `assetData`
31 | 1. Call `ERC721Token(erc721TokenAddress).transferFrom(from, to, tokenId)`
32 | 1. Revert if the call was unsuccessful
33 |
34 | #### Errors
35 |
36 | The `transferFrom` method may revert with the following errors:
37 |
38 | | Error | Condition |
39 | | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
40 | | [StandardError("SENDER_NOT_AUTHORIZED")](../v3/v3-specification.md#standard-error) | `msg.sender` has not been authorized |
41 | | [StandardError("INVALID_AMOUNT")](../v3/v3-specification.md#standard-error) | The `amount` does not equal 1 |
42 | | [StandardError("TRANSFER_FAILED")](../v3/v3-specification.md#standard-error) | The `ERC721Token.transferFrom` call failed for any reason (likely insufficient balance/allowance) |
43 |
44 | ## Encoding assetData
45 |
46 | This contract expects ERC-721 [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0x02571792`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
47 |
48 | ```solidity
49 | /// @dev Function signature for encoding ERC-721 assetData.
50 | /// @param tokenAddress Address of ERC-721 token contract.
51 | /// @param tokenId Id of ERC-721 token to be transferred.
52 | function ERC721Token(
53 | address tokenAddress,
54 | uint256 tokenId
55 | )
56 | external;
57 | ```
58 |
59 | In Solidity, this data can be encoded with:
60 |
61 | ```solidity
62 | bytes memory data = abi.encodeWithSelector(
63 | 0x02571792,
64 | erc721TokenAddress,
65 | tokenId
66 | );
67 | ```
68 |
69 | NOTE: The `ERC721Proxy` does not enforce strict length checks for `assetData`, which means that extra data may be appended to this field with any arbitrary encoding. Any extra data will be ignored by the `ERC721Proxy` but may be used in external contracts interacting with the [`Exchange`](../v3/v3-specification.md#exchange) contract. Relayers that do not desire this behavior should validate the length of all `assetData` fields contained in [orders](../v3/v3-specification.md#orders) before acceptance.
70 |
71 | ## Authorizations
72 |
73 | The `ERC721Proxy` has the following interface for managing which addresses are allowed to call this contract's `transferFrom` method. These authorization functions can only be called by the contract's `owner` (currently, the [`ZeroExGovernor`](../v3/zero-ex-governor.md) contract).
74 |
75 | ```solidity
76 | contract IAuthorizable {
77 |
78 | /// @dev Gets all authorized addresses.
79 | /// @return Array of authorized addresses.
80 | function getAuthorizedAddresses()
81 | external
82 | view
83 | returns (address[]);
84 |
85 | /// @dev Authorizes an address.
86 | /// @param target Address to authorize.
87 | function addAuthorizedAddress(address target)
88 | external;
89 |
90 | /// @dev Removes authorizion of an address.
91 | /// @param target Address to remove authorization from.
92 | function removeAuthorizedAddress(address target)
93 | external;
94 |
95 | /// @dev Removes authorizion of an address.
96 | /// @param target Address to remove authorization from.
97 | /// @param index Index of target in authorities array.
98 | function removeAuthorizedAddressAtIndex(
99 | address target,
100 | uint256 index
101 | )
102 | external;
103 | }
104 | ```
105 |
106 | The contracts that are currently authorized to call the `ERC20Proxy` contract's `transferFrom` method are:
107 |
108 | - [Exchange 2.0](../v2/v2-specification.md#exchange)
109 | - [Exchange 3.0](../v3/v3-specification.md#exchange)
110 | - [MultiAssetProxy](../asset-proxy/multi-asset-proxy.md)
111 |
--------------------------------------------------------------------------------
/asset-proxy/multi-asset-proxy.md:
--------------------------------------------------------------------------------
1 | # MultiAssetProxy
2 |
3 | ## ZEIP-23
4 |
5 | The `MultiAssetProxy` was first proposed in [ZEIP-23](https://github.com/0xProject/ZEIPs/issues/23). Please refer to the ZEIP for information and discussion about how this contract works at a higher level.
6 |
7 | ## Transferring multiple assets
8 |
9 | The `MultiAssetProxy` can transfer arbitrary bundles of assets in a single smart contract call. It expects a `values` (`uint256` array) and a `nestedAssetData` (array of [`assetData`](../v3/v3-specification.md#assetdata) byte arrays) to be encoded within its own `assetData`. Each element of `values` corresponds to an element at the same index of `nestedAssetData`. The `MultiAssetProxy` will multiply each `values` element by the `amount` passed into `MultiAssetProxy.transferFrom` and then dispatch the corresponding element of `nestedAssetProxy` to the relevant [`AssetProxy`](../v3/v3-specification.md#assetproxy) contract with the resulting `totalAmount`. This contract does not perform any `transferFrom` calls to assets directly and therefore does not require any additional user approvals.
10 |
11 | ### transferFrom
12 |
13 | This contract may dispatch transfers to other [`AssetProxy`](../v3/v3-specification.md#assetproxy) contracts if its `transferFrom` method is called from an authorized address.
14 |
15 | ```solidity
16 | /// @dev Transfers assets. Either succeeds or throws.
17 | /// @param assetData Byte array encoded for the respective asset proxy.
18 | /// @param from Address to transfer asset from.
19 | /// @param to Address to transfer asset to.
20 | /// @param amount Amount of asset to transfer.
21 | function transferFrom(
22 | bytes assetData,
23 | address from,
24 | address to,
25 | uint256 amount
26 | )
27 | external;
28 | ```
29 |
30 | #### Logic
31 |
32 | Calling `MultiAssetProxy.transferFrom` will perform the following steps:
33 |
34 | 1. Decode `values` and `nestedAssetData` from `assetData`
35 | 1. For each element of `values`, given index `i`:
36 | 1. Multiply `values[i]` by `amount`, resulting in a `scaledValue`
37 | 1. Decode an `assetProxyId` from `nestedAssetData[i]`
38 | 1. Load an `assetProxyAddress` that corresponds to the `assetProxyId`
39 | 1. Call `AssetProxy(assetProxyAddress).transferFrom(nestedAssetData[i], from, to, scaledValue)`
40 | 1. Revert if the call was unsuccessful
41 |
42 | #### Errors
43 |
44 | The `transferFrom` method may revert with the following errors:
45 |
46 | | Error | Condition |
47 | | ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- |
48 | | [StandardError("SENDER_NOT_AUTHORIZED")](../v3/v3-specification.md#standard-error) | `msg.sender` has not been authorized |
49 | | [StandardError("INVALID_ASSET_DATA_LENGTH")](../v3/v3-specification.md#standard-error) | The `assetData` is shorter than 68 bytes or is not a multiple of 32 (exluding the 4 byte id) |
50 | | [StandardError("INVALID_ASSET_DATA_END")](../v3/v3-specification.md#standard-error) | The offset to `assetData` points to outside the end of calldata |
51 | | [StandardError("LENGTH_MISMATCH")](../v3/v3-specification.md#standard-error) | The lengths of `values` and `nestedAssetData` are not equal |
52 | | [StandardError("UINT256_OVERFLOW)](../v3/v3-specification.md#standard-error) | The multiplication of an element of `values` and `amount` resulted in an overflow |
53 | | [StandardError("LENGTH_GREATER_THAN_3_REQUIRED")](../v3/v3-specification.md#standard-error) | An element of `nestedAssetData` is shorter than 4 bytes |
54 | | [StandardError("ASSET_PROXY_DOES_NOT_EXIST")](../v3/v3-specification.md#standard-error) | No `AssetProxy` contract exists for the given `assetProxyId` of an element of `nestedAssetData` |
55 | | \* | This contract will rethrow any revert data received from an unsuccessful call of an `AssetProxy` contract |
56 |
57 | ## Encoding assetData
58 |
59 | This contract expects MultiAsset [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0x94cfcdd7`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
60 |
61 | ```solidity
62 | /// @dev Function signature for encoding MultiAsset assetData.
63 | /// @param values Array of values that correspond to each asset to be transferred.
64 | /// Note that each value will be multiplied by the amount being filled in the order before transferring.
65 | /// @param nestedAssetData Array of assetData fields that will be be dispatched to their correspnding AssetProxy contract.
66 | function MultiAsset(
67 | uint256[] calldata values,
68 | bytes[] calldata nestedAssetData
69 | )
70 | external;
71 | ```
72 |
73 | In Solidity, this data can be encoded with:
74 |
75 | ```solidity
76 | bytes memory data = abi.encodeWithSelector(
77 | 0x94cfcdd7,
78 | values,
79 | nestedAssetData
80 | );
81 | ```
82 |
83 | Each element of `nestedAssetData` must be encoded according to the specification of the corresponding `AssetProxy` contract. Note that initially, the `MultiAssetProxy` will not support dispatching a transfer to itself.
84 |
85 | ## Authorizations
86 |
87 | The `MultiAssetProxy` has the following interface for managing which addresses are allowed to call this contract's `transferFrom` method. These authorization functions can only be called by the contract's `owner` (currently, the [`ZeroExGovernor`](../v3/v3-specification.md#zeroexgovernor) contract).
88 |
89 | ```solidity
90 | contract IAuthorizable {
91 |
92 | /// @dev Gets all authorized addresses.
93 | /// @return Array of authorized addresses.
94 | function getAuthorizedAddresses()
95 | external
96 | view
97 | returns (address[]);
98 |
99 | /// @dev Authorizes an address.
100 | /// @param target Address to authorize.
101 | function addAuthorizedAddress(address target)
102 | external;
103 |
104 | /// @dev Removes authorizion of an address.
105 | /// @param target Address to remove authorization from.
106 | function removeAuthorizedAddress(address target)
107 | external;
108 |
109 | /// @dev Removes authorizion of an address.
110 | /// @param target Address to remove authorization from.
111 | /// @param index Index of target in authorities array.
112 | function removeAuthorizedAddressAtIndex(
113 | address target,
114 | uint256 index
115 | )
116 | external;
117 | }
118 | ```
119 |
120 | The contracts that are currently authorized to call the `MultiAssetProxy` contract's `transferFrom` method are:
121 |
122 | - [Exchange 2.0](../v2/v2-specification.md#exchange)
123 | - [Exchange 3.0](../v3/v3-specification.md#exchange)
124 |
125 | ## Registering AssetProxy contracts
126 |
127 | The `MultiAssetProxy` can only dispatch transfers to other `AssetProxy` contracts that are registered within this contract. The `MultiAssetProxy` has the following interface for managing which `AssetProxy` contracts it is allowed to call. New registrations can only be initiated by the `owner` of this contract (currently, the [`ZeroExGovernor`](../v3/zero-ex-governor.md) contract).
128 |
129 | ```solidity
130 | contract IAssetProxyDispatcher {
131 |
132 | // Logs registration of new asset proxy
133 | event AssetProxyRegistered(
134 | bytes4 id, // Id of new registered AssetProxy.
135 | address assetProxy // Address of new registered AssetProxy.
136 | );
137 |
138 | /// @dev Registers an asset proxy to its asset proxy id.
139 | /// Once an asset proxy is registered, it cannot be unregistered.
140 | /// @param assetProxy Address of new asset proxy to register.
141 | function registerAssetProxy(address assetProxy)
142 | external;
143 |
144 | /// @dev Gets an asset proxy.
145 | /// @param assetProxyId Id of the asset proxy.
146 | /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
147 | function getAssetProxy(bytes4 assetProxyId)
148 | external
149 | view
150 | returns (address);
151 | }
152 |
153 | ```
154 |
155 | The `AssetProxy` contracts that are currently registered withing the `MultiAssetProxy` are:
156 |
157 | - [`ERC20Proxy`](../asset-proxy/erc20-proxy.md)
158 | - [`ERC721Proxy`](../asset-proxy/erc721-proxy.md)
159 | - [`ERC1155Proxy`](../asset-proxy/erc1155-proxy.md)
160 | - [`StaticCallProxy`](../asset-proxy/static-call-proxy.md)
161 | - [`ERC20BridgeProxy`](../asset-proxy/erc20-bridge-proxy.md)
162 |
--------------------------------------------------------------------------------
/asset-proxy/static-call-proxy.md:
--------------------------------------------------------------------------------
1 | # StaticCallProxy
2 |
3 | ## ZEIP-39
4 |
5 | The `StaticCallProxy` was first proposed in [ZEIP-39](https://github.com/0xProject/ZEIPs/issues/39). Please refer to the ZEIP for information and discussion about how this contract works at a higher level.
6 |
7 | ## Conditional transfers
8 |
9 | The `StaticCallProxy` itself cannot perform any transfers. Instead, it is primarily intended to be used in conjunction with the [`MultiAssetProxy`](../asset-proxy/multi-asset-proxy.md) to validate some on-chain state before or after any other transfers are performed. As implied in its name, the `StaticCallProxy` uses the `STATICCALL` opcode described in [EIP-214](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-214.md) to ensure that it cannot modify any on-chain state. Therefore, this contract does not require any user approvals to be used.
10 |
11 | ### transferFrom
12 |
13 | This contract may perform a `STATICCALL` and verify its result if its `transferFrom` method is called.
14 |
15 | ```solidity
16 | /// @dev Transfers assets. Either succeeds or throws.
17 | /// @param assetData Byte array encoded for the respective asset proxy.
18 | /// @param from Address to transfer asset from.
19 | /// @param to Address to transfer asset to.
20 | /// @param amount Amount of asset to transfer.
21 | function transferFrom(
22 | bytes assetData,
23 | address from,
24 | address to,
25 | uint256 amount
26 | )
27 | external;
28 | ```
29 |
30 | #### Logic
31 |
32 | Calling `StaticCallProxy.transferFrom` will perform the following steps:
33 |
34 | 1. Decode `staticCallTargetAddress`, `staticCallData`, and `expectedReturnDataHash` from `assetData`
35 | 1. Call `staticCallTargetAddress.staticcall(staticCallData)`
36 | 1. Revert if the call was unsuccessful
37 | 1. Calculate the Keccak-256 hash of the return data, resulting in `returnDataHash`
38 | 1. Revert if the `returnDataHash` does not exactly match the `expectedReturnDataHash`
39 |
40 | #### Errors
41 |
42 | The `transferFrom` method may revert with the following errors:
43 |
44 | | Error | Condition |
45 | | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
46 | | [StandardError("UNEXPECTED_STATIC_CALL_RESULT")](../v3/v3-specification.md#standard-error) | The hash of the return data does not match the `expectedReturnDataHash` |
47 | | \* | This contract will rethrow any revert data received from an unsuccessful call of `staticCallTargetAddress.staticcall(staticCallData)` |
48 |
49 | ## Encoding assetData
50 |
51 | This contract expects StaticCall [`assetData`](../v3/v3-specification.md#assetdata) to be encoded using [ABIv2](http://solidity.readthedocs.io/en/latest/abi-spec.html) with the following function signature. The id of this contract is `0xc339d10a`, which can be calculated as the [4 byte function selector](https://solidity.readthedocs.io/en/latest/abi-spec.html#function-selector) of the same signature.
52 |
53 | ```solidity
54 | /// @dev Function signature for encoding StaticCall assetData.
55 | /// @param staticCallTargetAddress Target address of staticcall.
56 | /// @param staticCallData Data that will be passed to staticCallTargetAddress in the staticcall.
57 | /// @param expectedReturnDataHash Expected Keccak-256 hash of the staticcall return data.
58 | function StaticCall(
59 | address staticCallTargetAddress,
60 | bytes calldata staticCallData,
61 | bytes32 expectedReturnDataHash
62 | )
63 | external;
64 | ```
65 |
66 | In Solidity, this data can be encoded with:
67 |
68 | ```solidity
69 | bytes memory data = abi.encodeWithSelector(
70 | 0xc339d10a,
71 | staticCallTargetAddress,
72 | staticCallData,
73 | expectedReturnDataHash
74 | );
75 | ```
76 |
77 | ## Authorizations
78 |
79 | Since this contract can only read from state, no authorizations are required. This contract's `transferFrom` method can be called by any address.
80 |
--------------------------------------------------------------------------------
/exchange-proxy/deployment.md:
--------------------------------------------------------------------------------
1 | # Exchange Proxy Deployment
2 |
3 | This document outlines procedures for deploying and upgrading the Exchange Proxy.
4 |
5 | ## Overview
6 |
7 | The Exchange Proxy is composed of several independently deployed contracts that broadly fit into these categories:
8 |
9 | - [Exchange Proxy](./exchange-proxy.md) (`ZeroEx`)
10 | - This is the Exchange Proxy instance itself. It serves as the entry point for all transactions, which it will reroute to the appropriate feature contract.
11 | - [Features](./features)
12 | - These contracts contain the implementation code for features that will be registered to the Exchange Proxy. The Exchange Proxy will ultimately `delegatecall` into these contracts.
13 | - Migration contracts
14 | - These are one-shot contracts that aid in bootstrapping/configuring the Exchange Proxy during deployment. These contracts will typically self-destruct after use.
15 | - [Transformers](./features/transform-erc20.md#transformers)
16 | - These are the composable transformations used by the `TransformERC20` feature.
17 | - [`TransformerDeployer`](https://github.com/0xProject/0x-monorepo/blob/development/contracts/zero-ex/contracts/src/external/TransformerDeployer.sol)
18 | - This is the contract that will be used to deploy the individual transformer contracts. Transformers [must](./features/transform-erc20.md#locking-down-transformers) be deployed by a designated address.
19 |
20 | This diagram loosely illustrates the direct dependencies of each contract in the system.
21 |
22 | 
23 |
24 | Once these contracts are deployed, they will need to be wired together using the [`FullMigration`](https://github.com/0xProject/0x-monorepo/blob/development/contracts/zero-ex/contracts/src/migrations/FullMigration.sol) contract.
25 |
26 | ### Administration
27 |
28 | The Exchange Proxy exposes a few admin-level functions, which are only accessible to its owner. In a production deployment, the owner would be set to the [`ZeroExGovernor`](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/zero-ex-governor.md) multisig contract.
29 |
30 | ## Deployment Steps
31 |
32 | ### 1. Deploy the contracts
33 | The following contracts need to be deployed individually. With the exception of transformers, any address may deploy these contracts.
34 | - `FullMigration`
35 | - `ZeroEx`
36 | - Passing `FullMigration.getBootstrapper()` to the constructor.
37 | - Features:
38 | - `Ownable`
39 | - `SimpleFunctionRegistry`
40 | - `SignatureValidator`
41 | - `TokenSpender`
42 | - `TransformERC20`
43 | - `MetaTransactions`
44 | - `TransformerDeployer`
45 | - Transformers (deployed through `TransformerDeployer`)
46 |
47 | ### 2. Wire it all together
48 | Afterwards, we call `FullMigration.initializeZeroEx()` (see the [ganache migration script](https://github.com/0xProject/0x-monorepo/blob/development/contracts/zero-ex/src/migration.ts#L173) for an example). This will do several things:
49 | - Create and configure the [`AllowanceTarget`](./features/token-spender.md#allowance-target) contract.
50 | - It will be owned by the configured owner (`ZeroExGovernor`) and the Exchange Proxy will be added as an authorized user.
51 | - Register and initialize all the features to the Exchange Proxy.
52 | - The `TransformERC20` feature will deploy a new `FlashWallet` instance during initialization.
53 | - Set the owner of the Exchange Proxy to the configured owner.
54 | - Self-destruct.
55 |
56 | At this point the exchange proxy is fully functional.
57 |
58 | ## Upgrades (Migrations)
59 | Migrations will often be highly specific, depending on whether a feature is being added, removed, or replaced. Feature contracts typically implement a `migrate()` function that registers and (re)initializes the feature. This should be executed through the Exchange Proxy's *own* `migrate()` function, and must be called by the owner (`ZeroExGovernor`).
60 |
61 | For example, if we were adding or replacing a feature, we would
62 | 1. Deploy the new feature contract `NewFeature`.
63 | 2. Call `ZeroEx.migrate(NewFeature.address, NewFeature.migrate(...).getABIEncodedTransactionData(), zeroExGovernor.owner)`
64 | - **Warning**: Note the last argument, which specifies the *new* owner of the Exchange Proxy. This should be set to the current owner if we do not wish to transfer ownership. Passing in an incorrect address can lock us out of administrative capability permanently.
65 |
66 | It is highly recommended that all migrations are first attempted on a ganache fork where final state can be double-checked.
67 |
--------------------------------------------------------------------------------
/exchange-proxy/exchange-proxy.md:
--------------------------------------------------------------------------------
1 | # ZeroEx (Exchange Proxy) Spec
2 |
3 | ## Motivation
4 |
5 | Pretty soon after V3 + staking were launched we started seeing pain points from integrations and migrations. Many of these issues required on-chain solutions to sort out. For some, we were able to (mostly) satisfy requirements with extension contracts and ERC20 bridge contracts. But there are still many cases where neither of these approaches provide elegant solutions, and the only real solution would be to amend the Exchange contract itself. However, the Exchange contract is monolithic and not designed to be upgradeable, so changes to one part of the contract would require deploying a new instance and migrating integrators to this new contract. It’s also highly likely that new issues would appear as the ecosystem matured and we’d have to make more changes, leading to more migrations, etc.
6 |
7 | At the same time, we have our mind on governance. Ideally, we’d like governance to be binding on a per-feature basis. But with a monolithic model, we’d always be deploying the full Exchange feature-set in one swoop, so people would technically always be voting on an entire system. This has the effect of reducing participation because it’s very difficult to engage and inform voters on a complex collection of of features vs just a handful of related ones.
8 |
9 | ## The Proxy Contract: ZeroEx
10 |
11 | The ZeroEx contract implements a per-function proxy pattern. Every function registered to the proxy contract can have a distinct implementation contract. Implementation contracts are called “features” and can expose multiple, related functions. Since features can be upgraded independently, there will no longer be a collective “version” of the API, defaulting to a rolling release model.
12 |
13 | 
14 |
15 | The ZeroEx contract’s only responsibility is to route (delegate) calls to per-function implementation contracts through its fallback.
16 |
17 | ```solidity
18 | contract ZeroEx {
19 |
20 | // Storage bucket for the proxy.
21 | struct ProxyStorage {
22 | // Mapping of function selector -> function implementation
23 | mapping(bytes4 => address) impls;
24 | }
25 |
26 | // Construct this contract.
27 | constructor() public {
28 | // Temporarily register the `bootstrap()` function.
29 | ProxyStorage.impls[IBootstrap.bootstrap.selector] =
30 | address(new Bootstrap(msg.sender));
31 | }
32 |
33 | fallback() external payable {
34 | address impl = getFunctionImplementation(msg.data.readBytes4());
35 | require(impl != address(0));
36 |
37 | // Forward the call.
38 | (bool success, bytes memory result) = impl.delegatecall(msg.data);
39 | if (!success) {
40 | revertData(result);
41 | }
42 | returnData(result);
43 | }
44 |
45 | receive() external payable {}
46 |
47 | // Get the implementation of a function selector.
48 | function getFunctionImplementation(bytes4 selector)
49 | public view returns (address)
50 | {
51 | return ProxyStorage.impls[msg.data.readBytes4()];
52 | }
53 | }
54 | ```
55 |
56 | ## Bootstrapping
57 |
58 | The ZeroEx contract comes pre-loaded with only one feature: Bootstrap. This exposes a `bootstrap()` function that can only be called by the deployer. `bootstrap()` does a few things:
59 |
60 | 1. De-register the `bootstrap()` function, which prevents it being called again.
61 | 2. Self-destruct.
62 | 3. Delegatecall the bootstrapper target contract and call data.
63 |
64 | This is a stripped down Bootstrap feature contract:
65 |
66 |
67 | ```solidity
68 | contract Bootstrap {
69 | // The ZeroEx contract.
70 | address immutable private _deployer;
71 | // The implementation address of this contract.
72 | address immutable private _implementation;
73 | // The allowed caller to `bootstrap()`.
74 | address immutable private _bootstrapCaller;
75 |
76 | constructor(address bootstrapCaller) public {
77 | _deployer = msg.sender;
78 | _implementation = address(this);
79 | _bootstrapCaller = bootstrapCaller;
80 | }
81 |
82 | // Execute a bootstrapper in the context of the proxy.
83 | function bootstrap(address target, bytes callData) external {
84 | // Only the bootstrap caller can call this function.
85 | require(msg.sender == _bootstrapCaller);
86 | // Deregister.
87 | LibProxyStorage.getStorage().impls[this.bootstrap.selector] = address(0);
88 | // Self-destruct.
89 | Bootstrap(_implementation).die();
90 | // Call the bootstrapper.
91 | target.delegatecall(callData);
92 | }
93 |
94 | function die() external {
95 | require(msg.sender == _deployer);
96 | selfdestruct(msg.sender);
97 | }
98 | }
99 | ```
100 |
101 |
102 | This is the basic execution flow using our deployment migration contract (InitialMigration), which acts as both the deployer and bootstrapper:
103 | 
104 | ## Function Registry Management
105 |
106 | One of the initial features InitialMigration bootstraps into the ZeroEx contract is the function registry feature, SimpleFunctionRegistry. This feature exposes the following function registry management features:
107 |
108 | * `extend()` - Register a new function (selector) and implementation (address). This also maintains a history of past implementations so we can roll back to one, if needed.
109 | * `rollback()` - Reverts a function implementation to a prior version in its history.
110 |
111 | ```solidity
112 | contract SimpleFunctionRegistry {
113 |
114 | // Storage bucket for this feature.
115 | struct SFRStorage {
116 | // Mapping of function selector -> implementation history.
117 | mapping(bytes4 => address[]) implHistory;
118 | }
119 |
120 | // Roll back to the last implementation of a function.
121 | function rollback(bytes4 selector)
122 | external
123 | onlyOwner
124 | {
125 | address[] storage history = SFRStorage.implHistory[selector];
126 | require(history.length > 0);
127 | ProxyStorage.impls[selector] = history[history.length - 1];
128 | history.pop();
129 | }
130 |
131 | // Register or replace a function.
132 | function extend(bytes4 selector, address impl)
133 | external
134 | onlyOwner
135 | {
136 | _extend(selector, impl);
137 | }
138 |
139 | // Register or replace a function.
140 | function _extend(bytes4 selector, address impl)
141 | private
142 | {
143 | address[] storage history = SFRStorage.implHistory[selector];
144 | history.push(ProxyStorage.impls[selector]);
145 | ProxyStorage.impls[selector] = impl;
146 | }
147 | }
148 | ```
149 |
150 | ## Ownership
151 |
152 | Another feature InitialMigration bootstraps into the proxy is the Ownable feature. This exposes ownership management functions: `transferOwner`ship`()` and `getOwner()`. This feature also enables ubiquitous modifiers such as `onlyOwner`, so it is an implicit dependency of nearly every other feature.
153 |
154 |
155 | ```solidity
156 | contract Ownable {
157 |
158 | // Storage bucket for this feature.
159 | struct OwnableStorage {
160 | // The owner of this contract.
161 | address owner;
162 | }
163 |
164 | // Change the owner of this contract.
165 | function transferOwnership(address newOwner)
166 | external
167 | onlyOwner
168 | {
169 | OwnableStorage.owner = newOwner;
170 | }
171 |
172 | // Get the owner of this contract.
173 | function getOwner() external view returns (address owner_) {
174 | return OwnableStorage.owner;
175 | }
176 | }
177 | ```
178 |
179 | ## Migrations
180 |
181 | Migrations are upgrade logic that run in the context of the proxy contract. To do this, the owner calls the `migrate()` function, provided by the Ownable feature. This follows a similar sequence as the bootstrap process. Notably, it temporarily sets the owner of the contract to itself for the duration of the migration call, which allows the migrator to perform admin-level operations through other features, such as registering or rolling back new functions (see [Function Registry Management](https://0xproject.quip.com/DudoAmHvpLaB#eADACA29a1H)). Before exiting, the owner is set to the `newOwner`, which is passed in to the call.
182 |
183 | One motivation for the existence of this function, as opposed to just having the make individual admin calls, is a shortcoming of the ZeroExGoverner contract, which is designed to perform one operation at a time, with no strict ordering of those operations.
184 |
185 | This is a stripped down `migrate()` feature implementation:
186 |
187 | ```solidity
188 | contract Ownable {
189 |
190 | // Execute a migration function in the context of the proxy contract.
191 | function migrate(address target, bytes calldata data, address newOwner)
192 | external
193 | onlyOwner
194 | {
195 | // If the owner is already set to ourselves then we've reentered.
196 | require(OwnableStorage.owner != address(this));
197 | // Temporarily set the owner to ourselves.
198 | OwnableStorage.owner = address(this);
199 |
200 | // Perform the migration.
201 | target.delegatecall(data);
202 |
203 | // Set the new owner.
204 | OwnableStorage.owner = newOWner;
205 | }
206 | }
207 | ```
208 |
209 |
210 | This is an example sequence of a migration:
211 |
212 | 
213 | ## Storage Buckets
214 |
215 | Because feature functions get delegatecalled into, they all share the same execution context and, thus, state space. It’s critical that storage for each feature be compartmentalized from other features to avoid accidentally writing to the same slot. We solve this by strictly adhering to a storage bucket pattern for our feature contracts. This rule also extends to all inherited contracts/mixins.
216 |
217 | Storage buckets are enabled by new language features in solidity 0.6, which allow us to rewrite a storage variable’s slot reference to a globally unique ID. These IDs are stored in an *append-only* enum defined in `LibStorage`, to enforce uniqueness. The true storage slot for a bucket is the feature’s storage ID multiplied by a large constant to prevent overlap between buckets.
218 |
219 | Example:
220 |
221 | ```solidity
222 | LibStorage {
223 | enum StorageId {
224 | MyFeature
225 | }
226 |
227 | function getStorageSlot(StorageId id) internal pure returns (uint256) {
228 | return uint256(id) * 1e18;
229 | }
230 | }
231 |
232 | LibMyFeatureStorage {
233 | // Storage layout for this feature.
234 | struct Storage {
235 | mapping(bytes32 => bytes) myData;
236 | }
237 |
238 | // Get the storage bucket for this feature.
239 | function getStorage() internal view returns (Storage storage st) {
240 | uint256 slot = LibStorage.getStorageSlot(
241 | LibStorage.StorageId.MyFeature
242 | );
243 | assembly { st_slot := slot }
244 | }
245 | }
246 | ```
247 |
248 | With the above pattern, writing to storage is simply:
249 |
250 | ```solidity
251 | LibMyFeatureStorage.getStorage().myData[...] = ...;
252 | ```
253 |
254 | ## Version Management
255 |
256 | ### Inspection
257 |
258 | This is a rolling release model, where every feature/function has its own version. All feature contracts (except Bootstrap because it’s ephemeral), implement the `IFeature` interface:
259 |
260 | ```solidity
261 | interface IFeature {
262 | // The name of this feature set.
263 | function FEATURE_NAME() external view returns (string memory name);
264 |
265 | // The version of this feature set.
266 | function FEATURE_VERSION() external view returns (uint256 version);
267 | }
268 | ```
269 |
270 | So, to get the version of a function one could do `IFeature(getFunctionImplementation(foo.selector)).FEATURE_VERSION`.
271 |
272 | ### Best Practices
273 |
274 | The registry is intentionally not prescriptive on how features should be migrated. But there are some general best practices we can follow to avoid harming users, and ourselves.
275 |
276 | **Deprecation**
277 |
278 | In general, unless a function has a vulnerability, we should keep it intact for the duration of the deprecation schedule. Afterwards, we can ultimately disable the function by either calling `extend()` with a null implementation or by calling `rollback()` to a null implementation.
279 |
280 | **Patches**
281 |
282 | These include bug-fixes, optimizations, or any other changes that preserve the intended behavior of the function. For these cases, we should upgrade the function in-place, i.e., using the same selector but changing the implementation contract, through `extend()`.
283 |
284 | **Vulnerabilities**
285 |
286 | If a vulnerability is found in a live function, we should call `rollback()` immediately to reset it to a non-vulnerable implementation. Because `rollback()` is a separate function from `extend()`, it can be exempted from timelocks to allow a swift response.
287 |
288 | **Upgrades**
289 |
290 | These involve meaningful behavioral changes, such as new settlement logic, changes to the order format (or its interpretation), etc. These should always be registered under a new selector, which comes free if the arguments also change, to allow users the opportunity to opt-in to new behavior. If the upgrade is intended to replace an existing feature, the old version should follow a deprecation schedule, unless we’re confident no one is using it.
291 |
292 | **Features used by features**
293 |
294 | Not all features are designed to be exclusively consumed by the public. We can have internal features by applying an `onlySelf` modifier to the function. We need to be mindful of another class of user: the contract itself. Avoiding missteps on this will require a combination of diligence and good regression test suites.
295 |
296 | ## Known Risks
297 |
298 | The extreme flexibility of this model means we have few built-in guardrails that more conventional architectures enjoy. To avoid pitfalls, we’ve established a few new patterns to follow during development, but the following areas will always need careful scrutiny during code reviews.
299 |
300 | ### Extended Attack Surface for Features
301 |
302 | Because features all run in the same execution context, they inherit potential vulnerabilities from other features. Some vulnerabilities may also arise from the interactions of separate features, which may not be obvious without examining the system as a whole. Reviewers will always need to be mindful of these scenarios and features should try to create as much isolation of responsibilities as possible.
303 |
304 | ### Storage Layout Risks
305 |
306 | All features registered to the proxy will run in the same storage context as the proxy itself. We employ a pattern of per-feature storage buckets (structs) with globally unique bucket slots to mitigate issues.
307 |
308 | **Slot Overlap**
309 |
310 | Every time we develop a new feature, an entry is appended to the `LibStorage.StorageId` enum, which is the bucket ID for the feature’s storage. This applies to the storage used by the proxy contract itself. When calculating the true slot for the storage bucket, this enum value is offset by `1` and bit shifted by `128`:
311 |
312 |
313 | ```solidity
314 | function getStorageSlot(StorageId id) internal pure returns (uint256) {
315 | return (uint256(id) + 1) << 128;
316 | }
317 | ```
318 |
319 |
320 | Given Solidity’s storage layout rules (https://solidity.readthedocs.io/en/v0.6.6/miscellaneous.html), subsequent storage buckets should always be 2^128 slots apart, which means buckets can have 2^128 flattened inline fields before overlapping. While it’s not impossible for buckets to overlap with this pattern, it should be extremely unlikely if we follow it closely. Maps and arrays are not stored sequentially but should also be affected by their base slot value to make collisions unlikely.
321 |
322 | **Inherited Storage**
323 |
324 | A more insidious way to corrupt storage buckets is to have a feature unintentionally inherit from a mixin that has plain (non-bucketed) state variables, because the mixin can potentially read/write to slots shared by other buckets through them. To avoid this:
325 |
326 | 1. We prefix all feature-compatible mixins with “Fixin” (“Feature” + “Mixin”) and only allow contract inheritance from these.
327 | 2. Storage IDs are offset by 1 before computing the slot value. This means the first real storage bucket will actually start at slot `2^128`, which gives us a safety buffer for these scenarios, since it’s unlikely a mixin would unintentionally access slots beyond `2^128`.
328 |
329 | **Shared Access to Storage**
330 |
331 | There is nothing stopping a feature from reaching into another feature’s storage bucket and reading/modifying it. Generally this pattern is discouraged but may be necessary in some cases, or may be preferable to save gas. This can create an implicit tight coupling between features and we need to take those interactions into account when upgrading the features that own those storage buckets.
332 |
333 | **Restricted Functions and Privilege Escalation**
334 |
335 | We will also be registering functions that have caller restrictions. Functions designed for internal use only will have an `onlySelf` modifier that asserts that `msg.sender == address(this)`. The other class of restricted functions are owner-only functions, which have an `onlyOwner` modifier that asserts that the `msg.sender == LibOwnableStorage.Storage.owner`.
336 |
337 | The check on owner-only functions can be easily circumvented in a feature by directly overwriting `LibOwnableStorage.Storage.owner` with another address. If best practices and patterns are adhered to, doing so would involve deliberate and obvious effort and should be caught in reviews.
338 |
339 | **Self-Destructing Features**
340 |
341 | A feature contract with self-destruct logic must safeguard this code path to only be executed after the feature is deregistered, otherwise its registered functions will fail. In most cases this would just cause the feature to temporarily go dark until we could redeploy it. But it may leave the proxy in an unusable state if this occurs in the contract of a mission-critical feature, e.g., Ownable or SimpleFunctionRegistry (neither of which can self-destruct).
342 |
343 | Features should also be careful that `selfdestruct` is never executed in the context of the proxy to avoid destroying the proxy itself.
344 |
345 | **Allowances**
346 |
347 | Although the proxy will not have access to the V3 asset proxies initially, early features will require taker allowances to be accessible to the proxy somehow. Instead of having the proxy contract itself be the allowance target, we intend on using a separate “Puppet” contract, callable only by the proxy contract. This creates a layer of separation between the proxy contract and allowances, so moving user funds is a much more deliberate action. In the event of a major vulnerability, the owner can simply detach the puppet contract from the proxy. This also avoids the situation where the proxy has lingering allowances if we decide grant it asset proxy authorization.
348 |
349 | **Balances**
350 |
351 | Inevitably, there will be features that will cause the Exchange Proxy to hold temporary balances (e.g., payable functions). Thus, it’s a good idea that no feature should cause the Exchange Proxy to hold a permanent balance of tokens or ether, since these balances can easily get mixed up with temporary balances.
352 |
--------------------------------------------------------------------------------
/exchange-proxy/features/meta-transactions.md:
--------------------------------------------------------------------------------
1 | # Exchange Proxy Feature `MetaTransactions`
2 |
3 | ## Summary
4 |
5 | A feature for executing Exchange Proxy functions on behalf of another user.
6 |
7 | ## Motivation
8 |
9 | We want to migrate 0x-API, and therefore Matcha, away from using the V3 Exchange contract directly. To have feature parity with Exchange V3, the Exchange Proxy will need to offer comparable meta-transaction support for swaps. This spec introduces two new features to get there.
10 |
11 | ## Architecture
12 |
13 | 
14 |
15 | ## Implementation
16 |
17 | In the [V3 Exchange architecture](../../v3/v3-specification.md#transactions), we simply set a `currentContextAddress` storage variable to the meta-transaction owner then `delegatecall`ed ourselves with the provided calldata. This pattern does not work for the Exchange Proxy.
18 |
19 | It would be pretty risky for the Exchange Proxy to `delegatecall` user-supplied calldata because there are several, sensitive “internal” functions registered to the Exchange Proxy that are guarded by an `onlySelf` modifier (`msg.sender == address(this)`). So some kind of white-list would be required.
20 |
21 | Also, Exchange Proxy features are encouraged to expose an internal variant of functions that allow the context address to be passed explicitly. This removes the need to store the context address to storage during a meta-transaction call. However, the calldata provided is no longer what we actually execute, as we now have to manually decode the meta-transaction calldata and translate the parameters to a call to the internal variant of the target function.
22 |
23 | ### The `MetaTransaction` Object
24 | Most of these fields are self-explanatory, but there are a couple things worth noting:
25 | - A fee can be added to the meta-transaction, payable in any ERC20 token that `signer` has approved the Exchange Proxy's allowance target for.
26 | - The amount of ETH to be attached to the call can be specified by `value`. It is possible for ETH to be generated by a prior meta-transaction and to be consumed by a subsequent one.
27 | - Gas price restrictions is now a range.
28 |
29 | ```solidity
30 | struct MetaTransactionData {
31 | // Signer of meta-transaction. On whose behalf we execute the MTX.
32 | address signer;
33 | // Allowed sender, or 0 for anyone.
34 | address sender;
35 | // Minimum gas price.
36 | uint256 minGasPrice;
37 | // Maximum gas price.
38 | uint256 maxGasPrice;
39 | // MTX is invalid after this time.
40 | uint256 expirationTimeSeconds;
41 | // Nonce to make this MTX unique.
42 | uint256 salt;
43 | // Call data to execute (but not really).
44 | // E.g., from `ITransformERC20.transformERC20(...).getABIEncodedTransactionData()`.
45 | bytes callData;
46 | // Amount of ETH to attach to the call.
47 | uint256 value;
48 | // ERC20 fee `signer` pays `sender`.
49 | IERC20 feeToken;
50 | // ERC20 fee amount.
51 | uint256 feeAmount;
52 | }
53 | ```
54 |
55 | The hash of a `MetaTransacitonData` is obtained by using [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) with the following domain:
56 |
57 | ```js
58 | {
59 | name: 'ZeroEx',
60 | version: '1.0.0',
61 | chainId: 1, // For mainnet.
62 | verifyingConract: EXCHANGE_PROXY_ADDRESS,
63 | }
64 | ```
65 |
66 | ### Functions
67 | The feature exposes the following functions:
68 | * `executeMetaTransaction()` Execute a single meta-transaction.
69 | * `batchExecuteMetaTransactions()` Execute multiple meta-transactions in sequence.
70 | * `_executeMetaTransaction()`Internal variant of `executeMetaTransaction()` that allows the sender to be explicitly provided.
71 | * `getMetaTransactionHash()` Return the hash of the meta-transaction.
72 | * `getMetaTransactionExecutedBlock()` Get the block height at which a meta-transaction was executed (0 if never executed).
73 | * `getMetaTransactionHashExecutedBlock()` Get the block height at which a meta-transaction *has* was executed (0 if never executed).
74 |
75 | ### Signatures
76 | Meta-transactions can be signed via any signature type supported by the [`SignatureValidator` feature](./signature-validator.md), since signature validation is entirely delegated to that feature.
77 |
78 | ### Supported Functions
79 | Only the [`TransformERC20.transformERC20()`](./transform-erc20.md) function is currently supported.
80 |
81 | The `MetaTransactions` feature understands [signed calldata](./transform-erc20.md#signed-calldata) to `TransformERC20.transformERC20()`. It uses the same parsing and validation library as the `TransformERC20` feature to signatures embedded in the meta-transaction's `callData` field.
82 |
83 | ### Replay Protection
84 | When a meta-transaction executes successfully, the feature will remember the block height at which it was executed. This state will be checked again to prevent a meta-transaction from being executed twice. This state is owned by this feature.
85 |
86 | ### Reentrancy
87 | All functions that execute meta-transactions (`executeMetaTransaction()` and `batchExecuteMetaTransactions()`) are under the same reentrancy guard.
88 |
89 | ### Value Refunds
90 | At the end of `executeMetaTransaction()` and `batchExecuteMetaTransactions()`, any ETH held by the Exchange Proxy *up to the original `msg.value`* will be returned to the caller. This ETH will typically be unspent protocol fees from `TransformERC20.transformERC20()`.
91 |
92 | ## Integrations
93 | A couple things to note for integrations.
94 | - These meta-transactions only work for operations inside the Exchange Proxy (not the V3 Exchange):
95 | - [`TransformERC20.transformERC20()`](./signature-validator.md) is the only supported function at the time of this writing.
96 | - The allowed signature are those supported by the [`SignatureValidator`](./signature-validator.md) feature.
97 |
98 | ## Challenges
99 | Meta-transactions still suffer from the same taker-related issues as the [TransformERC20 feature](./transform-erc20.md#rfq-models).
100 |
--------------------------------------------------------------------------------
/exchange-proxy/features/signature-validator.md:
--------------------------------------------------------------------------------
1 | # Exchange Proxy Feature: `SignatureValidator`
2 |
3 | ## Summary
4 |
5 | A feature for validating signatures inn Exchange Proxy.
6 |
7 | ## Motivation
8 |
9 | Many features will likely require some form of signature validation (e.g., [MetaTransactions](./meta-transactions.md). Rather than having each feature ship its own validation logic it makes sense to encapsulate this logic into a dedicated and upgradeable feature that they can all share. This way, we can support new signature types and patch vulnerabilities all at once.
10 |
11 | ## Architecture
12 |
13 | The `SignatureValidator` is entirely stateless and exposes two functions for validating hash-based signatures:
14 | - `validateHashSignature(bytes32 hash, address signer, bytes signature)`
15 | - Validates that `hash` was signed by `signer`, given `signature`. Reverts otherwise.
16 | - `isValidHashSignature(bytes32 hash, address signer, bytes signature)`
17 | - Returns true if `hash` was signed by `signer`, given `signature`.
18 |
19 | ## Implementation
20 |
21 | ### Format
22 | Signatures are constructed similar to those used by the [V3 Exchange](../../v3/v3-specification.md#signature-types):
23 |
24 | ```solidity
25 | abi.encodePacked(bytes(signature), uint8(signatureType))
26 | ```
27 |
28 | Where the contents of `signature` depends on the signature type.
29 |
30 | ### Types
31 | Initially this feature will only support the basic offline signature types, i.e., EIP712 and `eth_sign`, against a hash. The `Illegal` and `Invalid` failing signature types from the V3 Exchange contract will also be handled to simplify migration.
32 |
33 | ```solidity
34 | /// @dev Allowed signature types.
35 | enum SignatureType {
36 | Illegal, // 0x00, default value
37 | Invalid, // 0x01
38 | EIP712, // 0x02
39 | EthSign, // 0x03
40 | NSignatureTypes // 0x04, number of signature types. Always leave at end.
41 | }
42 | ```
43 |
44 | - **Illegal**
45 | - This signature type always fails, regardless of signature data.
46 | - **Invalid**
47 | - Also always failing, regardless of signature data.
48 | - **EIP712**
49 | - A signature generated via [EIP712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)'s `eth_signTypedData` RPC call *or* by directly signing a hash.
50 | - **EthSign**
51 | - A signature generated via the `eth_sign` RPC call (see [JSON-RPC reference](https://eth.wiki/json-rpc/API)).
52 |
--------------------------------------------------------------------------------
/exchange-proxy/features/token-spender.md:
--------------------------------------------------------------------------------
1 | # Exchange Proxy Feature: `TokenSpender`
2 |
3 | ## Summary
4 | A feature for managing and spending token allowances.
5 |
6 | ## Motivation
7 |
8 | We need a feature that encapsulates the pattern and logic and of moving funds from a user for an operation.
9 |
10 | ## Architecture
11 |
12 | 
13 |
14 | ### Functions
15 | The `TokenSpender` feature exposes the following functions:
16 | - `getAllowanceTarget()`
17 | - Returns the address of the allowance target for tokens the Exchange Proxy can spend. *This is different from the Exchange Proxy itself.*
18 | - `getSpendableERC20BalanceOf(IERC20 token, address owner)`
19 | - Returns the total quantity of an ERC20 token that can be moved from `owner` by the Exchange Proxy, given the current allowance and balance.
20 | - `_spendERC20Tokens(IERC20 token, address owner, address to, uint256 amount)`
21 | - Move `amount` of `token` from `owner` to `to`. *Only callable from inside the Exchange Proxy itself.*
22 |
23 | ## Implementation
24 |
25 | ### AllowanceTarget
26 |
27 | Allowances are not set directly on the Exchange Proxy. There are a few good reasons for this:
28 |
29 | * Lack of separation of complex logic vs access to funds.
30 | * No easy way to detach allowances in case of a critical vulnerability.
31 | * Allowances do not migrate if we ever redeploy the Exchange proxy.
32 | * Dangling allowances if we decide to migrate the Exchange proxy to use the V3 asset proxies for allowances.
33 |
34 | A separate contract, called the `AllowanceTarget`, will be the target for *all* token allowances. This contract is owned by the governor with the Exchange Proxy set as an authorized user.
35 |
36 | It exposes a single function, `executeCall(address target, bytes callData)`, which only the Exchange Proxy should be allowed to call. This function will perform a low-level `call()` to `target` passing `callData` as calldata. This allows the Exchange Proxy to perform token transfers from the context of the `AllowanceTarget`.
37 |
38 |
39 | ### `_spendERC20Tokens()`
40 | This function is only callable from inside the Exchange Proxy itself. It moves tokens from an account that has granted the `AllowanceTarget` an allowance by calling `AllowanceTarget.executeCall(token, abi.encode(ERC20.transferFrom.selector, owner, to, amount))`.
41 |
42 | ## Risks & Mitigations
43 | - Unlike with the V3 Exchange asset proxies, each token standard does not have its own, distinct allowance target, potentially increasing the attack surface with each feature that interacts with the `AllowanceTarget`. Thus, it is essential that the `TokenSpender` feature be the only allowed means of accessing user allowances.
44 | - There is nothing technically preventing another feature from directly access the `AllowanceTarget`. Developers must not allow this pattern to be used.
45 | - The `AllowanceTarget` instance is owned by the governor contract, so it can be detached in an emergency or a migration.
46 |
--------------------------------------------------------------------------------
/exchange-proxy/features/transform-erc20.md:
--------------------------------------------------------------------------------
1 | # Exchange Proxy Feature: `TransformERC20`
2 |
3 | ## Summary
4 | A feature that allows for composable transformations between ERC20 tokens (e.g, filling quotes).
5 |
6 | ## Motivation
7 |
8 | Integrations have taught us that there is no one-size-fits-all solution to a swap function. Different integrators will have different needs. Some prefer to interact with ETH over WETH, others want to collect affiliate fees, and we may see some that want to interact using wrapped tokens (cUSDC and the like). Therefore, we need a flexible and composable swap function that satisfies all these needs.
9 |
10 | ## Architecture
11 |
12 | The `TransformERC20` feature exposes a `transformERC20()` function that agnostically performs a set of given "transformations" on input tokens and produces output tokens in exchange. In a nutshell it will:
13 |
14 | 1. Take X input tokens from the taker.
15 | 2. Perform arbitrary transformations (provided by the taker) on the tokens we hold.
16 | 3. Validate that the taker has received Y output tokens.
17 |
18 | We don’t particularly care what goes on inside the transformations so long as the taker winds up receiving the required amount of output tokens at the end of it. This is similar to how [ERC20Bridges](../../asset-proxy/erc20-bridge-proxy.md) operate, in that the `ERC20BridgeProxy` does not know what the bridges are doing, but asserts at the end that the taker has received enough maker tokens
19 |
20 | ### Transformers
21 |
22 | We define each of our desired transformations as “Transformer” contracts. Similar to bridge contracts, transformers typically receive tokens and produce some kind of “output” token (but aren’t required to do either of these). Some example transformers we could have:
23 |
24 | * `WethTransformer`:
25 | * Either wraps or unwraps WETH.
26 | * `AffiliateFeeTransformer`:
27 | * Transfers % of the tokens received to an affiliate address.
28 | * `ProtocolFeeBrokerTransformer`:
29 | * Converts % of the tokens received into ETH, possibly by executing another swap.
30 | * `PayTakerTransformer`:
31 | * Transfers tokens directly to the taker.
32 | * `FillQuoteTransformer`:
33 | * Perform a market buy/sell on a set of orders, most likely generated by 0x-API.
34 | * `CompoundTokenTransformer`:
35 | * Wrap/unwrap cTokens (e.g., cDAI -> DAI).
36 |
37 | ### Transformation Pipeline
38 |
39 | `transformERC20()` executes each Transformer in sequence, manipulating the token balance held by the wallet. The final transformer will simply transfer the output token directly to the taker. This creates a composable pipeline of operations/transitions.
40 |
41 | 
42 |
43 | ### Key Benefits
44 |
45 | * Composable flexibility
46 | * Every integrator/taker has different needs. Allowing the taker to compose their own operations means they’re more likely to get the behavior they want.
47 | * Rapid development
48 | * By separating out transformation logic from the Exchange proxy, we can iterate on these operations much more quickly.
49 | * Peace-of-mind
50 | * By asserting that the taker has received the required amount of output tokens at the end, we can treat transformers as black boxes and have reasonable certainty that the taker is not being swindled.
51 | * Novel use cases
52 | * The pluggable nature of this function means it is not limited to fills. One could use it to perform many common DeFi operations in a single transaction.
53 |
54 | ## Implementation
55 |
56 | ### Flash Wallets
57 |
58 | A big difference from bridge contracts is that we `delegatecall` into transformers instead of performing a regular `call`. This is to avoid the massive gas overhead that would be incurred if we had to transfer intermediate tokens to each transformer contract. However, we don’t want to perform the `delegatecall` in the context of the Exchange proxy because that would give transformers full control over the Exchange proxy.
59 |
60 | So instead we use a middle-man contract called a `FlashWallet`. The wallet contract will hold all intermediate token balances and perform `delegatecall`s to the individual transformers, in a context separate from the Exchange proxy. The `TransformERC20` feature will hold a single `FlashWallet` contract instance which can be reused by `transformERC20()` all operations.
61 |
62 | The wallet instance can be (re)created with `createTransformWallet()`, which is only callable by the owner/governor. This allows us to deploy a fresh wallet in case we somehow break the old one, like if we accidentally `selfdestruct` it or clobber its state.
63 |
64 | The wallet also works with ERC1155 and ERC223 tokens by implementing the required fallbacks for those standards. This allows transformers to hold ERC1155 and ERC223 intermediate assets. If we decide to add support for more tokens, we will have to upgrade the `TransformERC20` feature since the FlashWallet bytecode is hardcoded into it.
65 |
66 | #### Example
67 |
68 | For example, if a taker wanted to do the following:
69 |
70 | 1. Pay ETH
71 | 2. Convert to WETH
72 | 3. Fill a WETH → USDC quote
73 | 4. Pay affiliate fee in USDC
74 | 5. Wrap the USDC in cUSDC
75 |
76 | The execution might look like:
77 |
78 | 
79 |
80 | ### Locking Down Transformers
81 |
82 | Transformers need to be permissioned since they’re taking control of the wallet instance. Though no loss of funds should be able to occur in otherwise, opening up wallets to arbitrary transformers could lead to griefing by transformers rendering the wallets unusable (e.g., `selfdestruct`).
83 |
84 | If we maintained a registry of allowed Transformer contracts on the Exchange proxy, changes would likely have to go through the governor and be subject to timelocks, which defeats the nimbleness aspect of this entire architecture. This also incurs an `SLOAD`. Instead, we can adopt the pattern of using a single, known deployer address to deploy all Transformers. Instead of taking an address of a transformer, the `transformERC20()` function takes a “deployment nonce,” which is the nonce the trusted deployer had when deploying the transformer. The address of the transformer will be derived from this value.
85 |
86 | A consequence of this approach is that a transformer will always be valid. If the transformer can render the state of the `FlashWallet` invalid, it can be perpetually used to grief the system. The recommendation here is to expose a self-destructing `die()` function on all transformers which:
87 |
88 | * Is only callable by the deployer/owner.
89 | * Checks that `address(this) == _implementation` to ensure it always self-destructs in its own context.
90 |
91 | ### Interfaces
92 |
93 | #### Feature Interface
94 |
95 | ```solidity
96 | interface ITransformERC20 {
97 |
98 | /// @dev Defines a transformation to run in `transformERC20()`.
99 | struct Transformation {
100 | // The deployment nonce of the transformer.
101 | // This is the nonce the deployer had when deploying the transformer.
102 | // The address of the transformer will be derived from this value.
103 | uint32 deploymentNonce;
104 | // Arbitrary data to pass to the transformer.
105 | bytes data;
106 | }
107 |
108 | /// @dev Executes a series of transformations to convert an ERC20 `inputToken`
109 | /// to an ERC20 `outputToken`.
110 | /// @param inputToken The token being provided by the sender.
111 | /// If `0xeee...`, ETH is implied and should be provided with the call.`
112 | /// @param outputToken The token to be acquired by the sender.
113 | /// `0xeee...` implies ETH.
114 | /// @param inputTokenAmount The amount of `inputToken` to take from the sender.
115 | /// May be `uint256(-1)` to indicate the maximum transferrable.
116 | /// @param minOutputTokenAmount The minimum amount of `outputToken` the sender
117 | /// must receive for the entire transformation to succeed.
118 | /// @param transformations Sequence of transformations to apply to the token
119 | /// balances.
120 | /// @return outputTokenAmount The amount of `outputToken` received by the sender.
121 | function transformERC20(
122 | IERC20TokenV06 inputToken,
123 | IERC20TokenV06 outputToken,
124 | uint256 inputTokenAmount,
125 | uint256 minOutputTokenAmount,
126 | Transformation[] calldata transformations
127 | )
128 | external
129 | payable
130 | returns (uint256 outputTokenAmount);
131 |
132 | /// @dev Arguments for `_transformERC20()`.
133 | struct TransformERC20Args {
134 | // The taker address.
135 | address payable taker;
136 | // The token being provided by the taker.
137 | // If `0xeee...`, ETH is implied and should be provided with the call.`
138 | IERC20TokenV06 inputToken;
139 | // The token to be acquired by the taker.
140 | // `0xeee...` implies ETH.
141 | IERC20TokenV06 outputToken;
142 | // The amount of `inputToken` to take from the taker.
143 | // If set to `uint256(-1)`, the entire spendable balance of the taker
144 | // will be solt.
145 | uint256 inputTokenAmount;
146 | // The minimum amount of `outputToken` the taker
147 | // must receive for the entire transformation to succeed. If set to zero,
148 | // the minimum output token transfer will not be asserted.
149 | uint256 minOutputTokenAmount;
150 | // The transformations to execute on the token balance(s)
151 | // in sequence.
152 | Transformation[] transformations;
153 | // The hash of the calldata for the `transformERC20()` call.
154 | bytes32 callDataHash;
155 | // The signature for `callDataHash` signed by `getQuoteSigner()`.
156 | bytes callDataSignature;
157 | }
158 |
159 | /// @dev Internal version of `transformERC20()`. Only callable from within.
160 | /// @param args A `TransformERC20Args` struct.
161 | /// @return outputTokenAmount The amount of `outputToken` received by the taker.
162 | function _transformERC20(TransformERC20Args calldata args)
163 | external
164 | payable
165 | returns (uint256 outputTokenAmount);
166 | }
167 | ```
168 |
169 | #### Transformer Interface
170 |
171 | ```solidity
172 | /// @dev A transformation callback used in `TransformERC20.transformERC20()`.
173 | interface IERC20Transformer {
174 |
175 | /// @dev Context information to pass into `transform()` by `TransformERC20.transformERC20()`.
176 | struct TransformContext {
177 | // The hash of the `TransformERC20.transformERC20()` calldata.
178 | bytes32 callDataHash;
179 | // The caller of `TransformERC20.transformERC20()`.
180 | address payable sender;
181 | // taker The taker address, which may be distinct from `sender` in the case
182 | // of meta-transactions.
183 | address payable taker;
184 | // Arbitrary data to pass to the transformer.
185 | bytes data;
186 | }
187 |
188 | /// @dev Called from `TransformERC20.transformERC20()`. This will be
189 | /// delegatecalled in the context of the FlashWallet instance being used.
190 | /// @param context Context information.
191 | /// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
192 | function transform(TransformContext calldata context)
193 | external
194 | returns (bytes4 success);
195 | }
196 | ```
197 |
198 | ### Signed CallData
199 | The calldata used to execute the `transformERC20()` call (`msg.data`) can, optionally, have extra signature data appended to it. This extra data will be ignored by the EVM but will be inspected by the `transformERC20()` function prior to executing transformers. If the signature data proves that the hash of the calldata (without the signature data) is signed by `getQuoteSigner()`, the transformers will receive a non-zero `callDataHash` parameter.
200 |
201 | The quote signer will be set to the address of the 0x-API signing key. This will allow certain transformers to "unlock" extra functionality if the quote is generated by 0x-API. Some examples of possible 0x-API-only features are:
202 |
203 | - Gas token burning.
204 | - Subsidizing the protocol fee.
205 | - Filling RFQT orders.
206 |
207 | #### Signed CallData Format
208 |
209 | The quote signer (0x-API) would generate signed calldata as such:
210 |
211 | ```ts
212 | export function signCallData(callData: string, privateKey: string): string {
213 | const {r, s, v} = ethjs.ecsign(
214 | ethjs.hashPersonalMessage(ethjs.toBuffer(hexUtils.hash(callData))),
215 | ethjs.toBuffer(privateKey),
216 | );
217 | const prefix = ethjs.keccak256('SignedCallDataSignature(bytes)');
218 | return hexUtils.concat(callData, prefix, v, r, s, SignatureType.EthSign);
219 | }
220 | ```
221 |
222 | Where `callData` is the ABI-encoded call to `transformERC20()`.
223 |
224 | The `transformERC20()` function will search for the 4-byte `prefix` (`0xf86d1d92`) at 70 bytes from the end of `msg,data` and, if present, pass the hash of the preceding bytes and signature data to `SignatureValidator.isValidHashSignature()` to determine if the calldata was properly signed or not.
225 |
226 |
227 | #### Quote Signer
228 | The quote signer will be set to the address of 0x-API's signing key. The current quote signer can be accessed through `getQuoteSigner()`. It can be changed by the owner/governor through `setQuoteSigner()`.
229 |
230 | ## Data
231 |
232 | The `transformERC20()` function will emit a `TransformedERC20` event upon completion:
233 |
234 | ```solidity
235 | event TransformedERC20(
236 | address indexed taker,
237 | address inputToken,
238 | address outputToken,
239 | uint256 inputTokenAmount,
240 | uint256 outputTokenAmount
241 | );
242 | ```
243 |
244 | ## Challenges
245 |
246 | ### RFQ Models
247 |
248 | Because any settlement of native orders must still go through the Exchange V3 contract, the Exchange Proxy operates similar to the Forwarder contract, where it assumes the role of temporary taker for the order when performing a fill. This can be a problem for RFQ models which set the taker of the order to the address fetching the quote. Because the Exchange Proxy (actually, the `FlashWallet` instance) will be the immediate taker of the order, taker-restricted orders will not be fillable through the Exchange Proxy. To get around this, the market maker can set the taker address to the `FlashWallet` instance, but there will be no way to discriminate between actual takers at the settlement layer.
249 |
--------------------------------------------------------------------------------
/exchange-proxy/img/ep-deployment-deps.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/ep-deployment-deps.png
--------------------------------------------------------------------------------
/exchange-proxy/img/exchange-proxy-bootstrap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/exchange-proxy-bootstrap.png
--------------------------------------------------------------------------------
/exchange-proxy/img/exchange-proxy-call-forwarding.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/exchange-proxy-call-forwarding.png
--------------------------------------------------------------------------------
/exchange-proxy/img/exchange-proxy-mtxs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/exchange-proxy-mtxs.png
--------------------------------------------------------------------------------
/exchange-proxy/img/token-spender.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/token-spender.png
--------------------------------------------------------------------------------
/exchange-proxy/img/transform-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/transform-example.png
--------------------------------------------------------------------------------
/exchange-proxy/img/transform-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/transform-pipeline.png
--------------------------------------------------------------------------------
/exchange-proxy/img/zero_ex_migrate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/exchange-proxy/img/zero_ex_migrate.png
--------------------------------------------------------------------------------
/img/0x_coordinator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/img/0x_coordinator.png
--------------------------------------------------------------------------------
/img/0x_forwarder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/img/0x_forwarder.png
--------------------------------------------------------------------------------
/img/0x_v2_trade_erc20_erc20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/img/0x_v2_trade_erc20_erc20.png
--------------------------------------------------------------------------------
/img/0x_v2_trade_erc20_erc721.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/img/0x_v2_trade_erc20_erc721.png
--------------------------------------------------------------------------------
/order-types/chai-bridge.md:
--------------------------------------------------------------------------------
1 | # ChaiBridge Orders
2 |
3 | `ChaiBridge` orders allow market makers to earn the [Dai Savings Rate](https://community-development.makerdao.com/makerdao-mcd-faqs/faqs/dsr) while providing liquidity in Dai. This uses a combination of the [Chai token contract](https://chai.money/about.html) and the [ERC20BridgeProxy](../asset-proxy/erc20-bridge-proxy.md#erc20bridgeproxy) to create a user experience that is comparable to filling regular Dai-denominated orders.
4 |
5 | ## ChaiBridge contract
6 |
7 | The `ChaiBridge` contract is a thin adapter between the Chai token contract and the `ERC20BridgeProxy` contract. Calling `ChaiBridge.bridgeTransferFrom` will perform the following steps:
8 |
9 | 1. Ensure that the sender is the `ERC20BridgeProxy`
10 | 1. Withdraw `amount` of Dai from the maker's (`from` address) Chai token balance
11 | 1. Transfer `amount` of Dai to the taker (`to` address)
12 |
13 | This allows the `ERC20BridgeProxy` to transfer Dai from the maker's address to the taker's address, even though the maker is actually only holding Chai tokens (which are earning the Dai Savings Rate).
14 |
15 | ## Errors
16 |
17 | The `ChaiBridge` contract's `bridgeTransferFrom` method may revert with the following errors:
18 |
19 | | Error | Condition |
20 | | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
21 | | [StandardError("ChaiBridge/ONLY_CALLABLE_BY_ERC20_BRIDGE_PROXY")](../v3/v3-specification.md#standard-error) | `bridgeTransferFrom` was called by an address other than the `ERC20BridgeProxy` |
22 | | [StandardError("ChaiBridge/DRAW_DAI_FAILED")](../v3/v3-specification.md#standard-error) | The `ChaiBridge` was not able to withdraw the specified `amount` of Dai from the maker's Chai tokens for any reason |
23 |
24 | ## Setup for ChaiBridge orders
25 |
26 | In order for a maker to begin providing Dai liquidity through the `ChaiBridge` contract, the maker must first convert their Dai to Chai and then approve the `ChaiBridge` contract to spend their Chai tokens.
27 |
28 | ```typescript
29 | const MAX_UINT256 = new BigNumber(2).pow(256).minus(1);
30 |
31 | // Approve Chai contract to spend maker's Dai
32 | await daiToken
33 | .approve(chaiToken.address, MAX_UINT256)
34 | .awaitTransactionSuccess({ from: makerAddress });
35 |
36 | // 10 Dai
37 | const daiAmount = Web3Wrapper.toBaseUnitAmount(10, 18);
38 |
39 | // Convert maker's Dai to Chai
40 | await chaiToken
41 | .join(makerAddress, daiAmount)
42 | .awaitTransactionSuccess({ from: makerAddress });
43 |
44 | // Approve ChaiBridge contract to spend maker's Chai
45 | await chaiToken.approve(chaiBridge.address, MAX_UINT256);
46 | ```
47 |
48 | ## Creating a ChaiBridge order
49 |
50 | The [`makerAssetData`](../v3/v3-specification.md#orders) of an order must be encoded using [`ERC20Bridge` assetData](../asset-proxy/erc20-bridge-proxy.md#encoding-assetdata) which specifies the `ChaiBridge` contract:
51 |
52 | ```solidity
53 | bytes memory makerAssetData = abi.encodeWithSelector(
54 | // Id of ERC20BridgeProxy
55 | 0xdc1600f3,
56 | // Dai mainnet address. This field will be ignored by the `ChaiBridge` contract. However,
57 | // it should be specified in order to standardize the parsing of `assetData` across different bridges.
58 | 0x6B175474E89094C44Da98b954EedeAC495271d0F,
59 | // ChaiBridge mainnet address
60 | 0x77C31EbA23043B9a72d13470F3A3a311344D7438,
61 | // The `bridgeData` field will be ignored.
62 | ""
63 | );
64 | ```
65 |
66 | This results in the following `makerAssetData`: `0xdc1600f30000000000000000000000006b175474e89094c44da98b954eedeac495271d0f00000000000000000000000077c31eba23043b9a72d13470f3a3a311344d743800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000`.
67 |
68 | ## Filling a ChaiBridge order
69 |
70 | Once a maker has completed the prerequisite setup and created an order with valid `ChaiBridge` `makerAssetData`, a taker can fill the order via the [Exchange contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#exchange) with no additonal allowances. These orders are functionally equivalent to a normal Dai order, where the `makerAssetAmount` of the order specifies the amount of Dai that will be transferred. The only difference to the taker is slightly increased gas costs (currently approximately 150K extra gas).
71 |
--------------------------------------------------------------------------------
/order-types/dydx.md:
--------------------------------------------------------------------------------
1 | # Dydx Orders
2 | Dydx orders allow a maker to trade borrowed ERC20 tokens from a Dydx account they own. In the same trade, taker tokens can also be deposited into the maker's Dydx account as collateral. From the taker's perspective, filling these orders is no different from filling a normal ERC20->ERC20 order.
3 |
4 | ## Settlement
5 | The taker side is settled through the regular `ERC20Proxy`. The maker side of Dydx orders are settled through the `ERC20BridgeProxy`, which calls the `DydxBridge` contract.
6 |
7 | The simplified settlment process is:
8 | 1. The `ERC20Proxy` transfers taker tokens from the taker to the *maker*.
9 | 2. The `ERC20BridgeProxy` calls the `DydxBridge` which in turn calls [`SoloMargin`](https://etherscan.io/address/0x1e0447b19bb6ecfdae1e4ae1694b0c3659614e4e) (Dydx) to:
10 | 1. Transfer taker tokens from the *maker* into their Dydx account (deposit).
11 | 2. Borrow maker tokens from the *maker's Dydx account* to the *taker* (withdraw).
12 |
13 | 
14 |
15 | ## Required Approvals
16 | - Makers must approve the Dydx [`SoloMargin`](https://etherscan.io/address/0x1e0447b19bb6ecfdae1e4ae1694b0c3659614e4e) contract to spend any *taker* tokens they intend to deposit into their account.
17 | - Makers must add the [`DydxBridge`](https://etherscan.io/address/0x55dc8f21d20d4c6ed3c82916a438a413ca68e335) contract as an operator for their Dydx account via the [`SoloMargin.setOperators()`](https://github.com/dydxprotocol/solo/blob/d3bdc38ddfd224628b6dcd03812e57b3b62f2c82/contracts/protocol/Permission.sol#L62) function.
18 |
19 | ## Creating An Order
20 | Dydx orders must correctly specify the following order fields:
21 |
22 | - `makerAddress`: The maker address (not the bridge address).
23 | - `takerAssetData`: Standard ERC20 token asset data.
24 | - `makerAssetData`: `DydxBridge` encoded asset data (See [Maker Asset Data](#maker-asset-data)).
25 |
26 | ### Maker Asset Data
27 | Dydx orders are actually `ERC20BridgeProxy` orders, so they have nested encodings. The (`DydxBridge`) `bridgeData` is nested inside a `ERC20BridgeProxy` asset data.
28 |
29 | ```solidity
30 | order.makerAssetData = abi.encodeWithSelector(
31 | // ERC20BridgeProxy selector: bytes4(keccack256("ERC20Bridge(address,address,bytes)"))
32 | 0xdc1600f3,
33 | // Maker token address
34 | makerToken,
35 | // Address of the DydxBridge contract
36 | DYDX_BRIDGE_ADDRESS,
37 | // DydxBridge bridgeData
38 | abi.encode(
39 | // uint256[] (dydx account numbers)
40 | accountNumbers,
41 | // DydxBridgeAction[] (deposit/withdraw actions)
42 | actions
43 | ),
44 | );
45 | ```
46 |
47 | Where `DydxBridgeAction` is defined as:
48 |
49 | ```solidity
50 | struct DydxBridgeAction {
51 | DydxBridgeActionType actionType;
52 | uint256 accountIdx;
53 | uint256 marketId;
54 | uint256 conversionRateNumerator;
55 | uint256 conversionRateDenominator;
56 | }
57 | ```
58 |
59 | And `DydxBridgeActionType` is:
60 |
61 | ```solidity
62 | enum DydxBridgeActionType {
63 | Deposit,
64 | Withdraw
65 | }
66 | ```
67 |
68 | ### Actions
69 | Dydx orders can execute one or more actions during settlement. There are only two supported actions: `Deposit` and `Withdraw`. The maker asset data can contain zero or more `Deposit` actions followed by *exactly one* `Withdraw` action, for the maker token.
70 |
71 | - `Deposit` actions will transfer tokens from the *maker* into the maker's Dydx account, as collateral. If this token is also the taker token, then at least some of it will be provided by the taker, because tokens are transferred from taker to maker first.
72 | - `Withdraw` actions will borrow tokens from the maker's Dydx account and transfer them to the taker.
73 |
74 | #### Account Indices
75 | Each action has an `accountIdx` field, which is an index into the `accountNumbers` array. This will determine the maker's Dydx account the action operates on.
76 |
77 | #### Market IDs
78 | The `marketId` is the market ID for the token the action operates with. You can call [`SoloMargin.getMarketTokenAddress()`](https://github.com/dydxprotocol/solo/blob/d3bdc38ddfd224628b6dcd03812e57b3b62f2c82/contracts/protocol/Getters.sol#L158) to discover what market IDs correspond to which tokens. For convenience, here are known market IDs at the time of this writing:
79 |
80 | | Market ID | Token Name | Token Address |
81 | |-----------|------------|---------------|
82 | |`0` | WETH | `0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2` |
83 | |`1` | SAI | `0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359` |
84 | |`2` | USDC | `0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48` |
85 | |`3` | DAI | `0x6B175474E89094C44Da98b954EedeAC495271d0F` |
86 |
87 |
88 | #### Rates
89 | Action rates are specified by two values: a `conversionRateNumerator` and `conversionRateDenominator`. These two values define a fraction which is multiplied by the maker asset amount to be transferred from the maker to the taker. This product is the actual amount the action will deposit or withdraw:
90 |
91 | ```solidity
92 | uint256 actionAmount =
93 | makerAssetAmountToTransfer
94 | * action.conversionRateNumerator
95 | / action.conversionRateDenominator;
96 | ```
97 |
98 | ##### Deposit Rates
99 | This determines the rate at which tokens are deposited from the *maker* into a maker's Dydx account. It's important to remember that this rate will be scaled by the *maker asset amount* being transferred during settlement. So this rate should also convert the maker units into the units of the token being transferred.
100 |
101 | For example, if a maker wants to deposit `0.5` WETH for every `1` maker token exchanged, they could compute the rate as:
102 |
103 | ```solidity
104 | conversionRateNumerator = 0.5 * (10 ** WETH_DECIMALS);
105 | conversionRateDenominator = (10 ** MAKER_DECIMALS);
106 | ```
107 |
108 | To deposit all the tokens received from the taker, one can simply set these values to:
109 |
110 | ```solidity
111 | conversionRateNumerator = order.takerAssetAmount;
112 | conversionRateDenominator = order.makerAssetAmount;
113 | ```
114 |
115 | ##### Withdraw Rates
116 | This determines the rate at which tokens are borrowed from a maker's Dydx account. Currently, only borrowing the maker token is supported.
117 |
118 | Again, these rates will be scaled by the *maker asset amount* being transferred during settlement. So to borrow the full amount of maker tokens to transfer to the taker, you can set these values to identity:
119 |
120 | ```solidity
121 | conversionRateNumerator = 1;
122 | conversionRateDenominator = 1;
123 | // or, this is an alias for 1/1:
124 | conversionRateNumerator = 0;
125 | conversionRateDenominator = 0;
126 | ```
127 |
128 | ### Example
129 | Suppose a maker wants to accept WETH and return USDC. They want to deposit *half* the WETH they receive into their Dydx account (#1337) and keep the rest. They could build an order as follows:
130 |
131 | ```js
132 | order = {
133 | ...OTHER_ORDER_FIELDS,
134 | makerAddress: MAKER_ADDRESS,
135 | takerAssetAmount: TAKER_ASSET_AMOUNT,
136 | makerAssetAmount: MAKER_ASSET_AMOUNT,
137 | takerAssetData: abi.encodeWithSelector(
138 | // ERC20Proxy selector
139 | 0xf47261b0,
140 | // Taker token
141 | WETH_ADDRESS
142 | ),
143 | makerAssetData: abi.encodeWithSelector(
144 | // ERC20BridgeProxy selector
145 | 0xdc1600f3,
146 | // Maker token
147 | USDC_ADDRESS,
148 | // Address of the DydxBridge contract.
149 | DYDX_BRIDGE_ADDRESS,
150 | // Bridge data
151 | abi.encode(
152 | // Dydx Account numbers
153 | [ 1337 ],
154 | [
155 | // Deposit action
156 | {
157 | accountType: DydxBridgeActionType.Deposit,
158 | // Account idx 0: 1337
159 | accountIdx: 0,
160 | // Market ID 0: WETH
161 | marketId: 0,
162 | // Deposit half the WETH received
163 | conversionRateNumerator: 0.5 * TAKER_ASSET_AMOUNT,
164 | conversionRateDenominator: MAKER_ASSET_AMOUNT
165 | },
166 | // Withdraw action
167 | {
168 | accountType: DydxBridgeActionType.Withdraw,
169 | // Account idx 0: 1337
170 | accountIdx: 0,
171 | // Market ID 0: WETH
172 | marketId: 0,
173 | conversionRateNumerator: 1,
174 | conversionRateDenominator: 1
175 | }
176 | ]
177 | )
178 | )
179 | }
180 |
181 | ```
182 |
183 | ## Benchmarks
184 |
185 | As expected, Dydx orders consume more gas. This gas cost scales with every action.
186 | - 1 withdraw action: `~315k`
187 | - 1 withdraw and 1 deposit action: `~375k`
188 |
--------------------------------------------------------------------------------
/order-types/img/dydx-settlement.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/order-types/img/dydx-settlement.png
--------------------------------------------------------------------------------
/order-types/property-based.md:
--------------------------------------------------------------------------------
1 | # Property-based Orders
2 | One of the asset types supported by the 0x Exchange are non-fungible ERC721 tokens, commonly used for collectibles and game items.
3 | To create a [regular 0x order]((https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#orders)) buying an [ERC721 asset](https://github.com/0xProject/0x-protocol-specification/blob/master/asset-proxy/erc721-proxy.md), one would need to specify exactly which token to buy.
4 | Property-based orders, on the other hand, can be created to buy *any* asset that satisfies the properties specified by the maker of the order.
5 |
6 | ## Broker contract
7 | Property-based orders cannot be filled directly via the Exchange contract because the taker asset is not fully determined until the fill.
8 | Instead, we introduce the [Broker](https://github.com/0xProject/ZEIPs/issues/75) architecture to serve as the taker's entry-point. Refer to the linked ZEIP for motivation and technical details of the contract architecture.
9 |
10 | The Broker contract exposes two entry-points:
11 | ```solidity
12 | /// @dev Fills a single property-based order by the given amount using the given assets.
13 | /// Pays protocol fees using either the ETH supplied by the taker to the transaction or
14 | /// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
15 | /// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
16 | /// @param order The property-based order to fill. The format of a property-based order is the
17 | /// same as that of a normal order, except the takerAssetData. Instaed of specifying a
18 | /// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
19 | /// underlying tokenAddress is this contract's address and the desired properties are
20 | /// encoded in the extra data field. Also note that takerFees must be denominated in
21 | /// WETH (or zero).
22 | /// @param takerAssetFillAmount The amount to fill the order by.
23 | /// @param signature The maker's signature of the given order.
24 | /// @param fillFunctionSelector The selector for either `fillOrder` or `fillOrKillOrder`.
25 | /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
26 | /// @param feeRecipients Addresses that will receive ETH when orders are filled.
27 | /// @return fillResults Amounts filled and fees paid by the maker and taker.
28 | function brokerTrade(
29 | uint256[] calldata brokeredTokenIds,
30 | LibOrder.Order calldata order,
31 | uint256 takerAssetFillAmount,
32 | bytes calldata signature,
33 | bytes4 fillFunctionSelector,
34 | uint256[] calldata ethFeeAmounts,
35 | address payable[] calldata feeRecipients
36 | )
37 | external
38 | payable
39 | returns (LibFillResults.FillResults memory fillResults);
40 |
41 | /// @dev Fills multiple property-based orders by the given amounts using the given assets.
42 | /// Pays protocol fees using either the ETH supplied by the taker to the transaction or
43 | /// WETH acquired from the maker during settlement. The final WETH balance is sent to the taker.
44 | /// @param brokeredTokenIds Token IDs specified by the taker to be used to fill the orders.
45 | /// @param orders The property-based orders to fill. The format of a property-based order is the
46 | /// same as that of a normal order, except the takerAssetData. Instaed of specifying a
47 | /// specific ERC721 asset, the takerAssetData should be ERC1155 assetData where the
48 | /// underlying tokenAddress is this contract's address and the desired properties are
49 | /// encoded in the extra data field. Also note that takerFees must be denominated in
50 | /// WETH (or zero).
51 | /// @param takerAssetFillAmounts The amounts to fill the orders by.
52 | /// @param signatures The makers' signatures for the given orders.
53 | /// @param batchFillFunctionSelector The selector for either `batchFillOrders`,
54 | /// `batchFillOrKillOrders`, or `batchFillOrdersNoThrow`.
55 | /// @param ethFeeAmounts Amounts of ETH, denominated in Wei, that are paid to corresponding feeRecipients.
56 | /// @param feeRecipients Addresses that will receive ETH when orders are filled.
57 | /// @return fillResults Amounts filled and fees paid by the makers and taker.
58 | function batchBrokerTrade(
59 | uint256[] calldata brokeredTokenIds,
60 | LibOrder.Order[] calldata orders,
61 | uint256[] calldata takerAssetFillAmounts,
62 | bytes[] calldata signatures,
63 | bytes4 batchFillFunctionSelector,
64 | uint256[] calldata ethFeeAmounts,
65 | address payable[] calldata feeRecipients
66 | )
67 | external
68 | payable
69 | returns (LibFillResults.FillResults[] memory fillResults);
70 | ```
71 |
72 | ## Creating a property-based order
73 | To create a property-based 0x order, we need:
74 | - The address of the `Broker` contract, which can be found in the [`@0x/contract-addresses` package](https://www.npmjs.com/package/@0x/contract-addresses).
75 | - The properties we'd like the taker asset to satisfy.
76 | - A property validator contract, which the Broker will `staticcall` to verify that the taker-supplied asset satisfies the specified properties. The validator must implement the [`IPropertyValidator` interface](#writing-a-property-validator-contract).
77 | - Any additional properties required by the validator.
78 |
79 | All these are then encoded into [ERC1155 asset data](https://github.com/0xProject/0x-protocol-specification/blob/master/asset-proxy/erc1155-proxy.md) interpretable by the Broker. Consider the following TypeScript example, which performs the encoding for [Gods Unchained cards](https://github.com/immutable/platform-contracts/blob/master/contracts/gods-unchained/contracts/Cards.sol) and the [`GodsUnchainedValidator` contract](https://github.com/0xProject/0x-monorepo/blob/development/contracts/broker/contracts/src/validators/GodsUnchainedValidator.sol).
80 | ```typescript
81 | /**
82 | * Encodes the given proto and quality into ERC1155 asset data to be used as the takerAssetData
83 | * of a property-based GodsUnchained order. Must also provide the addresses of the Broker,
84 | * GodsUnchained, and GodsUnchainedValidator contracts. The optional bundleSize parameter specifies
85 | * how many cards are expected for each "unit" of the takerAssetAmount. For example, If the
86 | * takerAssetAmount is 3 and the bundleSize is 2, the taker must provide 2, 4, or 6 cards
87 | * with the given proto and quality to fill the order. If an odd number is provided, the fill fails.
88 | */
89 | encodeBrokerAssetData(
90 | brokerAddress: string,
91 | godsUnchainedAddress: string,
92 | validatorAddress: string,
93 | proto: BigNumber,
94 | quality: BigNumber,
95 | bundleSize: number = 1,
96 | ): string {
97 | const dataEncoder = AbiEncoder.create([
98 | { name: 'godsUnchainedAddress', type: 'address' },
99 | { name: 'validatorAddress', type: 'address' },
100 | { name: 'propertyData', type: 'bytes' },
101 | ]);
102 | const propertyData = AbiEncoder.create([
103 | { name: 'proto', type: 'uint16' },
104 | { name: 'quality', type: 'uint8' },
105 | ]).encode({ proto, quality });
106 | const data = dataEncoder.encode({ godsUnchainedAddress, validatorAddress, propertyData });
107 | return assetDataUtils.encodeERC1155AssetData(brokerAddress, [], [new BigNumber(bundleSize)], data);
108 | }
109 | ```
110 | [\[Source\]](https://github.com/0xProject/0x-monorepo/blob/development/contracts/broker/src/gods_unchained_utils.ts)
111 |
112 | The above asset data can also be nested inside [MultiAsset](https://github.com/0xProject/0x-protocol-specification/blob/master/asset-proxy/multi-asset-proxy.md) asset data, so that one can create an order buying a "bundle" of property-satisfying assets.
113 |
114 | ## Filling a property-based order
115 | Property-based orders must be filled via the [Broker](https://github.com/0xProject/ZEIPs/issues/75).
116 | The taker must set allowances enabling the Broker to trade the brokered assets on their behalf.
117 |
118 | Orders filled through the Broker must satisfy the following properties:
119 | - The `takerAssetData` is a **brokered asset**, i.e. ERC1155 asset data in the format expected by the Broker; or it is WETH asset data.
120 | - The taker fee is either zero or denominated in WETH.
121 | In order to pay [protocol fees](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#protocol-fees) and fill orders with the taker asset or fee denominated in WETH, the taker must send sufficient ETH with their `brokerTrade`/`batchBrokerTrade` transaction. Any remaining ETH at the end of the transaction is refunded.
122 |
123 | If the order[s] provided to `brokerTrade` or `batchBrokerTrade` involve _multiple_ brokered assets, the taker must supply that many tokens in the `brokeredTokenIds` parameter.
124 | Those tokens will be used sequentially (in the order provided) every time a brokered asset is required.
125 |
126 | ### Errors
127 | In addition to [errors](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#exchange-errors) that can be thrown by the Exchange, the Broker itself can throw the following errors. The Broker will also rethrow any errors thrown by a property validator contract.
128 |
129 | | Error | Condition |
130 | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
131 | | OnlyERC1155ProxyError | An address other than the ERC1155AssetProxy calls `safeBatchTransferFrom` |
132 | | InvalidFromAddressError | An order specified brokered assets in its `makerAssetData` or `makerFeeAssetData` |
133 | | AmountsLengthMustEqualOneError | Only one asset amount should be specified in the ERC1155-encoded broker asset data |
134 | | TooFewBrokerAssetsProvidedError | Not enough token IDs supplied by the broker to perform the fill |
135 |
136 | These will normally be caught in the Exchange and rethrown as an [`AssetProxyTransferError`](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#assetproxytransfererror), where the nested `errorData` encodes the underlying Broker error.
137 |
138 | ## Writing a property validator contract
139 | You can implement and deploy your own property validator contract.
140 | To be compatible with the Broker contract, it should implement the following interface:
141 | ```solidity
142 | interface IPropertyValidator {
143 |
144 | /// @dev Checks that the given asset data satisfies the properties encoded in `propertyData`.
145 | /// Should revert if the asset does not satisfy the specified properties.
146 | /// @param tokenId The ERC721 tokenId of the asset to check.
147 | /// @param propertyData Encoded properties or auxiliary data needed to perform the check.
148 | function checkBrokerAsset(
149 | uint256 tokenId,
150 | bytes calldata propertyData
151 | )
152 | external
153 | view;
154 | }
155 | ```
156 | For an example of TypeScript tooling for a property validator, refer to [`gods_unchained_utils.ts`](https://github.com/0xProject/0x-monorepo/blob/development/contracts/broker/src/gods_unchained_utils.ts).
157 |
--------------------------------------------------------------------------------
/order-types/stop-limit.md:
--------------------------------------------------------------------------------
1 | # Stop-limit Orders
2 | [Stop-limit orders](https://www.investopedia.com/terms/s/stop-limitorder.asp) are orders that can only be filled if the market price of the token pair being traded (as reported by some oracle) is within a certain range.
3 | For background on the 0x order format, refer to the [v3 specification](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#orders).
4 |
5 | ## Chainlink stop-limit contract
6 | Stop-limit 0x orders are enabled by the `ChainlinkStopLimit.sol` contract, used in conjunction with the [StaticCall](https://github.com/0xProject/0x-protocol-specification/blob/master/asset-proxy/static-call-proxy.md) asset proxy.
7 | This contract exposes a single function, `checkStopLimit`, which checks the value returned by a given Chainlink reference contract and reverts if it is not within the specified range.
8 |
9 | ```solidity
10 | contract ChainlinkStopLimit {
11 |
12 | /// @dev Checks that the price returned by the encoded Chainlink reference contract is
13 | /// within the encoded price range.
14 | /// @param stopLimitData Encodes the address of the Chainlink reference contract and the
15 | /// valid price range.
16 | function checkStopLimit(bytes calldata stopLimitData)
17 | external
18 | view;
19 | }
20 | ```
21 |
22 | ## Creating a stop-limit order
23 | To create a stop-limit 0x order, we need:
24 | - The address of the `ChainlinkStopLimit` contract, which can be found in the [`@0x/contract-addresses` package](https://www.npmjs.com/package/@0x/contract-addresses).
25 | - The address of the Chainlink contract for the pair whose price we'd like to create the stop-limit around. A list of those contracts can be found [here](https://feeds.chain.link/).
26 | - The price range in which we'd like the order to be executed. Note that the value returned by Chainlink reference contracts is the price multiplied by 100000000 ([source](https://docs.chain.link/docs/using-chainlink-reference-contracts#section-live-reference-data-contracts-ethereum-mainnet)).
27 |
28 | The contract addresses and parameters must be encoded as StaticCall asset data and included in one of the order's asset data fields.
29 | For example, consider an order selling ZRX for ETH.
30 |
31 | ### Option 1: makerFeeAssetData
32 |
33 | If the `makerFeeAssetData` would otherwise be unused, we can simply set it to be the stop-limit StaticCall asset data.
34 | During order settlement, the Exchange will dispatch the StaticCall proxy to call the stop-limit contract.
35 |
36 | In TypeScript, this would look something like the following:
37 | ```typescript
38 | import { encodeStopLimitStaticCallData } from '@0x/contracts-integrations';
39 | import { assetDataUtils } from '@0x/order-utils';
40 |
41 | const makerFeeAssetData = encodeStopLimitStaticCallData(
42 | chainlinkStopLimit.address,
43 | {
44 | oracle: chainLinkZrxEthAggregator.address,
45 | minPrice,
46 | maxPrice,
47 | }
48 | );
49 |
50 | ```
51 |
52 | In order to avoid unexpected behavior caused by rounding and overflow, we recommend setting the `makerFee` amount to equal the `makerAssetAmount` (in this case, the amount of ZRX being sold).
53 | Note that despite the non-zero `makerFee`, the maker will not be transferring any assets for the "transfer", we are simply repurposing the field to perform the stop-limit check.
54 | As such, the `feeRecipientAddress` can be set to an arbitrary address or the null address.
55 |
56 | ### Option 2: Multi-asset makerAssetData
57 |
58 | Option 1 is the preferred way to create stop-limit orders for easy discoverability via [0x Mesh](https://github.com/0xProject/0x-mesh).
59 | However, if the maker would in fact like to use the maker fee field to transfer assets to a fee recipient, we can instead add the stop-limit check to the `makerAssetData`.
60 |
61 | Normally, the `makerAssetData` of a ZRX-ETH sell order would be the ERC20 asset data encoding of the ZRX token address.
62 | But to turn this into a stop-limit sell order, we can instead use [MultiAsset](https://github.com/0xProject/0x-protocol-specification/blob/master/asset-proxy/multi-asset-proxy.md) asset data, where the nested assets are ZRX and the stop-limit check (encoded as StaticCall asset data).
63 | During order settlement, the MultiAsset proxy will dispatch the ERC20 proxy to perform the ZRX transfer and then dispatch the StaticCall proxy to call the stop-limit contract.
64 |
65 | In TypeScript, this would look something like the following:
66 | ```typescript
67 | import { encodeStopLimitStaticCallData } from '@0x/contracts-integrations';
68 | import { assetDataUtils } from '@0x/order-utils';
69 |
70 | const makerAssetData = assetDataUtils.encodeMultiAssetData(
71 | [new BigNumber(100), new BigNumber(1)],
72 | [
73 | assetDataUtils.encodeERC20AssetData(zrxToken.address),
74 | encodeStopLimitStaticCallData(
75 | chainlinkStopLimit.address,
76 | {
77 | oracle: chainLinkZrxEthAggregator.address,
78 | minPrice,
79 | maxPrice,
80 | }
81 | ),
82 | ],
83 | );
84 | ```
85 |
86 | ## Filling a stop-limit order
87 | Stop-limit orders can be filled directly via the [Exchange](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#exchange) and do not require additional allowances. One can check whether an order is fillable at the current price by calling the `ChainlinkStopLimit` contract's `checkStopLimit` function.
88 |
89 | ### Errors
90 | If the stop-limit price check fails, the transaction will revert and throw an [`AssetProxyTransferError`](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#assetproxytransfererror), where the nested `errorData` encodes the error thrown by the stop-limit contract: `"ChainlinkStopLimit/OUT_OF_PRICE_RANGE"`.
91 |
92 | ## Writing a stop-limit validator contract
93 | Currently, the only on-chain oracles supported by 0x stop-limit orders are Chainlink reference contracts.
94 | That said, you can implement and deploy a validator contract with your oracle of choice.
95 | Refer to the [Chainlink stop-limit contract](https://github.com/0xProject/0x-monorepo/blob/development/contracts/integrations/contracts/src/ChainlinkStopLimit.sol) and [TypeScript tooling](https://github.com/0xProject/0x-monorepo/blob/development/contracts/integrations/src/chainlink_utils.ts) for how to do so.
96 |
--------------------------------------------------------------------------------
/staking/Adjusted_Staking_Percentage_Formula.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/Adjusted_Staking_Percentage_Formula.pdf
--------------------------------------------------------------------------------
/staking/assets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/.DS_Store
--------------------------------------------------------------------------------
/staking/assets/Batch-Calls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Batch-Calls.png
--------------------------------------------------------------------------------
/staking/assets/Batch-Calls.xml:
--------------------------------------------------------------------------------
1 |