From a8a0a5cbfb7299cd22181c8713e1da6dec412dae Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 22 Apr 2018 15:30:08 -0700 Subject: Combine Exchange with AssetProxyDispatcher --- .../current/protocol/AssetProxy/IAssetProxy.sol | 36 +++++++ .../protocol/AssetProxy/proxies/ERC20Proxy.sol | 50 ++++++++++ .../protocol/AssetProxy/proxies/ERC721Proxy.sol | 58 ++++++++++++ .../AssetProxyDispatcher/AssetProxyDispatcher.sol | 87 ----------------- .../protocol/AssetProxyDispatcher/IAssetProxy.sol | 36 ------- .../AssetProxyDispatcher/IAssetProxyDispatcher.sol | 56 ----------- .../AssetProxyDispatcher/proxies/ERC20Proxy.sol | 50 ---------- .../AssetProxyDispatcher/proxies/ERC721Proxy.sol | 58 ------------ .../current/protocol/Exchange/Exchange.sol | 12 ++- .../Exchange/MixinAssetProxyDispatcher.sol | 87 +++++++++++++++++ .../current/protocol/Exchange/MixinSettlement.sol | 88 ++++++++++++++++++ .../protocol/Exchange/MixinSettlementProxy.sol | 103 --------------------- .../Exchange/mixins/MAssetProxyDispatcher.sol | 60 ++++++++++++ 13 files changed, 386 insertions(+), 395 deletions(-) create mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/IAssetProxy.sol create mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC20Proxy.sol create mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20Proxy.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721Proxy.sol create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol delete mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol create mode 100644 packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol (limited to 'packages') diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/IAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/IAssetProxy.sol new file mode 100644 index 000000000..60e74723d --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/IAssetProxy.sol @@ -0,0 +1,36 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity ^0.4.21; + +import "../../utils/Authorizable/IAuthorizable.sol"; + +contract IAssetProxy is IAuthorizable { + + /// @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) + external; +} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC20Proxy.sol new file mode 100644 index 000000000..eef3a39db --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC20Proxy.sol @@ -0,0 +1,50 @@ +/* + + 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 "../../../tokens/ERC20Token/IERC20Token.sol"; + +contract ERC20Proxy is + LibBytes, + Authorizable, + IAssetProxy +{ + + /// @dev Transfers ERC20 tokens. Either succeeds or throws. + /// @param assetMetadata ERC20-encoded byte array. + /// @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) + external + onlyAuthorized + { + require(assetMetadata.length == 21); + address token = readAddress(assetMetadata, 1); + bool success = IERC20Token(token).transferFrom(from, to, amount); + require(success == true); + } +} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol new file mode 100644 index 000000000..466d0b871 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/proxies/ERC721Proxy.sol @@ -0,0 +1,58 @@ +/* + + 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-solidity/contracts/token/ERC721/ERC721Token.sol"; + +contract ERC721Proxy is + LibBytes, + Authorizable, + IAssetProxy +{ + + /// @dev Transfers ERC721 tokens. Either succeeds or throws. + /// @param assetMetadata ERC721-encoded byte array + /// @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) + external + onlyAuthorized + { + // There exists only 1 of each token. + require(amount == 1); + + // Decode metadata + require(assetMetadata.length == 53); + address token = readAddress(assetMetadata, 1); + uint256 tokenId = readUint256(assetMetadata, 21); + + // Either succeeds or throws. + // @TODO: Call safeTransferFrom if there is additional + // data stored in `assetMetadata`. + ERC721Token(token).transferFrom(from, to, tokenId); + } +} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol deleted file mode 100644 index 43fd7596e..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/AssetProxyDispatcher.sol +++ /dev/null @@ -1,87 +0,0 @@ -/* - - 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 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) - external - onlyAuthorized - { - // Lookup asset proxy - require(assetMetadata.length >= 1); - uint8 assetProxyId = uint8(assetMetadata[0]); - IAssetProxy assetProxy = assetProxies[assetProxyId]; - - // transferFrom will either succeed or throw. - assetProxy.transferFrom(assetMetadata, from, to, amount); - } - - /// @dev Registers an asset proxy to an asset proxy id. - /// An id can only be assigned to a single proxy at a given time, - /// however, an asset proxy may be registered to multiple ids. - /// @param assetProxyId Id to register`newAssetProxy` under. - /// @param newAssetProxy Address of new asset proxy to register, or 0x0 to unset assetProxyId. - /// @param oldAssetProxy Existing asset proxy to overwrite, or 0x0 if assetProxyId is currently unused. - function registerAssetProxy( - uint8 assetProxyId, - address newAssetProxy, - address oldAssetProxy) - external - onlyOwner - { - // Ensure the existing asset proxy is not unintentionally overwritten - require(oldAssetProxy == address(assetProxies[assetProxyId])); - - // Add asset proxy and log registration - assetProxies[assetProxyId] = IAssetProxy(newAssetProxy); - emit AssetProxySet(assetProxyId, newAssetProxy, oldAssetProxy); - } - - /// @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) - external view - returns (address) - { - IAssetProxy assetProxy = assetProxies[assetProxyId]; - return address(assetProxy); - } -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol deleted file mode 100644 index 60e74723d..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxy.sol +++ /dev/null @@ -1,36 +0,0 @@ -/* - - 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 "../../utils/Authorizable/IAuthorizable.sol"; - -contract IAssetProxy is IAuthorizable { - - /// @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) - external; -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol deleted file mode 100644 index 88bc0d353..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/IAssetProxyDispatcher.sol +++ /dev/null @@ -1,56 +0,0 @@ -/* - - 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 "../../utils/Ownable/IOwnable.sol"; -import "../../utils/Authorizable/IAuthorizable.sol"; -import "./IAssetProxy.sol"; - -contract IAssetProxyDispatcher is - IOwnable, - IAuthorizable, - IAssetProxy -{ - - // Logs registration of new asset proxy - event AssetProxySet( - uint8 id, - address newAssetProxy, - address oldAssetProxy - ); - - /// @dev Registers an asset proxy to an asset proxy id. - /// An id can only be assigned to a single proxy at a given time, - /// however, an asset proxy may be registered to multiple ids. - /// @param assetProxyId Id to register`newAssetProxy` under. - /// @param newAssetProxy Address of new asset proxy to register, or 0x0 to unset assetProxyId. - /// @param oldAssetProxy Existing asset proxy to overwrite, or 0x0 if assetProxyId is currently unused. - function registerAssetProxy( - uint8 assetProxyId, - address newAssetProxy, - address oldAssetProxy) - external; - - /// @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) - external view - returns (address); -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20Proxy.sol deleted file mode 100644 index eef3a39db..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC20Proxy.sol +++ /dev/null @@ -1,50 +0,0 @@ -/* - - 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 "../../../tokens/ERC20Token/IERC20Token.sol"; - -contract ERC20Proxy is - LibBytes, - Authorizable, - IAssetProxy -{ - - /// @dev Transfers ERC20 tokens. Either succeeds or throws. - /// @param assetMetadata ERC20-encoded byte array. - /// @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) - external - onlyAuthorized - { - require(assetMetadata.length == 21); - address token = readAddress(assetMetadata, 1); - bool success = IERC20Token(token).transferFrom(from, to, amount); - require(success == true); - } -} diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721Proxy.sol deleted file mode 100644 index 466d0b871..000000000 --- a/packages/contracts/src/contracts/current/protocol/AssetProxyDispatcher/proxies/ERC721Proxy.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - 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-solidity/contracts/token/ERC721/ERC721Token.sol"; - -contract ERC721Proxy is - LibBytes, - Authorizable, - IAssetProxy -{ - - /// @dev Transfers ERC721 tokens. Either succeeds or throws. - /// @param assetMetadata ERC721-encoded byte array - /// @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) - external - onlyAuthorized - { - // There exists only 1 of each token. - require(amount == 1); - - // Decode metadata - require(assetMetadata.length == 53); - address token = readAddress(assetMetadata, 1); - uint256 tokenId = readUint256(assetMetadata, 21); - - // Either succeeds or throws. - // @TODO: Call safeTransferFrom if there is additional - // data stored in `assetMetadata`. - ERC721Token(token).transferFrom(from, to, tokenId); - } -} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol index 995cbcf76..5ccb06b88 100644 --- a/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol +++ b/packages/contracts/src/contracts/current/protocol/Exchange/Exchange.sol @@ -21,24 +21,26 @@ pragma experimental ABIEncoderV2; import "./MixinExchangeCore.sol"; import "./MixinSignatureValidator.sol"; -import "./MixinSettlementProxy.sol"; +import "./MixinSettlement.sol"; import "./MixinWrapperFunctions.sol"; +import "./MixinAssetProxyDispatcher.sol"; contract Exchange is MixinExchangeCore, MixinSignatureValidator, - MixinSettlementProxy, - MixinWrapperFunctions + MixinSettlement, + MixinWrapperFunctions, + MixinAssetProxyDispatcher { string constant public VERSION = "2.0.1-alpha"; function Exchange( - address _assetProxyDispatcher, bytes memory _zrxProxyData) public MixinExchangeCore() MixinSignatureValidator() - MixinSettlementProxy(_assetProxyDispatcher, _zrxProxyData) + MixinSettlement(_zrxProxyData) MixinWrapperFunctions() + MixinAssetProxyDispatcher() {} } diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol new file mode 100644 index 000000000..eab8e04a1 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinAssetProxyDispatcher.sol @@ -0,0 +1,87 @@ +/* + + 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 "./mixins/MAssetProxyDispatcher.sol"; +import "../AssetProxy/IAssetProxy.sol"; +import "../../utils/Ownable/Ownable.sol"; + +contract MixinAssetProxyDispatcher is + Ownable, + MAssetProxyDispatcher +{ + // Mapping from Asset Proxy Id's to their respective Asset Proxy + mapping (uint8 => IAssetProxy) public assetProxies; + + /// @dev Forwards arguments to assetProxy and calls `transferFrom`. 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 dispatchTransferFrom( + bytes memory assetMetadata, + address from, + address to, + uint256 amount) + internal + { + // Do nothing if no amount should be transferred. + if (amount > 0) { + // Lookup asset proxy + require(assetMetadata.length >= 1); + uint8 assetProxyId = uint8(assetMetadata[0]); + IAssetProxy assetProxy = assetProxies[assetProxyId]; + + // transferFrom will either succeed or throw. + assetProxy.transferFrom(assetMetadata, from, to, amount); + } + } + + /// @dev Registers an asset proxy to an asset proxy id. + /// An id can only be assigned to a single proxy at a given time, + /// however, an asset proxy may be registered to multiple ids. + /// @param assetProxyId Id to register`newAssetProxy` under. + /// @param newAssetProxy Address of new asset proxy to register, or 0x0 to unset assetProxyId. + /// @param oldAssetProxy Existing asset proxy to overwrite, or 0x0 if assetProxyId is currently unused. + function registerAssetProxy( + uint8 assetProxyId, + address newAssetProxy, + address oldAssetProxy) + external + onlyOwner + { + // Ensure the existing asset proxy is not unintentionally overwritten + require(oldAssetProxy == address(assetProxies[assetProxyId])); + + // Add asset proxy and log registration + assetProxies[assetProxyId] = IAssetProxy(newAssetProxy); + emit AssetProxySet(assetProxyId, newAssetProxy, oldAssetProxy); + } + + /// @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) + external view + returns (address) + { + IAssetProxy assetProxy = assetProxies[assetProxyId]; + return address(assetProxy); + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol new file mode 100644 index 000000000..ae41c7a86 --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlement.sol @@ -0,0 +1,88 @@ +/* + + 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; +pragma experimental ABIEncoderV2; + +import "./mixins/MSettlement.sol"; +import "./mixins/MAssetProxyDispatcher.sol"; +import "./LibPartialAmount.sol"; +import "../AssetProxy/IAssetProxy.sol"; + +/// @dev Provides MixinSettlement +contract MixinSettlement is + MSettlement, + MAssetProxyDispatcher, + LibPartialAmount +{ + bytes ZRX_PROXY_DATA; + + function zrxProxyData() + external view + returns (bytes memory) + { + return ZRX_PROXY_DATA; + } + + function MixinSettlement(bytes memory _zrxProxyData) + public + { + ZRX_PROXY_DATA = _zrxProxyData; + } + + function settleOrder( + Order memory order, + address takerAddress, + uint256 takerAssetFilledAmount) + internal + returns ( + uint256 makerAssetFilledAmount, + uint256 makerFeePaid, + uint256 takerFeePaid + ) + { + makerAssetFilledAmount = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount); + dispatchTransferFrom( + order.makerAssetData, + order.makerAddress, + takerAddress, + makerAssetFilledAmount + ); + dispatchTransferFrom( + order.takerAssetData, + takerAddress, + order.makerAddress, + takerAssetFilledAmount + ); + makerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerFee); + dispatchTransferFrom( + ZRX_PROXY_DATA, + order.makerAddress, + order.feeRecipientAddress, + makerFeePaid + ); + takerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee); + dispatchTransferFrom( + ZRX_PROXY_DATA, + takerAddress, + order.feeRecipientAddress, + takerFeePaid + ); + return (makerAssetFilledAmount, makerFeePaid, takerFeePaid); + } +} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol deleted file mode 100644 index 5fff2cd60..000000000 --- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSettlementProxy.sol +++ /dev/null @@ -1,103 +0,0 @@ -/* - - 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; -pragma experimental ABIEncoderV2; - -import "./mixins/MSettlement.sol"; -import "./LibPartialAmount.sol"; -import "../AssetProxyDispatcher/IAssetProxy.sol"; - -/// @dev Provides MixinSettlement -contract MixinSettlementProxy is - MSettlement, - LibPartialAmount -{ - IAssetProxy ASSET_PROXY_DISPATCHER; - bytes ZRX_PROXY_DATA; - - function assetProxyDispatcher() - public view - returns (address) - { - return address(ASSET_PROXY_DISPATCHER); - } - - function zrxProxyData() - external view - returns (bytes memory) - { - return ZRX_PROXY_DATA; - } - - function MixinSettlementProxy( - address _assetProxyDispatcher, - bytes memory _zrxProxyData) - public - { - ASSET_PROXY_DISPATCHER = IAssetProxy(_assetProxyDispatcher); - ZRX_PROXY_DATA = _zrxProxyData; - } - - function settleOrder( - Order memory order, - address takerAddress, - uint256 takerAssetFilledAmount) - internal - returns ( - uint256 makerAssetFilledAmount, - uint256 makerFeePaid, - uint256 takerFeePaid - ) - { - makerAssetFilledAmount = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerAssetAmount); - ASSET_PROXY_DISPATCHER.transferFrom( - order.makerAssetData, - order.makerAddress, - takerAddress, - makerAssetFilledAmount - ); - ASSET_PROXY_DISPATCHER.transferFrom( - order.takerAssetData, - takerAddress, - order.makerAddress, - takerAssetFilledAmount - ); - if (order.feeRecipientAddress != address(0)) { - if (order.makerFee > 0) { - makerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.makerFee); - ASSET_PROXY_DISPATCHER.transferFrom( - ZRX_PROXY_DATA, - order.makerAddress, - order.feeRecipientAddress, - makerFeePaid - ); - } - if (order.takerFee > 0) { - takerFeePaid = getPartialAmount(takerAssetFilledAmount, order.takerAssetAmount, order.takerFee); - ASSET_PROXY_DISPATCHER.transferFrom( - ZRX_PROXY_DATA, - takerAddress, - order.feeRecipientAddress, - takerFeePaid - ); - } - } - return (makerAssetFilledAmount, makerFeePaid, takerFeePaid); - } -} diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol new file mode 100644 index 000000000..a46be9d0f --- /dev/null +++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MAssetProxyDispatcher.sol @@ -0,0 +1,60 @@ +/* + + 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 MAssetProxyDispatcher { + + // Logs registration of new asset proxy + event AssetProxySet( + uint8 id, + address newAssetProxy, + address oldAssetProxy + ); + + /// @dev Forwards arguments to assetProxy and calls `transferFrom`. 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 dispatchTransferFrom( + bytes memory assetMetadata, + address from, + address to, + uint256 amount) + internal; + + /// @dev Registers an asset proxy to an asset proxy id. + /// An id can only be assigned to a single proxy at a given time, + /// however, an asset proxy may be registered to multiple ids. + /// @param assetProxyId Id to register`newAssetProxy` under. + /// @param newAssetProxy Address of new asset proxy to register, or 0x0 to unset assetProxyId. + /// @param oldAssetProxy Existing asset proxy to overwrite, or 0x0 if assetProxyId is currently unused. + function registerAssetProxy( + uint8 assetProxyId, + address newAssetProxy, + address oldAssetProxy) + external; + + /// @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) + external view + returns (address); +} -- cgit v1.2.3 From 432b064601776d4daacfc2415c3da41c91a24d27 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Sun, 22 Apr 2018 17:25:28 -0700 Subject: Fix tests --- packages/contracts/package.json | 6 +- .../TestAssetProxyDispatcher.sol | 33 +++ packages/contracts/src/utils/exchange_wrapper.ts | 21 +- packages/contracts/src/utils/types.ts | 3 +- .../contracts/test/asset_proxy/authorizable.ts | 100 +++++++ packages/contracts/test/asset_proxy/proxies.ts | 244 +++++++++++++++++ .../contracts/test/asset_proxy_dispatcher/auth.ts | 104 -------- .../test/asset_proxy_dispatcher/dispatcher.ts | 296 --------------------- .../test/asset_proxy_dispatcher/proxies.ts | 251 ----------------- packages/contracts/test/exchange/core.ts | 69 +---- packages/contracts/test/exchange/dispatcher.ts | 267 +++++++++++++++++++ packages/contracts/test/exchange/helpers.ts | 5 +- packages/contracts/test/exchange/wrapper.ts | 40 +-- 13 files changed, 687 insertions(+), 752 deletions(-) create mode 100644 packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol create mode 100644 packages/contracts/test/asset_proxy/authorizable.ts create mode 100644 packages/contracts/test/asset_proxy/proxies.ts delete mode 100644 packages/contracts/test/asset_proxy_dispatcher/auth.ts delete mode 100644 packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts delete mode 100644 packages/contracts/test/asset_proxy_dispatcher/proxies.ts create mode 100644 packages/contracts/test/exchange/dispatcher.ts (limited to 'packages') diff --git a/packages/contracts/package.json b/packages/contracts/package.json index 0cb6f3310..a85e9aa4f 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -19,7 +19,7 @@ "Yarn workspaces do not link binaries correctly so we need to reference them directly https://github.com/yarnpkg/yarn/issues/3846", "compile": "node ../deployer/lib/src/cli.js compile --contracts ${npm_package_config_contracts} --contracts-dir src/contracts --artifacts-dir ../migrations/src/artifacts", - "clean": "shx rm -rf ./lib", + "clean": "shx rm -rf ./lib ./src/contract_wrappers/generated", "generate_contract_wrappers": "node ../abi-gen/lib/index.js --abis ${npm_package_config_abis} --template ../contract_templates/contract.handlebars --partials '../contract_templates/partials/**/*.handlebars' --output src/contract_wrappers/generated --backend ethers && prettier --write 'src/contract_wrappers/generated/**.ts'", "lint": "tslint --project . 'migrations/**/*.ts' 'test/**/*.ts' 'util/**/*.ts' 'deploy/**/*.ts'", @@ -30,9 +30,9 @@ }, "config": { "abis": - "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|AssetProxyDispatcher|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes).json", + "../migrations/src/artifacts/@(DummyERC20Token|Exchange|TokenRegistry|MultiSigWallet|MultiSigWalletWithTimeLock|MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress|TokenRegistry|ZRXToken|TestAssetProxyDispatcher|ERC20Proxy|ERC721Proxy|DummyERC721Token|LibBytes|Authorizable).json", "contracts": - "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,AssetProxyDispatcher,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes" + "Exchange,DummyERC20Token,ZRXToken,WETH9,MultiSigWallet,MultiSigWalletWithTimeLock,MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress,TokenRegistry,TestAssetProxyDispatcher,ERC20Proxy,ERC721Proxy,DummyERC721Token,LibBytes,Authorizable" }, "repository": { "type": "git", diff --git a/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol new file mode 100644 index 000000000..8206b84bd --- /dev/null +++ b/packages/contracts/src/contracts/current/test/TestAssetProxyDispatcher/TestAssetProxyDispatcher.sol @@ -0,0 +1,33 @@ +/* + + 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 "../../protocol/Exchange/MixinAssetProxyDispatcher.sol"; + +contract TestAssetProxyDispatcher is MixinAssetProxyDispatcher { + function publicDispatchTransferFrom( + bytes memory assetMetadata, + address from, + address to, + uint256 amount) + public + { + dispatchTransferFrom(assetMetadata, from, to, amount); + } +} diff --git a/packages/contracts/src/utils/exchange_wrapper.ts b/packages/contracts/src/utils/exchange_wrapper.ts index 26ce8b04e..7d06c321b 100644 --- a/packages/contracts/src/utils/exchange_wrapper.ts +++ b/packages/contracts/src/utils/exchange_wrapper.ts @@ -9,7 +9,7 @@ import { constants } from './constants'; import { formatters } from './formatters'; import { LogDecoder } from './log_decoder'; import { orderUtils } from './order_utils'; -import { SignedOrder } from './types'; +import { AssetProxyId, SignedOrder } from './types'; export class ExchangeWrapper { private _exchange: ExchangeContract; @@ -189,7 +189,24 @@ export class ExchangeWrapper { const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); return tx; } - + public async registerAssetProxyAsync( + assetProxyId: AssetProxyId, + assetProxyAddress: string, + from: string, + opts: { oldAssetProxyAddressIfExists?: string } = {}, + ): Promise { + const oldAssetProxyAddress = _.isUndefined(opts.oldAssetProxyAddressIfExists) + ? ZeroEx.NULL_ADDRESS + : opts.oldAssetProxyAddressIfExists; + const txHash = await this._exchange.registerAssetProxy.sendTransactionAsync( + assetProxyId, + assetProxyAddress, + oldAssetProxyAddress, + { from }, + ); + const tx = await this._getTxWithDecodedExchangeLogsAsync(txHash); + return tx; + } public async getOrderHashAsync(signedOrder: SignedOrder): Promise { const order = orderUtils.getOrderStruct(signedOrder); const orderHash = await this._exchange.getOrderHash.callAsync(order); diff --git a/packages/contracts/src/utils/types.ts b/packages/contracts/src/utils/types.ts index 629187e95..bba463bd2 100644 --- a/packages/contracts/src/utils/types.ts +++ b/packages/contracts/src/utils/types.ts @@ -93,11 +93,12 @@ export enum ContractName { AccountLevels = 'AccountLevels', EtherDelta = 'EtherDelta', Arbitrage = 'Arbitrage', - AssetProxyDispatcher = 'AssetProxyDispatcher', + TestAssetProxyDispatcher = 'TestAssetProxyDispatcher', ERC20Proxy = 'ERC20Proxy', ERC721Proxy = 'ERC721Proxy', DummyERC721Token = 'DummyERC721Token', LibBytes = 'LibBytes', + Authorizable = 'Authorizable', } export interface Artifact { diff --git a/packages/contracts/test/asset_proxy/authorizable.ts b/packages/contracts/test/asset_proxy/authorizable.ts new file mode 100644 index 000000000..cc4f78a31 --- /dev/null +++ b/packages/contracts/test/asset_proxy/authorizable.ts @@ -0,0 +1,100 @@ +import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; +import { Web3Wrapper } from '@0xproject/web3-wrapper'; +import * as chai from 'chai'; +import * as Web3 from 'web3'; + +import { AuthorizableContract } from '../../src/contract_wrappers/generated/authorizable'; +import { constants } from '../../src/utils/constants'; +import { ContractName } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Authorizable', () => { + let owner: string; + let notOwner: string; + let address: string; + let authorizable: AuthorizableContract; + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + owner = address = accounts[0]; + notOwner = accounts[1]; + const authorizableInstance = await deployer.deployAsync(ContractName.Authorizable); + authorizable = new AuthorizableContract(authorizableInstance.abi, authorizableInstance.address, provider); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('addAuthorizedAddress', () => { + it('should throw if not called by owner', async () => { + return expect( + authorizable.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), + ).to.be.rejectedWith(constants.REVERT); + }); + it('should allow owner to add an authorized address', async () => { + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); + const isAuthorized = await authorizable.authorized.callAsync(address); + expect(isAuthorized).to.be.true(); + }); + it('should throw if owner attempts to authorize a duplicate address', async () => { + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); + return expect( + authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), + ).to.be.rejectedWith(constants.REVERT); + }); + }); + + describe('removeAuthorizedAddress', () => { + it('should throw if not called by owner', async () => { + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); + return expect( + authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { + from: notOwner, + }), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should allow owner to remove an authorized address', async () => { + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); + await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { + from: owner, + }); + const isAuthorized = await authorizable.authorized.callAsync(address); + expect(isAuthorized).to.be.false(); + }); + + it('should throw if owner attempts to remove an address that is not authorized', async () => { + return expect( + authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { + from: owner, + }), + ).to.be.rejectedWith(constants.REVERT); + }); + }); + + describe('getAuthorizedAddresses', () => { + it('should return all authorized addresses', async () => { + const initial = await authorizable.getAuthorizedAddresses.callAsync(); + expect(initial).to.have.length(0); + await authorizable.addAuthorizedAddress.sendTransactionAsync(address, { + from: owner, + }); + const afterAdd = await authorizable.getAuthorizedAddresses.callAsync(); + expect(afterAdd).to.have.length(1); + expect(afterAdd).to.include(address); + + await authorizable.removeAuthorizedAddress.sendTransactionAsync(address, { + from: owner, + }); + const afterRemove = await authorizable.getAuthorizedAddresses.callAsync(); + expect(afterRemove).to.have.length(0); + }); + }); +}); diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts new file mode 100644 index 000000000..8d60ef0e9 --- /dev/null +++ b/packages/contracts/test/asset_proxy/proxies.ts @@ -0,0 +1,244 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import * as Web3 from 'web3'; + +import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; +import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token'; +import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; +import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; +import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; +import { AssetProxyId, ContractName } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('Asset Transfer Proxies', () => { + let owner: string; + let notAuthorized: string; + let exchangeAddress: string; + let makerAddress: string; + let takerAddress: string; + + let zrx: DummyERC20TokenContract; + let erc721Token: DummyERC721TokenContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + + let erc20Wrapper: ERC20Wrapper; + let erc721Wrapper: ERC721Wrapper; + let erc721MakerTokenId: BigNumber; + + before(async () => { + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const usedAddresses = ([owner, notAuthorized, exchangeAddress, makerAddress, takerAddress] = accounts); + + erc20Wrapper = new ERC20Wrapper(deployer, provider, usedAddresses, owner); + erc721Wrapper = new ERC721Wrapper(deployer, provider, usedAddresses, owner); + + [zrx] = await erc20Wrapper.deployDummyERC20TokensAsync(); + erc20Proxy = await erc20Wrapper.deployERC20ProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, { + from: owner, + }); + + [erc721Token] = await erc721Wrapper.deployDummyERC721TokensAsync(); + erc721Proxy = await erc721Wrapper.deployERC721ProxyAsync(); + await erc721Wrapper.setBalancesAndAllowancesAsync(); + const erc721Balances = await erc721Wrapper.getBalancesAsync(); + erc721MakerTokenId = erc721Balances[makerAddress][erc721Token.address][0]; + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, { + from: owner, + }); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('Transfer Proxy - ERC20', () => { + it('should successfully transfer tokens', async () => { + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(10); + await erc20Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: exchangeAddress }, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[makerAddress][zrx.address].minus(amount), + ); + expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[takerAddress][zrx.address].add(amount), + ); + }); + + it('should do nothing if transferring 0 amount of a token', async () => { + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(0); + await erc20Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: exchangeAddress }, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[makerAddress][zrx.address], + ); + expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[takerAddress][zrx.address], + ); + }); + + it('should throw if allowances are too low', async () => { + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Create allowance less than transfer amount. Set allowance on proxy. + const allowance = new BigNumber(0); + const transferAmount = new BigNumber(10); + await zrx.approve.sendTransactionAsync(erc20Proxy.address, allowance, { + from: makerAddress, + }); + // Perform a transfer; expect this to fail. + return expect( + erc20Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + transferAmount, + { from: notAuthorized }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should throw if requesting address is not authorized', async () => { + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Perform a transfer from makerAddress to takerAddress + const amount = new BigNumber(10); + return expect( + erc20Proxy.transferFrom.sendTransactionAsync(encodedProxyMetadata, makerAddress, takerAddress, amount, { + from: notAuthorized, + }), + ).to.be.rejectedWith(constants.REVERT); + }); + }); + + describe('Transfer Proxy - ERC721', () => { + it('should successfully transfer tokens', async () => { + // Construct metadata for ERC721 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(1); + await erc721Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: exchangeAddress }, + ); + // Verify transfer was successful + const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); + }); + + it('should throw if transferring 0 amount of a token', async () => { + // Construct metadata for ERC721 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(0); + return expect( + erc721Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: exchangeAddress }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should throw if transferring > 1 amount of a token', async () => { + // Construct metadata for ERC721 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); + // Verify pre-condition + const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); + expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(500); + return expect( + erc721Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: exchangeAddress }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should throw if allowances are too low', async () => { + // Construct metadata for ERC721 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); + // Remove transfer approval for makerAddress. + await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, { + from: makerAddress, + }); + // Perform a transfer; expect this to fail. + const amount = new BigNumber(1); + return expect( + erc20Proxy.transferFrom.sendTransactionAsync(encodedProxyMetadata, makerAddress, takerAddress, amount, { + from: notAuthorized, + }), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should throw if requesting address is not authorized', async () => { + // Construct metadata for ERC721 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); + // Perform a transfer from makerAddress to takerAddress + const amount = new BigNumber(1); + return expect( + erc721Proxy.transferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: notAuthorized }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + }); +}); diff --git a/packages/contracts/test/asset_proxy_dispatcher/auth.ts b/packages/contracts/test/asset_proxy_dispatcher/auth.ts deleted file mode 100644 index 9d8645ab7..000000000 --- a/packages/contracts/test/asset_proxy_dispatcher/auth.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { BlockchainLifecycle, devConstants, web3Factory } from '@0xproject/dev-utils'; -import { Web3Wrapper } from '@0xproject/web3-wrapper'; -import * as chai from 'chai'; -import * as Web3 from 'web3'; - -import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher'; -import { constants } from '../../src/utils/constants'; -import { ContractName } from '../../src/utils/types'; -import { chaiSetup } from '../utils/chai_setup'; -import { deployer } from '../utils/deployer'; -import { provider, web3Wrapper } from '../utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('AssetProxyDispatcher (Authorization Methods)', () => { - let owner: string; - let notOwner: string; - let address: string; - let assetProxyDispatcher: AssetProxyDispatcherContract; - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - owner = address = accounts[0]; - notOwner = accounts[1]; - const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher); - assetProxyDispatcher = new AssetProxyDispatcherContract( - assetProxyDispatcherInstance.abi, - assetProxyDispatcherInstance.address, - provider, - ); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('addAuthorizedAddress', () => { - it('should throw if not called by owner', async () => { - return expect( - assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(notOwner, { from: notOwner }), - ).to.be.rejectedWith(constants.REVERT); - }); - it('should allow owner to add an authorized address', async () => { - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); - const isAuthorized = await assetProxyDispatcher.authorized.callAsync(address); - expect(isAuthorized).to.be.true(); - }); - it('should throw if owner attempts to authorize a duplicate address', async () => { - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); - return expect( - assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }), - ).to.be.rejectedWith(constants.REVERT); - }); - }); - - describe('removeAuthorizedAddress', () => { - it('should throw if not called by owner', async () => { - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); - return expect( - assetProxyDispatcher.removeAuthorizedAddress.sendTransactionAsync(address, { - from: notOwner, - }), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should allow owner to remove an authorized address', async () => { - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { from: owner }); - await assetProxyDispatcher.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }); - const isAuthorized = await assetProxyDispatcher.authorized.callAsync(address); - expect(isAuthorized).to.be.false(); - }); - - it('should throw if owner attempts to remove an address that is not authorized', async () => { - return expect( - assetProxyDispatcher.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }), - ).to.be.rejectedWith(constants.REVERT); - }); - }); - - describe('getAuthorizedAddresses', () => { - it('should return all authorized addresses', async () => { - const initial = await assetProxyDispatcher.getAuthorizedAddresses.callAsync(); - expect(initial).to.have.length(0); - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }); - const afterAdd = await assetProxyDispatcher.getAuthorizedAddresses.callAsync(); - expect(afterAdd).to.have.length(1); - expect(afterAdd).to.include(address); - - await assetProxyDispatcher.removeAuthorizedAddress.sendTransactionAsync(address, { - from: owner, - }); - const afterRemove = await assetProxyDispatcher.getAuthorizedAddresses.callAsync(); - expect(afterRemove).to.have.length(0); - }); - }); -}); diff --git a/packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts b/packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts deleted file mode 100644 index b5630d807..000000000 --- a/packages/contracts/test/asset_proxy_dispatcher/dispatcher.ts +++ /dev/null @@ -1,296 +0,0 @@ -import { ZeroEx } from '0x.js'; -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import * as Web3 from 'web3'; - -import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher'; -import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; -import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; -import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; -import { constants } from '../../src/utils/constants'; -import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; -import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; -import { AssetProxyId, ContractName } from '../../src/utils/types'; -import { chaiSetup } from '../utils/chai_setup'; -import { deployer } from '../utils/deployer'; -import { provider, web3Wrapper } from '../utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('AssetProxyDispatcher', () => { - let owner: string; - let notOwner: string; - let notAuthorized: string; - let exchangeAddress: string; - let makerAddress: string; - let takerAddress: string; - - let zrx: DummyERC20TokenContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let assetProxyDispatcher: AssetProxyDispatcherContract; - - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - - before(async () => { - // Setup accounts & addresses - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, notOwner, exchangeAddress, makerAddress, takerAddress] = accounts); - notAuthorized = notOwner; - - erc20Wrapper = new ERC20Wrapper(deployer, provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(deployer, provider, usedAddresses, owner); - - [zrx] = await erc20Wrapper.deployDummyERC20TokensAsync(); - erc20Proxy = await erc20Wrapper.deployERC20ProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - - erc721Proxy = await erc721Wrapper.deployERC721ProxyAsync(); - - const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher); - assetProxyDispatcher = new AssetProxyDispatcherContract( - assetProxyDispatcherInstance.abi, - assetProxyDispatcherInstance.address, - provider, - ); - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchangeAddress, { - from: owner, - }); - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('registerAssetProxy', () => { - it('should record proxy upon registration', async () => { - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - }); - - it('should be able to record multiple proxies', async () => { - // Record first proxy - const prevERC20ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevERC20ProxyAddress, - { from: owner }, - ); - let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // Record another proxy - const prevERC721ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC721, - erc721Proxy.address, - prevERC721ProxyAddress, - { from: owner }, - ); - proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC721); - expect(proxyAddress).to.be.equal(erc721Proxy.address); - }); - - it('should replace proxy address upon re-registration', async () => { - // Initial registration - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // Deploy a new version of the ERC20 Transfer Proxy contract - const newErc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20Proxy); - const newErc20TransferProxy = new ERC20ProxyContract( - newErc20TransferProxyInstance.abi, - newErc20TransferProxyInstance.address, - provider, - ); - // Register new ERC20 Transfer Proxy contract - const newAddress = newErc20TransferProxy.address; - const currentAddress = erc20Proxy.address; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - newAddress, - currentAddress, - { from: owner }, - ); - // Verify new asset proxy has replaced initial version - proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(newAddress); - }); - - it('should throw if registering with incorrect "currentAssetProxyAddress" field', async () => { - // Initial registration - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // The following transaction will throw because the currentAddress is no longer ZeroEx.NULL_ADDRESS - return expect( - assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - ZeroEx.NULL_ADDRESS, - { from: owner }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should be able to reset proxy address to NULL', async () => { - // Initial registration - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - // The following transaction will reset the proxy address - const newProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - newProxyAddress, - erc20Proxy.address, - { from: owner }, - ); - const finalProxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(finalProxyAddress).to.be.equal(newProxyAddress); - }); - - it('should throw if requesting address is not owner', async () => { - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - return expect( - assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: notOwner }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - }); - - describe('getAssetProxy', () => { - it('should return correct address of registered proxy', async () => { - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(erc20Proxy.address); - }); - - it('should return NULL address if requesting non-existent proxy', async () => { - const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); - expect(proxyAddress).to.be.equal(ZeroEx.NULL_ADDRESS); - }); - }); - - describe('transferFrom', () => { - it('should dispatch transfer to registered proxy', async () => { - // Register ERC20 proxy - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - await assetProxyDispatcher.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: exchangeAddress }, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrx.address].minus(amount), - ); - expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrx.address].add(amount), - ); - }); - - it('should throw if dispatching to unregistered proxy', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - return expect( - assetProxyDispatcher.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: exchangeAddress }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should throw on transfer if requesting address is not authorized', async () => { - // Register ERC20 proxy - const prevProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevProxyAddress, - { from: owner }, - ); - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - return expect( - assetProxyDispatcher.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: notAuthorized }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - }); -}); diff --git a/packages/contracts/test/asset_proxy_dispatcher/proxies.ts b/packages/contracts/test/asset_proxy_dispatcher/proxies.ts deleted file mode 100644 index 830f40e13..000000000 --- a/packages/contracts/test/asset_proxy_dispatcher/proxies.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { ZeroEx } from '0x.js'; -import { BlockchainLifecycle } from '@0xproject/dev-utils'; -import { BigNumber } from '@0xproject/utils'; -import * as chai from 'chai'; -import * as Web3 from 'web3'; - -import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher'; -import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; -import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token'; -import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; -import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; -import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; -import { constants } from '../../src/utils/constants'; -import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; -import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; -import { AssetProxyId, ContractName } from '../../src/utils/types'; -import { chaiSetup } from '../utils/chai_setup'; -import { deployer } from '../utils/deployer'; -import { provider, web3Wrapper } from '../utils/web3_wrapper'; - -chaiSetup.configure(); -const expect = chai.expect; -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); - -describe('Asset Transfer Proxies', () => { - let owner: string; - let notAuthorized: string; - let assetProxyDispatcherAddress: string; - let makerAddress: string; - let takerAddress: string; - - let zrx: DummyERC20TokenContract; - let erc721Token: DummyERC721TokenContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - - let erc20Wrapper: ERC20Wrapper; - let erc721Wrapper: ERC721Wrapper; - let erc721MakerTokenId: BigNumber; - - before(async () => { - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([ - owner, - notAuthorized, - assetProxyDispatcherAddress, - makerAddress, - takerAddress, - ] = accounts); - - erc20Wrapper = new ERC20Wrapper(deployer, provider, usedAddresses, owner); - erc721Wrapper = new ERC721Wrapper(deployer, provider, usedAddresses, owner); - - [zrx] = await erc20Wrapper.deployDummyERC20TokensAsync(); - erc20Proxy = await erc20Wrapper.deployERC20ProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcherAddress, { - from: owner, - }); - - [erc721Token] = await erc721Wrapper.deployDummyERC721TokensAsync(); - erc721Proxy = await erc721Wrapper.deployERC721ProxyAsync(); - await erc721Wrapper.setBalancesAndAllowancesAsync(); - const erc721Balances = await erc721Wrapper.getBalancesAsync(); - erc721MakerTokenId = erc721Balances[makerAddress][erc721Token.address][0]; - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcherAddress, { - from: owner, - }); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('Transfer Proxy - ERC20', () => { - it('should successfully transfer tokens', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(10); - await erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: assetProxyDispatcherAddress }, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrx.address].minus(amount), - ); - expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrx.address].add(amount), - ); - }); - - it('should do nothing if transferring 0 amount of a token', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(0); - await erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: assetProxyDispatcherAddress }, - ); - // Verify transfer was successful - const newBalances = await erc20Wrapper.getBalancesAsync(); - expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[makerAddress][zrx.address], - ); - expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( - erc20Balances[takerAddress][zrx.address], - ); - }); - - it('should throw if allowances are too low', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Create allowance less than transfer amount. Set allowance on proxy. - const allowance = new BigNumber(0); - const transferAmount = new BigNumber(10); - await zrx.approve.sendTransactionAsync(erc20Proxy.address, allowance, { - from: makerAddress, - }); - // Perform a transfer; expect this to fail. - return expect( - erc20Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - transferAmount, - { from: notAuthorized }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should throw if requesting address is not authorized', async () => { - // Construct metadata for ERC20 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); - // Perform a transfer from makerAddress to takerAddress - const amount = new BigNumber(10); - return expect( - erc20Proxy.transferFrom.sendTransactionAsync(encodedProxyMetadata, makerAddress, takerAddress, amount, { - from: notAuthorized, - }), - ).to.be.rejectedWith(constants.REVERT); - }); - }); - - describe('Transfer Proxy - ERC721', () => { - it('should successfully transfer tokens', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); - // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(1); - await erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: assetProxyDispatcherAddress }, - ); - // Verify transfer was successful - const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress); - }); - - it('should throw if transferring 0 amount of a token', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); - // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(0); - return expect( - erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: assetProxyDispatcherAddress }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should throw if transferring > 1 amount of a token', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); - // Verify pre-condition - const ownerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId); - expect(ownerMakerAsset).to.be.bignumber.equal(makerAddress); - // Perform a transfer from makerAddress to takerAddress - const erc20Balances = await erc20Wrapper.getBalancesAsync(); - const amount = new BigNumber(500); - return expect( - erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: assetProxyDispatcherAddress }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should throw if allowances are too low', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); - // Remove transfer approval for makerAddress. - await erc721Token.setApprovalForAll.sendTransactionAsync(erc721Proxy.address, false, { - from: makerAddress, - }); - // Perform a transfer; expect this to fail. - const amount = new BigNumber(1); - return expect( - erc20Proxy.transferFrom.sendTransactionAsync(encodedProxyMetadata, makerAddress, takerAddress, amount, { - from: notAuthorized, - }), - ).to.be.rejectedWith(constants.REVERT); - }); - - it('should throw if requesting address is not authorized', async () => { - // Construct metadata for ERC721 proxy - const encodedProxyMetadata = assetProxyUtils.encodeERC721ProxyData(erc721Token.address, erc721MakerTokenId); - // Perform a transfer from makerAddress to takerAddress - const amount = new BigNumber(1); - return expect( - erc721Proxy.transferFrom.sendTransactionAsync( - encodedProxyMetadata, - makerAddress, - takerAddress, - amount, - { from: notAuthorized }, - ), - ).to.be.rejectedWith(constants.REVERT); - }); - }); -}); diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts index 9065c5e3f..4d462826a 100644 --- a/packages/contracts/test/exchange/core.ts +++ b/packages/contracts/test/exchange/core.ts @@ -5,7 +5,6 @@ import * as chai from 'chai'; import ethUtil = require('ethereumjs-util'); import * as _ from 'lodash'; -import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher'; import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token'; import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; @@ -51,7 +50,6 @@ describe('Exchange core', () => { let zrx: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; let exchange: ExchangeContract; - let assetProxyDispatcher: AssetProxyDispatcherContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; @@ -88,44 +86,24 @@ describe('Exchange core', () => { erc721MakerAssetIds = erc721Balances[makerAddress][erc721Token.address]; erc721TakerAssetIds = erc721Balances[takerAddress][erc721Token.address]; - const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher); - assetProxyDispatcher = new AssetProxyDispatcherContract( - assetProxyDispatcherInstance.abi, - assetProxyDispatcherInstance.address, - provider, - ); - const prevERC20ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevERC20ProxyAddress, - { from: owner }, - ); - const prevERC721ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC721, - erc721Proxy.address, - prevERC721ProxyAddress, - { from: owner }, - ); - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ - assetProxyDispatcher.address, assetProxyUtils.encodeERC20ProxyData(zrx.address), ]); exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider); - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }); zeroEx = new ZeroEx(provider, { exchangeContractAddress: exchange.address, networkId: constants.TESTRPC_NETWORK_ID, }); exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner); + + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }); + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }); defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; @@ -467,35 +445,6 @@ describe('Exchange core', () => { expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); }); - it('should log 1 event with the correct arguments when order has no feeRecipient', async () => { - signedOrder = orderFactory.newSignedOrder({ - feeRecipientAddress: ZeroEx.NULL_ADDRESS, - }); - const divisor = 2; - const res = await exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, { - takerAssetFillAmount: signedOrder.takerAssetAmount.div(divisor), - }); - expect(res.logs).to.have.length(1); - - const log = res.logs[0] as LogWithDecodedArgs; - const logArgs = log.args; - const expectedFilledMakerAssetAmount = signedOrder.makerAssetAmount.div(divisor); - const expectedFilledTakerAssetAmount = signedOrder.takerAssetAmount.div(divisor); - const expectedFeeMPaid = new BigNumber(0); - const expectedFeeTPaid = new BigNumber(0); - - expect(signedOrder.makerAddress).to.be.equal(logArgs.makerAddress); - expect(takerAddress).to.be.equal(logArgs.takerAddress); - expect(signedOrder.feeRecipientAddress).to.be.equal(logArgs.feeRecipientAddress); - expect(signedOrder.makerAssetData).to.be.equal(logArgs.makerAssetData); - expect(signedOrder.takerAssetData).to.be.equal(logArgs.takerAssetData); - expect(expectedFilledMakerAssetAmount).to.be.bignumber.equal(logArgs.makerAssetFilledAmount); - expect(expectedFilledTakerAssetAmount).to.be.bignumber.equal(logArgs.takerAssetFilledAmount); - expect(expectedFeeMPaid).to.be.bignumber.equal(logArgs.makerFeePaid); - expect(expectedFeeTPaid).to.be.bignumber.equal(logArgs.takerFeePaid); - expect(orderUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash); - }); - it('should throw when taker is specified and order is claimed by other', async () => { signedOrder = orderFactory.newSignedOrder({ takerAddress: feeRecipientAddress, diff --git a/packages/contracts/test/exchange/dispatcher.ts b/packages/contracts/test/exchange/dispatcher.ts new file mode 100644 index 000000000..7d40bcfe3 --- /dev/null +++ b/packages/contracts/test/exchange/dispatcher.ts @@ -0,0 +1,267 @@ +import { ZeroEx } from '0x.js'; +import { BlockchainLifecycle } from '@0xproject/dev-utils'; +import { BigNumber } from '@0xproject/utils'; +import * as chai from 'chai'; +import * as Web3 from 'web3'; + +import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; +import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; +import { ERC721ProxyContract } from '../../src/contract_wrappers/generated/e_r_c721_proxy'; +import { TestAssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/test_asset_proxy_dispatcher'; +import { assetProxyUtils } from '../../src/utils/asset_proxy_utils'; +import { constants } from '../../src/utils/constants'; +import { ERC20Wrapper } from '../../src/utils/erc20_wrapper'; +import { ERC721Wrapper } from '../../src/utils/erc721_wrapper'; +import { AssetProxyId, ContractName } from '../../src/utils/types'; +import { chaiSetup } from '../utils/chai_setup'; +import { deployer } from '../utils/deployer'; +import { provider, web3Wrapper } from '../utils/web3_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); + +describe('AssetProxyDispatcher', () => { + let owner: string; + let notOwner: string; + let notAuthorized: string; + let makerAddress: string; + let takerAddress: string; + + let zrx: DummyERC20TokenContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + let assetProxyDispatcher: TestAssetProxyDispatcherContract; + + let erc20Wrapper: ERC20Wrapper; + let erc721Wrapper: ERC721Wrapper; + + before(async () => { + // Setup accounts & addresses + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const usedAddresses = ([owner, notOwner, makerAddress, takerAddress] = accounts); + notAuthorized = notOwner; + + erc20Wrapper = new ERC20Wrapper(deployer, provider, usedAddresses, owner); + erc721Wrapper = new ERC721Wrapper(deployer, provider, usedAddresses, owner); + + [zrx] = await erc20Wrapper.deployDummyERC20TokensAsync(); + erc20Proxy = await erc20Wrapper.deployERC20ProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + + erc721Proxy = await erc721Wrapper.deployERC721ProxyAsync(); + + const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.TestAssetProxyDispatcher); + assetProxyDispatcher = new TestAssetProxyDispatcherContract( + assetProxyDispatcherInstance.abi, + assetProxyDispatcherInstance.address, + provider, + ); + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { + from: owner, + }); + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { + from: owner, + }); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('registerAssetProxy', () => { + it('should record proxy upon registration', async () => { + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + }); + + it('should be able to record multiple proxies', async () => { + // Record first proxy + const prevERC20ProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevERC20ProxyAddress, + { from: owner }, + ); + let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + // Record another proxy + const prevERC721ProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC721, + erc721Proxy.address, + prevERC721ProxyAddress, + { from: owner }, + ); + proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC721); + expect(proxyAddress).to.be.equal(erc721Proxy.address); + }); + + it('should replace proxy address upon re-registration', async () => { + // Initial registration + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + let proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + // Deploy a new version of the ERC20 Transfer Proxy contract + const newErc20TransferProxyInstance = await deployer.deployAsync(ContractName.ERC20Proxy); + const newErc20TransferProxy = new ERC20ProxyContract( + newErc20TransferProxyInstance.abi, + newErc20TransferProxyInstance.address, + provider, + ); + // Register new ERC20 Transfer Proxy contract + const newAddress = newErc20TransferProxy.address; + const currentAddress = erc20Proxy.address; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + newAddress, + currentAddress, + { from: owner }, + ); + // Verify new asset proxy has replaced initial version + proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(newAddress); + }); + + it('should throw if registering with incorrect "currentAssetProxyAddress" field', async () => { + // Initial registration + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + // The following transaction will throw because the currentAddress is no longer ZeroEx.NULL_ADDRESS + return expect( + assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + ZeroEx.NULL_ADDRESS, + { from: owner }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + + it('should be able to reset proxy address to NULL', async () => { + // Initial registration + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + // The following transaction will reset the proxy address + const newProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + newProxyAddress, + erc20Proxy.address, + { from: owner }, + ); + const finalProxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(finalProxyAddress).to.be.equal(newProxyAddress); + }); + + it('should throw if requesting address is not owner', async () => { + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + return expect( + assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: notOwner }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + }); + + describe('getAssetProxy', () => { + it('should return correct address of registered proxy', async () => { + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(erc20Proxy.address); + }); + + it('should return NULL address if requesting non-existent proxy', async () => { + const proxyAddress = await assetProxyDispatcher.getAssetProxy.callAsync(AssetProxyId.ERC20); + expect(proxyAddress).to.be.equal(ZeroEx.NULL_ADDRESS); + }); + }); + + describe('dispatchTransferFrom', () => { + it('should dispatch transfer to registered proxy', async () => { + // Register ERC20 proxy + const prevProxyAddress = ZeroEx.NULL_ADDRESS; + await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( + AssetProxyId.ERC20, + erc20Proxy.address, + prevProxyAddress, + { from: owner }, + ); + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(10); + await assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: owner }, + ); + // Verify transfer was successful + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances[makerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[makerAddress][zrx.address].minus(amount), + ); + expect(newBalances[takerAddress][zrx.address]).to.be.bignumber.equal( + erc20Balances[takerAddress][zrx.address].add(amount), + ); + }); + + it('should throw if dispatching to unregistered proxy', async () => { + // Construct metadata for ERC20 proxy + const encodedProxyMetadata = assetProxyUtils.encodeERC20ProxyData(zrx.address); + // Perform a transfer from makerAddress to takerAddress + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const amount = new BigNumber(10); + return expect( + assetProxyDispatcher.publicDispatchTransferFrom.sendTransactionAsync( + encodedProxyMetadata, + makerAddress, + takerAddress, + amount, + { from: owner }, + ), + ).to.be.rejectedWith(constants.REVERT); + }); + }); +}); diff --git a/packages/contracts/test/exchange/helpers.ts b/packages/contracts/test/exchange/helpers.ts index a5c2ca8d5..f986665ff 100644 --- a/packages/contracts/test/exchange/helpers.ts +++ b/packages/contracts/test/exchange/helpers.ts @@ -29,10 +29,7 @@ describe('Exchange helpers', () => { before(async () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); const makerAddress = accounts[0]; - const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ - ZeroEx.NULL_ADDRESS, - AssetProxyId.ERC20, - ]); + const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [AssetProxyId.ERC20]); const exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider); const zeroEx = new ZeroEx(provider, { networkId: constants.TESTRPC_NETWORK_ID }); exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); diff --git a/packages/contracts/test/exchange/wrapper.ts b/packages/contracts/test/exchange/wrapper.ts index 60f47373e..1d40bd3d9 100644 --- a/packages/contracts/test/exchange/wrapper.ts +++ b/packages/contracts/test/exchange/wrapper.ts @@ -6,7 +6,6 @@ import * as chai from 'chai'; import * as _ from 'lodash'; import * as Web3 from 'web3'; -import { AssetProxyDispatcherContract } from '../../src/contract_wrappers/generated/asset_proxy_dispatcher'; import { DummyERC20TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c20_token'; import { DummyERC721TokenContract } from '../../src/contract_wrappers/generated/dummy_e_r_c721_token'; import { ERC20ProxyContract } from '../../src/contract_wrappers/generated/e_r_c20_proxy'; @@ -45,7 +44,6 @@ describe('Exchange wrappers', () => { let zrx: DummyERC20TokenContract; let erc721Token: DummyERC721TokenContract; let exchange: ExchangeContract; - let assetProxyDispatcher: AssetProxyDispatcherContract; let erc20Proxy: ERC20ProxyContract; let erc721Proxy: ERC721ProxyContract; @@ -81,44 +79,24 @@ describe('Exchange wrappers', () => { erc721MakerAssetId = erc721Balances[makerAddress][erc721Token.address][0]; erc721TakerAssetId = erc721Balances[takerAddress][erc721Token.address][0]; - const assetProxyDispatcherInstance = await deployer.deployAsync(ContractName.AssetProxyDispatcher); - assetProxyDispatcher = new AssetProxyDispatcherContract( - assetProxyDispatcherInstance.abi, - assetProxyDispatcherInstance.address, - provider, - ); - const prevERC20ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC20, - erc20Proxy.address, - prevERC20ProxyAddress, - { from: owner }, - ); - const prevERC721ProxyAddress = ZeroEx.NULL_ADDRESS; - await assetProxyDispatcher.registerAssetProxy.sendTransactionAsync( - AssetProxyId.ERC721, - erc721Proxy.address, - prevERC721ProxyAddress, - { from: owner }, - ); - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(assetProxyDispatcher.address, { - from: owner, - }); - const exchangeInstance = await deployer.deployAsync(ContractName.Exchange, [ - assetProxyDispatcher.address, assetProxyUtils.encodeERC20ProxyData(zrx.address), ]); exchange = new ExchangeContract(exchangeInstance.abi, exchangeInstance.address, provider); - await assetProxyDispatcher.addAuthorizedAddress.sendTransactionAsync(exchange.address, { from: owner }); zeroEx = new ZeroEx(provider, { exchangeContractAddress: exchange.address, networkId: constants.TESTRPC_NETWORK_ID, }); exchangeWrapper = new ExchangeWrapper(exchange, zeroEx); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC20, erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(AssetProxyId.ERC721, erc721Proxy.address, owner); + + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }); + await erc721Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }); defaultMakerAssetAddress = erc20TokenA.address; defaultTakerAssetAddress = erc20TokenB.address; -- cgit v1.2.3 From 870693b968de3f29b0ac97f6f92715613e1064e3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 24 Apr 2018 16:54:08 +0900 Subject: Fix CI test failure --- packages/react-shared/src/components/anchor_title.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'packages') diff --git a/packages/react-shared/src/components/anchor_title.tsx b/packages/react-shared/src/components/anchor_title.tsx index f44354097..33bb3f684 100644 --- a/packages/react-shared/src/components/anchor_title.tsx +++ b/packages/react-shared/src/components/anchor_title.tsx @@ -27,12 +27,6 @@ const styles: Styles = { transform: 'rotate(45deg)', cursor: 'pointer', }, - headers: { - WebkitMarginStart: 0, - WebkitMarginEnd: 0, - fontWeight: 'bold', - display: 'block', - }, h1: { fontSize: '1.8em', }, @@ -58,7 +52,16 @@ export class AnchorTitle extends React.Component +
{this.props.title}
-- cgit v1.2.3 From 4c5a632095ce6394d5b8c0cc68d56ca90b95c28f Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 24 Apr 2018 17:28:18 +0900 Subject: Revert "Fix CI test failure" This reverts commit 870693b968de3f29b0ac97f6f92715613e1064e3. --- packages/react-shared/src/components/anchor_title.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'packages') diff --git a/packages/react-shared/src/components/anchor_title.tsx b/packages/react-shared/src/components/anchor_title.tsx index 33bb3f684..f44354097 100644 --- a/packages/react-shared/src/components/anchor_title.tsx +++ b/packages/react-shared/src/components/anchor_title.tsx @@ -27,6 +27,12 @@ const styles: Styles = { transform: 'rotate(45deg)', cursor: 'pointer', }, + headers: { + WebkitMarginStart: 0, + WebkitMarginEnd: 0, + fontWeight: 'bold', + display: 'block', + }, h1: { fontSize: '1.8em', }, @@ -52,16 +58,7 @@ export class AnchorTitle extends React.Component +
{this.props.title}
-- cgit v1.2.3 From a6d5cd4b39140c7ceca31644941c0e67405df7d5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 24 Apr 2018 17:38:25 +0900 Subject: Fix CI failures caused by Webkit css properties not being recognized by React types. This is a hack. --- packages/react-shared/src/components/anchor_title.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'packages') diff --git a/packages/react-shared/src/components/anchor_title.tsx b/packages/react-shared/src/components/anchor_title.tsx index f44354097..6e648739d 100644 --- a/packages/react-shared/src/components/anchor_title.tsx +++ b/packages/react-shared/src/components/anchor_title.tsx @@ -27,12 +27,6 @@ const styles: Styles = { transform: 'rotate(45deg)', cursor: 'pointer', }, - headers: { - WebkitMarginStart: 0, - WebkitMarginEnd: 0, - fontWeight: 'bold', - display: 'block', - }, h1: { fontSize: '1.8em', }, @@ -58,7 +52,18 @@ export class AnchorTitle extends React.Component +
{this.props.title}
-- cgit v1.2.3