aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0/protocol/AssetProxy
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src/2.0.0/protocol/AssetProxy')
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol132
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol218
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol118
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetData.sol38
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol47
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol53
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/libs/LibAssetProxyErrors.sol36
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol42
8 files changed, 684 insertions, 0 deletions
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol
new file mode 100644
index 000000000..aed62f54f
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol
@@ -0,0 +1,132 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../utils/LibBytes/LibBytes.sol";
+import "./MixinAuthorizable.sol";
+
+contract ERC20Proxy is
+ MixinAuthorizable
+{
+ // Id of this proxy.
+ bytes4 constant PROXY_ID = bytes4(keccak256("ERC20Token(address)"));
+
+ function ()
+ external
+ {
+ assembly {
+ // The first 4 bytes of calldata holds the function selector
+ let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
+
+ // `transferFrom` will be called with the following parameters:
+ // assetData Encoded byte array.
+ // from Address to transfer asset from.
+ // to Address to transfer asset to.
+ // amount Amount of asset to transfer.
+ // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
+ if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
+
+ // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
+ // where k is the key left padded to 32 bytes and p is the storage slot
+ let start := mload(64)
+ mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
+ mstore(add(start, 32), authorized_slot)
+
+ // Revert if authorized[msg.sender] == false
+ if iszero(sload(keccak256(start, 64))) {
+ // Revert with `Error("SENDER_NOT_AUTHORIZED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ /////// Token contract address ///////
+ // The token address is found as follows:
+ // * It is stored at offset 4 in `assetData` contents.
+ // * This is stored at offset 32 from `assetData`.
+ // * The offset to `assetData` from Params is stored at offset
+ // 4 in calldata.
+ // * The offset of Params in calldata is 4.
+ // So we read location 4 and add 32 + 4 + 4 to it.
+ let token := calldataload(add(calldataload(4), 40))
+
+ /////// Setup Header Area ///////
+ // This area holds the 4-byte `transferFrom` selector.
+ // Any trailing data in transferFromSelector will be
+ // overwritten in the next `mstore` call.
+ mstore(0, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
+
+ /////// Setup Params Area ///////
+ // We copy the fields `from`, `to` and `amount` in bulk
+ // from our own calldata to the new calldata.
+ calldatacopy(4, 36, 96)
+
+ /////// Call `token.transferFrom` using the calldata ///////
+ let success := call(
+ gas, // forward all gas
+ token, // call address of token contract
+ 0, // don't send any ETH
+ 0, // pointer to start of input
+ 100, // length of input
+ 0, // write output over input
+ 32 // output size should be 32 bytes
+ )
+
+ /////// Check return data. ///////
+ // If there is no return data, we assume the token incorrectly
+ // does not return a bool. In this case we expect it to revert
+ // on failure, which was handled above.
+ // If the token does return data, we require that it is a single
+ // nonzero 32 bytes value.
+ // So the transfer succeeded if the call succeeded and either
+ // returned nothing, or returned a non-zero 32 byte value.
+ success := and(success, or(
+ iszero(returndatasize),
+ and(
+ eq(returndatasize, 32),
+ gt(mload(0), 0)
+ )
+ ))
+ if success {
+ return(0, 0)
+ }
+
+ // Revert with `Error("TRANSFER_FAILED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+ }
+ }
+
+ /// @dev Gets the proxy id associated with the proxy address.
+ /// @return Proxy id.
+ function getProxyId()
+ external
+ pure
+ returns (bytes4)
+ {
+ return PROXY_ID;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
new file mode 100644
index 000000000..b73dc36cc
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
@@ -0,0 +1,218 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../utils/LibBytes/LibBytes.sol";
+import "./MixinAuthorizable.sol";
+
+contract ERC721Proxy is
+ MixinAuthorizable
+{
+ // Id of this proxy.
+ bytes4 constant PROXY_ID = bytes4(keccak256("ERC721Token(address,uint256,bytes)"));
+
+ function ()
+ external
+ {
+ assembly {
+ // The first 4 bytes of calldata holds the function selector
+ let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000)
+
+ // `transferFrom` will be called with the following parameters:
+ // assetData Encoded byte array.
+ // from Address to transfer asset from.
+ // to Address to transfer asset to.
+ // amount Amount of asset to transfer.
+ // bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
+ if eq(selector, 0xa85e59e400000000000000000000000000000000000000000000000000000000) {
+
+ // To lookup a value in a mapping, we load from the storage location keccak256(k, p),
+ // where k is the key left padded to 32 bytes and p is the storage slot
+ let start := mload(64)
+ mstore(start, and(caller, 0xffffffffffffffffffffffffffffffffffffffff))
+ mstore(add(start, 32), authorized_slot)
+
+ // Revert if authorized[msg.sender] == false
+ if iszero(sload(keccak256(start, 64))) {
+ // Revert with `Error("SENDER_NOT_AUTHORIZED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000001553454e4445525f4e4f545f415554484f52495a454400000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // `transferFrom`.
+ // The function is marked `external`, so no abi decodeding is done for
+ // us. Instead, we expect the `calldata` memory to contain the
+ // following:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 4 * 32 | function parameters: |
+ // | | 4 | | 1. offset to assetData (*) |
+ // | | 36 | | 2. from |
+ // | | 68 | | 3. to |
+ // | | 100 | | 4. amount |
+ // | Data | | | assetData: |
+ // | | 132 | 32 | assetData Length |
+ // | | 164 | ** | assetData Contents |
+ //
+ // (*): offset is computed from start of function parameters, so offset
+ // by an additional 4 bytes in the calldata.
+ //
+ // WARNING: The ABIv2 specification allows additional padding between
+ // the Params and Data section. This will result in a larger
+ // offset to assetData.
+
+ // Asset data itself is encoded as follows:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 3 * 32 | function parameters: |
+ // | | 4 | 12 + 20 | 1. token address |
+ // | | 36 | | 2. tokenId |
+ // | | 68 | | 3. offset to receiverData (*) |
+ // | Data | | | receiverData: |
+ // | | 100 | 32 | receiverData Length |
+ // | | 132 | ** | receiverData Contents |
+
+ // We construct calldata for the `token.safeTransferFrom` ABI.
+ // The layout of this calldata is in the table below.
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 4 * 32 | function parameters: |
+ // | | 4 | | 1. from |
+ // | | 36 | | 2. to |
+ // | | 68 | | 3. tokenId |
+ // | | 100 | | 4. offset to receiverData (*) |
+ // | Data | | | receiverData: |
+ // | | 132 | 32 | receiverData Length |
+ // | | 164 | ** | receiverData Contents |
+
+ // There exists only 1 of each token.
+ // require(amount == 1, "INVALID_AMOUNT")
+ if sub(calldataload(100), 1) {
+ // Revert with `Error("INVALID_AMOUNT")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000000e494e56414c49445f414d4f554e540000000000000000000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+
+ // Require assetData to be at least 132 bytes
+ let offset := calldataload(4)
+ if lt(calldataload(add(offset, 4)), 132) {
+ // Revert with `Error("LENGTH_GREATER_THAN_131_REQUIRED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x000000204c454e4754485f475245415445525f5448414e5f3133315f52455155)
+ mstore(96, 0x4952454400000000000000000000000000000000000000000000000000000000)
+ revert(0, 100)
+ }
+
+ /////// Setup State ///////
+ // `cdStart` is the start of the calldata for
+ // `token.safeTransferFrom` (equal to free memory ptr).
+ let cdStart := mload(64)
+ // `dataAreaLength` is the total number of words
+ // needed to store `receiverData`
+ // As-per the ABI spec, this value is padded up to
+ // the nearest multiple of 32,
+ // and includes 32-bytes for length.
+ // It's calculated as folows:
+ // - Unpadded length in bytes = `mload(receiverData) + 32`
+ // - Add 31 to convert rounding down to rounding up.
+ // Combined with the previous and this is `63`.
+ // - Round down to nearest multiple of 32 by clearing
+ // bits 0x1F. This is done with `and` and a mask.
+
+ /////// Setup Header Area ///////
+ // This area holds the 4-byte `transferFromSelector`.
+ // Any trailing data in transferFromSelector will be
+ // overwritten in the next `mstore` call.
+ mstore(cdStart, 0xb88d4fde00000000000000000000000000000000000000000000000000000000)
+
+ /////// Setup Params Area ///////
+ // Each parameter is padded to 32-bytes.
+ // The entire Params Area is 128 bytes.
+ // Notes:
+ // 1. A 20-byte mask is applied to addresses
+ // to zero-out the unused bytes.
+ // 2. The offset to `receiverData` is the length
+ // of the Params Area (128 bytes).
+
+ let length := calldataload(add(offset, 136))
+ let token := calldataload(add(offset, 40))
+
+ // Round length up to multiple of 32
+ length := and(add(length, 31), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE0)
+
+ // Copy `from` and `to`
+ calldatacopy(add(cdStart, 4), 36, 64)
+
+ // TokenId
+ mstore(add(cdStart, 68), calldataload(add(offset, 72)))
+
+ // Offset to receiverData
+ mstore(add(cdStart, 100), 128)
+
+ // receiverData (including length)
+ calldatacopy(add(cdStart, 132), add(offset, 136), add(length, 32))
+
+ /////// Call `token.safeTransferFrom` using the calldata ///////
+ let success := call(
+ gas, // forward all gas
+ token, // call address of token contract
+ 0, // don't send any ETH
+ cdStart, // pointer to start of input
+ add(length, 164), // length of input
+ 0, // write output to null
+ 0 // output size is 0 bytes
+ )
+ if success {
+ return(0, 0)
+ }
+
+ // Revert with `Error("TRANSFER_FAILED")`
+ mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
+ mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000)
+ mstore(64, 0x0000000f5452414e534645525f4641494c454400000000000000000000000000)
+ mstore(96, 0)
+ revert(0, 100)
+ }
+ }
+ }
+
+ /// @dev Gets the proxy id associated with the proxy address.
+ /// @return Proxy id.
+ function getProxyId()
+ external
+ pure
+ returns (bytes4)
+ {
+ return PROXY_ID;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol
new file mode 100644
index 000000000..3b9584a44
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/MixinAuthorizable.sol
@@ -0,0 +1,118 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../utils/Ownable/Ownable.sol";
+import "./mixins/MAuthorizable.sol";
+
+contract MixinAuthorizable is
+ Ownable,
+ MAuthorizable
+{
+
+ /// @dev Only authorized addresses can invoke functions with this modifier.
+ modifier onlyAuthorized {
+ require(
+ authorized[msg.sender],
+ "SENDER_NOT_AUTHORIZED"
+ );
+ _;
+ }
+
+ mapping (address => bool) public authorized;
+ address[] public authorities;
+
+ /// @dev Authorizes an address.
+ /// @param target Address to authorize.
+ function addAuthorizedAddress(address target)
+ external
+ onlyOwner
+ {
+ require(
+ !authorized[target],
+ "TARGET_ALREADY_AUTHORIZED"
+ );
+
+ authorized[target] = true;
+ authorities.push(target);
+ emit AuthorizedAddressAdded(target, msg.sender);
+ }
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ function removeAuthorizedAddress(address target)
+ external
+ onlyOwner
+ {
+ require(
+ authorized[target],
+ "TARGET_NOT_AUTHORIZED"
+ );
+
+ delete authorized[target];
+ for (uint256 i = 0; i < authorities.length; i++) {
+ if (authorities[i] == target) {
+ authorities[i] = authorities[authorities.length - 1];
+ authorities.length -= 1;
+ break;
+ }
+ }
+ emit AuthorizedAddressRemoved(target, msg.sender);
+ }
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ /// @param index Index of target in authorities array.
+ function removeAuthorizedAddressAtIndex(
+ address target,
+ uint256 index
+ )
+ external
+ onlyOwner
+ {
+ require(
+ authorized[target],
+ "TARGET_NOT_AUTHORIZED"
+ );
+ require(
+ index < authorities.length,
+ "INDEX_OUT_OF_BOUNDS"
+ );
+ require(
+ authorities[index] == target,
+ "AUTHORIZED_ADDRESS_MISMATCH"
+ );
+
+ delete authorized[target];
+ authorities[index] = authorities[authorities.length - 1];
+ authorities.length -= 1;
+ emit AuthorizedAddressRemoved(target, msg.sender);
+ }
+
+ /// @dev Gets all authorized addresses.
+ /// @return Array of authorized addresses.
+ function getAuthorizedAddresses()
+ external
+ view
+ returns (address[] memory)
+ {
+ return authorities;
+ }
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetData.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetData.sol
new file mode 100644
index 000000000..7ebd6acf0
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetData.sol
@@ -0,0 +1,38 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.23;
+
+// @dev Interface of the asset proxy's assetData.
+// The asset proxies take an ABI encoded `bytes assetData` as argument.
+// This argument is ABI encoded as one of the methods of this interface.
+interface IAssetData {
+
+ function ERC20Token(address tokenContract)
+ external
+ pure;
+
+ function ERC721Token(
+ address tokenContract,
+ uint256 tokenId,
+ bytes receiverData
+ )
+ external
+ pure;
+
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol
new file mode 100644
index 000000000..eacd5a412
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAssetProxy.sol
@@ -0,0 +1,47 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "./IAuthorizable.sol";
+
+contract IAssetProxy is
+ IAuthorizable
+{
+
+ /// @dev Transfers assets. Either succeeds or throws.
+ /// @param assetData Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer asset from.
+ /// @param to Address to transfer asset to.
+ /// @param amount Amount of asset to transfer.
+ function transferFrom(
+ bytes assetData,
+ address from,
+ address to,
+ uint256 amount
+ )
+ external;
+
+ /// @dev Gets the proxy id associated with the proxy address.
+ /// @return Proxy id.
+ function getProxyId()
+ external
+ pure
+ returns (bytes4);
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol
new file mode 100644
index 000000000..cedd1744c
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/interfaces/IAuthorizable.sol
@@ -0,0 +1,53 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../../utils/Ownable/IOwnable.sol";
+
+contract IAuthorizable is
+ IOwnable
+{
+
+ /// @dev Gets all authorized addresses.
+ /// @return Array of authorized addresses.
+ function getAuthorizedAddresses()
+ external
+ view
+ returns (address[]);
+
+ /// @dev Authorizes an address.
+ /// @param target Address to authorize.
+ function addAuthorizedAddress(address target)
+ external;
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ function removeAuthorizedAddress(address target)
+ external;
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ /// @param index Index of target in authorities array.
+ function removeAuthorizedAddressAtIndex(
+ address target,
+ uint256 index
+ )
+ external;
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/libs/LibAssetProxyErrors.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
new file mode 100644
index 000000000..338cb12e2
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
@@ -0,0 +1,36 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+
+/// @dev This contract documents the revert reasons used in the AssetProxy contracts.
+/// This contract is intended to serve as a reference, but is not actually used for efficiency reasons.
+contract LibAssetProxyErrors {
+
+ /// Authorizable errors ///
+ string constant SENDER_NOT_AUTHORIZED = "SENDER_NOT_AUTHORIZED"; // Sender not authorized to call this method.
+ string constant TARGET_NOT_AUTHORIZED = "TARGET_NOT_AUTHORIZED"; // Target address not authorized to call this method.
+ string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized.
+ string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds.
+ string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address.
+
+ /// Transfer errors ///
+ string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
+ string constant LENGTH_GREATER_THAN_131_REQUIRED = "LENGTH_GREATER_THAN_131_REQUIRED"; // Byte array must have a length greater than 0.
+}
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol
new file mode 100644
index 000000000..6f35bd7ec
--- /dev/null
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/mixins/MAuthorizable.sol
@@ -0,0 +1,42 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../interfaces/IAuthorizable.sol";
+
+contract MAuthorizable is
+ IAuthorizable
+{
+
+ // Event logged when a new address is authorized.
+ event AuthorizedAddressAdded(
+ address indexed target,
+ address indexed caller
+ );
+
+ // Event logged when a currently authorized address is unauthorized.
+ event AuthorizedAddressRemoved(
+ address indexed target,
+ address indexed caller
+ );
+
+ /// @dev Only authorized addresses can invoke functions with this modifier.
+ modifier onlyAuthorized { revert(); _; }
+}