├── 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 | ![deployment dependency graph](./img/ep-deployment-deps.png) 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 | ![exchange-proxy-call-forwarding](https://raw.githubusercontent.com/0xProject/0x-protocol-specification/master/exchange-proxy/img/exchange-proxy-call-forwarding.png) 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 | ![exchange-proxy-bootstrap](https://raw.githubusercontent.com/0xProject/0x-protocol-specification/master/exchange-proxy/img/exchange-proxy-bootstrap.png) 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 | ![zero_ex_migrate](https://raw.githubusercontent.com/0xProject/0x-protocol-specification/master/exchange-proxy/img/zero_ex_migrate.png) 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 | ![meta-transactions](../img/exchange-proxy-mtxs.png) 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 | ![token-spender](../img/token-spender.png) 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 | ![transform-pipeline](../img/transform-pipeline.png) 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 | ![transform-example](../img/transform-example.png) 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 | ![settlement-diagram](https://raw.githubusercontent.com/0xProject/0x-protocol-specification/master/order-types/img/dydx-settlement.png) 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 | 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 -------------------------------------------------------------------------------- /staking/assets/CobbDouglas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/CobbDouglas.png -------------------------------------------------------------------------------- /staking/assets/Epochs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Epochs.png -------------------------------------------------------------------------------- /staking/assets/Epochs.xml: -------------------------------------------------------------------------------- 1 | 5ZrbjtowEIafhsutkjgnLhf2pEpVV0Jqr914IFZDTB2zQJ++DnEgybDbbhXSqGYviH/b49jfeD1jMSHz9f5R0k36STDIJp7D9hNyN/E8141D/VUqh0qJIlIJK8lZJTlnYcF/gulZq1vOoDBaJSkhMsU3bTEReQ6JamlUSrFrN1uKjLWEDV0BEhYJzbD6lTOV1vNynHPFE/BVqro1a1q3NkKRUiZ2DYncT8hcCqGqp/V+Dlm5eu2FeXil9vRmEnL1Jx3UIn76cTN9nn6GL8HHeH/YLsmNX1l5odnWzNi8rDrUSwA5uy1XUpeSjBYFTyZklqp1pgVXPxaKSlW3ENpSrT3wrGzj6LK2YUplj6V+nItMyOMAhFGIl8mxlxTfoVETJjF8W+oaPFczfWAtfmbmjyDWoORBN9idqQUGRNrgVWsSMqr4S5s6Nc6zOpk7jfAsuH4Tz6kd3Td2jJt7sfMhaBspxFYmYPo1MXVMRfFvTenFXYFCpvRDY+pn6egH7/CJ4F0+kYtcizNGixSYAdzwjrGTO21Ps9zE+2tyXVNe7RQDcQtt4kac3rh1TQ3NLbKKW9wft+5/yoG5xTZx88PeuHVNDc1tahO3wO+NW9fU0NzqnMAOcGF/gUnX1ODgXJvARf1FJl1Tg4PzEDgXkVOwV92MsJnHGZzNpM9INOOrXBcTTRK0PnsBqbhOvm9NxZozVg4z26VcwWJDk3LMnaQbrUmxzdnRQcoMcyly1cgcnePnLW8px4L9m/7yWlw/PQFteFR8waM853XnabF6NxiCwOAt9d+DQYH7CMDgCxViIZhuZD4CMPhWw7cPDAq9RwAGX1sE9oFBsfUIwOB7idA+MCh2/vdg6qO+AeZ+I5L0CnBkNZ2RskG37hfZRIOywee/cwUuV9g00W3511PE3AUTXALjDwoGHzOuhWRIHQedyLiIy6Xc+Hpc8C2qjTuGTKMPIyND8HUbsZCMH42NCz7+fQu5BMH4dgw+/AMLyYRkbFxwJnNHD0WJhed6iTznEXIoeHEFVv1H0H2iQhH0BVQ9xc+6eP6tU3VBff7JGLn/BQ== -------------------------------------------------------------------------------- /staking/assets/ProtocolFee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/ProtocolFee.png -------------------------------------------------------------------------------- /staking/assets/ProtocolFee.xml: -------------------------------------------------------------------------------- 1 | 7Vpdd7I4EP41XtrDh1C9rB9tL953T9+257S+N3siRMiKxA2xfvz6TSBoSECpi63tbr0omYxjmOdhJjOkZQ/m6zsCFuFP7MOoZRn+umUPW5Zlml2X/eOSTSZxe1YmCAjyM5GxFzyhLRTfzKVL5MNEyDIRxTiiaFEUejiOoUcLMkAIXhXVpjjyC4IFCKAmePJApEtfkE/D/L4MYz9xD1EQUnVmDnJtIUhC4OOVJLJHLXtAMKbZ1Xw9gBH3XtExtxWzu5URGNM6X1gv6Lj7Or9dzTHdxn+/jZ1t3DYFPm8gWopbFqulm9wHMPZvuCvZyItAkiCvZfdDOo+YwGSXUxzTAY4wSdVtZ8A/TA595kphBhMa4gDHIBrtpX39FsRd8a9KAnFDdxDPISUbprDag+EI/4YSDLmMwAhQ9FYEEwhOBDtzu194wIitxDIEf7vCjCCv7RpFCwleEg+KL8muV+xYRtFQx1IMUUACSDVDzO1gI6ktuEJSvV6zYsFV61L1OyqFat6H0GcX2YrzkQTZXpTS8h0U/Z+hn8BQ27hMhubrOivjHI1xmPiQaLQjeBn7kFsyGFVWIaLwaQE8Prti6VDhIIoiiYNTh3+YPKEEz6A00x/yzwHWVnLyDRIK1wdZmc/ailvzgCSx1i5hbUdhhUxQCZP3u7zz/V1uKS63P87l15P7v9zteHRzszTe7pbjED3O2qbm8ka97QPYnXpl3na9LpxMm/FqR/VqCZG7JV7tnsurHevEdOWDJExdbRbdmrDIS3P1GMcwl90ivrIUGeY/snmVB2M+uHLy4XAtTw43u5EvrPBfgmtEX6VryQYb7U3wQW6hEsIs8RynX5ZXDjnUrplwBSPaxpVhOQVStM2uRooz5mQnX7L4eat3YlJ2DKtIbjUWVCTlvaFcEU+nCSzonJArD8LztcOI07m0MKInxCcKZigOmHDAshQBHgf4geD1RnM4u2uqRhHZeyKMyK4WIhChIOZxifmSpV+7z32IWD18IybmyPfTnXEZjEWgmwjvKi6OjkunBBe1vmoOF70aKcPlu+LhuBeGh3NqupWwODV7VvrzaE67rBrSbqqItM2iIbdmEdlULjJ1MozWXghi5maVFEeyEEgWWTtxitb8AapfS7jpn15LZH/NPIa2Wq1/YLpK7v4M6OvLj3D7+Hscvv7x+PDrV0kt8RPMSsq3JAQLfrmcRzcexXJA+wEmMHrACaII88A2wZTiOVOI+EQfeLMgDWcyCOlfSVCkWIETL2mEYgZT3iVuKj8pOHR0HMpqOvUBaw4HvXVxKBxipqd3ykQXnj1NVRXCvioYt6SioLRCyDNYBKf0CzTU3F6v/NF6bzBUDWmgNxcMy6mg71Ses2fSYJiAmAU+y2inE8YtZAM34vhMmIIb8KtD+03JRMvqS0aObnwITNAWTFIFTg/RMGTaTr/lDHXCVG54asQFlc06+Q4/RWoc2L1nEjfQkt/klMUH40opBRvhqHWl1rdFC41Ue6VtOr3Y+3ZtOvO0kH62zuj18Yj+3bKqpWHgahhYH4lBycYyjaXJfw6KjtH7ZChq7HC+Owi7ffyngVAjKJ36TrbSXxeyQTSvz/XK9XwbxHIQu+cD8fiL9U9oulgXxSO1dtTOhNTlUa9o57rm2ZLGaNR7F41EX7MOh3al50HcLxTO08PCsW7cmfHM6dM8njViwpdGXO12nhvx9569sdTMVTxLc1y/eFbnX5+9KX/B0sDWot4rc7mxdelcc9VjDqdGF1d9gfPR0UWvpF5Gz/dM8sxq/ViD+ku36TVnn69Nz4b7k8QZWPsD2fboHw== -------------------------------------------------------------------------------- /staking/assets/Read-Only Proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Read-Only Proxy.png -------------------------------------------------------------------------------- /staking/assets/Read-Only Proxy.xml: -------------------------------------------------------------------------------- 1 | 7VlLd+MmFP41OqddaI7edpa2k8ls2uZMFp2s5mAJyTSy8CCc2Pn1BYQQ6GE7fiSZabOJ+O4VhvvdF8jyZ8vNLQGrxR84gbnlOcnG8q8tz3PdccT+cWRbIaMrCWQEJVKpAe7RC5SgI9E1SmBpKFKMc4pWJhjjooAxNTBACH421VKcm7+6AhnsAPcxyLvo3yihi3pfjtMIvkCULWhbsgS1tgTKBUjwswb5N5Y/IxjT6mm5mcGcW682TPXe5wGpWhmBBT3kBRuPnNEmvX2Z/bjz/yxW38ehbV9VszyBfC13LBdLt7UJCF4XCeSTOJY/fV4gCu9XIObSZ0Y6wxZ0mbORyx5TlOcznGMi3vUTAMdpzPCSEvwINUkUj+E8ZRK5AEgo3AzuzFX2Yp4G8RJSsmUq8gW/NrF0sqjm4LmhbCyhhU5WJEEgvSRTUzd2ZA/SlP1mHc2//BO9PNxMJmvn6Xb9sEBfH233g5i1Y8MeSw+aVZnnw5g18Dp2ZQkHZoBCy4ty9vvTOWFPGX9iQZx30Q4PsEgmPFGwUZyDskTcrgkoF4Ib1+ShpIDQWr3ABayxz4jvRFDJDE623/TBAx988sJ6fL3RpddbNUrkNO4uAmFipKxB+mznk8N+U2fQdscdBsMeBmuMwBxQ9GTmwj5W5SLuMGKLVSvwgpYDBS3HKPGaxFC+paeu1kSdAG9PxCjIIN0xUa2I07SEho5wRGW9E3zT/xViPvx4MR907HpPwSMqMgbOMIsgEHOG7wjebDsWZ9um7RDWzSdjWLe1hECOsoInBWZMyPApNyJiSWUiBUuUJPxnenk0mT5HMh63iPG6xAQ9xHgX4yU6iJdflY+wzYf7znyE3dp4WK3TuBgqXfsql1EWMVtBqyzurGdV7t7fTO2te29T1Nq8j4+saa45z5VzWEk7olz1Nv5uX7mKwJIHSzEvV00DZWAHBDSBJXoBc6HAnWPFtyA2FU6t8PpVoZyDOcynIH7MRABrRS8Vf7vOD/JIKFdiqcjTXXBHJO3qq3yzrXLP4lhu0DfpWRuXXk+4bE8ts/dpDXWijzaIfqvzFXvWm2s2bDIUH6jWmuWOe7k+TOgCZ7gA+U2DTntzzGsOoe/fbgfOmdrtoJ3j2s3d27bbvV7b7QpP8MJ+nzrVpQzfbuptf2E8xvPeya2OrXjHuhUzIdhqarKgDC94oEQ3HljNeFZ/DDv++BWCxP6ryLc/xeHktAs4f38zHF2oGe5lo3s2OSU7XLB4vE0I+61T/ajdbB4cwvtywZlCuL3gNwnh0aucZv9l4SsrxMC5qe9W8D/gae2LqFE7V1zI00beG3ia2/d9oOqqyxUoDJ+Lfqz5F6Jpigtqp2CJWEHxJxZfpjyasdOQH3DXg/kT5FWgIxHas753AEEgH9YvQVHaJSQo5ZAQiHWU4kMdX4Wz2ijRXB3U7LiqV1yDZPPfWE1Wc3rBVfMcOr83W2zOE2H7k2LIrMtR8blMjWprh8LeDLnmz3zykFs0ZCzu03WVbh1ER03jNdNUpCpJipm/2wTyAivkzemKDaoDVf1qxbp6VfQHbFQlFQ67Yqh3CRwV9YsLmk7BgKtugUOyX+Cg2TFwoewZuFDrGrhE9A0crzsHDjoC0fZddRCHWFaJlFGbRBXy0FSagcaCs23wkY5Xqa2ZXZfJHNcQpclYjlB4pi2gzbkYKuJ10HRHqWf6bcrZqGKb5YoqvAeOzT/3XcpwTR3sG9mR2Y/Maw/7xMuUy5883e6FiRnj7euRJuBluP9PuxP5kUm79360WzKONfUmgv2bfwE= -------------------------------------------------------------------------------- /staking/assets/Rewards.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Rewards.png -------------------------------------------------------------------------------- /staking/assets/Rewards.xml: -------------------------------------------------------------------------------- 1 | 7Vltb6M4EP410X26E2AM6cc22+2dtF1V7Um3+9EFJ1gxmDXOJtlff2MwLwayTaOkqaJGquoZ48E8zzDPxJmgWbq5kyRP7kVM+cRz4s0EfZp4nutOA/inPdvKE/ph5VhIFlcup3U8sV/UrKy9KxbTwvgqlxKCK5bbzkhkGY2U5SNSirV92Vzw2HLkZEEHjqeI8KH3PxarpH4ux2kn/qZskaj+TErqq42jSEgs1h0Xup2gmRRCVaN0M6Nco2cD83nHbLMzSTO1zwL6FUVX+I7PHje/8uUc3f54/Pqnb/j5SfjKPLLZrdrWGEAYgBuMm3XCFH3KSaRn1sA4+BKVcrBcGJIirziYsw2Fu97MGeczwYUsA6EY02nsg79QUixpZ2bqPaMggBmzGyoV3ex8TrdBD/KOipQquYVLzAIcoGqJSTnfw5W9bgkMDSdJh7raR0zKLJrILagwMLi+AmOELw5jFDg2xnXWdzB2vRGQG+fxUXYuDuWpDXIDegdkzxkBuXEeHWR/D4yz+FoXXrAykWmsY1IkGsQS2Q7Ic5GpDnLVB/yAjtx+A6fz11Vtfi/NaW1+0gg5jbXtWg9UMnhaKmvnhqkqWjjFxq7COY3dxtPGtmP0o1XPS+OBbLxIaoc1PEJa7ZOUE8V+2uHHiDR3eBAMbtzkjIdCO2nC0A5RiJWMqFnVlYt+oN4rjvtvriJyQdUgEJBPtp3Lcn1BsXvDbi/L/cCSMRhUEdukbTA9PI/3Ub02jyNOioJFdvZaWb0rld99tvjj4L86WwaC0A+0I1uORejVgNB/hSJ62SNdExlDAjr/ZHo/8HcHgOnxbS6iZEA81GtlU20XeVPVuopgXISzRabzBcjVBeNGV38GzeS1mUhZHPNd8iPFKovLjHJOIh84HJEPd1Q+TqQerjdgqWKn7MylYeeeyCVV5WBJ5R+auRxa/svlCbnYJgqdnSj0Fjr/3oujH/ZqGjqwOA4CTU8jpb53Bil1X9cTXq6W+i9J4L7pgs+spe70g9Hx9+ngAtALhPbspY/G6LA9+lx+sdX7wI362sp7AsHldK7OLre+Z58PjX6tflO5rQO/QM9D2QU59zR9prI4AT3vpB/CdQGq+6Gxw6XgTQkanuCNvyJFQnI9XKX8OlKii+UX8kz5gyiYYkJj+iyUEilcwPXEDYmWixLJTqGcl58RPpTonVeJleIsA6rrU3DHIuZ3RXhA/jFeMcfuaJuC3GEQjRDYF7rjHQ6Gr5K0C21og76e+QfqWYB6gfpn50dqaPHVGRpa9NH+7EiXQxva8KVAJ25/8PDc4aNyj1fuAVVjZxEjKefjE1VuPGxdr1OAR6dXLkUupGalPOlToqQUhPlye6MA9yvv8MfNY/VGYLa/TlfvYvsjP7r9Hw== -------------------------------------------------------------------------------- /staking/assets/StakeManagementExample2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/StakeManagementExample2.png -------------------------------------------------------------------------------- /staking/assets/StakeManagementExample2.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Staking Architecture - Basic.png -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Basic.xml: -------------------------------------------------------------------------------- 1 | 7VrLcqM4FP0aL5PibXqZOK+qqVR1xT2T9OwUEI+KjFxCbsN8/UhGYJCwQzwYmKSdRdDVRUjnHF2JK2bmYpXdE7COHrEP0czQ/Gxm3swMQ9ddh/3jlrywzC29MIQk9oXT3rCM/4HCqAnrJvZh2nCkGCMar5tGDycJ9GjDBgjB26ZbgFHzqWsQQsWw9ABSrc+xT6NyXJq2r3iAcRhRuWYFSm9hSCPg423NZN7OzAXBmBZXq2wBEUevBKa47+5AbdUzAhPa5Ya3P/789vAUXmR3gbVB9yQPH4ML0covgDZixKKzNC8hIHiT+JA3os3M620UU7hcA4/XbhnpzBbRFWIlnV0GMUILjDDZ3Wv6ALqBx+wpJfgN1mocz4WvAatRh1H2CRIKs5pJDOse4hWkJGcuotZwBcRCZEbJwXZPWekS1ciyLGEEQiVh1fQeR3YhoPwArIYC623mRSBhYusT3sDmf63w7n78DpzQmr349QO7bpwGu3su1E0F9SUFb5CkM97Tu5nhINaF61fCrkJ+dQMRDAHFhz0exf2y/RPTaBoj02gpND4DhCDtQsM7wIN0XawSQZxxsqbNhO2MzIStMHGP2XASkDB8DW2ZpxSuvhor1aoxFiuOwopfxDGoUsF2MqgDQTDxr/huiZU8BNI05mu2D9JoF9H0Jl0pBYSW7glOYGm7i/lAdgGQwU3yl3rhJy9c2mXxJqtX3uRVyRet8CfBLKYvtetaG6y0b4IXyhYOMp7iDfHg+3shNpIQ0iN+88IP+o2t40H9XGiXmmE3JHShu4qE7BYJlTYCEaDxr+aetE1XohPfccwGX/XA0sxmhLclaRbQiLvqW0ipIdMxmlNBkxoqsDvSUOmIgyCFDZ/dVKjQO312zJXZ0etaPcyOtooxk9nRuq17qzgJmXHBgi8BHuf3O8FZrgDORk3lGFJHTwSROtTCBFAcJjwqMSwhs19zDGMW1a5ExSr2ff6YVhqbRPfAi6lJvJiGwovVwotxLlq+daLls9JhOROjo0xbHIs+rWttjYtTl86DeL67oOkdF7RhVitDin2WzFbX1UqfNxtynG6rVV8rkd4huVITg5hibUrQh1KC9VsJ9YYYNSCvua25Q9q9w47WSMmxi6LFfmWmJpumLjP7t8z6lJnlDiEzNbv2QZkN+jJnTEti8r5RVkZXib2r1TNJrCyfV2Jq5m/KEjMnLTFTzj9NXWKmM4TE1JSmIrGQvaesD8pCeTupDh/Ba9mCNmsVgrjL7nBuUoIxTEZRV1OKxyZe62tLt6xgPbunQnyUsrHnWZXzOm1aybNq4HcRNS3Wmr9hRtrvGeU4R8CjH53oasLs76cXZvgLbJCakvmPJyTDgGzL6ZaxD3x1Nf01StyaVgLFUfdul/ZpQcuRv6xQmzpz4CrXyvqnFE8LbpUPta7S3YnCgRT0//oIUqFh7IlnqKmtIrr9YOAknwt8V353Gx38jyV8vkjUm8uL0+lRT2F8+KinJlueb388fM4JJm8rRv8AyfhYHuKLTDD5tdU8NYel8N3buQwr7r+nLdz3nyWbt/8C -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Catastrophic Failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Staking Architecture - Catastrophic Failure.png -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Catastrophic Failure.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Read-Only.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Staking Architecture - Read-Only.png -------------------------------------------------------------------------------- /staking/assets/Staking Architecture - Read-Only.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /staking/assets/Staking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/Staking.png -------------------------------------------------------------------------------- /staking/assets/Staking.xml: -------------------------------------------------------------------------------- 1 | 7Vpbc9o4FP41PML4gh3yCCRNH7qdlOy00JeOsGVbG2ExtgiQX1/JlkCW7OACSWhmyUwiHUnH8nfuh3Tc8WJzl4Fl8g8JIe44VrjpuDcdx7Ft32Z/OGVbUq76ghBnKBSb9oQH9AwF0RLUFQphXtlICcEULavEgKQpDGiFBrKMrKvbIoKrT12CGBqEhwBgk/oDhTQpqQPP2tM/QxQn8sm2JVYWQG4WhDwBIVkrJPe2444zQmg5WmzGEHPwJC7luU8Nq7uLZTClbQ5czT//5z/PbofDlfV0t5olaPLYFVyeAF6JFxaXpVuJQEZWaQg5E6vjjtYJovBhCQK+umYyZ7SELjCb2WwYIYzHBJOsOOuGAA6igNFzmpFHqKz4wQDOI7Zivoa8E8wo3Cgk8Vp3kCwgzbZsi1yVEAsd8+V8rUhMkBJFWJIGhI7EO857GNlAIPkHqPadw7DCNBxy/WSzAIM8RxymEORJAbVdhTWnIKNye0pSKGmfEL9ZIRmGX7adqpMZn/Q8Ob3ZqIs3290sFFz4k+AG0akyVniw2Z4Fn0gOjSLMySoL4GH1Y28SQ/oSoG65EYYVa23UiK7VsxyvohRde2AohVejFJKWQQwoeqq6gTpNEZe4J4i9/e4GjryyeLx3rSlbiY04pZqtzshyqsrd1xiV4L3ASG4kUZTDyp5CuXfonaDv7kdwI07/0txI34D1gYJHlMaMOCbMikHABXyfkc3WAJy9NdW9iIqecCMq1IIEMIpT7pcYlpDRRxxDxGLiUCwsUBjyx9SKsSroc7h3XS6eKZd+jVycV5OL30ouH1Uejn9h8vCODbeKLI6Nno14HoxpdsuQ9jbxaqDJ1D8yXNlVPtdWu2h1rkh01SKhjZlFLNvbwa62AHPJwaqXlDjltkhHXf9NA8nV6QbSLgVVU0kT4hdF9t6GsK16rj9VfC1ItczSzqX3ZgL2czJlhO9ghc1QdCD7AvmyLKUjtOGB433SMVcPM++djl1fhhFdiLlItdeTM5/HzOMsyIxBOqtXtiIZvBQR307GXO98zHO3ecZGMR8N86KUaki9T7SvyOM/tfZVfPgJlmUq9PJzHrszxPDedmeb6V3p3P5l4KQfC3zfujDw+/97vRpX5emx6XivZ0j8Nb1efvcrptMfX5Lnyc9ZMv06uf/2raYHzKtZVmjqcs4TsOTD1QIPA0rUSvQLmEN8T3JEEeEV6ZxQShZsA+YLIxA8xkUdqhpa8ampZinRTJasKEYpM0XZ4j9TIetqyLs1eXqNDuml0TG2Vi+IFsWLYmuE7dPaZswxia9QmNNsau3u27mzjtLNrW3tytYDhhE9qdPbtoH7RlmLZnTeseXuQUavbbtmbCyMl5GY7EAa8BGJ2C9p0+x+o2Kj9dVQLrNFlcEcPYvCl+vDkr9I8WreqOPdmBrS2Jpq4Qh09TW17WWzOblyt3rVpr1zFmVzKjy72vmzdOXrcfH+WndyIW7Cd6971W+RfL2J2TrMG6ze3FWYXWuRQDe6iu7OUej1T8PJStPhA3oaaVGX6ml6+neep/saNt3/j0K5ff+fHu7tbw== -------------------------------------------------------------------------------- /staking/assets/reward_tracking/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/.DS_Store -------------------------------------------------------------------------------- /staking/assets/reward_tracking/Reward-Final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/Reward-Final.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/RewardAfterManyEpochs-InPractice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/RewardAfterManyEpochs-InPractice.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/RewardAfterManyEpochs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/RewardAfterManyEpochs.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/RewardFromWhatWeStore-Example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/RewardFromWhatWeStore-Example.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/RewardFromWhatWeStore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/RewardFromWhatWeStore.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/RewardSingleEpoch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/RewardSingleEpoch.png -------------------------------------------------------------------------------- /staking/assets/reward_tracking/WhatWeStore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/staking/assets/reward_tracking/WhatWeStore.png -------------------------------------------------------------------------------- /v1/v1-whitepaper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/v1/v1-whitepaper.pdf -------------------------------------------------------------------------------- /v2/forwarder-specification.md: -------------------------------------------------------------------------------- 1 | 1. [Architecture](#architecture) 2 | 1. [Contracts](#contracts) 3 | 1. [Forwarder](#forwarder) 4 | 1. [Contract Interactions](#contract-interactions) 5 | 1. [Fee Abstraction](#fee-abstraction) 6 | 1. [Events](#events) 7 | 1. [Forwarder events](#forwarder-events) 8 | 1. [Miscellaneous](#miscellaneous) 9 | 1. [Optimizing calldata](#optimizing-calldata) 10 | 11 | # Architecture 12 | 13 |
14 | 15 |
16 | 17 | The Forwarding contract acts as a middleman between the user and the 0x Exchange contract. Its purpose is to perform a number of useful actions on the users behalf. Conveniently reducing the number of steps and transactions. 18 | 19 | The 0x protocol [AssetProxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetproxy) contracts work as operators that are authorized to move tokens on behalf of users if an order with a valid signature can be produced. Since ETH is not a token and cannot be moved natively by a third party operator, Wrapped ETH (WETH) is introduced to adapt ETH to work in this environment. WETH requires a user to deposit ETH into the WETH contract (transaction) and approve (transaction) the AssetProxy operator to transfer the WETH on the user's behalf. 20 | 21 | When performing an exchange on 0x, fees may be required to be paid by the maker and takers in ZRX token. The AssetProxy needs approval to move these tokens (transaction) and ZRX tokens need to be pre-purchased (transaction). 22 | 23 | A user is then able to find an order and perform a TokenA/WETH `fillOrder` on the 0x [Exchange contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#exchange) (transaction). 24 | 25 | For first time purchasers of tokens, this is non-trivial setup that is unavoidable due to the design of the ERC20 standard. The Forwarding contracts purpose is to enable users to buy tokens in one transaction. 26 | 27 | # Contracts 28 | 29 | ## Forwarder 30 | 31 | The Forwarder contains all of the following logic: 32 | 33 | - Presets approval to the [ERC20Proxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#erc20proxy) and the [ERC721Proxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#erc721proxy) 34 | - Converts ETH into WETH on behalf of the user 35 | - Performs the exchange via the 0x Exchange contract 36 | - Purchases ZRX on behalf of the user (ZRX fee abstraction) 37 | - Transfers the newly purchased asset to the user 38 | 39 | There are two public entry points for users of the Forwarding contract, `marketSellOrdersWithEth` and `marketBuyOrdersWithEth`. 40 | 41 | The implementation of these methods follows a 4 step process: 42 | 43 | 1. Convert ETH to WETH. 44 | 2. Fill the orders using a `marketBuy` or `marketSell`. Note that any ZRX fees will be paid using the Forwarder contract's own ZRX balance. 45 | 3. If any ZRX fees were paid, repurchase that amount of ZRX. 46 | 4. If `feePercentage` and `feeRecipient` are supplied deduct the fees from the amount traded. 47 | 5. Transfer any remaining ETH back to sender. 48 | 49 | ### marketBuyOrdersWithEth 50 | 51 | This function buys a specific amount of assets (ERC20 or ERC721) given a set of TokenA/WETH orders. If fees are required on the orders, then `feeOrders` of ZRX/WETH are supplied and this is used to purchase the fees required. The user specifies the amount of assets they wish to buy with the `makerAssetFillAmount` parameter. `makerAssetFillAmount` must equal 1 if buying unique ERC721 tokens. The `makerAsset` of all supplied `orders` should be equivalent - any orders that contain `makerAssetData` different than the first order supplied will be ignored (gas will still be paid for attempting to fill these orders). 52 | 53 | This function will revert under the following conditions: 54 | 55 | - No ETH value was included in the transaction 56 | - The `makerAssetData` contains an [`assetProxyId`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetdata) that is not supported (currently ERC20 and ERC721 only) 57 | - The entire `makerAssetFillAmount` cannot be filled 58 | - The amount of ZRX paid in fees cannot be repurchased 59 | - The supplied `feePercentage` is greater than 5% (represented as 0.05 \* 10^18) 60 | - The required ETH fee cannot be paid 61 | 62 | ```solidity 63 | /// @dev Attempt to purchase makerAssetFillAmount of makerAsset by selling ETH provided with transaction. 64 | /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. 65 | /// Any ETH not spent will be refunded to sender. 66 | /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. 67 | /// @param makerAssetFillAmount Desired amount of makerAsset to purchase. 68 | /// @param signatures Proofs that orders have been created by makers. 69 | /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. 70 | /// @param feeSignatures Proofs that feeOrders have been created by makers. 71 | /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. 72 | /// @param feeRecipient Address that will receive ETH when orders are filled. 73 | /// @return Amounts filled and fees paid by maker and taker for both sets of orders. 74 | function marketBuyOrdersWithEth( 75 | LibOrder.Order[] memory orders, 76 | uint256 makerAssetFillAmount, 77 | bytes[] memory signatures, 78 | LibOrder.Order[] memory feeOrders, 79 | bytes[] memory feeSignatures, 80 | uint256 feePercentage, 81 | address feeRecipient 82 | ) 83 | public 84 | payable 85 | returns ( 86 | LibFillResults.FillResults memory orderFillResults, 87 | LibFillResults.FillResults memory feeOrderFillResults 88 | ); 89 | ``` 90 | 91 | As filling these orders is a moving target (a partial fill can occur while this transaction is pending) it is safe to send in additional orders and additional ETH. The `makerAssetFillAmount` is the total amount of assets the user wishes to purchase, and any additional ETH is returned to the user. This allows for the possibility of safely sending in 100 orders and 100 ETH to purchase 1 token, with all unspent ETH being returned. Any unused orders will increase the gas cost of the transaction due to additional calldata that must be stored in the blockchain's history. 92 | 93 | #### marketBuyOrdersWithEth when ZRX is the makerAsset 94 | 95 | Buying an exact amount of ZRX assets is a special case as fees (paid in ZRX) need to be accounted for. If a user wishes to purchase 100 ZRX and there is a 2 ZRX fee on this order, the Forwarding contract guarantees they will receive the expected amount (100 ZRX). To accomplish this we calculate an adjusted exchange rate (taking into account the fees). There is no need to provide any `feeOrders` since fees can be purchased from the same pool of orders. In this scenario the caller can supply an empty fee orders and fee signatures array. 96 | 97 | ### marketSellOrdersWithEth 98 | 99 | This function attempts to buy as many ERC20 tokens as possible given the amount of ETH sent in by performing a `marketSell`. If fees are required on the orders, then `feeOrders` of ZRX/WETH are supplied and this is used to purchase the fees required. 5% of the ETH included in the transaction are reserved for paying fees (i.e the sum of the value of the fees denominated in ZRX and ETH). The `makerAsset` of all supplied `orders` should be equivalent - any orders that contain `makerAssetData` different than the first order supplied will be ignored (gas will still be paid for attempting to fill these orders). 100 | 101 | This function will revert under the following conditions: 102 | 103 | - No ETH value was included in the transaction 104 | - The `makerAssetData` contains an [`assetProxyId`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetdata) that is not supported (currently ERC20 and ERC721 only) 105 | - The amount of ZRX paid in fees cannot be repurchased 106 | - The supplied `feePercentage` is greater than 5% (represented as 0.05 \* 10^18) 107 | - The required ETH fee cannot be paid 108 | - More ETH is sold than the value included in the transaction 109 | 110 | ```solidity 111 | /// @dev Purchases as much of orders' makerAssets as possible by selling up to 95% of transaction's ETH value. 112 | /// Any ZRX required to pay fees for primary orders will automatically be purchased by this contract. 113 | /// 5% of ETH value is reserved for paying fees to order feeRecipients (in ZRX) and forwarding contract feeRecipient (in ETH). 114 | /// Any ETH not spent will be refunded to sender. 115 | /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. 116 | /// @param signatures Proofs that orders have been created by makers. 117 | /// @param feeOrders Array of order specifications containing ZRX as makerAsset and WETH as takerAsset. Used to purchase ZRX for primary order fees. 118 | /// @param feeSignatures Proofs that feeOrders have been created by makers. 119 | /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. 120 | /// @param feeRecipient Address that will receive ETH when orders are filled. 121 | /// @return Amounts filled and fees paid by maker and taker for both sets of orders. 122 | function marketSellOrdersWithEth( 123 | LibOrder.Order[] memory orders, 124 | bytes[] memory signatures, 125 | LibOrder.Order[] memory feeOrders, 126 | bytes[] memory feeSignatures, 127 | uint256 feePercentage, 128 | address feeRecipient 129 | ) 130 | public 131 | payable 132 | returns ( 133 | LibFillResults.FillResults memory orderFillResults, 134 | LibFillResults.FillResults memory feeOrderFillResults 135 | ); 136 | ``` 137 | 138 | #### marketSellOrdersWithEth when ZRX is the makerAsset 139 | 140 | Similar to `marketBuyOrdersWithEth`, there is no need to provide `feeOrders` when the `makerAsset` of the primary orders is ZRX. ZRX fees can be baked into the price of the orders and withheld when transferring the purchased ZRX back to the sender. 141 | 142 | ### feePercentage and feeRecipient 143 | 144 | The `feePercentage` and `feeRecipient` are a feature in the Forwarding contract which is distinct from the ZRX fees of the orders. This incentivizes integrations from wallets and other dApps who are not the feeRecipients in these orders to host front ends for the Forwarder contract and earn revenue. The `feePercentage` is calculated off the total value of ETH traded, not the total amount of ETH sent to the contract when calling the function. 145 | 146 | `feePercentage` supports up to 18 decimial places, for example 0.59% is represented as 0.0059 \* 10^18. This value is limited to less than or equal to 5%. 147 | 148 | # Miscellaneous 149 | 150 | ## Optimizing calldata 151 | 152 | Calldata is expensive. As per Appendix G of the [ETHeum Yellowpaper](#https://ETHeum.github.io/yellowpaper/paper.pdf), every non-zero byte of calldata costs 68 gas, and every zero byte costs 4 gas. There are certain off-chain optimizations that can be made in order to maximize the amount of zeroes included in calldata. 153 | 154 | ### Assuming order parameters 155 | 156 | The Forwarding contract assumes that: 157 | 158 | - `takerAssetData` for both `orders` and `feeOrders` refers to WETH 159 | - `makerAssetData` for `orders` after the first order are equivalent to the `makerAssetData` of the first order (i.e `orders[1].makerAssetData == orders[0].makerAssetData`) 160 | - `makerAssetData` for `feeOrders` refers to ZRX 161 | 162 | This means users may pass in zero values for these parameters and the functions will still execute as if the values had been passed in as calldata. 163 | 164 | # Events 165 | 166 | There are no additional events emitted by the Forwarding contract. However, `Transfer` (ERC20 and ERC721), `Deposit` and `Withdraw` (WETH), and `Fill` (0x Exchange) are emitted in their respective contracts. 167 | 168 | ## Known Issues 169 | 170 | ### Over buying ZRX 171 | 172 | In order to avoid any rounding issues when performing ZRX fee abstraction, an additional 1 wei worth of ZRX is purchased per `feeOrder` that is filled. In certain scenarios this is left behind in the contract. It is unlikely this would ever accrue to a value worth extracting and is not worth the gas in transferring per trade. 173 | 174 | ### feePercentage and feeRecipient modifications 175 | 176 | These values are submitted by the user and can therefore be modified. After talking with many projects, we believe this is acceptable for multiple reasons. 177 | 178 | 1. It is possible for a wallet to copy this contract and hard code these values. 179 | 2. If a wallet is the source of liquidity, they can filter orders by `senderAddress` or `takerAddress`. 180 | 3. It is cheaper for an "attacker" to avoid the Forwarding contract entirely and transact directly with the 0x Exchange. 181 | -------------------------------------------------------------------------------- /v3/forwarder-specification.md: -------------------------------------------------------------------------------- 1 | # 0x 3.0 Forwarder Specification 2 | 3 | 1. [Architecture](#architecture) 4 | 1. [Contracts](#contracts) 5 | 1. [Forwarder](#forwarder) 6 | 1. [Contract Interactions](#contract-interactions) 7 | 1. [Fee Abstraction](#fee-abstraction) 8 | 1. [Events](#events) 9 | 1. [Forwarder events](#forwarder-events) 10 | 1. [Miscellaneous](#miscellaneous) 11 | 1. [Optimizing calldata](#optimizing-calldata) 12 | 13 | # Architecture 14 | 15 |
16 | 17 |
18 | 19 | The Forwarder contract acts as a middleman between a prospective taker and the 0x Exchange contract. Its purpose is to perform a number of useful actions on the taker's behalf, conveniently reducing the number of steps and transactions. 20 | 21 | The 0x protocol [AssetProxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#assetproxy) contracts work as operators that are authorized to move tokens on behalf of users if an order with a valid signature can be produced. Since ETH is not a token and cannot be moved natively by a third party operator, Wrapped ETH (WETH) is introduced to adapt ETH to work in this environment. WETH requires a user to deposit ETH into the WETH contract (one transaction) and approve the AssetProxy operator (a second transaction) to transfer the WETH on the user's behalf. 22 | 23 | A user is then able to find an order and perform a TokenXYZ/WETH `fillOrder` on the 0x [Exchange contract](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#exchange) (a third transaction). 24 | 25 | For a first time token purchaser, this is non-trivial setup necessitated by the design of the ERC20 token standard. The Forwarder contract's purpose is to offload this overhead from users, enabling them to buy tokens in a single transaction. 26 | 27 | # Contracts 28 | 29 | The Forwarder contains all of the following logic: 30 | 31 | - Presets approval to the [ERC20Proxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#erc20proxy) and the [ERC721Proxy](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#erc721proxy) 32 | - Converts ETH into WETH on behalf of the user 33 | - Performs the token exchange via the 0x Exchange contract 34 | - Transfers the newly purchased asset to the user 35 | 36 | The Forwarder contract has two public entry points: `marketSellOrdersWithEth` and `marketBuyOrdersWithEth`. 37 | 38 | Both methods' implementations follow the same 4-step process: 39 | 40 | 1. Convert ETH to WETH. 41 | 2. Fill the orders using a market sell or buy, respectively. 42 | 3. If a Forwarder fee recipient and fee percentage are supplied, deduct the fee from the remaining WETH balance. 43 | 4. Convert any remaining WETH back to ETH and return it to the sender. 44 | 45 | The Forwarder contract pays [protocol fees](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#protocol-fees) using the WETH obtained in step 1. Note that WETH spent on protocol fees is included in the total `wethSpentAmount` used to compute the Forwarder fee in step 3. 46 | 47 | As of v3, the 0x Exchange supports order fees denominated in [arbitrary tokens](https://github.com/0xProject/ZEIPs/issues/28). By consequence, the [ZRX fee abstraction](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/forwarder-specification.md#fee-abstraction) has been removed from the Forwarder. Instead, `marketSellOrdersWithEth` and `marketBuyOrdersWithEth` support orders with taker fees denominated in either **WETH** or the **maker asset**. Maker fees can be denominated in any asset. 48 | 49 | ## marketBuyOrdersWithEth 50 | 51 | This function buys a specific amount of asset (ERC20 or ERC721) given a set of TokenA/WETH orders. The user specifies the amount they wish to buy with the `makerAssetBuyAmount` parameter. `makerAssetBuyAmount` must equal 1 if buying unique ERC721 tokens. Note that the Forwarder contract does not enforce that `makerAsset` of all supplied `orders` are the same; the contract will fill the provided orders and transfer the acquired assets to the sender until the aggregate amount of assets acquired equals `makerAssetBuyAmount`. 52 | 53 | This function will revert under the following conditions: 54 | 55 | - No ETH value was included in the transaction 56 | - The `makerAssetData` contains an [`assetProxyId`](https://github.com/0xProject/0x-protocol-specification/blob/master/v3/v3-specification.md#assetdata) that is not supported (currently ERC20 and ERC721 only) 57 | - The contract attempts to fill an order with an unsupported fee (i.e. neither WETH nor the maker asset) 58 | - The entire `makerAssetBuyAmount` cannot be bought 59 | - The supplied `feePercentage` is greater than 5% (represented as 0.05 \* 10^18) 60 | - The required ETH fee cannot be paid 61 | 62 | ```solidity 63 | /// @dev Attempt to buy makerAssetBuyAmount of makerAsset by selling ETH provided with transaction. 64 | /// The Forwarder may *fill* more than makerAssetBuyAmount of the makerAsset so that it can 65 | /// pay takerFees where takerFeeAssetData == makerAssetData (i.e. percentage fees). 66 | /// Any ETH not spent will be refunded to sender. 67 | /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. 68 | /// @param makerAssetBuyAmount Desired amount of makerAsset to purchase. 69 | /// @param signatures Proofs that orders have been created by makers. 70 | /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. 71 | /// @param feeRecipient Address that will receive ETH when orders are filled. 72 | /// @return wethSpentAmount Amount of WETH spent on the given set of orders. 73 | /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. 74 | /// @return ethFeePaid Amount of ETH spent on the given forwarder fee. 75 | function marketBuyOrdersWithEth( 76 | LibOrder.Order[] memory orders, 77 | uint256 makerAssetBuyAmount, 78 | bytes[] memory signatures, 79 | uint256 feePercentage, 80 | address payable feeRecipient 81 | ) 82 | public 83 | payable 84 | returns ( 85 | uint256 wethSpentAmount, 86 | uint256 makerAssetAcquiredAmount, 87 | uint256 ethFeePaid 88 | ); 89 | ``` 90 | 91 | In order to account for partial fills that may occur while a `marketBuyOrdersWithEth` transaction is pending, one can safely provide additional orders and ETH as a buffer. The Forwarder contract will only ever purchase `makerAssetBuyAmount`, and any additional ETH is returned to the user. This allows a user to e.g. provide 100 orders and 100 ETH to purchase one token, with the guarantee that all unspent ETH will be returned. Note that any unused orders will increase the gas cost of the transaction due to the additional calldata that must be stored in the blockchain's history. 92 | 93 | ## marketSellOrdersWithEth 94 | 95 | This function attempts to buy as many tokens as possible given the amount of ETH sent in by performing a "market sell". The WETH required to pay the Forwarder fee is set aside and the Forwarder attempts to sell the rest by filling the provided orders. Note that the Forwarder contract does not enforce that `makerAsset` of all supplied `orders` are the same, but the `makerAssetAcquiredAmount` return value might not be meaningful if different assets are acquired. 96 | 97 | This function will revert under the following conditions: 98 | 99 | - No ETH value was included in the transaction 100 | - The `makerAssetData` contains an [`assetProxyId`](https://github.com/0xProject/0x-protocol-specification/blob/master/v2/v2-specification.md#assetdata) that is not supported (currently ERC20 and ERC721 only) 101 | - The contract attempts to fill an order with an unsupported fee (i.e. neither WETH nor the maker asset) 102 | - The supplied `feePercentage` is greater than 5% (represented as 0.05 \* 10^18) 103 | - More ETH is sold than the value included in the transaction 104 | 105 | ```solidity 106 | /// @dev Purchases as much of orders' makerAssets as possible by selling as much of the ETH value sent 107 | /// as possible, accounting for order and forwarder fees. 108 | /// @param orders Array of order specifications used containing desired makerAsset and WETH as takerAsset. 109 | /// @param signatures Proofs that orders have been created by makers. 110 | /// @param feePercentage Percentage of WETH sold that will payed as fee to forwarding contract feeRecipient. 111 | /// @param feeRecipient Address that will receive ETH when orders are filled. 112 | /// @return wethSpentAmount Amount of WETH spent on the given set of orders. 113 | /// @return makerAssetAcquiredAmount Amount of maker asset acquired from the given set of orders. 114 | /// @return ethFeePaid Amount of ETH spent on the given forwarder fee. 115 | function marketSellOrdersWithEth( 116 | LibOrder.Order[] memory orders, 117 | bytes[] memory signatures, 118 | uint256 feePercentage, 119 | address payable feeRecipient 120 | ) 121 | public 122 | payable 123 | returns ( 124 | uint256 wethSpentAmount, 125 | uint256 makerAssetAcquiredAmount, 126 | uint256 ethFeePaid 127 | ); 128 | ``` 129 | 130 | ## feePercentage and feeRecipient 131 | 132 | As alluded to previously, the `feePercentage` and `feeRecipient` in `marketSellOrdersWithEth` and `marketBuyOrdersWithEth` specify a _Forwarder fee_, which is different from the _order fees_ (maker/taker fees) and _protocol fees_ required to fill any 0x order. The Forwarder fee incentivizes integrations from wallets and other dApps –– entities that otherwise wouldn't be receiving fees from 0x orders –– to host front ends for the Forwarder contract and earn revenue. The Forwarder fee is calculated as a percentage of the total value of ETH spent (including taker and protocol fees), not the total amount of ETH sent to the contract when calling the function. 133 | 134 | `feePercentage` supports 18 decimal places of precision; for example 0.59% is represented as 0.0059 \* 10^18. This value is upper-bounded by 5%. 135 | 136 | # Miscellaneous 137 | 138 | ## Events 139 | 140 | There are no additional events emitted by the Forwarding contract. However, `Transfer` (ERC20 and ERC721), `Deposit` and `Withdraw` (WETH), and `Fill` (0x Exchange) are emitted in their respective contracts. 141 | 142 | ## Known Issues 143 | 144 | ### Rounding 145 | 146 | As a byproduct of rounding, up to 1 wei worth of WETH may be oversold in the last order filled by `marketSellOrdersWithEth` (equivalently, up to 1 wei worth of maker asset may be overbought in the last order filled by `marketBuyOrdersWithEth`). 147 | 148 | ### feePercentage and feeRecipient modifications 149 | 150 | These values are submitted by the user and can therefore be modified. After talking with many projects, we believe this is acceptable for multiple reasons. 151 | 152 | 1. It is possible for a wallet to copy this contract and hard code these values. 153 | 2. If a wallet is the source of liquidity, they can filter orders by `senderAddress` or `takerAddress`. 154 | 3. It is cheaper for an "attacker" to avoid the Forwarder contract entirely and transact directly with the 0x Exchange. 155 | -------------------------------------------------------------------------------- /v3/protocol-fees.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/0xProject/0x-protocol-specification/562aefe06bcbc82a3b76571ad71b451f0b11af5f/v3/protocol-fees.pdf -------------------------------------------------------------------------------- /v3/zero-ex-governor.md: -------------------------------------------------------------------------------- 1 | # ZeroExGovernor 2 | 3 | The `ZeroExGovernor` is a time-locked multi-signature wallet that has permission to perform administrative functions within the protocol. By default, submitted transactions must pass a 14 day timelock before they are executed. However, the `ZeroExGovernor` also allows custom timelocks to be registered to arbitrary function calls at different destination addresses. Many functions that can be used to mitigate damage in case of emergencies (for example, if a vulnerability is discovered that puts user funds at risk) do not have a timelock. Other functions that affect the system of staking contracts have timelocks that are denominated in terms of 10 day epochs in order to maximize security of upgrades. 4 | 5 | The `ZeroExGovernor` has authorizations to perform the following functions within the protocol: 6 | 7 | ## Managing ownership of all contracts 8 | 9 | The `ZeroExGovernor` can transfer ownership of any contract for which it is the `owner` by calling the following function: 10 | 11 | ```solidity 12 | /// @dev Transfers ownership to a new address. 13 | /// @param newOwner Address of the new owner. 14 | function transferOwnership(address newOwner) 15 | public; 16 | ``` 17 | 18 | ## Managing authorizations in the StakingProxy, ZrxVault, and AssetProxy contracts 19 | 20 | Most [`AssetProxy`](./v3-specification.md#assetproxy) require that the caller of their `transferFrom` function is authorized to make the call. The `ZeroExGovernor` is responsible for adding or removing authorizations (and may bypass the timelock when removing an authorization). This is also the mechanism used for upgrading the `Exchange` contract without redeploying each individual `AssetProxy`. A new `Exchange` contract can be authorized while the authorizations of old `Exchange` contracts are removed. Multiple contracts can also be simultaneously authorized. 21 | 22 | The `ZeroExGovernor` can also manage authorizations for the `StakingProxy` and `ZrxVault` contracts. While the `ZeroExGovernor` itself is currently the only authorized address in these contracts, this feature can be used to allow new contracts to perform admin functions under different conditions in the future (such as with an on-chain token vote). 23 | 24 | ## Registering AssetProxy contracts in the Exchange and MultiAssetProxy 25 | 26 | [`AssetProxy`](./v3-specification.md#assetproxy) contracts must be registered in the `Exchange` and `MultiAssetProxy` contracts in order to be utilized when filling orders. The `ZeroExGovernor` can register new `AssetProxy` contracts by calling the following function: 27 | 28 | ```solidity 29 | /// @dev Registers an asset proxy to its asset proxy id. 30 | /// Once an asset proxy is registered, it cannot be unregistered. 31 | /// @param assetProxy Address of new asset proxy to register. 32 | function registerAssetProxy(address assetProxy) 33 | external; 34 | ``` 35 | 36 | ## Setting the protocol fee multiplier in the Exchange 37 | 38 | The `ZeroExGovernor` can update the [protocol fee multiplier](./v3-specification.md#calculating-the-protocol-fee) by calling the following function: 39 | 40 | ```solidity 41 | /// @dev Allows the owner to update the protocol fee multiplier. 42 | /// @param updatedProtocolFeeMultiplier The updated protocol fee multiplier. 43 | function setProtocolFeeMultiplier(uint256 updatedProtocolFeeMultiplier) 44 | external; 45 | ``` 46 | 47 | Setting the `protocolFeeMultiplier` will emit a [`ProtocolFeeMultiplier`](./v3-specification.md#protocolfeemultiplier) event. 48 | 49 | ## Setting the protocol fee collector (StakingProxy) in the Exchange 50 | 51 | The `ZeroExGovernor` can update the protocol fee collector contract (currently the [Staking](./v3-specification.md#staking) contract) by calling either of the following functions: 52 | 53 | ```solidity 54 | /// @dev Allows the owner to update the protocolFeeCollector address. 55 | /// @param updatedProtocolFeeCollector The updated protocolFeeCollector contract address. 56 | function setProtocolFeeCollectorAddress(address updatedProtocolFeeCollector) 57 | external; 58 | 59 | /// @dev Sets the protocolFeeCollector contract address to 0. 60 | /// Only callable by owner. 61 | function detachProtocolFeeCollector() 62 | external; 63 | ``` 64 | 65 | Setting the `protocolFeeCollector` will emit a [`ProtocolFeeCollectorAddress`](./v3-specification.md#protocolfeecollectoraddress) event. 66 | 67 | ## Updating staking parameters in the StakingProxy 68 | 69 | The `ZeroExGovernor` can update individual staking parameters by calling the following function: 70 | 71 | ```solidity 72 | /// @dev Set all configurable parameters at once. 73 | /// @param _epochDurationInSeconds Minimum seconds between epochs. 74 | /// @param _rewardDelegatedStakeWeight How much delegated stake is weighted vs operator stake, in ppm. 75 | /// @param _minimumPoolStake Minimum amount of stake required in a pool to collect rewards. 76 | /// @param _cobbDouglasAlphaNumerator Numerator for cobb douglas alpha factor. 77 | /// @param _cobbDouglasAlphaDenominator Denominator for cobb douglas alpha factor. 78 | function setParams( 79 | uint256 _epochDurationInSeconds, 80 | uint32 _rewardDelegatedStakeWeight, 81 | uint256 _minimumPoolStake, 82 | uint32 _cobbDouglasAlphaNumerator, 83 | uint32 _cobbDouglasAlphaDenominator 84 | ) 85 | external; 86 | ``` 87 | 88 | ## Adding or removing Exchange contracts that are allowed to pay protocol fees to the StakingProxy 89 | 90 | The `ZeroExGovernor` can add or remove an `Exchange` contract from the `StakingProxy` by calling either of the following functions: 91 | 92 | ```solidity 93 | /// @dev Adds a new exchange address 94 | /// @param addr Address of exchange contract to add 95 | function addExchangeAddress(address addr) 96 | external; 97 | 98 | /// @dev Removes an existing exchange address 99 | /// @param addr Address of exchange contract to remove 100 | function removeExchangeAddress(address addr) 101 | external; 102 | ``` 103 | 104 | ## Upgrading the staking logic contract that is attached to the StakingProxy 105 | 106 | The `ZeroExGovernor` can upgrade the logic of the StakingProxy by calling either of the following functions: 107 | 108 | ```solidity 109 | /// @dev Attach a staking contract; future calls will be delegated to the staking contract. 110 | /// Note that this is callable only by an authorized address. 111 | /// @param _stakingContract Address of staking contract. 112 | function attachStakingContract(address _stakingContract) 113 | external; 114 | 115 | /// @dev Detach the current staking contract. 116 | /// Note that this is callable only by an authorized address. 117 | function detachStakingContract() 118 | external; 119 | ``` 120 | 121 | ## Setting the StakingProxy that is allowed to trigger deposits and withdrawals from the ZrxVault 122 | 123 | The `ZeroExGovernor` can replace the `StakingProxy` contract that triggers deposits and withdrawals in the `ZrxVault` by calling the following function: 124 | 125 | ```solidity 126 | /// @dev Sets the address of the StakingProxy contract. 127 | /// Note that only the contract owner can call this function. 128 | /// @param _stakingProxyAddress Address of Staking proxy contract. 129 | function setStakingProxy(address _stakingProxyAddress) 130 | external; 131 | ``` 132 | 133 | ## Setting the AssetProxy that is used used to deposit to the ZrxVault 134 | 135 | The `ZeroExGovernor` can replace the `AssetProxy` contract used to perform deposits into the `ZrxVault` by calling the following function: 136 | 137 | ```solidity 138 | /// @dev Sets the Zrx proxy. 139 | /// Note that only an authorized address can call this function. 140 | /// Note that this can only be called when *not* in Catastrophic Failure mode. 141 | /// @param _zrxProxyAddress Address of the 0x Zrx Proxy. 142 | function setZrxProxy(address _zrxProxyAddress) 143 | external; 144 | ``` 145 | 146 | ## Entering catastrophic failure mode in the ZrxVault 147 | 148 | The `ZeroExGovernor` can enter catastrophic failure mode in the `ZrxVault` in emergencies by calling the following function: 149 | 150 | ```solidity 151 | /// @dev Vault enters into Catastrophic Failure Mode. 152 | /// *** WARNING - ONCE IN CATOSTROPHIC FAILURE MODE, YOU CAN NEVER GO BACK! *** 153 | /// Note that only the contract owner can call this function. 154 | function enterCatastrophicFailure() 155 | external; 156 | ``` 157 | 158 | ## List of all administrative functions and timelocks 159 | 160 | Function timelocks are represented in days, where one day is equivalent to 86,400 seconds. Custom timelocks are in bold. 161 | 162 | | Contract | Function | Selector | Timelock | 163 | | ---------------- | -------------------------------- | -------- | ----------- | 164 | | Exchange | `registerAssetProxy` | c585bb93 | 14 days | 165 | | Exchange | `setProtocolFeeMultiplier` | 9331c742 | **7 days** | 166 | | Exchange | `setProtocolFeeCollectorAddress` | c0fa16cc | **14 days** | 167 | | Exchange | `detachProtocolFeeCollector` | 0efca185 | **0 days** | 168 | | Exchange | `transferOwnership` | f2fde38b | 14 days | 169 | | StakingProxy | `addExchangeAddress` | 8a2e271a | **14 days** | 170 | | StakingProxy | `removeExchangeAddress` | 01e28d84 | **14 days** | 171 | | StakingProxy | `attachStakingContract` | 66615d56 | **14 days** | 172 | | StakingProxy | `detachStakingContract` | 37b006a6 | **14 days** | 173 | | StakingProxy | `setParams` | 9c3ccc82 | **7 days** | 174 | | StakingProxy | `addAuthorizedAddress` | 42f1181e | **14 days** | 175 | | StakingProxy | `removeAuthorizedAddress` | 70712939 | **14 days** | 176 | | StakingProxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **14 days** | 177 | | StakingProxy | `transferOwnership` | f2fde38b | **14 days** | 178 | | ZrxVault | `setStakingProxy` | 6bf3f9e5 | **14 days** | 179 | | ZrxVault | `enterCatastrophicFailure` | c02e5a7f | **0 days** | 180 | | ZrxVault | `setZrxProxy` | ca5b0218 | **14 days** | 181 | | ZrxVault | `addAuthorizedAddress` | 42f1181e | **14 days** | 182 | | ZrxVault | `removeAuthorizedAddress` | 70712939 | **14 days** | 183 | | ZrxVault | `removeAuthorizedAddressAtIndex` | 9ad26744 | **14 days** | 184 | | ZrxVault | `transferOwnership` | f2fde38b | **14 days** | 185 | | ERC20Proxy | `addAuthorizedAddress` | 42f1181e | 14 days | 186 | | ERC20Proxy | `removeAuthorizedAddress` | 70712939 | **0 days** | 187 | | ERC20Proxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **0 days** | 188 | | ERC20Proxy | `transferOwnership` | f2fde38b | 14 days | 189 | | ERC721Proxy | `addAuthorizedAddress` | 42f1181e | 14 days | 190 | | ERC721Proxy | `removeAuthorizedAddress` | 70712939 | **0 days** | 191 | | ERC721Proxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **0 days** | 192 | | ERC721Proxy | `transferOwnership` | f2fde38b | 14 days | 193 | | ERC1155Proxy | `addAuthorizedAddress` | 42f1181e | 14 days | 194 | | ERC1155Proxy | `removeAuthorizedAddress` | 70712939 | **0 days** | 195 | | ERC1155Proxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **0 days** | 196 | | ERC1155Proxy | `transferOwnership` | f2fde38b | 14 days | 197 | | ERC20BridgeProxy | `addAuthorizedAddress` | 42f1181e | 14 days | 198 | | ERC20BridgeProxy | `removeAuthorizedAddress` | 70712939 | **0 days** | 199 | | ERC20BridgeProxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **0 days** | 200 | | ERC20BridgeProxy | `transferOwnership` | f2fde38b | 14 days | 201 | | MultiAssetProxy | `addAuthorizedAddress` | 42f1181e | 14 days | 202 | | MultiAssetProxy | `removeAuthorizedAddress` | 70712939 | **0 days** | 203 | | MultiAssetProxy | `removeAuthorizedAddressAtIndex` | 9ad26744 | **0 days** | 204 | | MultiAssetProxy | `transferOwnership` | f2fde38b | 14 days | 205 | | MultiAssetProxy | `registerAssetProxy` | c585bb93 | 14 days | 206 | --------------------------------------------------------------------------------