diff --git a/contracts/erc20/contracts/test/UntransferrableDummyERC20Token.sol b/contracts/erc20/contracts/test/UntransferrableDummyERC20Token.sol
index e1e3b3b68d..5c0e424608 100644
--- a/contracts/erc20/contracts/test/UntransferrableDummyERC20Token.sol
+++ b/contracts/erc20/contracts/test/UntransferrableDummyERC20Token.sol
@@ -22,6 +22,7 @@ import "./DummyERC20Token.sol";
// solhint-disable no-empty-blocks
+// solhint-disable no-unused-vars
contract UntransferrableDummyERC20Token is
DummyERC20Token
{
diff --git a/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol b/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol
index 032067627e..33672623da 100644
--- a/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol
+++ b/contracts/exchange-forwarder/contracts/src/MixinExchangeWrapper.sol
@@ -21,17 +21,17 @@ pragma experimental ABIEncoderV2;
import "./libs/LibConstants.sol";
import "./mixins/MExchangeWrapper.sol";
-import "@0x/contracts-exchange-libs/contracts/src/LibAbiEncoder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
+import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
contract MixinExchangeWrapper is
- LibAbiEncoder,
LibFillResults,
LibMath,
LibConstants,
+ LibExchangeSelectors,
MExchangeWrapper
{
/// @dev Fills the input order.
@@ -49,7 +49,8 @@ contract MixinExchangeWrapper is
returns (FillResults memory fillResults)
{
// ABI encode calldata for `fillOrder`
- bytes memory fillOrderCalldata = _abiEncodeFillOrder(
+ bytes memory fillOrderCalldata = abi.encodeWithSelector(
+ FILL_ORDER_SELECTOR,
order,
takerAssetFillAmount,
signature
diff --git a/contracts/exchange-libs/compiler.json b/contracts/exchange-libs/compiler.json
index 179dd9683c..2ecb45ea12 100644
--- a/contracts/exchange-libs/compiler.json
+++ b/contracts/exchange-libs/compiler.json
@@ -23,7 +23,6 @@
}
},
"contracts": [
- "src/LibAbiEncoder.sol",
"src/LibConstants.sol",
"src/LibEIP712ExchangeDomain.sol",
"src/LibFillResults.sol",
diff --git a/contracts/exchange-libs/contracts/src/LibAbiEncoder.sol b/contracts/exchange-libs/contracts/src/LibAbiEncoder.sol
deleted file mode 100644
index a83d45560e..0000000000
--- a/contracts/exchange-libs/contracts/src/LibAbiEncoder.sol
+++ /dev/null
@@ -1,215 +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.5.5;
-pragma experimental ABIEncoderV2;
-
-import "./LibOrder.sol";
-
-
-contract LibAbiEncoder {
-
- /// @dev ABI encodes calldata for `fillOrder`.
- /// @param order Order struct containing order specifications.
- /// @param takerAssetFillAmount Desired amount of takerAsset to sell.
- /// @param signature Proof that order has been created by maker.
- /// @return ABI encoded calldata for `fillOrder`.
- function _abiEncodeFillOrder(
- LibOrder.Order memory order,
- uint256 takerAssetFillAmount,
- bytes memory signature
- )
- internal
- pure
- returns (bytes memory fillOrderCalldata)
- {
- // We need to call MExchangeCore.fillOrder using a delegatecall in
- // assembly so that we can intercept a call that throws. For this, we
- // need the input encoded in memory in the Ethereum ABIv2 format [1].
-
- // | Area | Offset | Length | Contents |
- // | -------- |--------|---------|-------------------------------------------- |
- // | Header | 0x00 | 4 | function selector |
- // | Params | | 3 * 32 | function parameters: |
- // | | 0x00 | | 1. offset to order (*) |
- // | | 0x20 | | 2. takerAssetFillAmount |
- // | | 0x40 | | 3. offset to signature (*) |
- // | Data | | 12 * 32 | order: |
- // | | 0x000 | | 1. senderAddress |
- // | | 0x020 | | 2. makerAddress |
- // | | 0x040 | | 3. takerAddress |
- // | | 0x060 | | 4. feeRecipientAddress |
- // | | 0x080 | | 5. makerAssetAmount |
- // | | 0x0A0 | | 6. takerAssetAmount |
- // | | 0x0C0 | | 7. makerFeeAmount |
- // | | 0x0E0 | | 8. takerFeeAmount |
- // | | 0x100 | | 9. expirationTimeSeconds |
- // | | 0x120 | | 10. salt |
- // | | 0x140 | | 11. Offset to makerAssetData (*) |
- // | | 0x160 | | 12. Offset to takerAssetData (*) |
- // | | 0x180 | 32 | makerAssetData Length |
- // | | 0x1A0 | ** | makerAssetData Contents |
- // | | 0x1C0 | 32 | takerAssetData Length |
- // | | 0x1E0 | ** | takerAssetData Contents |
- // | | 0x200 | 32 | signature Length |
- // | | 0x220 | ** | signature Contents |
-
- // * Offsets are calculated from the beginning of the current area: Header, Params, Data:
- // An offset stored in the Params area is calculated from the beginning of the Params section.
- // An offset stored in the Data area is calculated from the beginning of the Data section.
-
- // ** The length of dynamic array contents are stored in the field immediately preceeding the contents.
-
- // [1]: https://solidity.readthedocs.io/en/develop/abi-spec.html
-
- assembly {
-
- // Areas below may use the following variables:
- // 1. Start -- Start of this area in memory
- // 2. End -- End of this area in memory. This value may
- // be precomputed (before writing contents),
- // or it may be computed as contents are written.
- // 3. Offset -- Current offset into area. If an area's End
- // is precomputed, this variable tracks the
- // offsets of contents as they are written.
-
- /////// Setup Header Area ///////
- // Load free memory pointer
- fillOrderCalldata := mload(0x40)
- // bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)"))
- // = 0xb4be83d5
- // Leave 0x20 bytes to store the length
- mstore(add(fillOrderCalldata, 0x20), 0xb4be83d500000000000000000000000000000000000000000000000000000000)
- let headerAreaEnd := add(fillOrderCalldata, 0x24)
-
- /////// Setup Params Area ///////
- // This area is preallocated and written to later.
- // This is because we need to fill in offsets that have not yet been calculated.
- let paramsAreaStart := headerAreaEnd
- let paramsAreaEnd := add(paramsAreaStart, 0x60)
- let paramsAreaOffset := paramsAreaStart
-
- /////// Setup Data Area ///////
- let dataAreaStart := paramsAreaEnd
- let dataAreaEnd := dataAreaStart
-
- // Offset from the source data we're reading from
- let sourceOffset := order
- // arrayLenBytes and arrayLenWords track the length of a dynamically-allocated bytes array.
- let arrayLenBytes := 0
- let arrayLenWords := 0
-
- /////// Write order Struct ///////
- // Write memory location of Order, relative to the start of the
- // parameter list, then increment the paramsAreaOffset respectively.
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
- paramsAreaOffset := add(paramsAreaOffset, 0x20)
-
- // Write values for each field in the order
- // It would be nice to use a loop, but we save on gas by writing
- // the stores sequentially.
- mstore(dataAreaEnd, mload(sourceOffset)) // makerAddress
- mstore(add(dataAreaEnd, 0x20), mload(add(sourceOffset, 0x20))) // takerAddress
- mstore(add(dataAreaEnd, 0x40), mload(add(sourceOffset, 0x40))) // feeRecipientAddress
- mstore(add(dataAreaEnd, 0x60), mload(add(sourceOffset, 0x60))) // senderAddress
- mstore(add(dataAreaEnd, 0x80), mload(add(sourceOffset, 0x80))) // makerAssetAmount
- mstore(add(dataAreaEnd, 0xA0), mload(add(sourceOffset, 0xA0))) // takerAssetAmount
- mstore(add(dataAreaEnd, 0xC0), mload(add(sourceOffset, 0xC0))) // makerFeeAmount
- mstore(add(dataAreaEnd, 0xE0), mload(add(sourceOffset, 0xE0))) // takerFeeAmount
- mstore(add(dataAreaEnd, 0x100), mload(add(sourceOffset, 0x100))) // expirationTimeSeconds
- mstore(add(dataAreaEnd, 0x120), mload(add(sourceOffset, 0x120))) // salt
- mstore(add(dataAreaEnd, 0x140), mload(add(sourceOffset, 0x140))) // Offset to makerAssetData
- mstore(add(dataAreaEnd, 0x160), mload(add(sourceOffset, 0x160))) // Offset to takerAssetData
- dataAreaEnd := add(dataAreaEnd, 0x180)
- sourceOffset := add(sourceOffset, 0x180)
-
- // Write offset to
- mstore(add(dataAreaStart, mul(10, 0x20)), sub(dataAreaEnd, dataAreaStart))
-
- // Calculate length of
- sourceOffset := mload(add(order, 0x140)) // makerAssetData
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- // Write offset to
- mstore(add(dataAreaStart, mul(11, 0x20)), sub(dataAreaEnd, dataAreaStart))
-
- // Calculate length of
- sourceOffset := mload(add(order, 0x160)) // takerAssetData
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- /////// Write takerAssetFillAmount ///////
- mstore(paramsAreaOffset, takerAssetFillAmount)
- paramsAreaOffset := add(paramsAreaOffset, 0x20)
-
- /////// Write signature ///////
- // Write offset to paramsArea
- mstore(paramsAreaOffset, sub(dataAreaEnd, paramsAreaStart))
-
- // Calculate length of signature
- sourceOffset := signature
- arrayLenBytes := mload(sourceOffset)
- sourceOffset := add(sourceOffset, 0x20)
- arrayLenWords := div(add(arrayLenBytes, 0x1F), 0x20)
-
- // Write length of signature
- mstore(dataAreaEnd, arrayLenBytes)
- dataAreaEnd := add(dataAreaEnd, 0x20)
-
- // Write contents of signature
- for {let i := 0} lt(i, arrayLenWords) {i := add(i, 1)} {
- mstore(dataAreaEnd, mload(sourceOffset))
- dataAreaEnd := add(dataAreaEnd, 0x20)
- sourceOffset := add(sourceOffset, 0x20)
- }
-
- // Set length of calldata
- mstore(fillOrderCalldata, sub(dataAreaEnd, add(fillOrderCalldata, 0x20)))
-
- // Increment free memory pointer
- mstore(0x40, dataAreaEnd)
- }
-
- return fillOrderCalldata;
- }
-}
diff --git a/contracts/exchange-libs/contracts/test/TestLibs.sol b/contracts/exchange-libs/contracts/test/TestLibs.sol
index 1ae1843059..8548e0fbdd 100644
--- a/contracts/exchange-libs/contracts/test/TestLibs.sol
+++ b/contracts/exchange-libs/contracts/test/TestLibs.sol
@@ -23,7 +23,6 @@ import "../src/LibEIP712ExchangeDomain.sol";
import "../src/LibMath.sol";
import "../src/LibOrder.sol";
import "../src/LibFillResults.sol";
-import "../src/LibAbiEncoder.sol";
// solhint-disable no-empty-blocks
@@ -31,31 +30,13 @@ contract TestLibs is
LibEIP712ExchangeDomain,
LibMath,
LibOrder,
- LibFillResults,
- LibAbiEncoder
+ LibFillResults
{
constructor (uint256 chainId)
public
LibEIP712ExchangeDomain(chainId, address(0))
{}
- function abiEncodeFillOrder(
- Order memory order,
- uint256 takerAssetFillAmount,
- bytes memory signature
- )
- public
- pure
- returns (bytes memory fillOrderCalldata)
- {
- fillOrderCalldata = _abiEncodeFillOrder(
- order,
- takerAssetFillAmount,
- signature
- );
- return fillOrderCalldata;
- }
-
function getPartialAmountFloor(
uint256 numerator,
uint256 denominator,
diff --git a/contracts/exchange-libs/package.json b/contracts/exchange-libs/package.json
index 5f3e5c7f50..9c3eba6202 100644
--- a/contracts/exchange-libs/package.json
+++ b/contracts/exchange-libs/package.json
@@ -34,7 +34,7 @@
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
},
"config": {
- "abis": "./generated-artifacts/@(LibAbiEncoder|LibAssetProxyErrors|LibConstants|LibEIP712ExchangeDomain|LibFillResults|LibMath|LibOrder|LibZeroExTransaction|TestLibs).json",
+ "abis": "./generated-artifacts/@(LibAssetProxyErrors|LibConstants|LibEIP712ExchangeDomain|LibFillResults|LibMath|LibOrder|LibZeroExTransaction|TestLibs).json",
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
},
"repository": {
diff --git a/contracts/exchange-libs/src/artifacts.ts b/contracts/exchange-libs/src/artifacts.ts
index 700d08af69..6b3159f20a 100644
--- a/contracts/exchange-libs/src/artifacts.ts
+++ b/contracts/exchange-libs/src/artifacts.ts
@@ -5,7 +5,6 @@
*/
import { ContractArtifact } from 'ethereum-types';
-import * as LibAbiEncoder from '../generated-artifacts/LibAbiEncoder.json';
import * as LibConstants from '../generated-artifacts/LibConstants.json';
import * as LibEIP712ExchangeDomain from '../generated-artifacts/LibEIP712ExchangeDomain.json';
import * as LibFillResults from '../generated-artifacts/LibFillResults.json';
@@ -14,7 +13,6 @@ import * as LibOrder from '../generated-artifacts/LibOrder.json';
import * as LibZeroExTransaction from '../generated-artifacts/LibZeroExTransaction.json';
import * as TestLibs from '../generated-artifacts/TestLibs.json';
export const artifacts = {
- LibAbiEncoder: LibAbiEncoder as ContractArtifact,
LibConstants: LibConstants as ContractArtifact,
LibFillResults: LibFillResults as ContractArtifact,
LibMath: LibMath as ContractArtifact,
diff --git a/contracts/exchange-libs/src/wrappers.ts b/contracts/exchange-libs/src/wrappers.ts
index 4d9adda8cf..4908d5a01a 100644
--- a/contracts/exchange-libs/src/wrappers.ts
+++ b/contracts/exchange-libs/src/wrappers.ts
@@ -3,7 +3,6 @@
* Warning: This file is auto-generated by contracts-gen. Don't edit manually.
* -----------------------------------------------------------------------------
*/
-export * from '../generated-wrappers/lib_abi_encoder';
export * from '../generated-wrappers/lib_constants';
export * from '../generated-wrappers/lib_e_i_p712_exchange_domain';
export * from '../generated-wrappers/lib_fill_results';
diff --git a/contracts/exchange-libs/tsconfig.json b/contracts/exchange-libs/tsconfig.json
index 08efc17267..c189082e61 100644
--- a/contracts/exchange-libs/tsconfig.json
+++ b/contracts/exchange-libs/tsconfig.json
@@ -3,7 +3,6 @@
"compilerOptions": { "outDir": "lib", "rootDir": ".", "resolveJsonModule": true },
"include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"],
"files": [
- "generated-artifacts/LibAbiEncoder.json",
"generated-artifacts/LibConstants.json",
"generated-artifacts/LibEIP712ExchangeDomain.json",
"generated-artifacts/LibFillResults.json",
diff --git a/contracts/exchange/contracts/src/MixinWrapperFunctions.sol b/contracts/exchange/contracts/src/MixinWrapperFunctions.sol
index 3da63dbd4f..d7f8ccd1b6 100644
--- a/contracts/exchange/contracts/src/MixinWrapperFunctions.sol
+++ b/contracts/exchange/contracts/src/MixinWrapperFunctions.sol
@@ -23,7 +23,7 @@ import "@0x/contracts-utils/contracts/src/ReentrancyGuard.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibMath.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
import "@0x/contracts-exchange-libs/contracts/src/LibFillResults.sol";
-import "@0x/contracts-exchange-libs/contracts/src/LibAbiEncoder.sol";
+import "@0x/contracts-exchange-libs/contracts/src/LibExchangeSelectors.sol";
import "./mixins/MExchangeCore.sol";
import "./mixins/MWrapperFunctions.sol";
import "./mixins/MExchangeRichErrors.sol";
@@ -33,7 +33,7 @@ contract MixinWrapperFunctions is
ReentrancyGuard,
LibMath,
LibFillResults,
- LibAbiEncoder,
+ LibExchangeSelectors,
MExchangeCore,
MWrapperFunctions,
MExchangeRichErrors
@@ -74,7 +74,8 @@ contract MixinWrapperFunctions is
returns (FillResults memory fillResults)
{
// ABI encode calldata for `fillOrder`
- bytes memory fillOrderCalldata = _abiEncodeFillOrder(
+ bytes memory fillOrderCalldata = abi.encodeWithSelector(
+ FILL_ORDER_SELECTOR,
order,
takerAssetFillAmount,
signature
@@ -374,6 +375,18 @@ contract MixinWrapperFunctions is
return totalFillResults;
}
+ /// @dev After calling, the order can not be filled anymore.
+ /// @return True if the order was cancelled successfully.
+ /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
+ function cancelOrderNoThrow(LibOrder.Order memory order)
+ public
+ returns (bool didCancel)
+ {
+ bytes memory cancelOrderCallData = abi.encodeWithSelector(CANCEL_ORDER_SELECTOR, order);
+ (didCancel,) = address(this).delegatecall(cancelOrderCallData);
+ return didCancel;
+ }
+
/// @dev Synchronously cancels multiple orders in a single transaction.
/// @param orders Array of order specifications.
function batchCancelOrders(LibOrder.Order[] memory orders)
@@ -386,6 +399,21 @@ contract MixinWrapperFunctions is
}
}
+ /// @dev Synchronously cancels multiple orders in a single transaction.
+ /// @param orders Array of order specifications.
+ /// @return Bool array containing results of each individual cancellation.
+ function batchCancelOrdersNoThrow(LibOrder.Order[] memory orders)
+ public
+ returns (bool[] memory)
+ {
+ uint256 ordersLength = orders.length;
+ bool[] memory didCancel = new bool[](ordersLength);
+ for (uint256 i = 0; i != ordersLength; i++) {
+ didCancel[i] = cancelOrderNoThrow(orders[i]);
+ }
+ return didCancel;
+ }
+
/// @dev Fetches information for all passed in orders.
/// @param orders Array of order specifications.
/// @return Array of OrderInfo instances that correspond to each order.
diff --git a/contracts/exchange/test/utils/exchange_wrapper.ts b/contracts/exchange/test/utils/exchange_wrapper.ts
index a02d8e1f46..a69cd080b4 100644
--- a/contracts/exchange/test/utils/exchange_wrapper.ts
+++ b/contracts/exchange/test/utils/exchange_wrapper.ts
@@ -54,6 +54,15 @@ export class ExchangeWrapper {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
+ public async cancelOrderNoThrowAsync(
+ signedOrder: SignedOrder,
+ from: string,
+ ): Promise {
+ const params = orderUtils.createCancel(signedOrder);
+ const txHash = await this._exchange.cancelOrderNoThrow.sendTransactionAsync(params.order, { from });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
public async fillOrKillOrderAsync(
signedOrder: SignedOrder,
from: string,
@@ -198,6 +207,15 @@ export class ExchangeWrapper {
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
return tx;
}
+ public async batchCancelOrdersNoThrowAsync(
+ orders: SignedOrder[],
+ from: string,
+ ): Promise {
+ const params = formatters.createBatchCancel(orders);
+ const txHash = await this._exchange.batchCancelOrdersNoThrow.sendTransactionAsync(params.orders, { from });
+ const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
+ return tx;
+ }
public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise {
const txHash = await this._exchange.cancelOrdersUpTo.sendTransactionAsync(salt, { from });
const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash);
diff --git a/contracts/exchange/test/utils/fill_order_combinatorial_utils.ts b/contracts/exchange/test/utils/fill_order_combinatorial_utils.ts
index a3cb1e25f7..3e331c7b31 100644
--- a/contracts/exchange/test/utils/fill_order_combinatorial_utils.ts
+++ b/contracts/exchange/test/utils/fill_order_combinatorial_utils.ts
@@ -431,8 +431,6 @@ export class FillOrderCombinatorialUtils {
lazyStore,
fillRevertReasonIfExists,
);
-
- await this._abiEncodeFillOrderAndAssertOutcomeAsync(signedOrder, takerAssetFillAmount);
}
private async _fillOrderAndAssertOutcomeAsync(
signedOrder: SignedOrder,
@@ -609,19 +607,6 @@ export class FillOrderCombinatorialUtils {
'ZRXAssetBalanceOfFeeRecipient',
);
}
- private async _abiEncodeFillOrderAndAssertOutcomeAsync(
- signedOrder: SignedOrder,
- takerAssetFillAmount: BigNumber,
- ): Promise {
- const params = orderUtils.createFill(signedOrder, takerAssetFillAmount);
- const abiDataEncodedByContract = await this.testLibsContract.abiEncodeFillOrder.callAsync(
- params.order,
- params.takerAssetFillAmount,
- params.signature,
- );
- const paramsDecodedByClient = this.exchangeWrapper.abiDecodeFillOrder(abiDataEncodedByContract);
- expect(paramsDecodedByClient).to.be.deep.equal(params, 'ABIEncodedFillOrderData');
- }
private async _getTakerAssetFillAmountAsync(
signedOrder: SignedOrder,
takerAssetFillAmountScenario: TakerAssetFillAmountScenario,
diff --git a/contracts/exchange/test/wrapper.ts b/contracts/exchange/test/wrapper.ts
index 06878fe6aa..575f48215d 100644
--- a/contracts/exchange/test/wrapper.ts
+++ b/contracts/exchange/test/wrapper.ts
@@ -18,11 +18,13 @@ import { OrderStatus, RevertReason, SignedOrder } from '@0x/types';
import { BigNumber, providerUtils } from '@0x/utils';
import { Web3Wrapper } from '@0x/web3-wrapper';
import * as chai from 'chai';
+import { LogWithDecodedArgs } from 'ethereum-types';
import * as _ from 'lodash';
import {
artifacts,
constants as exchangeConstants,
+ ExchangeCancelEventArgs,
ExchangeContract,
ExchangeWrapper,
ReentrantERC20TokenContract,
@@ -32,6 +34,7 @@ chaiSetup.configure();
const expect = chai.expect;
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
+// tslint:disable:no-unnecessary-type-assertion
describe('Exchange wrappers', () => {
let chainId: number;
let makerAddress: string;
@@ -441,6 +444,88 @@ describe('Exchange wrappers', () => {
});
});
+ describe('cancelOrderNoThrow', () => {
+ it('should return false if not sent by maker', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync();
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ const didCancel = await exchange.cancelOrderNoThrow.callAsync(signedOrder, { from: takerAddress });
+ const isCancelled = await exchange.cancelled.callAsync(orderHash);
+ expect(didCancel).to.equal(false);
+ expect(isCancelled).to.equal(false);
+ });
+
+ it('should return false if makerAssetAmount is 0', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync({
+ makerAssetAmount: new BigNumber(0),
+ });
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ const didCancel = await exchange.cancelOrderNoThrow.callAsync(signedOrder, { from: makerAddress });
+ const isCancelled = await exchange.cancelled.callAsync(orderHash);
+ expect(didCancel).to.equal(false);
+ expect(isCancelled).to.equal(false);
+ });
+
+ it('should return false if takerAssetAmount is 0', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync({
+ takerAssetAmount: new BigNumber(0),
+ });
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ const didCancel = await exchange.cancelOrderNoThrow.callAsync(signedOrder, { from: makerAddress });
+ const isCancelled = await exchange.cancelled.callAsync(orderHash);
+ expect(didCancel).to.equal(false);
+ expect(isCancelled).to.equal(false);
+ });
+
+ it('should be able to cancel an order', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync();
+ await exchangeWrapper.cancelOrderNoThrowAsync(signedOrder, makerAddress);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHash, OrderStatus.Cancelled);
+ const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress, {
+ takerAssetFillAmount: signedOrder.takerAssetAmount.div(2),
+ });
+ return expect(tx).to.revertWith(expectedError);
+ });
+
+ it('should log 1 event with correct arguments if successful', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync();
+ const res = await exchangeWrapper.cancelOrderNoThrowAsync(signedOrder, makerAddress);
+ expect(res.logs).to.have.length(1);
+
+ const log = res.logs[0] as LogWithDecodedArgs;
+ const logArgs = log.args;
+
+ expect(signedOrder.makerAddress).to.be.equal(logArgs.makerAddress);
+ expect(signedOrder.makerAddress).to.be.equal(logArgs.senderAddress);
+ 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(orderHashUtils.getOrderHashHex(signedOrder)).to.be.equal(logArgs.orderHash);
+ });
+
+ it('should return false if already cancelled', async () => {
+ const signedOrder = await orderFactory.newSignedOrderAsync();
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ await exchangeWrapper.cancelOrderAsync(signedOrder, makerAddress);
+ const isCancelled = await exchange.cancelled.callAsync(orderHash);
+ expect(isCancelled).to.equal(true);
+ const didCancel = await exchange.cancelOrderNoThrow.callAsync(signedOrder, { from: makerAddress });
+ expect(didCancel).to.equal(false);
+ });
+
+ it('should return false if order is expired', async () => {
+ const currentTimestamp = await getLatestBlockTimestampAsync();
+ const signedOrder = await orderFactory.newSignedOrderAsync({
+ expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10),
+ });
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrder);
+ const didCancel = await exchange.cancelOrderNoThrow.callAsync(signedOrder, { from: makerAddress });
+ const isCancelled = await exchange.cancelled.callAsync(orderHash);
+ expect(didCancel).to.equal(false);
+ expect(isCancelled).to.equal(false);
+ });
+ });
+
describe('batch functions', () => {
let signedOrders: SignedOrder[];
beforeEach(async () => {
@@ -1279,6 +1364,34 @@ describe('Exchange wrappers', () => {
const newBalances = await erc20Wrapper.getBalancesAsync();
expect(erc20Balances).to.be.deep.equal(newBalances);
});
+ it('should revert if a single cancel fails', async () => {
+ await exchangeWrapper.cancelOrderAsync(signedOrders[1], makerAddress);
+ const orderHash = orderHashUtils.getOrderHashHex(signedOrders[1]);
+ const expectedError = new ExchangeRevertErrors.OrderStatusError(orderHash, OrderStatus.Cancelled);
+ const tx = exchangeWrapper.batchCancelOrdersAsync(signedOrders, makerAddress);
+ return expect(tx).to.revertWith(expectedError);
+ });
+ });
+
+ describe('batchCancelOrdersNoThrow', () => {
+ it('should be able to cancel multiple signedOrders', async () => {
+ const takerAssetCancelAmounts = _.map(signedOrders, signedOrder => signedOrder.takerAssetAmount);
+ await exchangeWrapper.batchCancelOrdersNoThrowAsync(signedOrders, makerAddress);
+
+ await exchangeWrapper.batchFillOrdersNoThrowAsync(signedOrders, takerAddress, {
+ takerAssetFillAmounts: takerAssetCancelAmounts,
+ });
+ const newBalances = await erc20Wrapper.getBalancesAsync();
+ expect(erc20Balances).to.be.deep.equal(newBalances);
+ });
+ it('should return false for cancelled orders', async () => {
+ await exchangeWrapper.cancelOrderAsync(signedOrders[1], makerAddress);
+ const didCancelArray = await exchange.batchCancelOrdersNoThrow.callAsync(signedOrders, {
+ from: makerAddress,
+ });
+ expect(didCancelArray[0]).to.equal(true);
+ expect(didCancelArray[1]).to.equal(false);
+ });
});
describe('getOrdersInfo', () => {