aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2018-04-11 08:53:34 +0800
committerAmir Bandeali <abandeali1@gmail.com>2018-04-21 04:56:17 +0800
commit78d81f193f3b9358ab86819f83c76b8bcd52a9c9 (patch)
tree6f3532ebbf6ed2bc3d07747ba442ec77d4d9ebe3 /packages/contracts/src
parentb9e0cd4512e6c7cd7584961df6ba106541826836 (diff)
downloaddexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar.gz
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar.bz2
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar.lz
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar.xz
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.tar.zst
dexon-sol-tools-78d81f193f3b9358ab86819f83c76b8bcd52a9c9.zip
Asset Proxy Dispatcher
Diffstat (limited to 'packages/contracts/src')
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol86
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol34
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol51
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy.sol80
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy_v1.sol89
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721TransferProxy.sol94
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol7
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol5
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol10
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol77
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol228
-rw-r--r--packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol46
-rw-r--r--packages/contracts/src/contracts/current/utils/Authorizable/Authorizable.sol109
-rw-r--r--packages/contracts/src/contracts/current/utils/Authorizable/IAuthorizable.sol53
-rw-r--r--packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol126
-rw-r--r--packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol13
-rw-r--r--packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol4
-rw-r--r--packages/contracts/src/utils/asset_proxy_utils.ts67
-rw-r--r--packages/contracts/src/utils/constants.ts1
-rw-r--r--packages/contracts/src/utils/crypto.ts2
-rw-r--r--packages/contracts/src/utils/order_utils.ts6
-rw-r--r--packages/contracts/src/utils/types.ts20
23 files changed, 1088 insertions, 122 deletions
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol
new file mode 100644
index 000000000..cce330818
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol
@@ -0,0 +1,86 @@
+/*
+
+ 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.21;
+
+import "./IAssetProxyDispatcher.sol";
+import "./IAssetProxy.sol";
+import "../../utils/Ownable/Ownable.sol";
+import "../../utils/Authorizable/Authorizable.sol";
+
+contract AssetProxyDispatcher is
+ Ownable,
+ Authorizable,
+ IAssetProxyDispatcher
+{
+ // Mapping from Asset Proxy Id's to their respective Asset Proxy
+ mapping (uint8 => IAssetProxy) public assetProxies;
+
+ /// @dev Delegates transfer to the corresponding asset proxy.
+ /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer token from.
+ /// @param to Address to transfer token to.
+ /// @param amount Amount of token to transfer.
+ function transferFrom(
+ bytes assetMetadata,
+ address from,
+ address to,
+ uint256 amount)
+ public
+ onlyAuthorized
+ {
+ // Lookup asset proxy
+ require(assetMetadata.length >= 1);
+ uint8 assetProxyId = uint8(assetMetadata[0]);
+ IAssetProxy assetProxy = assetProxies[assetProxyId];
+
+ // Dispatch transfer to asset proxy
+ // transferFrom will either succeed or throw.
+ assetProxy.transferFrom(assetMetadata, from, to, amount);
+ }
+
+ /// @dev Registers a new asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @param newAssetProxyAddress Address of the asset proxy contract to register.
+ /// @param currentAssetProxyAddress Address of existing asset proxy to overwrite.
+ function setAssetProxy(
+ uint8 assetProxyId,
+ address newAssetProxyAddress,
+ address currentAssetProxyAddress)
+ public
+ onlyOwner
+ {
+ // Ensure any existing asset proxy is not unintentionally overwritten
+ require(currentAssetProxyAddress == address(assetProxies[assetProxyId]));
+
+ // Store asset proxy and log registration
+ assetProxies[assetProxyId] = IAssetProxy(newAssetProxyAddress);
+ emit AssetProxyChanged(assetProxyId, newAssetProxyAddress, currentAssetProxyAddress);
+ }
+
+ /// @dev Gets an asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
+ function getAssetProxy(uint8 assetProxyId)
+ public view
+ returns (IAssetProxy)
+ {
+ IAssetProxy assetProxy = assetProxies[assetProxyId];
+ return assetProxy;
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol
new file mode 100644
index 000000000..5c5f7e605
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol
@@ -0,0 +1,34 @@
+/*
+
+ 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.21;
+
+contract IAssetProxy {
+
+ /// @dev Transfers assets. Either succeeds or throws.
+ /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer token from.
+ /// @param to Address to transfer token to.
+ /// @param amount Amount of token to transfer.
+ function transferFrom(
+ bytes assetMetadata,
+ address from,
+ address to,
+ uint256 amount)
+ public;
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol
new file mode 100644
index 000000000..9fe7b49a3
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol
@@ -0,0 +1,51 @@
+/*
+
+ 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.21;
+
+import "./IAssetProxy.sol";
+import "../../utils/Authorizable/IAuthorizable.sol";
+
+contract IAssetProxyDispatcher is
+ IAuthorizable,
+ IAssetProxy
+{
+ // Logs registration of new asset proxy
+ event AssetProxyChanged(
+ uint8 id,
+ address newAssetClassAddress,
+ address oldAssetClassAddress
+ );
+
+ /// @dev Sets a new asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @param newAssetProxyAddress Address of the asset proxy contract to register.
+ /// @param currentAssetProxyAddress Address of existing asset proxy to overwrite.
+ function setAssetProxy(
+ uint8 assetProxyId,
+ address newAssetProxyAddress,
+ address currentAssetProxyAddress)
+ public;
+
+ /// @dev Gets an asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @return The asset proxy registered to assetProxyId. Returns 0x0 if no proxy is registered.
+ function getAssetProxy(uint8 assetProxyId)
+ public view
+ returns (IAssetProxy);
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy.sol
new file mode 100644
index 000000000..61fcd9d00
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy.sol
@@ -0,0 +1,80 @@
+/*
+
+ 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.21;
+
+import "../IAssetProxy.sol";
+import "../../../utils/LibBytes/LibBytes.sol";
+import "../../../utils/Authorizable/Authorizable.sol";
+import { Token_v1 as ERC20Token } from "../../../../previous/Token/Token_v1.sol";
+
+contract ERC20TransferProxy is
+ LibBytes,
+ Authorizable,
+ IAssetProxy
+{
+
+ /// @dev Transfers ERC20 tokens.
+ /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer token from.
+ /// @param to Address to transfer token to.
+ /// @param amount Amount of token to transfer.
+ function transferFrom(
+ bytes assetMetadata,
+ address from,
+ address to,
+ uint256 amount)
+ public
+ onlyAuthorized
+ {
+ address token = decodeMetadata(assetMetadata);
+ bool success = ERC20Token(token).transferFrom(from, to, amount);
+ require(success == true);
+ }
+
+ /// @dev Encodes ERC20 byte array for the ERC20 asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @param tokenAddress Address of the asset.
+ /// @return assetMetadata Byte array encoded for the ERC20 asset proxy.
+ function encodeMetadata(
+ uint8 assetProxyId,
+ address tokenAddress)
+ public pure
+ returns (bytes assetMetadata)
+ {
+ // 0 is reserved as invalid proxy id
+ require(assetProxyId != 0);
+
+ // Encode fields into a byte array
+ assetMetadata = new bytes(21);
+ assetMetadata[0] = byte(assetProxyId);
+ writeAddress(tokenAddress, assetMetadata, 1);
+ return assetMetadata;
+ }
+
+ /// @dev Decodes ERC20-encoded byte array for the ERC20 asset proxy.
+ /// @param assetMetadata Byte array encoded for the ERC20 asset proxy.
+ /// @return tokenAddress Address of ERC20 token.
+ function decodeMetadata(bytes assetMetadata)
+ public pure
+ returns (address tokenAddress)
+ {
+ require(assetMetadata.length == 21);
+ return readAddress(assetMetadata, 1);
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy_v1.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy_v1.sol
new file mode 100644
index 000000000..6b19f1a52
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20TransferProxy_v1.sol
@@ -0,0 +1,89 @@
+/*
+
+ 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.21;
+
+import "../IAssetProxy.sol";
+import "../../../utils/LibBytes/LibBytes.sol";
+import "../../TokenTransferProxy/ITokenTransferProxy.sol";
+import "../../../utils/Authorizable/Authorizable.sol";
+
+contract ERC20TransferProxy_v1 is
+ LibBytes,
+ Authorizable,
+ IAssetProxy
+{
+ ITokenTransferProxy TRANSFER_PROXY;
+
+ /// @dev Contract constructor.
+ /// @param tokenTransferProxyContract erc20 token transfer proxy contract.
+ function ERC20TransferProxy_v1(ITokenTransferProxy tokenTransferProxyContract)
+ public
+ {
+ TRANSFER_PROXY = tokenTransferProxyContract;
+ }
+
+ /// @dev Transfers ERC20 tokens.
+ /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer token from.
+ /// @param to Address to transfer token to.
+ /// @param amount Amount of token to transfer.
+ function transferFrom(
+ bytes assetMetadata,
+ address from,
+ address to,
+ uint256 amount)
+ public
+ onlyAuthorized
+ {
+ address token = decodeMetadata(assetMetadata);
+ bool success = TRANSFER_PROXY.transferFrom(token, from, to, amount);
+ require(success == true);
+ }
+
+ /// @dev Encodes ERC20 byte array for the ERC20 asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @param tokenAddress Address of the asset.
+ /// @return assetMetadata Byte array encoded for the ERC20 asset proxy.
+ function encodeMetadata(
+ uint8 assetProxyId,
+ address tokenAddress)
+ public pure
+ returns (bytes assetMetadata)
+ {
+ // 0 is reserved as invalid proxy id
+ require(assetProxyId != 0);
+
+ // Encode fields into a byte array
+ assetMetadata = new bytes(21);
+ assetMetadata[0] = byte(assetProxyId);
+ writeAddress(tokenAddress, assetMetadata, 1);
+ return assetMetadata;
+ }
+
+ /// @dev Decodes ERC20-encoded byte array for the ERC20 asset proxy.
+ /// @param assetMetadata Byte array encoded for the ERC20 asset proxy.
+ /// @return tokenAddress Address of ERC20 token.
+ function decodeMetadata(bytes assetMetadata)
+ public pure
+ returns (address tokenAddress)
+ {
+ require(assetMetadata.length == 21);
+ return readAddress(assetMetadata, 1);
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721TransferProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721TransferProxy.sol
new file mode 100644
index 000000000..51f027a56
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721TransferProxy.sol
@@ -0,0 +1,94 @@
+/*
+
+ 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.21;
+
+import "../IAssetProxy.sol";
+import "../../../utils/LibBytes/LibBytes.sol";
+import "../../../utils/Authorizable/Authorizable.sol";
+import "/zeppelin/contracts/token/ERC721/ERC721Token.sol";
+
+
+contract ERC721TransferProxy is
+ LibBytes,
+ Authorizable,
+ IAssetProxy
+{
+
+ /// @dev Transfers ERC20 tokens.
+ /// @param assetMetadata Byte array encoded for the respective asset proxy.
+ /// @param from Address to transfer token from.
+ /// @param to Address to transfer token to.
+ /// @param amount Amount of token to transfer.
+ function transferFrom(
+ bytes assetMetadata,
+ address from,
+ address to,
+ uint256 amount)
+ public
+ onlyAuthorized
+ {
+ // Decode metadata
+ address token;
+ uint256 tokenId;
+ (token, tokenId) = decodeMetadata(assetMetadata);
+
+ // There exists only 1 of each token.
+ require(amount == 1);
+
+ // Call ERC721 contract. Either succeeds or throws.
+ ERC721Token(token).transferFrom(from, to, tokenId);
+ }
+
+ /// @dev Encodes ERC721 byte array for the ERC20 asset proxy.
+ /// @param assetProxyId Id of the asset proxy.
+ /// @param tokenAddress Address of the asset.
+ /// @param tokenId Id of ERC721 token.
+ /// @return assetMetadata Byte array encoded for the ERC721 asset proxy.
+ function encodeMetadata(
+ uint8 assetProxyId,
+ address tokenAddress,
+ uint256 tokenId)
+ public pure
+ returns (bytes assetMetadata)
+ {
+ // 0 is reserved as invalid proxy id
+ require(assetProxyId != 0);
+
+ // Encode fields into a byte array
+ assetMetadata = new bytes(53);
+ assetMetadata[0] = byte(assetProxyId);
+ writeAddress(tokenAddress, assetMetadata, 1);
+ writeUint256(tokenId, assetMetadata, 21);
+ return assetMetadata;
+ }
+
+ /// @dev Decodes ERC721-encoded byte array for the ERC721 asset proxy.
+ /// @param assetMetadata Byte array encoded for the ERC721 asset proxy.
+ /// @return tokenAddress Address of ERC721 token.
+ /// @return tokenId Id of ERC721 token.
+ function decodeMetadata(bytes assetMetadata)
+ public pure
+ returns (address tokenAddress, uint256 tokenId)
+ {
+ require(assetMetadata.length == 53);
+ tokenAddress = readAddress(assetMetadata, 1);
+ tokenId = readUint256(assetMetadata, 21);
+ return (tokenAddress, tokenId);
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
index 7a705a0ee..13623894a 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol
@@ -23,6 +23,8 @@ import "./MixinExchangeCore.sol";
import "./MixinSignatureValidator.sol";
import "./MixinSettlementProxy.sol";
import "./MixinWrapperFunctions.sol";
+import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
+import "../TokenTransferProxy/ITokenTransferProxy.sol";
contract Exchange is
MixinExchangeCore,
@@ -34,11 +36,12 @@ contract Exchange is
function Exchange(
IToken _zrxToken,
- ITokenTransferProxy _tokenTransferProxy)
+ bytes _zrxProxyMetadata,
+ IAssetProxyDispatcher _assetProxyDispatcher)
public
MixinExchangeCore()
MixinSignatureValidator()
- MixinSettlementProxy(_tokenTransferProxy, _zrxToken)
+ MixinSettlementProxy(_assetProxyDispatcher, _zrxToken, _zrxProxyMetadata)
MixinWrapperFunctions()
{}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol
index b7164a4e9..3315e270f 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/IExchange.sol
@@ -54,11 +54,6 @@ contract IExchange {
bytes32 indexed orderHash
);
- event LogCancelBefore(
- address indexed maker,
- uint256 salt
- );
-
function ZRX_TOKEN_CONTRACT()
public view
returns (address);
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol
index 5562e692e..759619fc4 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibOrder.sol
@@ -33,7 +33,9 @@ contract LibOrder {
"uint256 makerFee",
"uint256 takerFee",
"uint256 expirationTimeSeconds",
- "uint256 salt"
+ "uint256 salt",
+ "bytes makerAssetProxyData",
+ "bytes takerAssetProxyData"
);
struct Order {
@@ -48,6 +50,8 @@ contract LibOrder {
uint256 takerFee;
uint256 expirationTimeSeconds;
uint256 salt;
+ bytes makerAssetProxyData;
+ bytes takerAssetProxyData;
}
/// @dev Calculates Keccak-256 hash of the order.
@@ -73,7 +77,9 @@ contract LibOrder {
order.makerFee,
order.takerFee,
order.expirationTimeSeconds,
- order.salt
+ order.salt,
+ order.makerAssetProxyData,
+ order.takerAssetProxyData
)
);
return orderHash;
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol b/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol
index 0cf636840..2ff244e98 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/LibPartialAmount.sol
@@ -36,5 +36,3 @@ contract LibPartialAmount is SafeMath {
return partialAmount;
}
}
-
-
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol
index 2d7e98184..be5fec96a 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol
@@ -20,22 +20,22 @@ pragma solidity ^0.4.21;
pragma experimental ABIEncoderV2;
import "./mixins/MSettlement.sol";
-import "../TokenTransferProxy/ITokenTransferProxy.sol";
import "../../tokens/Token/IToken.sol";
import "./LibPartialAmount.sol";
+import "../AssetProxyDispatcher/IAssetProxyDispatcher.sol";
/// @dev Provides MixinSettlement
contract MixinSettlementProxy is
MSettlement,
LibPartialAmount
{
-
- ITokenTransferProxy TRANSFER_PROXY;
+ IAssetProxyDispatcher TRANSFER_PROXY;
+ bytes ZRX_PROXY_METADATA;
IToken ZRX_TOKEN;
function transferProxy()
- external view
- returns (ITokenTransferProxy)
+ public view
+ returns (IAssetProxyDispatcher)
{
return TRANSFER_PROXY;
}
@@ -47,15 +47,26 @@ contract MixinSettlementProxy is
return ZRX_TOKEN;
}
+ function zrxProxyMetadata()
+ external view
+ returns (bytes)
+ {
+ return ZRX_PROXY_METADATA;
+ }
+
function MixinSettlementProxy(
- ITokenTransferProxy _proxyContract,
- IToken _zrxToken)
+ IAssetProxyDispatcher assetProxyDispatcherContract,
+ IToken zrxToken,
+ bytes zrxProxyMetadata)
public
{
- ZRX_TOKEN = _zrxToken;
- TRANSFER_PROXY = _proxyContract;
+ ZRX_TOKEN = zrxToken;
+ TRANSFER_PROXY = assetProxyDispatcherContract;
+ ZRX_PROXY_METADATA = zrxProxyMetadata;
}
+
+
function settleOrder(
Order memory order,
address takerAddress,
@@ -68,43 +79,35 @@ contract MixinSettlementProxy is
)
{
makerTokenFilledAmount = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerTokenAmount);
- require(
- TRANSFER_PROXY.transferFrom(
- order.makerTokenAddress,
- order.makerAddress,
- takerAddress,
- makerTokenFilledAmount
- )
+ TRANSFER_PROXY.transferFrom(
+ order.makerAssetProxyData,
+ order.makerAddress,
+ takerAddress,
+ makerTokenFilledAmount
);
- require(
- TRANSFER_PROXY.transferFrom(
- order.takerTokenAddress,
- takerAddress,
- order.makerAddress,
- takerTokenFilledAmount
- )
+ TRANSFER_PROXY.transferFrom(
+ order.takerAssetProxyData,
+ takerAddress,
+ order.makerAddress,
+ takerTokenFilledAmount
);
if (order.feeRecipientAddress != address(0)) {
if (order.makerFee > 0) {
makerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.makerFee);
- require(
- TRANSFER_PROXY.transferFrom(
- ZRX_TOKEN,
- order.makerAddress,
- order.feeRecipientAddress,
- makerFeePaid
- )
+ TRANSFER_PROXY.transferFrom(
+ ZRX_PROXY_METADATA,
+ order.makerAddress,
+ order.feeRecipientAddress,
+ makerFeePaid
);
}
if (order.takerFee > 0) {
takerFeePaid = getPartialAmount(takerTokenFilledAmount, order.takerTokenAmount, order.takerFee);
- require(
- TRANSFER_PROXY.transferFrom(
- ZRX_TOKEN,
- takerAddress,
- order.feeRecipientAddress,
- takerFeePaid
- )
+ TRANSFER_PROXY.transferFrom(
+ ZRX_PROXY_METADATA,
+ takerAddress,
+ order.feeRecipientAddress,
+ takerFeePaid
);
}
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
index 8f52043c4..fdc906076 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
@@ -65,76 +65,163 @@ contract MixinWrapperFunctions is
// We need to call MExchangeCore.fillOrder using a delegatecall in
// assembly so that we can intercept a call that throws. For this, we
// need the input encoded in memory in the Ethereum ABIv2 format [1].
-
- // | Offset | Length | Contents |
- // |--------|---------|------------------------------|
- // | 0 | 4 | function selector |
- // | 4 | 11 * 32 | Order order |
- // | 356 | 32 | uint256 takerTokenFillAmount |
- // | 388 | 32 | offset to signature (416) |
- // | 420 | 32 | len(signature) |
- // | 452 | (1) | signature |
- // | (2) | (3) | padding (zero) |
- // | (4) | | end of input |
-
- // (1): len(signature)
- // (2): 452 + len(signature)
- // (3): (32 - len(signature)) mod 32
- // (4): 452 + len(signature) + (32 - len(signature)) mod 32
-
+
+ // | Area | Offset | Length | Contents |
+ // | -------- |--------|---------|-------------------------------------------- |
+ // | Header | 0x00 | 4 | function selector |
+ // | Params | | 3 * 32 | function parameters: |
+ // | | 0x00 | | 1. offset to order (*) |
+ // | | 0x20 | | 2. takerTokenFillAmount |
+ // | | 0x40 | | 3. offset to signature (*) |
+ // | Data | | 13 * 32 | order: |
+ // | | 0x000 | | 1. makerAddress |
+ // | | 0x020 | | 2. takerAddress |
+ // | | 0x040 | | 3. makerTokenAddress |
+ // | | 0x060 | | 4. takerTokenAddress |
+ // | | 0x080 | | 5. feeRecipientAddress |
+ // | | 0x0A0 | | 6. makerTokenAmount |
+ // | | 0x0C0 | | 7. takerTokenAmount |
+ // | | 0x0E0 | | 8. makerFeeAmount |
+ // | | 0x100 | | 9. takerFeeAmount |
+ // | | 0x120 | | 10. expirationTimeSeconds |
+ // | | 0x140 | | 11. salt |
+ // | | 0x160 | | 12. Offset to makerAssetProxyMetadata (*) |
+ // | | 0x180 | | 13. Offset to takerAssetProxyMetadata (* |
+ // | | 0x1A0 | 32 | makerAssetProxyMetadata Length |
+ // | | 0x1C0 | ** | makerAssetProxyMetadata Contents |
+ // | | 0x1E0 | 32 | takerAssetProxyMetadata Length |
+ // | | 0x200 | ** | takerAssetProxyMetadata Contents |
+ // | | 0x220 | 32 | signature Length |
+ // | | 0x240 | ** | signature Contents |
+
+ // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
+ // An offset stored in the Params area is calculated from the beginning of the Params section.
+ // An offset stored in the Data area is calculated from the beginning of the Data section.
+
+ // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
+
// [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
bytes4 fillOrderSelector = this.fillOrder.selector;
assembly {
+
+ // Areas below may use the following variables:
+ // 1. <area>Start -- Start of this area in memory
+ // 2. <area>End -- End of this area in memory. This value may
+ // be precomputed (before writing contents),
+ // or it may be computed as contents are written.
+ // 3. <area>Offset -- Current offset into area. If an area's End
+ // is precomputed, this variable tracks the
+ // offsets of contents as they are written.
+
+ /////// Setup Header Area ///////
// Load free memory pointer
- let start := mload(0x40)
-
- // Write function signature
- mstore(start, fillOrderSelector)
-
- // Write order struct
- mstore(add(start, 4), mload(order)) // makerAddress
- mstore(add(start, 36), mload(add(order, 32))) // takerAddress
- mstore(add(start, 68), mload(add(order, 64))) // makerTokenAddress
- mstore(add(start, 100), mload(add(order, 96))) // takerTokenAddress
- mstore(add(start, 132), mload(add(order, 128))) // feeRecipientAddress
- mstore(add(start, 164), mload(add(order, 160))) // makerTokenAmount
- mstore(add(start, 196), mload(add(order, 192))) // takerTokenAmount
- mstore(add(start, 228), mload(add(order, 224))) // makerFeeAmount
- mstore(add(start, 260), mload(add(order, 256))) // takerFeeAmount
- mstore(add(start, 292), mload(add(order, 288))) // expirationTimeSeconds
- mstore(add(start, 324), mload(add(order, 320))) // salt
-
- // Write takerTokenFillAmount
- mstore(add(start, 356), takerTokenFillAmount)
-
- // Write signature offset
- mstore(add(start, 388), 416)
-
- // Write signature length
- let sigLen := mload(signature)
- mstore(add(start, 420), sigLen)
-
- // Calculate signature length with padding
- let paddingLen := mod(sub(0, sigLen), 32)
- let sigLenWithPadding := add(sigLen, paddingLen)
-
- // Write signature
- let sigStart := add(signature, 32)
- for { let curr := 0 }
- lt(curr, sigLenWithPadding)
- { curr := add(curr, 32) }
- { mstore(add(start, add(452, curr)), mload(add(sigStart, curr))) } // Note: we assume that padding consists of only 0's
+ let headerAreaStart := mload(0x40)
+ mstore(headerAreaStart, fillOrderSelector)
+ let headerAreaEnd := add(headerAreaStart, 0x4)
+
+ /////// Setup Params Area ///////
+ // This area is preallocated and written to later.
+ // This is because we need to fill in offsets that have not yet been calculated.
+ let paramsAreaStart := headerAreaEnd
+ let paramsAreaEnd := add(paramsAreaStart, 0x60)
+ let paramsAreaOffset := paramsAreaStart
+
+ /////// Setup Data Area ///////
+ let dataAreaStart := paramsAreaEnd
+ let dataAreaEnd := dataAreaStart
+
+ // Offset from the source data we're reading from
+ let sourceOffset := order
+ // bytesLen and bytesLenPadded track the length of a dynamically-allocated bytes array.
+ let bytesLen := 0
+ let bytesLenPadded := 0
+
+ /////// Write order Struct ///////
+ // Write memory location of Order, relative to the start of the
+ // parameter list, then increment the paramsAreaOffset respectively.
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ // Write values for each field in the order
+ for{let i := 0} lt(i, 13) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Write offset to <order.makerAssetProxyMetadata>
+ mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.makerAssetProxyMetadata>
+ bytesLen := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
+
+ // Write length of <order.makerAssetProxyMetadata>
+ mstore(dataAreaEnd, bytesLen)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.makerAssetProxyMetadata>
+ for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ // Write offset to <order.takerAssetProxyMetadata>
+ mstore(add(dataAreaStart, mul(12, 0x20)), sub(dataAreaEnd, dataAreaStart))
+
+ // Calculate length of <order.takerAssetProxyMetadata>
+ bytesLen := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
+
+ // Write length of <order.takerAssetProxyMetadata>
+ mstore(dataAreaEnd, bytesLen)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of <order.takerAssetProxyMetadata>
+ for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
+
+ /////// Write takerTokenFillAmount ///////
+ mstore(paramsAreaOffset, takerTokenFillAmount)
+ paramsAreaOffset := add(paramsAreaOffset, 0x20)
+
+ /////// Write signature ///////
+ // Write offset to paramsArea
+ mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
+
+ // Calculate length of signature
+ sourceOffset := signature
+ bytesLen := mload(sourceOffset)
+ sourceOffset := add(sourceOffset, 0x20)
+ bytesLenPadded := add(div(bytesLen, 0x20), gt(mod(bytesLen, 0x20), 0))
+
+ // Write length of signature
+ mstore(dataAreaEnd, bytesLen)
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+
+ // Write contents of signature
+ for {let i := 0} lt(i, bytesLenPadded) {i := add(i, 1)} {
+ mstore(dataAreaEnd, mload(sourceOffset))
+ dataAreaEnd := add(dataAreaEnd, 0x20)
+ sourceOffset := add(sourceOffset, 0x20)
+ }
// Execute delegatecall
let success := delegatecall(
- gas, // forward all gas, TODO: look into gas consumption of assert/throw
- address, // call address of this contract
- start, // pointer to start of input
- add(452, sigLenWithPadding), // input length is 420 + signature length + padding length
- start, // write output over input
- 128 // output size is 128 bytes
+ gas, // forward all gas, TODO: look into gas consumption of assert/throw
+ address, // call address of this contract
+ headerAreaStart, // pointer to start of input
+ sub(dataAreaEnd, headerAreaStart), // length of input
+ headerAreaStart, // write output over input
+ 128 // output size is 128 bytes
)
switch success
case 0 {
@@ -144,12 +231,11 @@ contract MixinWrapperFunctions is
mstore(add(fillResults, 96), 0)
}
case 1 {
- mstore(fillResults, mload(start))
- mstore(add(fillResults, 32), mload(add(start, 32)))
- mstore(add(fillResults, 64), mload(add(start, 64)))
- mstore(add(fillResults, 96), mload(add(start, 96)))
+ mstore(fillResults, mload(headerAreaStart))
+ mstore(add(fillResults, 32), mload(add(headerAreaStart, 32)))
+ mstore(add(fillResults, 64), mload(add(headerAreaStart, 64)))
+ mstore(add(fillResults, 96), mload(add(headerAreaStart, 96)))
}
-
}
return fillResults;
}
@@ -228,10 +314,10 @@ contract MixinWrapperFunctions is
// Token being sold by taker must be the same for each order
require(orders[i].takerTokenAddress == orders[0].takerTokenAddress);
-
+
// Calculate the remaining amount of takerToken to sell
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
-
+
// Attempt to sell the remaining amount of takerToken
FillResults memory singleFillResults = fillOrder(
orders[i],
@@ -270,7 +356,7 @@ contract MixinWrapperFunctions is
// Calculate the remaining amount of takerToken to sell
uint256 remainingTakerTokenFillAmount = safeSub(takerTokenFillAmount, totalFillResults.takerTokenFilledAmount);
-
+
// Attempt to sell the remaining amount of takerToken
FillResults memory singleFillResults = fillOrderNoThrow(
orders[i],
@@ -308,7 +394,7 @@ contract MixinWrapperFunctions is
// Calculate the remaining amount of makerToken to buy
uint256 remainingMakerTokenFillAmount = safeSub(makerTokenFillAmount, totalFillResults.makerTokenFilledAmount);
-
+
// Convert the remaining amount of makerToken to buy into remaining amount
// of takerToken to sell, assuming entire amount can be sold in the current order
uint256 remainingTakerTokenFillAmount = getPartialAmount(
@@ -405,5 +491,5 @@ contract MixinWrapperFunctions is
totalFillResults.makerFeePaid = safeAdd(totalFillResults.makerFeePaid, singleFillResults.makerFeePaid);
totalFillResults.takerFeePaid = safeAdd(totalFillResults.takerFeePaid, singleFillResults.takerFeePaid);
}
-
+
}
diff --git a/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
new file mode 100644
index 000000000..61732a382
--- /dev/null
+++ b/packages/contracts/src/contracts/current/test/DummyERC721Token/DummyERC721Token.sol
@@ -0,0 +1,46 @@
+/*
+
+ 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.21;
+import "/zeppelin/contracts/token/ERC721/ERC721Token.sol";
+import "../../utils/Ownable/Ownable.sol";
+
+contract DummyERC721Token is
+ Ownable,
+ ERC721Token
+{
+ function DummyERC721Token(
+ string name,
+ string symbol)
+ public
+ ERC721Token(name, symbol)
+ {}
+
+ /**
+ * @dev Internal function to mint a new token
+ * @dev Reverts if the given token ID already exists
+ * @param to address the beneficiary that will own the minted token
+ * @param tokenId uint256 ID of the token to be minted by the msg.sender
+ */
+ function mint(address to, uint256 tokenId)
+ public
+ onlyOwner
+ {
+ super._mint(to, tokenId);
+ }
+}
diff --git a/packages/contracts/src/contracts/current/utils/Authorizable/Authorizable.sol b/packages/contracts/src/contracts/current/utils/Authorizable/Authorizable.sol
new file mode 100644
index 000000000..cc27dd107
--- /dev/null
+++ b/packages/contracts/src/contracts/current/utils/Authorizable/Authorizable.sol
@@ -0,0 +1,109 @@
+/*
+
+ 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.21;
+
+import "./IAuthorizable.sol";
+import "../Ownable/Ownable.sol";
+
+contract Authorizable is
+ Ownable,
+ IAuthorizable
+{
+
+ /// @dev Only authorized addresses can invoke functions with this modifier.
+ modifier onlyAuthorized {
+ require(authorized[msg.sender]);
+ _;
+ }
+
+ modifier targetAuthorized(address target) {
+ require(authorized[target]);
+ _;
+ }
+
+ modifier targetNotAuthorized(address target) {
+ require(!authorized[target]);
+ _;
+ }
+
+ mapping (address => bool) public authorized;
+ address[] public authorities;
+
+ /*
+ * Public functions
+ */
+
+ /// @dev Authorizes an address.
+ /// @param target Address to authorize.
+ function addAuthorizedAddress(address target)
+ public
+ onlyOwner
+ targetNotAuthorized(target)
+ {
+ authorized[target] = true;
+ authorities.push(target);
+ emit LogAuthorizedAddressAdded(target, msg.sender);
+ }
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ function removeAuthorizedAddress(address target)
+ public
+ onlyOwner
+ targetAuthorized(target)
+ {
+ delete authorized[target];
+ for (uint i = 0; i < authorities.length; i++) {
+ if (authorities[i] == target) {
+ authorities[i] = authorities[authorities.length - 1];
+ authorities.length -= 1;
+ break;
+ }
+ }
+ emit LogAuthorizedAddressRemoved(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)
+ public
+ {
+ require(index < authorities.length);
+ require(authorities[index] == target);
+ delete authorized[target];
+ authorities[index] = authorities[authorities.length - 1];
+ authorities.length -= 1;
+ emit LogAuthorizedAddressRemoved(target, msg.sender);
+ }
+
+ /*
+ * Public constant functions
+ */
+
+ /// @dev Gets all authorized addresses.
+ /// @return Array of authorized addresses.
+ function getAuthorizedAddresses()
+ public
+ constant
+ returns (address[])
+ {
+ return authorities;
+ }
+}
diff --git a/packages/contracts/src/contracts/current/utils/Authorizable/IAuthorizable.sol b/packages/contracts/src/contracts/current/utils/Authorizable/IAuthorizable.sol
new file mode 100644
index 000000000..903fc1667
--- /dev/null
+++ b/packages/contracts/src/contracts/current/utils/Authorizable/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.21;
+
+/// @title TokenTransferProxy - Transfers tokens on behalf of contracts that have been approved via decentralized governance.
+contract IAuthorizable {
+
+ /// @dev Gets all authorized addresses.
+ /// @return Array of authorized addresses.
+ function getAuthorizedAddresses()
+ public view
+ returns (address[]);
+
+ /// @dev Authorizes an address.
+ /// @param target Address to authorize.
+ function addAuthorizedAddress(address target)
+ public;
+
+ /// @dev Removes authorizion of an address.
+ /// @param target Address to remove authorization from.
+ function removeAuthorizedAddress(address target)
+ public;
+
+ /// @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)
+ public;
+
+ event LogAuthorizedAddressAdded(
+ address indexed target,
+ address indexed caller);
+
+ event LogAuthorizedAddressRemoved(
+ address indexed target,
+ address indexed caller);
+}
diff --git a/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
new file mode 100644
index 000000000..32a51d8ab
--- /dev/null
+++ b/packages/contracts/src/contracts/current/utils/LibBytes/LibBytes.sol
@@ -0,0 +1,126 @@
+/*
+
+ 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.21;
+
+contract LibBytes {
+
+ /// @dev Reads an address from a position in a byte array.
+ /// @param b Byte array containing an address.
+ /// @param index Index in byte array of address.
+ /// @return address from byte array.
+ function readAddress(
+ bytes b,
+ uint256 index)
+ public pure
+ returns (address result)
+ {
+ require(b.length >= index + 20); // 20 is length of address
+
+ // Add offset to index:
+ // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
+ // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
+ index += 20;
+
+ // Read address from array memory
+ assembly {
+ // 1. Add index to to address of bytes array
+ // 2. Load 32-byte word from memory
+ // 3. Apply 20-byte mask to obtain address
+ result := and(mload(add(b, index)), 0xffffffffffffffffffffffffffffffffffffffff)
+ }
+ return result;
+ }
+
+ /// @dev Writes an address into a specific position in a byte array.
+ /// @param input Address to put into byte array.
+ /// @param b Byte array to insert address into.
+ /// @param index Index in byte array of address.
+ function writeAddress(
+ address input,
+ bytes b,
+ uint256 index)
+ public pure
+ {
+ require(b.length >= index + 20); // 20 is length of address
+
+ // Add offset to index:
+ // 1. Arrays are prefixed by 32-byte length parameter (add 32 to index)
+ // 2. Account for size difference between address length and 32-byte storage word (subtract 12 from index)
+ index += 20;
+
+ // Store address into array memory
+ assembly {
+ // The address occupies 20 bytes and mstore stores 32 bytes.
+ // First fetch the 32-byte word where we'll be storing the address, then
+ // apply a mask so we have only the bytes in the word that the address will not occupy.
+ // Then combine these bytes with the address and store the 32 bytes back to memory with mstore.
+
+ // 1. Add index to address of bytes array
+ // 2. Load 32-byte word from memory
+ // 3. Apply 12-byte mask to obtain extra bytes occupying word of memory where we'll store the address
+ let neighbors := and(mload(add(b, index)), 0xffffffffffffffffffffffff0000000000000000000000000000000000000000)
+
+ // Store the neighbors and address into memory
+ mstore(add(b, index), xor(input, neighbors))
+ }
+ }
+
+ /// @dev Reads a uint256 value from a position in a byte array.
+ /// @param b Byte array containing a uint256 value.
+ /// @param index Index in byte array of uint256 value.
+ /// @return uint256 value from byte array.
+ function readUint256(
+ bytes b,
+ uint256 index)
+ public pure
+ returns (uint256 result)
+ {
+ require(b.length >= index + 32);
+
+ // Arrays are prefixed by a 256 bit length parameter
+ index += 32;
+
+ // Read the uint256 from array memory
+ assembly {
+ result := mload(add(b, index))
+ }
+ return result;
+ }
+
+ /// @dev Writes a uint256 into a specific position in a byte array.
+ /// @param input uint256 to put into byte array.
+ /// @param b Byte array to insert <input> into.
+ /// @param index Index in byte array of <input>.
+ function writeUint256(
+ uint256 input,
+ bytes b,
+ uint256 index)
+ public pure
+ {
+ require(b.length >= index + 32);
+
+ // Arrays are prefixed by a 256 bit length parameter
+ index += 32;
+
+ // Read the uint256 from array memory
+ assembly {
+ mstore(add(b, index), input)
+ }
+ }
+}
diff --git a/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol b/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol
new file mode 100644
index 000000000..7784a7ba9
--- /dev/null
+++ b/packages/contracts/src/contracts/current/utils/Ownable/IOwnable.sol
@@ -0,0 +1,13 @@
+pragma solidity ^0.4.21;
+
+/*
+ * Ownable
+ *
+ * Base contract with an owner.
+ * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
+ */
+
+contract IOwnable {
+ function transferOwnership(address newOwner)
+ public;
+}
diff --git a/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol b/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
index cb50f7252..91a5cb7ae 100644
--- a/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
+++ b/packages/contracts/src/contracts/current/utils/Ownable/Ownable.sol
@@ -7,7 +7,9 @@ pragma solidity ^0.4.21;
* Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner.
*/
-contract Ownable {
+import "../Ownable/IOwnable.sol";
+
+contract Ownable is IOwnable {
address public owner;
function Ownable()
diff --git a/packages/contracts/src/utils/asset_proxy_utils.ts b/packages/contracts/src/utils/asset_proxy_utils.ts
new file mode 100644
index 000000000..938110a75
--- /dev/null
+++ b/packages/contracts/src/utils/asset_proxy_utils.ts
@@ -0,0 +1,67 @@
+import { BigNumber } from '@0xproject/utils';
+import * as Web3 from 'web3';
+
+import { AssetProxyId } from './types';
+const ethersUtils = require('ethers-utils');
+
+export function zeroPad(value: string, width: number): string {
+ return '0'.repeat(width - value.length) + value;
+}
+
+export function encodeAssetProxyId(assetProxyId: AssetProxyId, encoded_metadata: { value: string }) {
+ encoded_metadata.value += zeroPad(new BigNumber(assetProxyId).toString(16), 2);
+}
+
+export function encodeAddress(address: string, encoded_metadata: { value: string }) {
+ encoded_metadata.value += zeroPad(address.replace('0x', ''), 40);
+}
+
+export function encodeUint256(value: BigNumber, encoded_metadata: { value: string }) {
+ encoded_metadata.value += zeroPad(value.toString(16), 64);
+}
+
+export function encodeERC20ProxyMetadata_V1(tokenAddress: string) {
+ // Encode metadata
+ const encoded_metadata = { value: '0x' };
+ encodeAssetProxyId(AssetProxyId.ERC20_V1, encoded_metadata);
+ encodeAddress(tokenAddress, encoded_metadata);
+
+ // Verify encoding length - '0x' plus 21 bytes of encoded data
+ if (encoded_metadata.value.length != 44) {
+ throw Error('Bad encoding length. Expected 44, got ' + encoded_metadata.value.length);
+ }
+
+ // Return encoded metadata
+ return encoded_metadata.value;
+}
+
+export function encodeERC20ProxyMetadata(tokenAddress: string) {
+ // Encode metadata
+ const encoded_metadata = { value: '0x' };
+ encodeAssetProxyId(AssetProxyId.ERC20, encoded_metadata);
+ encodeAddress(tokenAddress, encoded_metadata);
+
+ // Verify encoding length - '0x' plus 21 bytes of encoded data
+ if (encoded_metadata.value.length != 44) {
+ throw Error('Bad encoding length. Expected 44, got ' + encoded_metadata.value.length);
+ }
+
+ // Return encoded metadata
+ return encoded_metadata.value;
+}
+
+export function encodeERC721ProxyMetadata(tokenAddress: string, tokenId: BigNumber) {
+ // Encode metadata
+ const encoded_metadata = { value: '0x' };
+ encodeAssetProxyId(AssetProxyId.ERC721, encoded_metadata);
+ encodeAddress(tokenAddress, encoded_metadata);
+ encodeUint256(tokenId, encoded_metadata);
+
+ // Verify encoding length - '0x' plus 53 bytes of encoded data
+ if (encoded_metadata.value.length != 108) {
+ throw Error('Bad encoding length. Expected 108, got ' + encoded_metadata.value.length);
+ }
+
+ // Return encoded metadata
+ return encoded_metadata.value;
+}
diff --git a/packages/contracts/src/utils/constants.ts b/packages/contracts/src/utils/constants.ts
index d31e1e285..49872fc59 100644
--- a/packages/contracts/src/utils/constants.ts
+++ b/packages/contracts/src/utils/constants.ts
@@ -26,5 +26,6 @@ export const constants = {
MAX_TOKEN_TRANSFERFROM_GAS: 80000,
MAX_TOKEN_APPROVE_GAS: 60000,
DUMMY_TOKEN_ARGS: [DUMMY_TOKEN_NAME, DUMMY_TOKEN_SYMBOL, DUMMY_TOKEN_DECIMALS, DUMMY_TOKEN_TOTAL_SUPPLY],
+ DUMMY_ERC721TOKEN_ARGS: [DUMMY_TOKEN_NAME, DUMMY_TOKEN_SYMBOL],
TESTRPC_PRIVATE_KEYS: _.map(TESTRPC_PRIVATE_KEYS_STRINGS, privateKeyString => ethUtil.toBuffer(privateKeyString)),
};
diff --git a/packages/contracts/src/utils/crypto.ts b/packages/contracts/src/utils/crypto.ts
index 810072d2f..5bc678cdf 100644
--- a/packages/contracts/src/utils/crypto.ts
+++ b/packages/contracts/src/utils/crypto.ts
@@ -31,6 +31,8 @@ export const crypto = {
argTypes.push('address');
} else if (_.isString(arg)) {
argTypes.push('string');
+ } else if (arg instanceof Buffer) {
+ argTypes.push('bytes');
} else if (_.isBoolean(arg)) {
argTypes.push('bool');
} else {
diff --git a/packages/contracts/src/utils/order_utils.ts b/packages/contracts/src/utils/order_utils.ts
index 26336c81d..8eb4da35a 100644
--- a/packages/contracts/src/utils/order_utils.ts
+++ b/packages/contracts/src/utils/order_utils.ts
@@ -35,6 +35,8 @@ export const orderUtils = {
takerFee: signedOrder.takerFee,
expirationTimeSeconds: signedOrder.expirationTimeSeconds,
salt: signedOrder.salt,
+ makerAssetProxyData: signedOrder.makerAssetProxyData,
+ takerAssetProxyData: signedOrder.takerAssetProxyData,
};
return orderStruct;
},
@@ -52,6 +54,8 @@ export const orderUtils = {
'uint256 takerFee',
'uint256 expirationTimeSeconds',
'uint256 salt',
+ 'bytes makerAssetProxyData',
+ 'bytes takerAssetProxyData',
]);
const orderParamsHashBuff = crypto.solSHA3([
order.exchangeAddress,
@@ -66,6 +70,8 @@ export const orderUtils = {
order.takerFee,
order.expirationTimeSeconds,
order.salt,
+ ethUtil.toBuffer(order.makerAssetProxyData),
+ ethUtil.toBuffer(order.takerAssetProxyData),
]);
const orderSchemaHashHex = `0x${orderSchemaHashBuff.toString('hex')}`;
const orderParamsHashHex = `0x${orderParamsHashBuff.toString('hex')}`;
diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts
index ed0ebeee9..f1636929b 100644
--- a/packages/contracts/src/utils/types.ts
+++ b/packages/contracts/src/utils/types.ts
@@ -37,6 +37,13 @@ export interface CancelOrdersBefore {
salt: BigNumber;
}
+export enum AssetProxyId {
+ INVALID,
+ ERC20_V1,
+ ERC20,
+ ERC721,
+}
+
export interface DefaultOrderParams {
exchangeAddress: string;
makerAddress: string;
@@ -45,8 +52,10 @@ export interface DefaultOrderParams {
takerTokenAddress: string;
makerTokenAmount: BigNumber;
takerTokenAmount: BigNumber;
- makerFee: BigNumber;
- takerFee: BigNumber;
+ makerFeeAmount: BigNumber;
+ takerFeeAmount: BigNumber;
+ makerAssetProxyData: string;
+ takerAssetProxyData: string;
}
export interface TransactionDataParams {
@@ -100,6 +109,11 @@ export enum ContractName {
AccountLevels = 'AccountLevels',
EtherDelta = 'EtherDelta',
Arbitrage = 'Arbitrage',
+ AssetProxyDispatcher = 'AssetProxyDispatcher',
+ ERC20TransferProxy = 'ERC20TransferProxy',
+ ERC20TransferProxy_V1 = 'ERC20TransferProxy_v1',
+ ERC721TransferProxy = 'ERC721TransferProxy',
+ DummyERC721Token = 'DummyERC721Token',
}
export interface Artifact {
@@ -134,6 +148,8 @@ export interface OrderStruct {
takerFee: BigNumber;
expirationTimeSeconds: BigNumber;
salt: BigNumber;
+ makerAssetProxyData: string;
+ takerAssetProxyData: string;
}
export interface UnsignedOrder extends OrderStruct {