aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts')
-rw-r--r--packages/contracts/compiler.json1
-rw-r--r--packages/contracts/package.json27
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol59
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol72
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol81
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol88
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol4
-rw-r--r--packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol25
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol36
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol2
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol21
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol7
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol14
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol17
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol4
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol3
-rw-r--r--packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol14
-rw-r--r--packages/contracts/src/contracts/current/test/ExchangeWrapper/ExchangeWrapper.sol98
-rw-r--r--packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol2
-rw-r--r--packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol22
-rw-r--r--packages/contracts/src/utils/artifacts.ts2
-rw-r--r--packages/contracts/test/asset_proxy/proxies.ts8
-rw-r--r--packages/contracts/test/exchange/core.ts30
-rw-r--r--packages/contracts/test/exchange/transactions.ts112
24 files changed, 532 insertions, 217 deletions
diff --git a/packages/contracts/compiler.json b/packages/contracts/compiler.json
index 6ef8e6a95..a5cfa8761 100644
--- a/packages/contracts/compiler.json
+++ b/packages/contracts/compiler.json
@@ -26,6 +26,7 @@
"ERC20Proxy",
"ERC721Proxy",
"Exchange",
+ "ExchangeWrapper",
"MixinAuthorizable",
"MultiSigWallet",
"MultiSigWalletWithTimeLock",
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index 2495795dc..cf3f6f01d 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -1,7 +1,7 @@
{
"private": true,
"name": "contracts",
- "version": "2.1.29",
+ "version": "2.1.33",
"engines": {
"node": ">=6.12"
},
@@ -33,7 +33,8 @@
"test:circleci": "yarn test"
},
"config": {
- "abis": "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibMem|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
+ "abis":
+ "../migrations/artifacts/2.0.0/@(AssetProxyOwner|DummyERC20Token|DummyERC721Receiver|DummyERC721Token|ERC20Proxy|ERC721Proxy|Exchange|ExchangeWrapper|MixinAuthorizable|MultiSigWallet|MultiSigWalletWithTimeLock|TestAssetDataDecoders|TestAssetProxyDispatcher|TestLibBytes|TestLibMem|TestLibs|TestSignatureValidator|TokenRegistry|Whitelist|WETH9|ZRXToken).json"
},
"repository": {
"type": "git",
@@ -46,11 +47,11 @@
},
"homepage": "https://github.com/0xProject/0x-monorepo/packages/contracts/README.md",
"devDependencies": {
- "@0xproject/abi-gen": "^0.3.0",
- "@0xproject/dev-utils": "^0.4.2",
- "@0xproject/tslint-config": "^0.4.18",
+ "@0xproject/abi-gen": "^0.3.2",
+ "@0xproject/dev-utils": "^0.4.4",
+ "@0xproject/tslint-config": "^0.4.20",
"@0xproject/subproviders": "^0.10.1",
- "@0xproject/sol-cov": "^0.0.11",
+ "@0xproject/sol-cov": "^0.1.1",
"@types/lodash": "4.14.104",
"@types/bn.js": "^4.11.0",
"@types/node": "^8.0.53",
@@ -71,18 +72,18 @@
"yargs": "^10.0.3"
},
"dependencies": {
- "@0xproject/base-contract": "^0.3.2",
- "@0xproject/order-utils": "^0.0.6",
- "@0xproject/sol-compiler": "^0.5.0",
+ "@0xproject/base-contract": "^0.3.4",
+ "@0xproject/order-utils": "^1.0.0",
+ "@0xproject/sol-compiler": "^0.5.2",
"@0xproject/types": "^1.0.0",
- "@0xproject/typescript-typings": "^0.3.2",
- "@0xproject/utils": "^0.6.2",
- "@0xproject/web3-wrapper": "^0.6.4",
+ "@0xproject/typescript-typings": "^0.4.1",
+ "@0xproject/utils": "^0.7.1",
+ "@0xproject/web3-wrapper": "^0.7.1",
"ethereum-types": "^0.0.1",
"bn.js": "^4.11.8",
"ethereumjs-abi": "^0.6.4",
"ethereumjs-util": "^5.1.1",
- "ethers": "^3.0.15",
+ "ethers": "3.0.22",
"lodash": "^4.17.4",
"web3": "^0.20.0"
}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
index eeddb9707..7ca823d1f 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC20Proxy.sol
@@ -22,69 +22,16 @@ pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "./MixinAssetProxy.sol";
import "./MixinAuthorizable.sol";
-import "../../tokens/ERC20Token/IERC20Token.sol";
+import "./MixinERC20Transfer.sol";
contract ERC20Proxy is
- LibBytes,
MixinAssetProxy,
- MixinAuthorizable
+ MixinAuthorizable,
+ MixinERC20Transfer
{
-
// Id of this proxy.
uint8 constant PROXY_ID = 1;
- /// @dev Internal version of `transferFrom`.
- /// @param assetData Encoded byte array.
- /// @param from Address to transfer asset from.
- /// @param to Address to transfer asset to.
- /// @param amount Amount of asset to transfer.
- function transferFromInternal(
- bytes memory assetData,
- address from,
- address to,
- uint256 amount
- )
- internal
- {
- // Decode asset data.
- address token = readAddress(assetData, 0);
-
- // Transfer tokens.
- // We do a raw call so we can check the success separate
- // from the return data.
- bool success = token.call(abi.encodeWithSelector(
- IERC20Token(token).transferFrom.selector,
- from,
- to,
- amount
- ));
- require(
- success,
- TRANSFER_FAILED
- );
-
- // Check return data.
- // If there is no return data, we assume the token incorrectly
- // does not return a bool. In this case we expect it to revert
- // on failure, which was handled above.
- // If the token does return data, we require that it is a single
- // value that evaluates to true.
- assembly {
- if returndatasize {
- success := 0
- if eq(returndatasize, 32) {
- // First 64 bytes of memory are reserved scratch space
- returndatacopy(0, 0, 32)
- success := mload(0)
- }
- }
- }
- require(
- success,
- TRANSFER_FAILED
- );
- }
-
/// @dev Gets the proxy id associated with the proxy address.
/// @return Proxy id.
function getProxyId()
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
index 861fac2c1..7ff25aea3 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/ERC721Proxy.sol
@@ -22,52 +22,16 @@ pragma experimental ABIEncoderV2;
import "../../utils/LibBytes/LibBytes.sol";
import "./MixinAssetProxy.sol";
import "./MixinAuthorizable.sol";
-import "../../tokens/ERC721Token/ERC721Token.sol";
+import "./MixinERC721Transfer.sol";
contract ERC721Proxy is
- LibBytes,
MixinAssetProxy,
- MixinAuthorizable
+ MixinAuthorizable,
+ MixinERC721Transfer
{
-
// Id of this proxy.
uint8 constant PROXY_ID = 2;
- /// @dev Internal version of `transferFrom`.
- /// @param assetData Encoded byte array.
- /// @param from Address to transfer asset from.
- /// @param to Address to transfer asset to.
- /// @param amount Amount of asset to transfer.
- function transferFromInternal(
- bytes memory assetData,
- address from,
- address to,
- uint256 amount
- )
- internal
- {
- // There exists only 1 of each token.
- require(
- amount == 1,
- INVALID_AMOUNT
- );
-
- // Decode asset data.
- (
- address token,
- uint256 tokenId,
- bytes memory receiverData
- ) = decodeERC721AssetData(assetData);
-
- // Transfer token. Saves gas by calling safeTransferFrom only
- // when there is receiverData present. Either succeeds or throws.
- if (receiverData.length > 0) {
- ERC721Token(token).safeTransferFrom(from, to, tokenId, receiverData);
- } else {
- ERC721Token(token).transferFrom(from, to, tokenId);
- }
- }
-
/// @dev Gets the proxy id associated with the proxy address.
/// @return Proxy id.
function getProxyId()
@@ -77,34 +41,4 @@ contract ERC721Proxy is
{
return PROXY_ID;
}
-
- /// @dev Decodes ERC721 Asset data.
- /// @param assetData Encoded byte array.
- /// @return proxyId Intended ERC721 proxy id.
- /// @return token ERC721 token address.
- /// @return tokenId ERC721 token id.
- /// @return receiverData Additional data with no specific format, which
- /// is passed to the receiving contract's onERC721Received.
- function decodeERC721AssetData(bytes memory assetData)
- internal
- pure
- returns (
- address token,
- uint256 tokenId,
- bytes memory receiverData
- )
- {
- // Decode asset data.
- token = readAddress(assetData, 0);
- tokenId = readUint256(assetData, 20);
- if (assetData.length > 52) {
- receiverData = readBytes(assetData, 52);
- }
-
- return (
- token,
- tokenId,
- receiverData
- );
- }
}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol
new file mode 100644
index 000000000..4af39a00b
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC20Transfer.sol
@@ -0,0 +1,81 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../utils/LibBytes/LibBytes.sol";
+import "../../tokens/ERC20Token/IERC20Token.sol";
+import "./libs/LibTransferErrors.sol";
+
+contract MixinERC20Transfer is
+ LibBytes,
+ LibTransferErrors
+{
+ /// @dev Internal version of `transferFrom`.
+ /// @param assetData Encoded byte array.
+ /// @param from Address to transfer asset from.
+ /// @param to Address to transfer asset to.
+ /// @param amount Amount of asset to transfer.
+ function transferFromInternal(
+ bytes memory assetData,
+ address from,
+ address to,
+ uint256 amount
+ )
+ internal
+ {
+ // Decode asset data.
+ address token = readAddress(assetData, 0);
+
+ // Transfer tokens.
+ // We do a raw call so we can check the success separate
+ // from the return data.
+ bool success = token.call(abi.encodeWithSelector(
+ IERC20Token(token).transferFrom.selector,
+ from,
+ to,
+ amount
+ ));
+ require(
+ success,
+ TRANSFER_FAILED
+ );
+
+ // Check return data.
+ // If there is no return data, we assume the token incorrectly
+ // does not return a bool. In this case we expect it to revert
+ // on failure, which was handled above.
+ // If the token does return data, we require that it is a single
+ // value that evaluates to true.
+ assembly {
+ if returndatasize {
+ success := 0
+ if eq(returndatasize, 32) {
+ // First 64 bytes of memory are reserved scratch space
+ returndatacopy(0, 0, 32)
+ success := mload(0)
+ }
+ }
+ }
+ require(
+ success,
+ TRANSFER_FAILED
+ );
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.sol
new file mode 100644
index 000000000..9dc9e6525
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/MixinERC721Transfer.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.24;
+pragma experimental ABIEncoderV2;
+
+import "../../utils/LibBytes/LibBytes.sol";
+import "../../tokens/ERC721Token/ERC721Token.sol";
+import "./libs/LibTransferErrors.sol";
+
+contract MixinERC721Transfer is
+ LibBytes,
+ LibTransferErrors
+{
+ /// @dev Internal version of `transferFrom`.
+ /// @param assetData Encoded byte array.
+ /// @param from Address to transfer asset from.
+ /// @param to Address to transfer asset to.
+ /// @param amount Amount of asset to transfer.
+ function transferFromInternal(
+ bytes memory assetData,
+ address from,
+ address to,
+ uint256 amount
+ )
+ internal
+ {
+ // There exists only 1 of each token.
+ require(
+ amount == 1,
+ INVALID_AMOUNT
+ );
+
+ // Decode asset data.
+ (
+ address token,
+ uint256 tokenId,
+ bytes memory receiverData
+ ) = decodeERC721AssetData(assetData);
+
+ ERC721Token(token).safeTransferFrom(from, to, tokenId, receiverData);
+ }
+
+ /// @dev Decodes ERC721 Asset data.
+ /// @param assetData Encoded byte array.
+ /// @return proxyId Intended ERC721 proxy id.
+ /// @return token ERC721 token address.
+ /// @return tokenId ERC721 token id.
+ /// @return receiverData Additional data with no specific format, which
+ /// is passed to the receiving contract's onERC721Received.
+ function decodeERC721AssetData(bytes memory assetData)
+ internal
+ pure
+ returns (
+ address token,
+ uint256 tokenId,
+ bytes memory receiverData
+ )
+ {
+ // Decode asset data.
+ token = readAddress(assetData, 0);
+ tokenId = readUint256(assetData, 20);
+ if (assetData.length > 52) {
+ receiverData = readBytes(assetData, 52);
+ }
+
+ return (
+ token,
+ tokenId,
+ receiverData
+ );
+ }
+}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
index 65bdacdb7..dca4f400f 100644
--- a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibAssetProxyErrors.sol
@@ -25,8 +25,4 @@ contract LibAssetProxyErrors {
string constant TARGET_ALREADY_AUTHORIZED = "TARGET_ALREADY_AUTHORIZED"; // Target address must not already be authorized.
string constant INDEX_OUT_OF_BOUNDS = "INDEX_OUT_OF_BOUNDS"; // Specified array index is out of bounds.
string constant AUTHORIZED_ADDRESS_MISMATCH = "AUTHORIZED_ADDRESS_MISMATCH"; // Address at index does not match given target address.
-
- /// AssetProxy errors ///
- string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
- string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
}
diff --git a/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol
new file mode 100644
index 000000000..ba784ab22
--- /dev/null
+++ b/packages/contracts/src/contracts/current/protocol/AssetProxy/libs/LibTransferErrors.sol
@@ -0,0 +1,25 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+
+contract LibTransferErrors {
+ /// Transfer errors ///
+ string constant INVALID_AMOUNT = "INVALID_AMOUNT"; // Transfer amount must equal 1.
+ string constant TRANSFER_FAILED = "TRANSFER_FAILED"; // Transfer failed.
+}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
index 0a0f0209a..c406354a7 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinExchangeCore.sol
@@ -44,32 +44,36 @@ contract MixinExchangeCore is
// Mapping of orderHash => cancelled
mapping (bytes32 => bool) public cancelled;
- // Mapping of makerAddress => lowest salt an order can have in order to be fillable
- // Orders with a salt less than their maker's epoch are considered cancelled
- mapping (address => uint256) public makerEpoch;
+ // Mapping of makerAddress => senderAddress => lowest salt an order can have in order to be fillable
+ // Orders with specified senderAddress and with a salt less than their epoch to are considered cancelled
+ mapping (address => mapping (address => uint256)) public orderEpoch;
////// Core exchange functions //////
- /// @dev Cancels all orders created by sender with a salt less than or equal to the specified salt value.
- /// @param salt Orders created with a salt less or equal to this value will be cancelled.
- function cancelOrdersUpTo(uint256 salt)
+ /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
+ /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).
+ /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
+ function cancelOrdersUpTo(uint256 targetOrderEpoch)
external
{
address makerAddress = getCurrentContextAddress();
+ // If this function is called via `executeTransaction`, we only update the orderEpoch for the makerAddress/msg.sender combination.
+ // This allows external filter contracts to add rules to how orders are cancelled via this function.
+ address senderAddress = makerAddress == msg.sender ? address(0) : msg.sender;
- // makerEpoch is initialized to 0, so to cancelUpTo we need salt + 1
- uint256 newMakerEpoch = salt + 1;
- uint256 oldMakerEpoch = makerEpoch[makerAddress];
+ // orderEpoch is initialized to 0, so to cancelUpTo we need salt + 1
+ uint256 newOrderEpoch = targetOrderEpoch + 1;
+ uint256 oldOrderEpoch = orderEpoch[makerAddress][senderAddress];
- // Ensure makerEpoch is monotonically increasing
+ // Ensure orderEpoch is monotonically increasing
require(
- newMakerEpoch > oldMakerEpoch,
- INVALID_NEW_MAKER_EPOCH
+ newOrderEpoch > oldOrderEpoch,
+ INVALID_NEW_ORDER_EPOCH
);
- // Update makerEpoch
- makerEpoch[makerAddress] = newMakerEpoch;
- emit CancelUpTo(makerAddress, newMakerEpoch);
+ // Update orderEpoch
+ orderEpoch[makerAddress][senderAddress] = newOrderEpoch;
+ emit CancelUpTo(makerAddress, senderAddress, newOrderEpoch);
}
/// @dev Fills the input order.
@@ -180,7 +184,7 @@ contract MixinExchangeCore is
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
return orderInfo;
}
- if (makerEpoch[order.makerAddress] > order.salt) {
+ if (orderEpoch[order.makerAddress][order.senderAddress] > order.salt) {
orderInfo.orderStatus = uint8(OrderStatus.CANCELLED);
return orderInfo;
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
index 1a556dfe2..8ad15aaff 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinSignatureValidator.sol
@@ -82,7 +82,7 @@ contract MixinSignatureValidator is
address signer,
bytes memory signature
)
- internal
+ public
view
returns (bool isValid)
{
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
index a7849f4cb..724f95518 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/MixinWrapperFunctions.sol
@@ -263,40 +263,50 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
+ returns (FillResults memory totalFillResults)
{
for (uint256 i = 0; i < orders.length; i++) {
- fillOrder(
+ FillResults memory singleFillResults = fillOrder(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
+ addFillResults(totalFillResults, singleFillResults);
}
+ return totalFillResults;
}
/// @dev Synchronously executes multiple calls of fillOrKill.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
+ returns (FillResults memory totalFillResults)
{
for (uint256 i = 0; i < orders.length; i++) {
- fillOrKillOrder(
+ FillResults memory singleFillResults = fillOrKillOrder(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
+ addFillResults(totalFillResults, singleFillResults);
}
+ return totalFillResults;
}
/// @dev Fills an order with specified parameters and ECDSA signature.
@@ -304,20 +314,25 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
+ /// NOTE: makerAssetFilledAmount and takerAssetFilledAmount may include amounts filled of different assets.
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
public
+ returns (FillResults memory totalFillResults)
{
for (uint256 i = 0; i < orders.length; i++) {
- fillOrderNoThrow(
+ FillResults memory singleFillResults = fillOrderNoThrow(
orders[i],
takerAssetFillAmounts[i],
signatures[i]
);
+ addFillResults(totalFillResults, singleFillResults);
}
+ return totalFillResults;
}
/// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
index 7ca2dd052..98222f33f 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IExchangeCore.sol
@@ -24,9 +24,10 @@ import "../libs/LibFillResults.sol";
contract IExchangeCore {
- /// @dev Cancels all orders reated by sender with a salt less than or equal to the specified salt value.
- /// @param salt Orders created with a salt less or equal to this value will be cancelled.
- function cancelOrdersUpTo(uint256 salt)
+ /// @dev Cancels all orders created by makerAddress with a salt less than or equal to the targetOrderEpoch
+ /// and senderAddress equal to msg.sender (or null address if msg.sender == makerAddress).
+ /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
+ function cancelOrdersUpTo(uint256 targetOrderEpoch)
external;
/// @dev Fills the input order.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
index 26e360c91..02aa9776e 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/ISignatureValidator.sol
@@ -39,4 +39,18 @@ contract ISignatureValidator {
bool approval
)
external;
+
+ /// @dev Verifies that a signature is valid.
+ /// @param hash Message hash that is signed.
+ /// @param signer Address of signer.
+ /// @param signature Proof of signing.
+ /// @return Validity of order signature.
+ function isValidSignature(
+ bytes32 hash,
+ address signer,
+ bytes memory signature
+ )
+ public
+ view
+ returns (bool isValid);
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
index acd4f359c..84bb683bc 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/interfaces/IWrapperFunctions.sol
@@ -22,10 +22,7 @@ pragma experimental ABIEncoderV2;
import "../libs/LibOrder.sol";
import "../libs/LibFillResults.sol";
-contract IWrapperFunctions is
- LibOrder,
- LibFillResults
-{
+contract IWrapperFunctions {
/// @dev Fills the input order. Reverts if exact takerAssetFillAmount not filled.
/// @param order LibOrder.Order struct containing order specifications.
/// @param takerAssetFillAmount Desired amount of takerAsset to sell.
@@ -56,35 +53,41 @@ contract IWrapperFunctions is
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
function batchFillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
- public;
+ public
+ returns (LibFillResults.FillResults memory totalFillResults);
/// @dev Synchronously executes multiple calls of fillOrKill.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
function batchFillOrKillOrders(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
- public;
+ public
+ returns (LibFillResults.FillResults memory totalFillResults);
/// @dev Fills an order with specified parameters and ECDSA signature.
/// Returns false if the transaction would otherwise revert.
/// @param orders Array of order specifications.
/// @param takerAssetFillAmounts Array of desired amounts of takerAsset to sell in orders.
/// @param signatures Proofs that orders have been created by makers.
+ /// @return Amounts filled and fees paid by makers and taker.
function batchFillOrdersNoThrow(
LibOrder.Order[] memory orders,
uint256[] memory takerAssetFillAmounts,
bytes[] memory signatures
)
- public;
+ public
+ returns (LibFillResults.FillResults memory totalFillResults);
/// @dev Synchronously executes multiple calls of fillOrder until total amount of takerAsset is sold by taker.
/// @param orders Array of order specifications.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
index 48dd0f8be..adf27bec3 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/libs/LibExchangeErrors.sol
@@ -36,7 +36,7 @@ contract LibExchangeErrors {
string constant SIGNATURE_UNSUPPORTED = "SIGNATURE_UNSUPPORTED"; // Signature type unsupported.
/// cancelOrdersUptTo errors ///
- string constant INVALID_NEW_MAKER_EPOCH = "INVALID_NEW_MAKER_EPOCH"; // Specified salt must be greater than or equal to existing makerEpoch.
+ string constant INVALID_NEW_ORDER_EPOCH = "INVALID_NEW_ORDER_EPOCH"; // Specified salt must be greater than or equal to existing orderEpoch.
/// fillOrKillOrder errors ///
string constant COMPLETE_FILL_FAILED = "COMPLETE_FILL_FAILED"; // Desired takerAssetFillAmount could not be completely filled.
@@ -55,7 +55,7 @@ contract LibExchangeErrors {
string constant ASSET_PROXY_ID_MISMATCH = "ASSET_PROXY_ID_MISMATCH"; // newAssetProxyId does not match given assetProxyId.
/// Length validation errors ///
- string constant LENGTH_GREATER_THAN_0_REQUIRED = "LENGTH_GREATER_THAN_0_REQUIRED"; // Byte array must have a length greater than 0.
+ string constant LENGTH_GREATER_THAN_0_REQUIRED = "LENGTH_GREATER_THAN_0_REQUIRED"; // Byte array must have a length greater than 0.
string constant LENGTH_0_REQUIRED = "LENGTH_1_REQUIRED"; // Byte array must have a length of 1.
string constant LENGTH_65_REQUIRED = "LENGTH_66_REQUIRED"; // Byte array must have a length of 66.
}
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
index de7c4d3af..fb345294c 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MExchangeCore.sol
@@ -52,7 +52,8 @@ contract MExchangeCore is
// CancelUpTo event is emitted whenever `cancelOrdersUpTo` is executed succesfully.
event CancelUpTo(
address indexed makerAddress,
- uint256 makerEpoch
+ address indexed senderAddress,
+ uint256 orderEpoch
);
/// @dev Updates state with results of a fill order.
diff --git a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
index 7eed453ff..5e286e43a 100644
--- a/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
+++ b/packages/contracts/src/contracts/current/protocol/Exchange/mixins/MSignatureValidator.sol
@@ -35,18 +35,4 @@ contract MSignatureValidator is
PreSigned, // 0x07
Trezor // 0x08
}
-
- /// @dev Verifies that a signature is valid.
- /// @param hash Message hash that is signed.
- /// @param signer Address of signer.
- /// @param signature Proof of signing.
- /// @return Validity of order signature.
- function isValidSignature(
- bytes32 hash,
- address signer,
- bytes memory signature
- )
- internal
- view
- returns (bool isValid);
}
diff --git a/packages/contracts/src/contracts/current/test/ExchangeWrapper/ExchangeWrapper.sol b/packages/contracts/src/contracts/current/test/ExchangeWrapper/ExchangeWrapper.sol
new file mode 100644
index 000000000..5baaf6e5a
--- /dev/null
+++ b/packages/contracts/src/contracts/current/test/ExchangeWrapper/ExchangeWrapper.sol
@@ -0,0 +1,98 @@
+/*
+
+ Copyright 2018 ZeroEx Intl.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+*/
+
+pragma solidity ^0.4.24;
+pragma experimental ABIEncoderV2;
+
+import "../../protocol/Exchange/interfaces/IExchange.sol";
+import "../../protocol/Exchange/libs/LibOrder.sol";
+
+contract ExchangeWrapper {
+
+ // Exchange contract.
+ IExchange EXCHANGE;
+
+ constructor (address _exchange)
+ public
+ {
+ EXCHANGE = IExchange(_exchange);
+ }
+
+ /// @dev Fills an order using `msg.sender` as the taker.
+ /// @param order Order struct containing order specifications.
+ /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
+ /// @param salt Arbitrary value to gaurantee uniqueness of 0x transaction hash.
+ /// @param orderSignature Proof that order has been created by maker.
+ /// @param takerSignature Proof that taker wishes to call this function with given params.
+ function fillOrder(
+ LibOrder.Order memory order,
+ uint256 takerAssetFillAmount,
+ uint256 salt,
+ bytes memory orderSignature,
+ bytes memory takerSignature
+ )
+ public
+ {
+ address takerAddress = msg.sender;
+
+ // Encode arguments into byte array.
+ bytes memory data = abi.encodeWithSelector(
+ EXCHANGE.fillOrder.selector,
+ order,
+ takerAssetFillAmount,
+ orderSignature
+ );
+
+ // Call `fillOrder` via `executeTransaction`.
+ EXCHANGE.executeTransaction(
+ salt,
+ takerAddress,
+ data,
+ takerSignature
+ );
+ }
+
+ /// @dev Cancels all orders created by sender with a salt less than or equal to the targetOrderEpoch
+ /// and senderAddress equal to this contract.
+ /// @param targetOrderEpoch Orders created with a salt less or equal to this value will be cancelled.
+ /// @param salt Arbitrary value to gaurantee uniqueness of 0x transaction hash.
+ /// @param makerSignature Proof that maker wishes to call this function with given params.
+ function cancelOrdersUpTo(
+ uint256 targetOrderEpoch,
+ uint256 salt,
+ bytes makerSignature
+ )
+ external
+ {
+ address makerAddress = msg.sender;
+
+ // Encode arguments into byte array.
+ bytes memory data = abi.encodeWithSelector(
+ EXCHANGE.cancelOrdersUpTo.selector,
+ targetOrderEpoch
+ );
+
+ // Call `cancelOrdersUpTo` via `executeTransaction`.
+ EXCHANGE.executeTransaction(
+ salt,
+ makerAddress,
+ data,
+ makerSignature
+ );
+ }
+}
diff --git a/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol b/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol
index 0594e2767..460c9ea42 100644
--- a/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol
+++ b/packages/contracts/src/contracts/current/test/Whitelist/Whitelist.sol
@@ -16,7 +16,7 @@
*/
-pragma solidity ^0.4.23;
+pragma solidity ^0.4.24;
pragma experimental ABIEncoderV2;
import "../../protocol/Exchange/interfaces/IExchange.sol";
diff --git a/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol b/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol
index 6afb9973a..97fb5fb0f 100644
--- a/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol
+++ b/packages/contracts/src/contracts/current/utils/LibMem/LibMem.sol
@@ -80,9 +80,6 @@ contract LibMem
//
if (source > dest) {
assembly {
- // Record the total number of full words to copy
- let nWords := div(length, 32)
-
// We subtract 32 from `sEnd` and `dEnd` because it
// is easier to compare with in the loop, and these
// are also the addresses we need for copying the
@@ -98,20 +95,19 @@ contract LibMem
let last := mload(sEnd)
// Copy whole words front to back
- for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
+ // Note: the first check is always true,
+ // this could have been a do-while loop.
+ for {} lt(source, sEnd) {} {
mstore(dest, mload(source))
source := add(source, 32)
dest := add(dest, 32)
}
-
+
// Write the last 32 bytes
mstore(dEnd, last)
}
} else {
assembly {
- // Record the total number of full words to copy
- let nWords := div(length, 32)
-
// We subtract 32 from `sEnd` and `dEnd` because those
// are the starting points when copying a word at the end.
length := sub(length, 32)
@@ -125,12 +121,18 @@ contract LibMem
let first := mload(source)
// Copy whole words back to front
- for {let i := 0} lt(i, nWords) {i := add(i, 1)} {
+ // We use a signed comparisson here to allow dEnd to become
+ // negative (happens when source and dest < 32). Valid
+ // addresses in local memory will never be larger than
+ // 2**255, so they can be safely re-interpreted as signed.
+ // Note: the first check is always true,
+ // this could have been a do-while loop.
+ for {} slt(dest, dEnd) {} {
mstore(dEnd, mload(sEnd))
sEnd := sub(sEnd, 32)
dEnd := sub(dEnd, 32)
}
-
+
// Write the first 32 bytes
mstore(dest, first)
}
diff --git a/packages/contracts/src/utils/artifacts.ts b/packages/contracts/src/utils/artifacts.ts
index bf7221d6d..7ba467708 100644
--- a/packages/contracts/src/utils/artifacts.ts
+++ b/packages/contracts/src/utils/artifacts.ts
@@ -7,6 +7,7 @@ import * as DummyERC721Token from '../artifacts/DummyERC721Token.json';
import * as ERC20Proxy from '../artifacts/ERC20Proxy.json';
import * as ERC721Proxy from '../artifacts/ERC721Proxy.json';
import * as Exchange from '../artifacts/Exchange.json';
+import * as ExchangeWrapper from '../artifacts/ExchangeWrapper.json';
import * as MixinAuthorizable from '../artifacts/MixinAuthorizable.json';
import * as MultiSigWallet from '../artifacts/MultiSigWallet.json';
import * as MultiSigWalletWithTimeLock from '../artifacts/MultiSigWalletWithTimeLock.json';
@@ -29,6 +30,7 @@ export const artifacts = {
ERC20Proxy: (ERC20Proxy as any) as ContractArtifact,
ERC721Proxy: (ERC721Proxy as any) as ContractArtifact,
Exchange: (Exchange as any) as ContractArtifact,
+ ExchangeWrapper: (ExchangeWrapper as any) as ContractArtifact,
EtherToken: (EtherToken as any) as ContractArtifact,
MixinAuthorizable: (MixinAuthorizable as any) as ContractArtifact,
MultiSigWallet: (MultiSigWallet as any) as ContractArtifact,
diff --git a/packages/contracts/test/asset_proxy/proxies.ts b/packages/contracts/test/asset_proxy/proxies.ts
index 9760d3b9c..2c27f7382 100644
--- a/packages/contracts/test/asset_proxy/proxies.ts
+++ b/packages/contracts/test/asset_proxy/proxies.ts
@@ -276,7 +276,7 @@ describe('Asset Transfer Proxies', () => {
expect(newOwnerMakerAsset).to.be.bignumber.equal(takerAddress);
});
- it('should not call onERC721Received when transferring to a smart contract without receiver data', async () => {
+ it('should call onERC721Received when transferring to a smart contract without receiver data', async () => {
// Construct ERC721 asset data
const encodedAssetData = assetProxyUtils.encodeERC721AssetData(erc721Token.address, erc721MakerTokenId);
const encodedAssetDataWithoutProxyId = encodedAssetData.slice(0, -2);
@@ -297,7 +297,11 @@ describe('Asset Transfer Proxies', () => {
const logDecoder = new LogDecoder(web3Wrapper, erc721Receiver.address);
const tx = await logDecoder.getTxWithDecodedLogsAsync(txHash);
// Verify that no log was emitted by erc721 receiver
- expect(tx.logs.length).to.be.equal(0);
+ expect(tx.logs.length).to.be.equal(1);
+ const tokenReceivedLog = tx.logs[0] as LogWithDecodedArgs<TokenReceivedContractEventArgs>;
+ expect(tokenReceivedLog.args.from).to.be.equal(makerAddress);
+ expect(tokenReceivedLog.args.tokenId).to.be.bignumber.equal(erc721MakerTokenId);
+ expect(tokenReceivedLog.args.data).to.be.equal(constants.NULL_BYTES);
// Verify transfer was successful
const newOwnerMakerAsset = await erc721Token.ownerOf.callAsync(erc721MakerTokenId);
expect(newOwnerMakerAsset).to.be.bignumber.equal(erc721Receiver.address);
diff --git a/packages/contracts/test/exchange/core.ts b/packages/contracts/test/exchange/core.ts
index 63c2fa6c0..ea37a1e99 100644
--- a/packages/contracts/test/exchange/core.ts
+++ b/packages/contracts/test/exchange/core.ts
@@ -620,30 +620,30 @@ describe('Exchange core', () => {
});
describe('cancelOrdersUpTo', () => {
- it('should fail to set makerEpoch less than current makerEpoch', async () => {
- const makerEpoch = new BigNumber(1);
- await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress);
- const lesserMakerEpoch = new BigNumber(0);
+ it('should fail to set orderEpoch less than current orderEpoch', async () => {
+ const orderEpoch = new BigNumber(1);
+ await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress);
+ const lesserOrderEpoch = new BigNumber(0);
return expectRevertOrAlwaysFailingTransactionAsync(
- exchangeWrapper.cancelOrdersUpToAsync(lesserMakerEpoch, makerAddress),
+ exchangeWrapper.cancelOrdersUpToAsync(lesserOrderEpoch, makerAddress),
);
});
- it('should fail to set makerEpoch equal to existing makerEpoch', async () => {
- const makerEpoch = new BigNumber(1);
- await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress);
+ it('should fail to set orderEpoch equal to existing orderEpoch', async () => {
+ const orderEpoch = new BigNumber(1);
+ await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress);
return expectRevertOrAlwaysFailingTransactionAsync(
- exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress),
+ exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress),
);
});
- it('should cancel only orders with a makerEpoch less than existing makerEpoch', async () => {
- // Cancel all transactions with a makerEpoch less than 1
- const makerEpoch = new BigNumber(1);
- await exchangeWrapper.cancelOrdersUpToAsync(makerEpoch, makerAddress);
+ it('should cancel only orders with a orderEpoch less than existing orderEpoch', async () => {
+ // Cancel all transactions with a orderEpoch less than 1
+ const orderEpoch = new BigNumber(1);
+ await exchangeWrapper.cancelOrdersUpToAsync(orderEpoch, makerAddress);
- // Create 3 orders with makerEpoch values: 0,1,2,3
- // Since we cancelled with makerEpoch=1, orders with makerEpoch<=1 will not be processed
+ // Create 3 orders with orderEpoch values: 0,1,2,3
+ // Since we cancelled with orderEpoch=1, orders with orderEpoch<=1 will not be processed
erc20Balances = await erc20Wrapper.getBalancesAsync();
const signedOrders = [
orderFactory.newSignedOrder({
diff --git a/packages/contracts/test/exchange/transactions.ts b/packages/contracts/test/exchange/transactions.ts
index 21ff123e5..9a625880c 100644
--- a/packages/contracts/test/exchange/transactions.ts
+++ b/packages/contracts/test/exchange/transactions.ts
@@ -7,6 +7,7 @@ import * as chai from 'chai';
import { DummyERC20TokenContract } from '../../src/generated_contract_wrappers/dummy_e_r_c20_token';
import { ERC20ProxyContract } from '../../src/generated_contract_wrappers/e_r_c20_proxy';
import { ExchangeContract } from '../../src/generated_contract_wrappers/exchange';
+import { ExchangeWrapperContract } from '../../src/generated_contract_wrappers/exchange_wrapper';
import { WhitelistContract } from '../../src/generated_contract_wrappers/whitelist';
import { artifacts } from '../../src/utils/artifacts';
import { expectRevertOrAlwaysFailingTransactionAsync } from '../../src/utils/assertions';
@@ -198,6 +199,117 @@ describe('Exchange transactions', () => {
);
});
});
+
+ describe('cancelOrdersUpTo', () => {
+ let exchangeWrapperContract: ExchangeWrapperContract;
+
+ before(async () => {
+ exchangeWrapperContract = await ExchangeWrapperContract.deployFrom0xArtifactAsync(
+ artifacts.ExchangeWrapper,
+ provider,
+ txDefaults,
+ exchange.address,
+ );
+ });
+
+ it("should cancel an order if called from the order's sender", async () => {
+ const orderSalt = new BigNumber(0);
+ signedOrder = orderFactory.newSignedOrder({
+ senderAddress: exchangeWrapperContract.address,
+ salt: orderSalt,
+ });
+ const targetOrderEpoch = orderSalt.add(1);
+ const cancelData = exchange.cancelOrdersUpTo.getABIEncodedTransactionData(targetOrderEpoch);
+ const signedCancelTx = makerTransactionFactory.newSignedTransaction(cancelData);
+ await exchangeWrapperContract.cancelOrdersUpTo.sendTransactionAsync(
+ targetOrderEpoch,
+ signedCancelTx.salt,
+ signedCancelTx.signature,
+ {
+ from: makerAddress,
+ },
+ );
+
+ const takerAssetFillAmount = signedOrder.takerAssetAmount;
+ orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
+ const fillData = exchange.fillOrder.getABIEncodedTransactionData(
+ orderWithoutExchangeAddress,
+ takerAssetFillAmount,
+ signedOrder.signature,
+ );
+ const signedFillTx = takerTransactionFactory.newSignedTransaction(fillData);
+ return expectRevertOrAlwaysFailingTransactionAsync(
+ exchangeWrapperContract.fillOrder.sendTransactionAsync(
+ orderWithoutExchangeAddress,
+ takerAssetFillAmount,
+ signedFillTx.salt,
+ signedOrder.signature,
+ signedFillTx.signature,
+ { from: takerAddress },
+ ),
+ );
+ });
+
+ it("should not cancel an order if not called from the order's sender", async () => {
+ const orderSalt = new BigNumber(0);
+ signedOrder = orderFactory.newSignedOrder({
+ senderAddress: exchangeWrapperContract.address,
+ salt: orderSalt,
+ });
+ const targetOrderEpoch = orderSalt.add(1);
+ await exchangeWrapper.cancelOrdersUpToAsync(targetOrderEpoch, makerAddress);
+
+ erc20Balances = await erc20Wrapper.getBalancesAsync();
+ const takerAssetFillAmount = signedOrder.takerAssetAmount;
+ orderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress(signedOrder);
+ const data = exchange.fillOrder.getABIEncodedTransactionData(
+ orderWithoutExchangeAddress,
+ takerAssetFillAmount,
+ signedOrder.signature,
+ );
+ signedTx = takerTransactionFactory.newSignedTransaction(data);
+ await exchangeWrapperContract.fillOrder.sendTransactionAsync(
+ orderWithoutExchangeAddress,
+ takerAssetFillAmount,
+ signedTx.salt,
+ signedOrder.signature,
+ signedTx.signature,
+ { from: takerAddress },
+ );
+
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ const makerAssetFillAmount = takerAssetFillAmount
+ .times(signedOrder.makerAssetAmount)
+ .dividedToIntegerBy(signedOrder.takerAssetAmount);
+ const makerFeePaid = signedOrder.makerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(signedOrder.makerAssetAmount);
+ const takerFeePaid = signedOrder.takerFee
+ .times(makerAssetFillAmount)
+ .dividedToIntegerBy(signedOrder.makerAssetAmount);
+ expect(newBalances[makerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal(
+ erc20Balances[makerAddress][defaultMakerTokenAddress].minus(makerAssetFillAmount),
+ );
+ expect(newBalances[makerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal(
+ erc20Balances[makerAddress][defaultTakerTokenAddress].add(takerAssetFillAmount),
+ );
+ expect(newBalances[makerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[makerAddress][zrxToken.address].minus(makerFeePaid),
+ );
+ expect(newBalances[takerAddress][defaultTakerTokenAddress]).to.be.bignumber.equal(
+ erc20Balances[takerAddress][defaultTakerTokenAddress].minus(takerAssetFillAmount),
+ );
+ expect(newBalances[takerAddress][defaultMakerTokenAddress]).to.be.bignumber.equal(
+ erc20Balances[takerAddress][defaultMakerTokenAddress].add(makerAssetFillAmount),
+ );
+ expect(newBalances[takerAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[takerAddress][zrxToken.address].minus(takerFeePaid),
+ );
+ expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal(
+ erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)),
+ );
+ });
+ });
});
describe('Whitelist', () => {