diff options
author | Greg Hysen <greg.hysen@gmail.com> | 2018-04-11 08:53:34 +0800 |
---|---|---|
committer | Amir Bandeali <abandeali1@gmail.com> | 2018-04-21 04:56:17 +0800 |
commit | 78d81f193f3b9358ab86819f83c76b8bcd52a9c9 (patch) | |
tree | 6f3532ebbf6ed2bc3d07747ba442ec77d4d9ebe3 /packages/contracts/src | |
parent | b9e0cd4512e6c7cd7584961df6ba106541826836 (diff) | |
download | dexon-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')
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 { |