From f104d91595397a4e7ce82a36de92b09e76d9d507 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Mon, 26 Nov 2018 17:29:32 -0800 Subject: Broken commit -- saving current state of getting wyre compliance token contract setup --- contracts/test-utils/src/types.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'contracts') diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index d738fcd4e..04f95e1a8 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -99,6 +99,7 @@ export enum ContractName { ERC721Proxy = 'ERC721Proxy', DummyERC721Receiver = 'DummyERC721Receiver', DummyERC721Token = 'DummyERC721Token', + DummyYesComplianceToken = 'DummyYesComplianceToken', TestLibBytes = 'TestLibBytes', TestWallet = 'TestWallet', Authorizable = 'Authorizable', -- cgit v1.2.3 From 88595718c3f78d3facbd4ac67ba8328ce9b2bc8a Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Wed, 28 Nov 2018 15:37:11 -0800 Subject: Yes Compliance Token --- contracts/test-utils/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index 04f95e1a8..f0830deb4 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -84,6 +84,7 @@ export enum ContractName { MultiSigWalletWithTimeLock = 'MultiSigWalletWithTimeLock', Exchange = 'Exchange', ZRXToken = 'ZRXToken', + YesComplianceToken = 'YesComplianceToken', DummyERC20Token = 'DummyERC20Token', EtherToken = 'WETH9', DutchAuction = 'DutchAuction', @@ -99,7 +100,6 @@ export enum ContractName { ERC721Proxy = 'ERC721Proxy', DummyERC721Receiver = 'DummyERC721Receiver', DummyERC721Token = 'DummyERC721Token', - DummyYesComplianceToken = 'DummyYesComplianceToken', TestLibBytes = 'TestLibBytes', TestWallet = 'TestWallet', Authorizable = 'Authorizable', -- cgit v1.2.3 From 0e0e05e0e07aca3cfbfd13b3fdc00183b7ac5a87 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Wed, 28 Nov 2018 16:05:18 -0800 Subject: Compile Compliant Forwarder contract --- contracts/test-utils/src/types.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'contracts') diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index f0830deb4..46b7ad941 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -105,6 +105,7 @@ export enum ContractName { Authorizable = 'Authorizable', Whitelist = 'Whitelist', Forwarder = 'Forwarder', + CompliantForwarder = 'CompliantForwarder', } export interface SignedTransaction { -- cgit v1.2.3 From b4aca370defb9dfe6c01b60d1b522d4a7b731f43 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Wed, 28 Nov 2018 18:09:50 -0800 Subject: Writing tests for Compliant Forwarder --- contracts/extensions/test/extensions/forwarder.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/forwarder.ts b/contracts/extensions/test/extensions/forwarder.ts index 4027f493d..a4afa283e 100644 --- a/contracts/extensions/test/extensions/forwarder.ts +++ b/contracts/extensions/test/extensions/forwarder.ts @@ -61,6 +61,7 @@ describe(ContractName.Forwarder, () => { let wethContract: WETH9Contract; let forwarderWrapper: ForwarderWrapper; let exchangeWrapper: ExchangeWrapper; + let takerPrivateKey: Buffer; let orderWithoutFee: SignedOrder; let orderWithFee: SignedOrder; -- cgit v1.2.3 From 9f68ac7bbecea109692d62d5555bac67e86c123a Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Fri, 30 Nov 2018 16:43:04 -0800 Subject: Making progress on generalized forwarder --- contracts/protocol/test/utils/exchange_wrapper.ts | 1 + 1 file changed, 1 insertion(+) (limited to 'contracts') diff --git a/contracts/protocol/test/utils/exchange_wrapper.ts b/contracts/protocol/test/utils/exchange_wrapper.ts index cb6dce901..6106e78ca 100644 --- a/contracts/protocol/test/utils/exchange_wrapper.ts +++ b/contracts/protocol/test/utils/exchange_wrapper.ts @@ -214,6 +214,7 @@ export class ExchangeWrapper { { from }, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); + console.log(JSON.stringify(tx)); return tx; } public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { -- cgit v1.2.3 From 1cdd82178ff630827095e778a222fafa4161969e Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 4 Dec 2018 15:23:25 -0800 Subject: ComplianceForwarder renamed to BalanceThresholdFilter --- contracts/test-utils/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index 46b7ad941..cbdd513eb 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -105,7 +105,7 @@ export enum ContractName { Authorizable = 'Authorizable', Whitelist = 'Whitelist', Forwarder = 'Forwarder', - CompliantForwarder = 'CompliantForwarder', + BalanceThresholdFilter = 'BalanceThresholdFilter', } export interface SignedTransaction { -- cgit v1.2.3 From 8d6219296a4ac0c2ec46ae077eb87cebb19f8b55 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Mon, 10 Dec 2018 17:08:16 -0800 Subject: Removed Yes Token - its no longer needed to test Balance Threshold Filter --- contracts/test-utils/src/types.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'contracts') diff --git a/contracts/test-utils/src/types.ts b/contracts/test-utils/src/types.ts index cbdd513eb..1630eab0d 100644 --- a/contracts/test-utils/src/types.ts +++ b/contracts/test-utils/src/types.ts @@ -84,7 +84,6 @@ export enum ContractName { MultiSigWalletWithTimeLock = 'MultiSigWalletWithTimeLock', Exchange = 'Exchange', ZRXToken = 'ZRXToken', - YesComplianceToken = 'YesComplianceToken', DummyERC20Token = 'DummyERC20Token', EtherToken = 'WETH9', DutchAuction = 'DutchAuction', -- cgit v1.2.3 From 4417c76b130655da3b14209148191d43037e8f8a Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 14:47:52 -0800 Subject: Ported Balance Threshold Filter to new contract directory structure --- contracts/extensions/compiler.json | 2 +- .../BalanceThresholdFilter.sol | 44 + .../MixinBalanceThresholdFilterCore.sol | 319 ++++ .../interfaces/IThresholdAsset.sol | 30 + .../mixins/MBalanceThresholdFilterCore.sol | 81 ++ contracts/extensions/package.json | 2 +- contracts/extensions/src/artifacts/index.ts | 2 + contracts/extensions/src/wrappers/index.ts | 1 + .../test/extensions/balance_threshold_filter.ts | 1530 ++++++++++++++++++++ .../test/utils/balance_threshold_wrapper.ts | 246 ++++ contracts/extensions/tsconfig.json | 2 +- 11 files changed, 2256 insertions(+), 3 deletions(-) create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol create mode 100644 contracts/extensions/test/extensions/balance_threshold_filter.ts create mode 100644 contracts/extensions/test/utils/balance_threshold_wrapper.ts (limited to 'contracts') diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index 69d607b3e..e6ed0c215 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -18,5 +18,5 @@ } } }, - "contracts": ["DutchAuction", "Forwarder"] + "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"] } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol new file mode 100644 index 000000000..a68f6805d --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol @@ -0,0 +1,44 @@ +/* + + 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 "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; +import "./interfaces/IThresholdAsset.sol"; +import "./MixinBalanceThresholdFilterCore.sol"; + + +contract BalanceThresholdFilter is MixinBalanceThresholdFilterCore { + + /// @dev Constructs BalanceThresholdFilter. + /// @param exchange Address of 0x exchange. + /// @param thresholdAsset The asset that must be held by makers/takers. + /// @param thresholdBalance The minimum balance of `thresholdAsset` that must be held by makers/takers. + constructor( + address exchange, + address thresholdAsset, + uint256 thresholdBalance + ) + public + { + EXCHANGE = IExchange(exchange); + THRESHOLD_ASSET = IThresholdAsset(thresholdAsset); + THRESHOLD_BALANCE = thresholdBalance; + } +} diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol new file mode 100644 index 000000000..0ad8ccddf --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -0,0 +1,319 @@ +/* + + 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 "./mixins/MBalanceThresholdFilterCore.sol"; + + +contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { + + /// @dev Executes an Exchange transaction iff the maker and taker meet + /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR + /// the exchange function is a cancellation. + /// Supported Exchange functions: + /// - batchFillOrders + /// - batchFillOrdersNoThrow + /// - batchFillOrKillOrders + /// - fillOrder + /// - fillOrderNoThrow + /// - fillOrKillOrder + /// - marketBuyOrders + /// - marketBuyOrdersNoThrow + /// - marketSellOrders + /// - marketSellOrdersNoThrow + /// - matchOrders + /// - cancelOrder + /// - batchCancelOrders + /// - cancelOrdersUpTo + /// Trying to call any other exchange function will throw. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signerAddress Address of transaction signer. + /// @param signedExchangeTransaction AbiV2 encoded calldata. + /// @param signature Proof of signer transaction by signer. + function executeTransaction( + uint256 salt, + address signerAddress, + bytes signedExchangeTransaction, + bytes signature + ) + external + { + // Validate addresses. + validateBalanceThresholdsOrRevert(); + + // All addresses are valid. Execute fillOrder. + EXCHANGE.executeTransaction( + salt, + signerAddress, + signedExchangeTransaction, + signature + ); + } + + /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` + /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold + /// then this function will revert. Which addresses are validated depends on + /// which Exchange function is to be called (defined by `signedExchangeTransaction` above). + /// No parameters are taken as this function reads arguments directly from calldata, to save gas. + /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all + /// of the addresses whose balance thresholds it checked. + function validateBalanceThresholdsOrRevert() + internal + { + // Addresses that are validated below. + address[] memory validatedAddresses; + + ///// Do not add variables after this point. ///// + ///// The assembly block may overwrite their values. ///// + + // Validate addresses + assembly { + /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, + /// which is accessed through `signedExchangeTransaction`. + /// @param offset - Offset into the Exchange calldata. + /// @return value - Corresponding 32 byte value stored at `offset`. + function exchangeCalldataload(offset) -> value { + // Pointer to exchange transaction + // 0x04 for calldata selector + // 0x40 to access `signedExchangeTransaction`, which is the third parameter + let exchangeTxPtr := calldataload(0x44) + + // Offset into Exchange calldata + // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. + // 0x04 for calldata selector + // 0x20 for length field of `signedExchangeTransaction` + let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) + value := calldataload(exchangeCalldataOffset) + } + + /// @dev Convenience function that skips the 4 byte selector when loading + /// from the embedded Exchange calldata. + /// @param offset - Offset into the Exchange calldata (minus the 4 byte selector) + /// @return value - Corresponding 32 byte value stored at `offset` + 4. + function loadExchangeData(offset) -> value { + value := exchangeCalldataload(add(offset, 0x4)) + } + + /// @dev A running list is maintained of addresses to validate. + /// This function records an address in this array. + /// @param addressToValidate - Address to record for validation. + function recordAddressToValidate(addressToValidate) { + // Compute `addressesToValidate` memory offset + let addressesToValidate_ := mload(0x40) + let nAddressesToValidate_ := mload(addressesToValidate_) + + // Increment length + nAddressesToValidate_ := add(mload(addressesToValidate_), 0x01) + mstore(addressesToValidate_, nAddressesToValidate_) + + // Append address to validate + let offset := mul(nAddressesToValidate_, 0x20) + mstore(add(addressesToValidate_, offset), addressToValidate) + } + + /// @dev Extracts the maker address from an order stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records it in + /// the running list of addresses to validate. + /// @param orderParamIndex - Index of the order in the Exchange function's signature + function recordMakerAddressFromOrder(orderParamIndex) { + let orderPtr := loadExchangeData(mul(orderParamIndex, 0x20)) + let makerAddress := loadExchangeData(orderPtr) + recordAddressToValidate(makerAddress) + } + + /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records them in + /// the running list of addresses to validate. + /// @param orderArrayParamIndex - Index of the order array in the Exchange function's signature + function recordMakerAddressesFromOrderArray(orderArrayParamIndex) { + let orderArrayPtr := loadExchangeData(mul(orderArrayParamIndex, 0x20)) + let orderArrayLength := loadExchangeData(orderArrayPtr) + let orderArrayElementPtr := add(orderArrayPtr, 0x20) + let orderArrayElementEndPtr := add(orderArrayElementPtr, mul(orderArrayLength, 0x20)) + for {let orderPtrOffset := orderArrayElementPtr} lt(orderPtrOffset, orderArrayElementEndPtr) {orderPtrOffset := add(orderPtrOffset, 0x20)} { + let orderPtr := loadExchangeData(orderPtrOffset) + let makerAddress := loadExchangeData(add(orderPtr, orderArrayElementPtr)) + recordAddressToValidate(makerAddress) + } + } + + /// @dev Records address of signer in the running list of addresses to validate. + /// @note: We cannot access `signerAddress` directly from within the asm function, + /// so it is loaded from the calldata. + function recordSignerAddress() { + // Load the signer address from calldata + // 0x04 for selector + // 0x20 to access `signerAddress`, which is the second parameter. + let signerAddress_ := calldataload(0x24) + recordAddressToValidate(signerAddress_) + } + + /// @dev Records addresses to be validated when Exchange transaction is a batch fill variant. + /// This is one of: batchFillOrders, batchFillOrKillOrders, batchFillNoThrow + /// Reference signature<T>: <batchFillVariant>(Order[],uint256[],bytes[]) + function recordAddressesForBatchFillVariant() { + // Record maker addresses from order array (parameter index 0) + // The signer is the taker for these orders and must also be validated. + recordMakerAddressesFromOrderArray(0) + recordSignerAddress() + } + + /// @dev Records addresses to be validated when Exchange transaction is a fill order variant. + /// This is one of: fillOrder, fillOrKillOrder, fillOrderNoThrow + /// Reference signature<T>: <fillOrderVariant>(Order,uint256,bytes) + function recordAddressesForFillOrderVariant() { + // Record maker address from the order (param index 0) + // The signer is the taker for this order and must also be validated. + recordMakerAddressFromOrder(0) + recordSignerAddress() + } + + /// @dev Records addresses to be validated when Exchange transaction is a market fill variant. + /// This is one of: marketBuyOrders, marketBuyOrdersNoThrow, marketSellOrders, marketSellOrdersNoThrow + /// Reference signature<T>: <marketFillInvariant>(Order[],uint256,bytes[]) + function recordAddressesForMarketFillVariant() { + // Record maker addresses from order array (parameter index 0) + // The signer is the taker for these orders and must also be validated. + recordMakerAddressesFromOrderArray(0) + recordSignerAddress() + } + + /// @dev Records addresses to be validated when Exchange transaction is matchOrders. + /// Reference signature: matchOrders(Order,Order) + function recordAddressesForMatchOrders() { + // Record maker address from both orders (param indices 0 & 1). + // The signer is the taker and must also be validated. + recordMakerAddressFromOrder(0) + recordMakerAddressFromOrder(1) + recordSignerAddress() + } + + ///// Record Addresses to Validate ///// + + // Addresses needing validation depends on which Exchange function is being called. + // Step 1/2 Read the exchange function selector. + let exchangeFunctionSelector := and( + exchangeCalldataload(0x0), + 0xffffffff00000000000000000000000000000000000000000000000000000000 + ) + + // Step 2/2 Extract addresses to validate based on this selector. + // See ../../utils/ExchangeSelectors/ExchangeSelectors.sol for selectors + switch exchangeFunctionSelector + case 0x297bb70b00000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrders + case 0x50dde19000000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrdersNoThrow + case 0x4d0ae54600000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrKillOrders + case 0xb4be83d500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrder + case 0x3e228bae00000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrderNoThrow + case 0x64a3bc1500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrKillOrder + case 0xe5fa431b00000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrders + case 0xa3e2038000000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrdersNoThrow + case 0x7e1d980800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrders + case 0xdd1c7d1800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrdersNoThrow + case 0x3c28d86100000000000000000000000000000000000000000000000000000000 { recordAddressesForMatchOrders() } // matchOrders + case 0xd46b02c300000000000000000000000000000000000000000000000000000000 {} // cancelOrder + case 0x4ac1478200000000000000000000000000000000000000000000000000000000 {} // batchCancelOrders + case 0x4f9559b100000000000000000000000000000000000000000000000000000000 {} // cancelOrdersUpTo + default { + // Revert with `Error("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR")` + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(0x40, 0x00000024494e56414c49445f4f525f424c4f434b45445f45584348414e47455f) + mstore(0x60, 0x53454c4543544f52000000000000000000000000000000000000000000000000) + mstore(0x80, 0x00000000) + // Revert length calculation: + // 4 -- error selector + // 32 -- offset to string + // 32 -- string length field + // 64 -- strlen(INVALID_OR_BLOCKED_EXCHANGE_SELECTOR) rounded up to nearest 32-byte word. + revert(0, 132) + } + + ///// Validate Recorded Addresses ///// + + // Load from memory the addresses to validate + let addressesToValidate := mload(0x40) + let addressesToValidateLength := mload(addressesToValidate) + let addressesToValidateElementPtr := add(addressesToValidate, 0x20) + let addressesToValidateElementEndPtr := add(addressesToValidateElementPtr, mul(addressesToValidateLength, 0x20)) + + // Set free memory pointer to after `addressesToValidate` array. + // This is to avoid corruption when making calls in the loop below. + let freeMemPtr := addressesToValidateElementEndPtr + mstore(0x40, freeMemPtr) + + // Validate addresses + let thresholdAssetAddress := sload(THRESHOLD_ASSET_slot) + let thresholdBalance := sload(THRESHOLD_BALANCE_slot) + for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} { + // Construct calldata for `THRESHOLD_ASSET.balanceOf` + mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000) + mstore(add(freeMemPtr, 0x04), mload(addressToValidate)) + + // call `THRESHOLD_ASSET.balanceOf` + let success := call( + gas, // forward all gas + thresholdAssetAddress, // call address of asset proxy + 0, // don't send any ETH + freeMemPtr, // pointer to start of input + 0x24, // length of input (one padded address) + freeMemPtr, // write output to next free memory offset + 0x20 // reserve space for return balance (0x20 bytes) + ) + if eq(success, 0) { + // @TODO Revert with `Error("BALANCE_QUERY_FAILED")` + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(0x40, 0x0000001442414c414e43455f51554552595f4641494c45440000000000000000) + mstore(0x60, 0x00000000) + // Revert length calculation: + // 4 -- error selector + // 32 -- offset to string + // 32 -- string length field + // 32 -- strlen(BALANCE_QUERY_FAILED) rounded up to nearest 32-byte word. + revert(0, 100) + } + + // Revert if balance not held + let addressBalance := mload(freeMemPtr) + if lt(addressBalance, thresholdBalance) { + // Revert with `Error("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD")` + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(0x40, 0x0000003441545f4c454153545f4f4e455f414444524553535f444f45535f4e4f) + mstore(0x60, 0x545f4d4545545f42414c414e43455f5448524553484f4c440000000000000000) + mstore(0x80, 0x00000000) + // Revert length calculation: + // 4 -- error selector + // 32 -- offset to string + // 32 -- string length field + // 64 -- strlen(AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD) rounded up to nearest 32-byte word. + revert(0, 132) + } + } + + // Record validated addresses + validatedAddresses := addressesToValidate + } + + ///// If we hit this point then all addresses are valid ///// + emit ValidatedAddresses(validatedAddresses); + } +} \ No newline at end of file diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol new file mode 100644 index 000000000..61acaba0a --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol @@ -0,0 +1,30 @@ +/* + + 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 IThresholdAsset { + + /// @param _owner The address from which the balance will be retrieved + /// @return Balance of owner + function balanceOf(address _owner) + external + view + returns (uint256); + +} \ No newline at end of file diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol new file mode 100644 index 000000000..ff0042f58 --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.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 "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; +import "../interfaces/IThresholdAsset.sol"; + + +contract MBalanceThresholdFilterCore { + + // Points to 0x exchange contract + IExchange internal EXCHANGE; + + // The asset that must be held by makers/takers + IThresholdAsset internal THRESHOLD_ASSET; + + // The minimum balance of `THRESHOLD_ASSET` that must be held by makers/takers + uint256 internal THRESHOLD_BALANCE; + + // Addresses that hold at least `THRESHOLD_BALANCE` of `THRESHOLD_ASSET` + event ValidatedAddresses ( + address[] addresses + ); + + /// @dev Executes an Exchange transaction iff the maker and taker meet + /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR + /// the exchange function is a cancellation. + /// Supported Exchange functions: + /// - batchFillOrders + /// - batchFillOrdersNoThrow + /// - batchFillOrKillOrders + /// - fillOrder + /// - fillOrderNoThrow + /// - fillOrKillOrder + /// - marketBuyOrders + /// - marketBuyOrdersNoThrow + /// - marketSellOrders + /// - marketSellOrdersNoThrow + /// - matchOrders + /// - cancelOrder + /// - batchCancelOrders + /// - cancelOrdersUpTo + /// Trying to call any other exchange function will throw. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signerAddress Address of transaction signer. + /// @param signedExchangeTransaction AbiV2 encoded calldata. + /// @param signature Proof of signer transaction by signer. + function executeTransaction( + uint256 salt, + address signerAddress, + bytes signedExchangeTransaction, + bytes signature + ) + external; + + /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` + /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold + /// then this function will revert. Which addresses are validated depends on + /// which Exchange function is to be called (defined by `signedExchangeTransaction` above). + /// No parameters are taken as this function reads arguments directly from calldata, to save gas. + /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all + /// of the addresses whose balance thresholds it checked. + function validateBalanceThresholdsOrRevert() internal; +} \ No newline at end of file diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index 938e1138c..2d9ed4dcd 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -31,7 +31,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(DutchAuction|Forwarder).json" + "abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder).json" }, "repository": { "type": "git", diff --git a/contracts/extensions/src/artifacts/index.ts b/contracts/extensions/src/artifacts/index.ts index 7588178f0..ebf0b8050 100644 --- a/contracts/extensions/src/artifacts/index.ts +++ b/contracts/extensions/src/artifacts/index.ts @@ -1,9 +1,11 @@ import { ContractArtifact } from 'ethereum-types'; +import * as BalanceThresholdFilter from '../../generated-artifacts/BalanceThresholdFilter.json'; import * as DutchAuction from '../../generated-artifacts/DutchAuction.json'; import * as Forwarder from '../../generated-artifacts/Forwarder.json'; export const artifacts = { + BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact, DutchAuction: DutchAuction as ContractArtifact, Forwarder: Forwarder as ContractArtifact, }; diff --git a/contracts/extensions/src/wrappers/index.ts b/contracts/extensions/src/wrappers/index.ts index 90880e37f..8a8122caa 100644 --- a/contracts/extensions/src/wrappers/index.ts +++ b/contracts/extensions/src/wrappers/index.ts @@ -1,2 +1,3 @@ +export * from '../../generated-wrappers/balance_threshold_filter'; export * from '../../generated-wrappers/dutch_auction'; export * from '../../generated-wrappers/forwarder'; diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts new file mode 100644 index 000000000..34339564c --- /dev/null +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -0,0 +1,1530 @@ +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { RevertReason, SignedOrder, Order } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import * as ethUtil from 'ethereumjs-util'; +import * as _ from 'lodash'; +import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; + +import { + artifacts as protocolArtifacts, + ERC20Wrapper, + ERC721Wrapper, + ExchangeContract, + ExchangeWrapper, + TestExchangeInternalsContract, +} from '@0x/contracts-protocol'; +import { + chaiSetup, + constants, + ContractName, + ERC20BalancesByOwner, + expectTransactionFailedAsync, + expectTransactionFailedWithoutReasonAsync, + getLatestBlockTimestampAsync, + LogDecoder, + OrderFactory, + OrderStatus, + orderUtils, + provider, + SignedTransaction, + txDefaults, + TransactionFactory, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { + artifacts as tokensArtifacts, + DummyERC20TokenContract, + DummyERC721TokenContract, + WETH9Contract, +} from '@0x/contracts-tokens'; + +import { artifacts } from '../../src/artifacts'; +import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter'; +import { BalanceThresholdWrapper } from '../utils/balance_threshold_wrapper'; + +chaiSetup.configure(); +const expect = chai.expect; +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +const DECIMALS_DEFAULT = 18; + +interface ValidatedAddressesLog { + args: {addresses: string[]} +} + +describe.only(ContractName.BalanceThresholdFilter, () => { + let compliantMakerAddress: string; + let compliantMakerAddress2: string; + let owner: string; + let compliantTakerAddress: string; + let feeRecipientAddress: string; + let nonCompliantAddress: string; + let defaultMakerAssetAddress: string; + let defaultTakerAssetAddress: string; + let zrxAssetData: string; + let zrxToken: DummyERC20TokenContract; + let exchangeInstance: ExchangeContract; + let exchangeWrapper: ExchangeWrapper; + + let orderFactory: OrderFactory; + let orderFactory2: OrderFactory; + let nonCompliantOrderFactory: OrderFactory; + let erc20Wrapper: ERC20Wrapper; + let erc20Balances: ERC20BalancesByOwner; + let erc20TakerBalanceThresholdWrapper: BalanceThresholdWrapper; + let erc721TakerBalanceThresholdWrapper: BalanceThresholdWrapper; + let erc721MakerBalanceThresholdWrapper: BalanceThresholdWrapper; + let erc721NonCompliantBalanceThresholdWrapper: BalanceThresholdWrapper; + + let takerTransactionFactory: TransactionFactory; + let makerTransactionFactory: TransactionFactory; + let compliantSignedOrder: SignedOrder; + let compliantSignedOrder2: SignedOrder; + let compliantSignedFillOrderTx: SignedTransaction; + + let logDecoder: LogDecoder; + let exchangeInternals: TestExchangeInternalsContract; + + let defaultOrderParams: Partial<Order>; + + const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); + const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT); + const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT); + + let erc721CompliantForwarderInstance: BalanceThresholdFilterContract; + let erc20CompliantForwarderInstance: BalanceThresholdFilterContract; + + const assertValidatedAddressesLog = async (txReceipt: TransactionReceiptWithDecodedLogs, expectedValidatedAddresses: string[]) => { + expect(txReceipt.logs.length).to.be.gte(1); + const validatedAddressesLog = (txReceipt.logs[0] as any) as ValidatedAddressesLog; + const validatedAddresses = validatedAddressesLog.args.addresses; + // @HACK-hysz: Nested addresses are not translated to lower-case but this will change once + // the new ABI Encoder/Decoder is used by the contract templates. + let validatedAddressesNormalized: string[] = []; + _.each(validatedAddresses, (address) => { + const normalizedAddress = _.toLower(address); + validatedAddressesNormalized.push(normalizedAddress); + }); + expect(validatedAddressesNormalized).to.be.deep.equal(expectedValidatedAddresses); + }; + + before(async () => { + // Create accounts + await blockchainLifecycle.startAsync(); + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + const usedAddresses = ([ + owner, + compliantMakerAddress, + compliantMakerAddress2, + compliantTakerAddress, + feeRecipientAddress, + nonCompliantAddress, + ] = accounts); + // Create wrappers + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + let compliantAddresses = _.cloneDeepWith(usedAddresses); + _.remove(compliantAddresses, (address: string) => { + return address === nonCompliantAddress; + }); + const erc721Wrapper = new ERC721Wrapper(provider, compliantAddresses, owner); + // Deploy ERC20 tokens + const numDummyErc20ToDeploy = 4; + let erc20TokenA: DummyERC20TokenContract; + let erc20TokenB: DummyERC20TokenContract; + let erc20BalanceThresholdAsset: DummyERC20TokenContract; + [erc20TokenA, erc20TokenB, zrxToken, erc20BalanceThresholdAsset] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, + ); + defaultMakerAssetAddress = erc20TokenA.address; + defaultTakerAssetAddress = erc20TokenB.address; + zrxAssetData = assetDataUtils.encodeERC20AssetData(zrxToken.address); + // Create proxies + const erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + // Deploy Exchange congtract + exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( + protocolArtifacts.Exchange, + provider, + txDefaults, + zrxAssetData, + ); + exchangeWrapper = new ExchangeWrapper(exchangeInstance, provider); + // Register proxies + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, { + from: owner, + }); + // Deploy Compliant Forwarder + const erc721alanceThreshold = new BigNumber(1); + await erc721Wrapper.deployProxyAsync(); + const [erc721BalanceThresholdAsset] = await erc721Wrapper.deployDummyTokensAsync(); + await erc721Wrapper.setBalancesAndAllowancesAsync(); + erc721CompliantForwarderInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( + artifacts.BalanceThresholdFilter, + provider, + txDefaults, + exchangeInstance.address, + erc721BalanceThresholdAsset.address, + erc721alanceThreshold + ); + const erc20BalanceThreshold = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 10); + erc20CompliantForwarderInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( + artifacts.BalanceThresholdFilter, + provider, + txDefaults, + exchangeInstance.address, + erc20BalanceThresholdAsset.address, + erc20BalanceThreshold + ); + + // Default order parameters + defaultOrderParams = { + exchangeAddress: exchangeInstance.address, + feeRecipientAddress, + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), + makerAssetAmount, + takerAssetAmount, + makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), DECIMALS_DEFAULT), + takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(150), DECIMALS_DEFAULT), + senderAddress: erc721CompliantForwarderInstance.address, + }; + const defaultOrderParams1 = { + makerAddress: compliantMakerAddress, + ... + defaultOrderParams, + } + const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress)]; + takerTransactionFactory = new TransactionFactory(makerPrivateKey, exchangeInstance.address); + orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams1); + const defaultOrderParams2 = { + makerAddress: compliantMakerAddress2, + ... + defaultOrderParams, + } + const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress2)]; + orderFactory2 = new OrderFactory(secondMakerPrivateKey, defaultOrderParams2); + + const nonCompliantPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(nonCompliantAddress)]; + const defaultNonCompliantOrderParams = { + makerAddress: nonCompliantAddress, + ... + defaultOrderParams, + }; + nonCompliantOrderFactory = new OrderFactory(nonCompliantPrivateKey, defaultNonCompliantOrderParams); + // Create Valid/Invalid orders + const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantTakerAddress)]; + takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchangeInstance.address); + compliantSignedOrder = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + }); + const compliantSignedOrderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress( + compliantSignedOrder, + ); + const compliantSignedOrderWithoutExchangeAddressData = exchangeInstance.fillOrder.getABIEncodedTransactionData( + compliantSignedOrderWithoutExchangeAddress, + takerAssetFillAmount, + compliantSignedOrder.signature, + ); + compliantSignedFillOrderTx = takerTransactionFactory.newSignedTransaction( + compliantSignedOrderWithoutExchangeAddressData, + ); + + logDecoder = new LogDecoder(web3Wrapper, {... artifacts, ... protocolArtifacts, ... tokensArtifacts}); + erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc20CompliantForwarderInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider); + erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider); + erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(makerPrivateKey, exchangeInstance.address), provider); + erc721NonCompliantBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(nonCompliantPrivateKey, exchangeInstance.address), provider); + + // Instantiate internal exchange contract + exchangeInternals = await TestExchangeInternalsContract.deployFrom0xArtifactAsync( + protocolArtifacts.TestExchangeInternals, + provider, + txDefaults, + ); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + + describe('General Sanity Checks', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both maker/taker when both maker and taker exceed the balance threshold of an ERC20 token', async () => { + const compliantSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({ + ... + defaultOrderParams, + makerAddress: compliantMakerAddress, + senderAddress: erc20TakerBalanceThresholdWrapper.getBalanceThresholdAddress(), + }); + // Execute a valid fill + const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrderERC20Sender, compliantTakerAddress, {takerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + ); + }); + it('should revert if the signed transaction is not intended for supported', async () => { + // Create signed order without the fillOrder function selector + const txDataBuf = ethUtil.toBuffer(compliantSignedFillOrderTx.data); + const selectorLengthInBytes = 4; + const txDataBufMinusSelector = txDataBuf.slice(selectorLengthInBytes); + const badSelector = '0x00000000'; + const badSelectorBuf = ethUtil.toBuffer(badSelector); + const txDataBufWithBadSelector = Buffer.concat([badSelectorBuf, txDataBufMinusSelector]); + const txDataBufWithBadSelectorHex = ethUtil.bufferToHex(txDataBufWithBadSelector); + // Call compliant forwarder + return expectTransactionFailedWithoutReasonAsync(erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( + compliantSignedFillOrderTx.salt, + compliantSignedFillOrderTx.signerAddress, + txDataBufWithBadSelectorHex, + compliantSignedFillOrderTx.signature, + )); + }); + it('should revert if senderAddress is not set to the compliant forwarding contract', async () => { + // Create signed order with incorrect senderAddress + const notBalanceThresholdFilterAddress = zrxToken.address; + const signedOrderWithBadSenderAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: notBalanceThresholdFilterAddress, + }); + const signedOrderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress( + signedOrderWithBadSenderAddress, + ); + const signedOrderWithoutExchangeAddressData = exchangeInstance.fillOrder.getABIEncodedTransactionData( + signedOrderWithoutExchangeAddress, + takerAssetFillAmount, + compliantSignedOrder.signature, + ); + const signedFillOrderTx = takerTransactionFactory.newSignedTransaction( + signedOrderWithoutExchangeAddressData, + ); + // Call compliant forwarder + return expectTransactionFailedWithoutReasonAsync(erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( + signedFillOrderTx.salt, + signedFillOrderTx.signerAddress, + signedFillOrderTx.data, + signedFillOrderTx.signature, + )); + }); + // @TODO - greater than 1 balance + }); + + describe('batchFillOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .times(2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.times(2).add(takerFeePaid)), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersAsync( + orders, + nonCompliantAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('batchFillOrdersNoThrow', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .times(2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.times(2).add(takerFeePaid)), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( + orders, + nonCompliantAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('batchFillOrKillOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .times(2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.times(2).add(takerFeePaid)), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.batchFillOrKillOrdersAsync( + orders, + nonCompliantAddress, + {takerAssetFillAmounts} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if one takerAssetFillAmount is not fully filled', async () => { + const tooBigTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.times(2); + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const takerAssetFillAmounts = [takerAssetFillAmount, tooBigTakerAssetFillAmount]; + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmounts} + ), + RevertReason.FailedExecution + ); + }); + }); + + describe('fillOrder', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { + // Execute a valid fill + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + ); + }); + it('should revert if maker does not meet the balance threshold', async () => { + // Create signed order with non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + makerAddress: nonCompliantAddress + }); + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.fillOrderAsync( + signedOrderWithBadMakerAddress, + compliantTakerAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.fillOrderAsync( + compliantSignedOrder, + nonCompliantAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('fillOrderNoThrow', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { + // Execute a valid fill + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + ); + }); + it('should revert if maker does not meet the balance threshold', async () => { + // Create signed order with non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + makerAddress: nonCompliantAddress + }); + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( + signedOrderWithBadMakerAddress, + compliantTakerAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.fillOrderNoThrowAsync( + compliantSignedOrder, + nonCompliantAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('fillOrKillOrder', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { + // Execute a valid fill + const takerAssetFillAmount_ = compliantSignedOrder.takerAssetAmount; + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount: takerAssetFillAmount_}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount = takerAssetFillAmount_ + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid = compliantSignedOrder.makerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee + .times(makerAssetFillAmount) + .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount_), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + ); + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount_), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), + ); + }); + it('should revert if maker does not meet the balance threshold', async () => { + // Create signed order with non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + makerAddress: nonCompliantAddress + }); + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( + signedOrderWithBadMakerAddress, + compliantTakerAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.fillOrKillOrderAsync( + compliantSignedOrder, + nonCompliantAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if takerAssetFillAmount is not fully filled', async () => { + const tooBigTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.times(2); + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( + compliantSignedOrder, + compliantTakerAddress, + {takerAssetFillAmount: tooBigTakerAssetFillAmount} + ), + RevertReason.FailedExecution + ); + }); + }); + + describe('marketSellOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmount: cumulativeTakerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount2 = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid2 = compliantSignedOrder2.takerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersAsync( + orders, + nonCompliantAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('marketSellOrdersNoThrow', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, compliantTakerAddress, {takerAssetFillAmount: cumulativeTakerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerAssetFillAmount2 = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid2 = compliantSignedOrder2.takerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( + orders, + compliantTakerAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( + orders, + nonCompliantAddress, + {takerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('marketBuyOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const makerAssetFillAmount2 = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, compliantTakerAddress, {makerAssetFillAmount: cumulativeMakerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid2 = compliantSignedOrder2.takerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + const dummyMakerAssetFillAmount = new BigNumber(0); + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync( + orders, + compliantTakerAddress, + {makerAssetFillAmount: dummyMakerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const dummyMakerAssetFillAmount = new BigNumber(0); + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersAsync( + orders, + nonCompliantAddress, + {makerAssetFillAmount: dummyMakerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('marketBuyOrdersNoThrowAsync', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { + // Execute a valid fill + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const makerAssetFillAmount2 = takerAssetFillAmount + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, compliantTakerAddress, {makerAssetFillAmount: cumulativeMakerAssetFillAmount}); + // Assert validated addresses + const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid2 = compliantSignedOrder2.takerFee + .times(makerAssetFillAmount2) + .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); + const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + // Maker #1 + expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + ); + expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + ); + // Maker #2 + expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + ); + expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + ); + expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + ); + // Taker + expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + ); + expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + ); + // Fee recipient + expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + ); + }); + it('should revert if one maker does not meet the balance threshold', async () => { + // Create order set with one non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + makerAddress: nonCompliantAddress + }); + const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + // Execute transaction + const dummyMakerAssetFillAmount = new BigNumber(0); + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( + orders, + compliantTakerAddress, + {makerAssetFillAmount: dummyMakerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + const orders = [compliantSignedOrder, compliantSignedOrder2]; + const dummyMakerAssetFillAmount = new BigNumber(0); + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( + orders, + nonCompliantAddress, + {makerAssetFillAmount: dummyMakerAssetFillAmount} + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('matchOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('Should transfer correct amounts when both makers and taker meet the balance threshold', async () => { + // Test values/results taken from Match Orders test: + // 'Should transfer correct amounts when right order is fully filled and values pass isRoundingErrorFloor but fail isRoundingErrorCeil' + // Create orders to match + const signedOrderLeft = await orderFactory.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(17), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0), + makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), + takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), + feeRecipientAddress: feeRecipientAddress, + }); + const signedOrderRight = await orderFactory2.newSignedOrderAsync({ + makerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), + takerAssetData: assetDataUtils.encodeERC20AssetData(defaultMakerAssetAddress), + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), + takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), + feeRecipientAddress: feeRecipientAddress, + }); + // Compute expected transfer amounts + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + amountBoughtByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + feePaidByLeftMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.4705882352941176'), 16), // 76.47% + // Right Maker + amountSoldByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(75), 0), + amountBoughtByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), + feePaidByRightMaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + // Taker + amountReceivedByTaker: Web3Wrapper.toBaseUnitAmount(new BigNumber(0), 0), + feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53% + feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% + }; + const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, compliantTakerAddress); + // Assert validated addresses + const expectedValidatedAddresseses = [signedOrderLeft.makerAddress, signedOrderRight.makerAddress, compliantTakerAddress]; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check balances + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect( + newBalances[signedOrderLeft.makerAddress][defaultMakerAssetAddress], + 'Checking left maker egress ERC20 account balance', + ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][defaultMakerAssetAddress].sub(expectedTransferAmounts.amountSoldByLeftMaker)); + expect( + newBalances[signedOrderRight.makerAddress][defaultTakerAssetAddress], + 'Checking right maker ingress ERC20 account balance', + ).to.be.bignumber.equal(erc20Balances[signedOrderRight.makerAddress][defaultTakerAssetAddress].sub(expectedTransferAmounts.amountSoldByRightMaker)); + expect( + newBalances[compliantTakerAddress][defaultMakerAssetAddress], + 'Checking taker ingress ERC20 account balance', + ).to.be.bignumber.equal(erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(expectedTransferAmounts.amountReceivedByTaker)); + expect( + newBalances[signedOrderLeft.makerAddress][defaultTakerAssetAddress], + 'Checking left maker ingress ERC20 account balance', + ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][defaultTakerAssetAddress].add(expectedTransferAmounts.amountBoughtByLeftMaker)); + expect( + newBalances[signedOrderRight.makerAddress][defaultMakerAssetAddress], + 'Checking right maker egress ERC20 account balance', + ).to.be.bignumber.equal( + erc20Balances[signedOrderRight.makerAddress][defaultMakerAssetAddress].add(expectedTransferAmounts.amountBoughtByRightMaker), + ); + // Paid fees + expect( + newBalances[signedOrderLeft.makerAddress][zrxToken.address], + 'Checking left maker egress ERC20 account fees', + ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByLeftMaker)); + expect( + newBalances[signedOrderRight.makerAddress][zrxToken.address], + 'Checking right maker egress ERC20 account fees', + ).to.be.bignumber.equal(erc20Balances[signedOrderRight.makerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByRightMaker)); + expect( + newBalances[compliantTakerAddress][zrxToken.address], + 'Checking taker egress ERC20 account fees', + ).to.be.bignumber.equal(erc20Balances[compliantTakerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByTakerLeft).sub(expectedTransferAmounts.feePaidByTakerRight)); + // Received fees + expect( + newBalances[signedOrderLeft.feeRecipientAddress][zrxToken.address], + 'Checking left fee recipient ingress ERC20 account fees', + ).to.be.bignumber.equal( + erc20Balances[feeRecipientAddress][zrxToken.address].add(expectedTransferAmounts.feePaidByLeftMaker).add(expectedTransferAmounts.feePaidByRightMaker).add(expectedTransferAmounts.feePaidByTakerLeft).add(expectedTransferAmounts.feePaidByTakerRight), + ); + }); + it('should revert if left maker does not meet the balance threshold', async () => { + // Create signed order with non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + makerAddress: nonCompliantAddress + }); + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.matchOrdersAsync( + compliantSignedOrder, + signedOrderWithBadMakerAddress, + compliantTakerAddress, + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if right maker does not meet the balance threshold', async () => { + // Create signed order with non-compliant maker address + const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ + senderAddress: erc721CompliantForwarderInstance.address, + makerAddress: nonCompliantAddress + }); + // Execute transaction + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.matchOrdersAsync( + signedOrderWithBadMakerAddress, + compliantSignedOrder, + compliantTakerAddress, + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + it('should revert if taker does not meet the balance threshold', async () => { + return expectTransactionFailedAsync( + erc721NonCompliantBalanceThresholdWrapper.matchOrdersAsync( + compliantSignedOrder, + compliantSignedOrder, + nonCompliantAddress, + ), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + ); + }); + }); + + describe('cancelOrder', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + }); + it('Should successfully cancel order if maker meets balance threshold', async () => { + // Verify order is not cancelled + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + // Cancel + const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync(compliantSignedOrder, compliantSignedOrder.makerAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + }); + it('Should successfully cancel order if maker does not meet balance threshold', async () => { + // Create order where maker does not meet balance threshold + const signedOrderWithBadMakerAddress = await nonCompliantOrderFactory.newSignedOrderAsync({}); + // Verify order is not cancelled + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(signedOrderWithBadMakerAddress) + expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + // Cancel + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrderAsync(signedOrderWithBadMakerAddress, signedOrderWithBadMakerAddress.makerAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(signedOrderWithBadMakerAddress) + expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + }); + }); + + describe('batchCancelOrders', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + }); + it('Should successfully batch cancel orders if maker meets balance threshold', async () => { + // Create orders to cancel + const compliantSignedOrders = [ + await orderFactory.newSignedOrderAsync(), + await orderFactory.newSignedOrderAsync(), + await orderFactory.newSignedOrderAsync(), + ]; + // Verify orders are not cancelled + await _.each(compliantSignedOrders, async (compliantSignedOrder) => { + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + }); + // Cancel + const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync(compliantSignedOrders, compliantSignedOrder.makerAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + await _.each(compliantSignedOrders, async (compliantSignedOrder) => { + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + }); + }); + it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { + // Create orders to cancel + const nonCompliantSignedOrders = [ + await nonCompliantOrderFactory.newSignedOrderAsync(), + await nonCompliantOrderFactory.newSignedOrderAsync(), + await nonCompliantOrderFactory.newSignedOrderAsync(), + ]; + // Verify orders are not cancelled + await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + }); + // Cancel + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.batchCancelOrdersAsync(nonCompliantSignedOrders, nonCompliantAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { + const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + }); + }); + }); + + describe('cancelOrdersUpTo', () => { + beforeEach(async () => { + erc20Balances = await erc20Wrapper.getBalancesAsync(); + }); + it('Should successfully batch cancel orders if maker meets balance threshold', async () => { + // Create orders to cancel + const compliantSignedOrders = [ + await orderFactory.newSignedOrderAsync({salt: new BigNumber(0)}), + await orderFactory.newSignedOrderAsync({salt: new BigNumber(1)}), + await orderFactory.newSignedOrderAsync({salt: new BigNumber(2)}), + ]; + // Verify orders are not cancelled + await _.each(compliantSignedOrders, async (compliantSignedOrder) => { + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + }); + // Cancel + const cancelOrdersUpToThisSalt = new BigNumber(1); + const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync(cancelOrdersUpToThisSalt, compliantSignedOrder.makerAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + await _.each(compliantSignedOrders, async (compliantSignedOrder, salt: number) => { + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + const saltAsBigNumber = new BigNumber(salt); + if (saltAsBigNumber.lessThanOrEqualTo(cancelOrdersUpToThisSalt)) { + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + } else { + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + } + }); + }); + it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { + // Create orders to cancel + const nonCompliantSignedOrders = [ + await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(0)}), + await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(1)}), + await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(2)}), + ]; + // Verify orders are not cancelled + await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + }); + // Cancel + const cancelOrdersUpToThisSalt = new BigNumber(1); + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrdersUpToAsync(cancelOrdersUpToThisSalt, nonCompliantAddress); + // Assert validated addresses + const expectedValidatedAddresseses: string[] = []; + assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + // Check that order was cancelled + await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder, salt: number) => { + const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + const saltAsBigNumber = new BigNumber(salt); + if (saltAsBigNumber.lessThanOrEqualTo(cancelOrdersUpToThisSalt)) { + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); + } else { + return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); + } + }); + }); + }); +}); +// tslint:disable:max-file-line-count +// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/test/utils/balance_threshold_wrapper.ts b/contracts/extensions/test/utils/balance_threshold_wrapper.ts new file mode 100644 index 000000000..63128686c --- /dev/null +++ b/contracts/extensions/test/utils/balance_threshold_wrapper.ts @@ -0,0 +1,246 @@ +import { SignedOrder } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { ExchangeContract } from '@0x/contracts-protocol'; +import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter'; + +import { formatters, LogDecoder, orderUtils, OrderInfo, TransactionFactory } from '@0x/contracts-test-utils'; +import { artifacts } from '../../src/artifacts'; +import {artifacts as protocolArtifacts} from '@0x/contracts-protocol'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; + +export class BalanceThresholdWrapper { + private readonly _balanceThresholdFilter: BalanceThresholdFilterContract; + private readonly _signerTransactionFactory: TransactionFactory; + private readonly _exchange: ExchangeContract; + private readonly _web3Wrapper: Web3Wrapper; + private readonly _logDecoder: LogDecoder; + constructor(balanceThresholdFilter: BalanceThresholdFilterContract, exchangeContract: ExchangeContract, signerTransactionFactory: TransactionFactory, provider: Provider) { + this._balanceThresholdFilter = balanceThresholdFilter; + this._exchange = exchangeContract; + this._signerTransactionFactory = signerTransactionFactory; + this._web3Wrapper = new Web3Wrapper(provider); + this._logDecoder = new LogDecoder(this._web3Wrapper, {... artifacts, ... tokensArtifacts, ... protocolArtifacts} ); + } + public async fillOrderAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const data = this._exchange.fillOrder.getABIEncodedTransactionData( + params.order, + params.takerAssetFillAmount, + params.signature, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async fillOrKillOrderAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const data = this._exchange.fillOrKillOrder.getABIEncodedTransactionData( + params.order, + params.takerAssetFillAmount, + params.signature, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async fillOrderNoThrowAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber; gas?: number } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const data = this._exchange.fillOrderNoThrow.getABIEncodedTransactionData( + params.order, + params.takerAssetFillAmount, + params.signature, + ); + const txReceipt = this._executeTransaction(data, from, opts.gas); + return txReceipt; + } + public async batchFillOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const data = this._exchange.batchFillOrders.getABIEncodedTransactionData( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async batchFillOrKillOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[] } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const data = this._exchange.batchFillOrKillOrders.getABIEncodedTransactionData( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async batchFillOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmounts?: BigNumber[]; gas?: number } = {}, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchFill(orders, opts.takerAssetFillAmounts); + const data = this._exchange.batchFillOrKillOrders.getABIEncodedTransactionData( + params.orders, + params.takerAssetFillAmounts, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from, opts.gas); + return txReceipt; + } + public async marketSellOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); + const data = this._exchange.marketSellOrders.getABIEncodedTransactionData( + params.orders, + params.takerAssetFillAmount, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async marketSellOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { takerAssetFillAmount: BigNumber; gas?: number }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketSellOrders(orders, opts.takerAssetFillAmount); + const data = this._exchange.marketSellOrdersNoThrow.getABIEncodedTransactionData( + params.orders, + params.takerAssetFillAmount, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from, opts.gas); + return txReceipt; + } + public async marketBuyOrdersAsync( + orders: SignedOrder[], + from: string, + opts: { makerAssetFillAmount: BigNumber }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); + const data = this._exchange.marketBuyOrders.getABIEncodedTransactionData( + params.orders, + params.makerAssetFillAmount, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async marketBuyOrdersNoThrowAsync( + orders: SignedOrder[], + from: string, + opts: { makerAssetFillAmount: BigNumber; gas?: number }, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createMarketBuyOrders(orders, opts.makerAssetFillAmount); + const data = this._exchange.marketBuyOrdersNoThrow.getABIEncodedTransactionData( + params.orders, + params.makerAssetFillAmount, + params.signatures, + ); + const txReceipt = this._executeTransaction(data, from, opts.gas); + return txReceipt; + } + public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createCancel(signedOrder); + const data = this._exchange.cancelOrder.getABIEncodedTransactionData(params.order); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async batchCancelOrdersAsync( + orders: SignedOrder[], + from: string, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = formatters.createBatchCancel(orders); + const data = this._exchange.batchCancelOrders.getABIEncodedTransactionData(params.orders); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { + const data = this._exchange.cancelOrdersUpTo.getABIEncodedTransactionData(salt); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { + const filledAmount = await this._exchange.filled.callAsync(orderHashHex); + return filledAmount; + } + public async isCancelledAsync(orderHashHex: string): Promise<boolean> { + const isCancelled = await this._exchange.cancelled.callAsync(orderHashHex); + return isCancelled; + } + public async getOrderEpochAsync(makerAddress: string, senderAddress: string): Promise<BigNumber> { + const orderEpoch = await this._exchange.orderEpoch.callAsync(makerAddress, senderAddress); + return orderEpoch; + } + public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> { + const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo; + return orderInfo; + } + public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise<OrderInfo[]> { + const ordersInfo = (await this._exchange.getOrdersInfo.callAsync(signedOrders)) as OrderInfo[]; + return ordersInfo; + } + public async matchOrdersAsync( + signedOrderLeft: SignedOrder, + signedOrderRight: SignedOrder, + from: string, + ): Promise<TransactionReceiptWithDecodedLogs> { + const params = orderUtils.createMatchOrders(signedOrderLeft, signedOrderRight); + const data = await this._exchange.matchOrders.getABIEncodedTransactionData( + params.left, + params.right, + params.leftSignature, + params.rightSignature + ); + const txReceipt = this._executeTransaction(data, from); + return txReceipt; + } + public getBalanceThresholdAddress(): string { + return this._balanceThresholdFilter.address; + } + public getExchangeAddress(): string { + return this._exchange.address; + } + // Exchange functions + //abiEncodeFillOrder + //getFillOrderResultsAsync + // + private async _executeTransaction(abiEncodedExchangeTxData: string, from: string, gas?: number): Promise<TransactionReceiptWithDecodedLogs> { + const signedExchangeTx = this._signerTransactionFactory.newSignedTransaction(abiEncodedExchangeTxData); + const txOpts = _.isUndefined(gas) ? {from} : {from, gas}; + const txHash = await this._balanceThresholdFilter.executeTransaction.sendTransactionAsync( + signedExchangeTx.salt, + signedExchangeTx.signerAddress, + signedExchangeTx.data, + signedExchangeTx.signature, + txOpts, + ); + const txReceipt = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); + return txReceipt; + } +} diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index a4ce1e002..5d3ebfe74 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -6,6 +6,6 @@ "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": ["./generated-artifacts/DutchAuction.json", "./generated-artifacts/Forwarder.json"], + "files": ["./generated-artifacts/BalanceThresholdFilter.json", "./generated-artifacts/DutchAuction.json", "./generated-artifacts/Forwarder.json"], "exclude": ["./deploy/solc/solc_bin"] } -- cgit v1.2.3 From 6a0f5f39eea5fef285a1e2e962be03c2fa8847ab Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 15:55:36 -0800 Subject: Prettier / Linter fixes for TS --- .../test/extensions/balance_threshold_filter.ts | 779 +++++++++++++-------- contracts/extensions/test/extensions/forwarder.ts | 1 - .../test/utils/balance_threshold_wrapper.ts | 95 ++- contracts/extensions/tsconfig.json | 6 +- 4 files changed, 543 insertions(+), 338 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index 34339564c..e4eda3354 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -1,12 +1,12 @@ import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; -import { RevertReason, SignedOrder, Order } from '@0x/types'; +import { Order, RevertReason, SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; +import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; -import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import { artifacts as protocolArtifacts, @@ -14,7 +14,6 @@ import { ERC721Wrapper, ExchangeContract, ExchangeWrapper, - TestExchangeInternalsContract, } from '@0x/contracts-protocol'; import { chaiSetup, @@ -23,26 +22,19 @@ import { ERC20BalancesByOwner, expectTransactionFailedAsync, expectTransactionFailedWithoutReasonAsync, - getLatestBlockTimestampAsync, - LogDecoder, OrderFactory, OrderStatus, orderUtils, provider, SignedTransaction, - txDefaults, TransactionFactory, + txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; -import { - artifacts as tokensArtifacts, - DummyERC20TokenContract, - DummyERC721TokenContract, - WETH9Contract, -} from '@0x/contracts-tokens'; +import { DummyERC20TokenContract } from '@0x/contracts-tokens'; -import { artifacts } from '../../src/artifacts'; import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter'; +import { artifacts } from '../../src/artifacts'; import { BalanceThresholdWrapper } from '../utils/balance_threshold_wrapper'; chaiSetup.configure(); @@ -51,7 +43,7 @@ const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); const DECIMALS_DEFAULT = 18; interface ValidatedAddressesLog { - args: {addresses: string[]} + args: { addresses: string[] }; } describe.only(ContractName.BalanceThresholdFilter, () => { @@ -79,14 +71,10 @@ describe.only(ContractName.BalanceThresholdFilter, () => { let erc721NonCompliantBalanceThresholdWrapper: BalanceThresholdWrapper; let takerTransactionFactory: TransactionFactory; - let makerTransactionFactory: TransactionFactory; let compliantSignedOrder: SignedOrder; let compliantSignedOrder2: SignedOrder; let compliantSignedFillOrderTx: SignedTransaction; - let logDecoder: LogDecoder; - let exchangeInternals: TestExchangeInternalsContract; - let defaultOrderParams: Partial<Order>; const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); @@ -96,14 +84,17 @@ describe.only(ContractName.BalanceThresholdFilter, () => { let erc721CompliantForwarderInstance: BalanceThresholdFilterContract; let erc20CompliantForwarderInstance: BalanceThresholdFilterContract; - const assertValidatedAddressesLog = async (txReceipt: TransactionReceiptWithDecodedLogs, expectedValidatedAddresses: string[]) => { + const assertValidatedAddressesLog = async ( + txReceipt: TransactionReceiptWithDecodedLogs, + expectedValidatedAddresses: string[], + ) => { expect(txReceipt.logs.length).to.be.gte(1); const validatedAddressesLog = (txReceipt.logs[0] as any) as ValidatedAddressesLog; const validatedAddresses = validatedAddressesLog.args.addresses; // @HACK-hysz: Nested addresses are not translated to lower-case but this will change once // the new ABI Encoder/Decoder is used by the contract templates. - let validatedAddressesNormalized: string[] = []; - _.each(validatedAddresses, (address) => { + const validatedAddressesNormalized: string[] = []; + _.each(validatedAddresses, address => { const normalizedAddress = _.toLower(address); validatedAddressesNormalized.push(normalizedAddress); }); @@ -124,7 +115,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { ] = accounts); // Create wrappers erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - let compliantAddresses = _.cloneDeepWith(usedAddresses); + const compliantAddresses = _.cloneDeepWith(usedAddresses); _.remove(compliantAddresses, (address: string) => { return address === nonCompliantAddress; }); @@ -168,7 +159,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { txDefaults, exchangeInstance.address, erc721BalanceThresholdAsset.address, - erc721alanceThreshold + erc721alanceThreshold, ); const erc20BalanceThreshold = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 10); erc20CompliantForwarderInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( @@ -177,7 +168,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { txDefaults, exchangeInstance.address, erc20BalanceThresholdAsset.address, - erc20BalanceThreshold + erc20BalanceThreshold, ); // Default order parameters @@ -194,25 +185,22 @@ describe.only(ContractName.BalanceThresholdFilter, () => { }; const defaultOrderParams1 = { makerAddress: compliantMakerAddress, - ... - defaultOrderParams, - } + ...defaultOrderParams, + }; const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress)]; takerTransactionFactory = new TransactionFactory(makerPrivateKey, exchangeInstance.address); orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams1); const defaultOrderParams2 = { makerAddress: compliantMakerAddress2, - ... - defaultOrderParams, - } + ...defaultOrderParams, + }; const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress2)]; orderFactory2 = new OrderFactory(secondMakerPrivateKey, defaultOrderParams2); const nonCompliantPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(nonCompliantAddress)]; const defaultNonCompliantOrderParams = { makerAddress: nonCompliantAddress, - ... - defaultOrderParams, + ...defaultOrderParams, }; nonCompliantOrderFactory = new OrderFactory(nonCompliantPrivateKey, defaultNonCompliantOrderParams); // Create Valid/Invalid orders @@ -233,17 +221,29 @@ describe.only(ContractName.BalanceThresholdFilter, () => { compliantSignedOrderWithoutExchangeAddressData, ); - logDecoder = new LogDecoder(web3Wrapper, {... artifacts, ... protocolArtifacts, ... tokensArtifacts}); - erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc20CompliantForwarderInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider); - erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider); - erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(makerPrivateKey, exchangeInstance.address), provider); - erc721NonCompliantBalanceThresholdWrapper = new BalanceThresholdWrapper(erc721CompliantForwarderInstance, exchangeInstance, new TransactionFactory(nonCompliantPrivateKey, exchangeInstance.address), provider); - - // Instantiate internal exchange contract - exchangeInternals = await TestExchangeInternalsContract.deployFrom0xArtifactAsync( - protocolArtifacts.TestExchangeInternals, + erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( + erc20CompliantForwarderInstance, + exchangeInstance, + new TransactionFactory(takerPrivateKey, exchangeInstance.address), + provider, + ); + erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( + erc721CompliantForwarderInstance, + exchangeInstance, + new TransactionFactory(takerPrivateKey, exchangeInstance.address), + provider, + ); + erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper( + erc721CompliantForwarderInstance, + exchangeInstance, + new TransactionFactory(makerPrivateKey, exchangeInstance.address), + provider, + ); + erc721NonCompliantBalanceThresholdWrapper = new BalanceThresholdWrapper( + erc721CompliantForwarderInstance, + exchangeInstance, + new TransactionFactory(nonCompliantPrivateKey, exchangeInstance.address), provider, - txDefaults, ); }); beforeEach(async () => { @@ -261,16 +261,22 @@ describe.only(ContractName.BalanceThresholdFilter, () => { }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker exceed the balance threshold of an ERC20 token', async () => { const compliantSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({ - ... - defaultOrderParams, + ...defaultOrderParams, makerAddress: compliantMakerAddress, senderAddress: erc20TakerBalanceThresholdWrapper.getBalanceThresholdAddress(), }); // Execute a valid fill - const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrderERC20Sender, compliantTakerAddress, {takerAssetFillAmount}); + const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync( + compliantSignedOrderERC20Sender, + compliantTakerAddress, + { takerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount @@ -314,12 +320,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const txDataBufWithBadSelector = Buffer.concat([badSelectorBuf, txDataBufMinusSelector]); const txDataBufWithBadSelectorHex = ethUtil.bufferToHex(txDataBufWithBadSelector); // Call compliant forwarder - return expectTransactionFailedWithoutReasonAsync(erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( - compliantSignedFillOrderTx.salt, - compliantSignedFillOrderTx.signerAddress, - txDataBufWithBadSelectorHex, - compliantSignedFillOrderTx.signature, - )); + return expectTransactionFailedWithoutReasonAsync( + erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( + compliantSignedFillOrderTx.salt, + compliantSignedFillOrderTx.signerAddress, + txDataBufWithBadSelectorHex, + compliantSignedFillOrderTx.signature, + ), + ); }); it('should revert if senderAddress is not set to the compliant forwarding contract', async () => { // Create signed order with incorrect senderAddress @@ -339,12 +347,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { signedOrderWithoutExchangeAddressData, ); // Call compliant forwarder - return expectTransactionFailedWithoutReasonAsync(erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( - signedFillOrderTx.salt, - signedFillOrderTx.signerAddress, - signedFillOrderTx.data, - signedFillOrderTx.signature, - )); + return expectTransactionFailedWithoutReasonAsync( + erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( + signedFillOrderTx.salt, + signedFillOrderTx.signerAddress, + signedFillOrderTx.data, + signedFillOrderTx.signature, + ), + ); }); // @TODO - greater than 1 balance }); @@ -359,10 +369,18 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync( + orders, + compliantTakerAddress, + { takerAssetFillAmounts }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); @@ -396,11 +414,11 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); @@ -416,29 +434,25 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create order set with one non-compliant maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, compliantTakerAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersAsync( - orders, - nonCompliantAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersAsync(orders, nonCompliantAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -453,10 +467,18 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( + orders, + compliantTakerAddress, + { takerAssetFillAmounts }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); @@ -490,11 +512,11 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); @@ -510,29 +532,25 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create order set with one non-compliant maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, compliantTakerAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( - orders, - nonCompliantAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, nonCompliantAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -547,10 +565,18 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmounts}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( + orders, + compliantTakerAddress, + { takerAssetFillAmounts }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); @@ -584,11 +610,11 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - + expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); @@ -604,29 +630,25 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create order set with one non-compliant maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrKillOrdersAsync( - orders, - nonCompliantAddress, - {takerAssetFillAmounts} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, nonCompliantAddress, { + takerAssetFillAmounts, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if one takerAssetFillAmount is not fully filled', async () => { @@ -634,12 +656,10 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, tooBigTakerAssetFillAmount]; return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmounts} - ), - RevertReason.FailedExecution + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, { + takerAssetFillAmounts, + }), + RevertReason.FailedExecution, ); }); }); @@ -651,10 +671,17 @@ describe.only(ContractName.BalanceThresholdFilter, () => { }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync( + compliantSignedOrder, + compliantTakerAddress, + { takerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount @@ -692,26 +719,24 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create signed order with non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrderAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, - {takerAssetFillAmount} + compliantTakerAddress, + { takerAssetFillAmount }, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.fillOrderAsync( - compliantSignedOrder, - nonCompliantAddress, - {takerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrder, nonCompliantAddress, { + takerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -723,10 +748,17 @@ describe.only(ContractName.BalanceThresholdFilter, () => { }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( + compliantSignedOrder, + compliantTakerAddress, + { takerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount @@ -764,26 +796,26 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create signed order with non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, - {takerAssetFillAmount} + compliantTakerAddress, + { takerAssetFillAmount }, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( erc721NonCompliantBalanceThresholdWrapper.fillOrderNoThrowAsync( compliantSignedOrder, - nonCompliantAddress, - {takerAssetFillAmount} + nonCompliantAddress, + { takerAssetFillAmount }, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -796,10 +828,17 @@ describe.only(ContractName.BalanceThresholdFilter, () => { it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill const takerAssetFillAmount_ = compliantSignedOrder.takerAssetAmount; - const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(compliantSignedOrder, compliantTakerAddress, {takerAssetFillAmount: takerAssetFillAmount_}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( + compliantSignedOrder, + compliantTakerAddress, + { takerAssetFillAmount: takerAssetFillAmount_ }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount_ @@ -837,37 +876,35 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Create signed order with non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, - {takerAssetFillAmount} + compliantTakerAddress, + { takerAssetFillAmount }, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( erc721NonCompliantBalanceThresholdWrapper.fillOrKillOrderAsync( compliantSignedOrder, - nonCompliantAddress, - {takerAssetFillAmount} + nonCompliantAddress, + { takerAssetFillAmount }, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if takerAssetFillAmount is not fully filled', async () => { const tooBigTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.times(2); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( - compliantSignedOrder, - compliantTakerAddress, - {takerAssetFillAmount: tooBigTakerAssetFillAmount} - ), - RevertReason.FailedExecution + erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(compliantSignedOrder, compliantTakerAddress, { + takerAssetFillAmount: tooBigTakerAssetFillAmount, + }), + RevertReason.FailedExecution, ); }); }); @@ -882,10 +919,18 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [compliantSignedOrder, compliantSignedOrder2]; const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, compliantTakerAddress, {takerAssetFillAmount: cumulativeTakerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync( + orders, + compliantTakerAddress, + { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount2 = takerAssetFillAmount @@ -901,10 +946,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( + compliantSignedOrder.makerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( + compliantSignedOrder.takerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), @@ -919,7 +968,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); @@ -931,34 +980,33 @@ describe.only(ContractName.BalanceThresholdFilter, () => { ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + erc20Balances[feeRecipientAddress][zrxToken.address] + .add(compliantSignedOrder.makerFee) + .add(makerFeePaid2) + .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { // Create order set with one non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, compliantTakerAddress, { + takerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersAsync( - orders, - nonCompliantAddress, - {takerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersAsync(orders, nonCompliantAddress, { + takerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -973,10 +1021,18 @@ describe.only(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [compliantSignedOrder, compliantSignedOrder2]; const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, compliantTakerAddress, {takerAssetFillAmount: cumulativeTakerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( + orders, + compliantTakerAddress, + { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount2 = takerAssetFillAmount @@ -992,10 +1048,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( + compliantSignedOrder.makerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( + compliantSignedOrder.takerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), @@ -1010,7 +1070,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); @@ -1022,34 +1082,33 @@ describe.only(ContractName.BalanceThresholdFilter, () => { ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + erc20Balances[feeRecipientAddress][zrxToken.address] + .add(compliantSignedOrder.makerFee) + .add(makerFeePaid2) + .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { // Create order set with one non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( - orders, - compliantTakerAddress, - {takerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, compliantTakerAddress, { + takerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( - orders, - nonCompliantAddress, - {takerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, nonCompliantAddress, { + takerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -1065,13 +1124,21 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, compliantTakerAddress, {makerAssetFillAmount: cumulativeMakerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync( + orders, + compliantTakerAddress, + { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerFeePaid2 = compliantSignedOrder2.makerFee @@ -1083,10 +1150,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( + compliantSignedOrder.makerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( + compliantSignedOrder.takerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), @@ -1101,7 +1172,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); @@ -1113,36 +1184,35 @@ describe.only(ContractName.BalanceThresholdFilter, () => { ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + erc20Balances[feeRecipientAddress][zrxToken.address] + .add(compliantSignedOrder.makerFee) + .add(makerFeePaid2) + .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { // Create order set with one non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync( - orders, - compliantTakerAddress, - {makerAssetFillAmount: dummyMakerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, compliantTakerAddress, { + makerAssetFillAmount: dummyMakerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersAsync( - orders, - nonCompliantAddress, - {makerAssetFillAmount: dummyMakerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersAsync(orders, nonCompliantAddress, { + makerAssetFillAmount: dummyMakerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -1158,13 +1228,21 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); + .times(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, compliantTakerAddress, {makerAssetFillAmount: cumulativeMakerAssetFillAmount}); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( + orders, + compliantTakerAddress, + { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, + ); // Assert validated addresses - const expectedValidatedAddresseses = [compliantSignedOrder.makerAddress, compliantSignedOrder2.makerAddress, compliantSignedFillOrderTx.signerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + compliantSignedOrder.makerAddress, + compliantSignedOrder2.makerAddress, + compliantSignedFillOrderTx.signerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerFeePaid2 = compliantSignedOrder2.makerFee @@ -1176,10 +1254,14 @@ describe.only(ContractName.BalanceThresholdFilter, () => { const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(compliantSignedOrder.makerAssetAmount), + erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( + compliantSignedOrder.makerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(compliantSignedOrder.takerAssetAmount), + erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( + compliantSignedOrder.takerAssetAmount, + ), ); expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), @@ -1194,7 +1276,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); - // Taker + // Taker expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); @@ -1206,40 +1288,39 @@ describe.only(ContractName.BalanceThresholdFilter, () => { ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(compliantSignedOrder.makerFee).add(makerFeePaid2).add(takerFeePaid), + erc20Balances[feeRecipientAddress][zrxToken.address] + .add(compliantSignedOrder.makerFee) + .add(makerFeePaid2) + .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { // Create order set with one non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( - orders, - compliantTakerAddress, - {makerAssetFillAmount: dummyMakerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, compliantTakerAddress, { + makerAssetFillAmount: dummyMakerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { const orders = [compliantSignedOrder, compliantSignedOrder2]; const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( - orders, - nonCompliantAddress, - {makerAssetFillAmount: dummyMakerAssetFillAmount} - ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, nonCompliantAddress, { + makerAssetFillAmount: dummyMakerAssetFillAmount, + }), + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); - + describe('matchOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); @@ -1255,7 +1336,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(98), 0), makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - feeRecipientAddress: feeRecipientAddress, + feeRecipientAddress, }); const signedOrderRight = await orderFactory2.newSignedOrderAsync({ makerAssetData: assetDataUtils.encodeERC20AssetData(defaultTakerAssetAddress), @@ -1264,7 +1345,7 @@ describe.only(ContractName.BalanceThresholdFilter, () => { takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(13), 0), makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 18), - feeRecipientAddress: feeRecipientAddress, + feeRecipientAddress, }); // Compute expected transfer amounts const expectedTransferAmounts = { @@ -1281,85 +1362,127 @@ describe.only(ContractName.BalanceThresholdFilter, () => { feePaidByTakerLeft: Web3Wrapper.toBaseUnitAmount(new BigNumber('76.5306122448979591'), 16), // 76.53% feePaidByTakerRight: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 16), // 100% }; - const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync(signedOrderLeft, signedOrderRight, compliantTakerAddress); + const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync( + signedOrderLeft, + signedOrderRight, + compliantTakerAddress, + ); // Assert validated addresses - const expectedValidatedAddresseses = [signedOrderLeft.makerAddress, signedOrderRight.makerAddress, compliantTakerAddress]; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + const expectedValidatedAddresseses = [ + signedOrderLeft.makerAddress, + signedOrderRight.makerAddress, + compliantTakerAddress, + ]; + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); expect( newBalances[signedOrderLeft.makerAddress][defaultMakerAssetAddress], 'Checking left maker egress ERC20 account balance', - ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][defaultMakerAssetAddress].sub(expectedTransferAmounts.amountSoldByLeftMaker)); + ).to.be.bignumber.equal( + erc20Balances[signedOrderLeft.makerAddress][defaultMakerAssetAddress].sub( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); expect( newBalances[signedOrderRight.makerAddress][defaultTakerAssetAddress], 'Checking right maker ingress ERC20 account balance', - ).to.be.bignumber.equal(erc20Balances[signedOrderRight.makerAddress][defaultTakerAssetAddress].sub(expectedTransferAmounts.amountSoldByRightMaker)); + ).to.be.bignumber.equal( + erc20Balances[signedOrderRight.makerAddress][defaultTakerAssetAddress].sub( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); expect( newBalances[compliantTakerAddress][defaultMakerAssetAddress], 'Checking taker ingress ERC20 account balance', - ).to.be.bignumber.equal(erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(expectedTransferAmounts.amountReceivedByTaker)); + ).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add( + expectedTransferAmounts.amountReceivedByTaker, + ), + ); expect( newBalances[signedOrderLeft.makerAddress][defaultTakerAssetAddress], 'Checking left maker ingress ERC20 account balance', - ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][defaultTakerAssetAddress].add(expectedTransferAmounts.amountBoughtByLeftMaker)); + ).to.be.bignumber.equal( + erc20Balances[signedOrderLeft.makerAddress][defaultTakerAssetAddress].add( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); expect( newBalances[signedOrderRight.makerAddress][defaultMakerAssetAddress], 'Checking right maker egress ERC20 account balance', ).to.be.bignumber.equal( - erc20Balances[signedOrderRight.makerAddress][defaultMakerAssetAddress].add(expectedTransferAmounts.amountBoughtByRightMaker), + erc20Balances[signedOrderRight.makerAddress][defaultMakerAssetAddress].add( + expectedTransferAmounts.amountBoughtByRightMaker, + ), ); // Paid fees expect( newBalances[signedOrderLeft.makerAddress][zrxToken.address], 'Checking left maker egress ERC20 account fees', - ).to.be.bignumber.equal(erc20Balances[signedOrderLeft.makerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByLeftMaker)); + ).to.be.bignumber.equal( + erc20Balances[signedOrderLeft.makerAddress][zrxToken.address].minus( + expectedTransferAmounts.feePaidByLeftMaker, + ), + ); expect( newBalances[signedOrderRight.makerAddress][zrxToken.address], 'Checking right maker egress ERC20 account fees', - ).to.be.bignumber.equal(erc20Balances[signedOrderRight.makerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByRightMaker)); + ).to.be.bignumber.equal( + erc20Balances[signedOrderRight.makerAddress][zrxToken.address].minus( + expectedTransferAmounts.feePaidByRightMaker, + ), + ); expect( newBalances[compliantTakerAddress][zrxToken.address], 'Checking taker egress ERC20 account fees', - ).to.be.bignumber.equal(erc20Balances[compliantTakerAddress][zrxToken.address].minus(expectedTransferAmounts.feePaidByTakerLeft).sub(expectedTransferAmounts.feePaidByTakerRight)); + ).to.be.bignumber.equal( + erc20Balances[compliantTakerAddress][zrxToken.address] + .minus(expectedTransferAmounts.feePaidByTakerLeft) + .sub(expectedTransferAmounts.feePaidByTakerRight), + ); // Received fees expect( newBalances[signedOrderLeft.feeRecipientAddress][zrxToken.address], 'Checking left fee recipient ingress ERC20 account fees', ).to.be.bignumber.equal( - erc20Balances[feeRecipientAddress][zrxToken.address].add(expectedTransferAmounts.feePaidByLeftMaker).add(expectedTransferAmounts.feePaidByRightMaker).add(expectedTransferAmounts.feePaidByTakerLeft).add(expectedTransferAmounts.feePaidByTakerRight), + erc20Balances[feeRecipientAddress][zrxToken.address] + .add(expectedTransferAmounts.feePaidByLeftMaker) + .add(expectedTransferAmounts.feePaidByRightMaker) + .add(expectedTransferAmounts.feePaidByTakerLeft) + .add(expectedTransferAmounts.feePaidByTakerRight), ); }); it('should revert if left maker does not meet the balance threshold', async () => { // Create signed order with non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.matchOrdersAsync( compliantSignedOrder, signedOrderWithBadMakerAddress, - compliantTakerAddress, + compliantTakerAddress, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if right maker does not meet the balance threshold', async () => { // Create signed order with non-compliant maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress + makerAddress: nonCompliantAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.matchOrdersAsync( signedOrderWithBadMakerAddress, compliantSignedOrder, - compliantTakerAddress, + compliantTakerAddress, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { @@ -1367,9 +1490,9 @@ describe.only(ContractName.BalanceThresholdFilter, () => { erc721NonCompliantBalanceThresholdWrapper.matchOrdersAsync( compliantSignedOrder, compliantSignedOrder, - nonCompliantAddress, + nonCompliantAddress, ), - RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold + RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); }); @@ -1382,30 +1505,44 @@ describe.only(ContractName.BalanceThresholdFilter, () => { }); it('Should successfully cancel order if maker meets balance threshold', async () => { // Verify order is not cancelled - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + compliantSignedOrder, + ); expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); // Cancel - const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync(compliantSignedOrder, compliantSignedOrder.makerAddress); + const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync( + compliantSignedOrder, + compliantSignedOrder.makerAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + compliantSignedOrder, + ); expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); }); it('Should successfully cancel order if maker does not meet balance threshold', async () => { // Create order where maker does not meet balance threshold const signedOrderWithBadMakerAddress = await nonCompliantOrderFactory.newSignedOrderAsync({}); // Verify order is not cancelled - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(signedOrderWithBadMakerAddress) + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + signedOrderWithBadMakerAddress, + ); expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); // Cancel - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrderAsync(signedOrderWithBadMakerAddress, signedOrderWithBadMakerAddress.makerAddress); + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrderAsync( + signedOrderWithBadMakerAddress, + signedOrderWithBadMakerAddress.makerAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(signedOrderWithBadMakerAddress) + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + signedOrderWithBadMakerAddress, + ); expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); }); }); @@ -1420,20 +1557,27 @@ describe.only(ContractName.BalanceThresholdFilter, () => { await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync(), - ]; + ]; // Verify orders are not cancelled - await _.each(compliantSignedOrders, async (compliantSignedOrder) => { - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + _.each(compliantSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel - const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync(compliantSignedOrders, compliantSignedOrder.makerAddress); + const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync( + compliantSignedOrders, + compliantSignedOrder.makerAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - await _.each(compliantSignedOrders, async (compliantSignedOrder) => { - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + _.each(compliantSignedOrders, async signedOrder => { + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); }); }); @@ -1443,20 +1587,27 @@ describe.only(ContractName.BalanceThresholdFilter, () => { await nonCompliantOrderFactory.newSignedOrderAsync(), await nonCompliantOrderFactory.newSignedOrderAsync(), await nonCompliantOrderFactory.newSignedOrderAsync(), - ]; + ]; // Verify orders are not cancelled - await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + _.each(nonCompliantSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.batchCancelOrdersAsync(nonCompliantSignedOrders, nonCompliantAddress); + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.batchCancelOrdersAsync( + nonCompliantSignedOrders, + nonCompliantAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { - const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + _.each(nonCompliantSignedOrders, async signedOrder => { + const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); }); }); @@ -1469,24 +1620,31 @@ describe.only(ContractName.BalanceThresholdFilter, () => { it('Should successfully batch cancel orders if maker meets balance threshold', async () => { // Create orders to cancel const compliantSignedOrders = [ - await orderFactory.newSignedOrderAsync({salt: new BigNumber(0)}), - await orderFactory.newSignedOrderAsync({salt: new BigNumber(1)}), - await orderFactory.newSignedOrderAsync({salt: new BigNumber(2)}), - ]; + await orderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), + await orderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), + await orderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), + ]; // Verify orders are not cancelled - await _.each(compliantSignedOrders, async (compliantSignedOrder) => { - const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + _.each(compliantSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel const cancelOrdersUpToThisSalt = new BigNumber(1); - const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync(cancelOrdersUpToThisSalt, compliantSignedOrder.makerAddress); + const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync( + cancelOrdersUpToThisSalt, + compliantSignedOrder.makerAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - await _.each(compliantSignedOrders, async (compliantSignedOrder, salt: number) => { - const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync(compliantSignedOrder) + _.each(compliantSignedOrders, async (signedOrder, salt: number) => { + const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); const saltAsBigNumber = new BigNumber(salt); if (saltAsBigNumber.lessThanOrEqualTo(cancelOrdersUpToThisSalt)) { return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); @@ -1498,24 +1656,31 @@ describe.only(ContractName.BalanceThresholdFilter, () => { it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { // Create orders to cancel const nonCompliantSignedOrders = [ - await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(0)}), - await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(1)}), - await nonCompliantOrderFactory.newSignedOrderAsync({salt: new BigNumber(2)}), - ]; + await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), + await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), + await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), + ]; // Verify orders are not cancelled - await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder) => { - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + _.each(nonCompliantSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel const cancelOrdersUpToThisSalt = new BigNumber(1); - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrdersUpToAsync(cancelOrdersUpToThisSalt, nonCompliantAddress); + const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrdersUpToAsync( + cancelOrdersUpToThisSalt, + nonCompliantAddress, + ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; - assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); + await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - await _.each(nonCompliantSignedOrders, async (nonCompliantSignedOrder, salt: number) => { - const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync(nonCompliantSignedOrder) + _.each(nonCompliantSignedOrders, async (signedOrder, salt: number) => { + const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + signedOrder, + ); const saltAsBigNumber = new BigNumber(salt); if (saltAsBigNumber.lessThanOrEqualTo(cancelOrdersUpToThisSalt)) { return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); diff --git a/contracts/extensions/test/extensions/forwarder.ts b/contracts/extensions/test/extensions/forwarder.ts index a4afa283e..4027f493d 100644 --- a/contracts/extensions/test/extensions/forwarder.ts +++ b/contracts/extensions/test/extensions/forwarder.ts @@ -61,7 +61,6 @@ describe(ContractName.Forwarder, () => { let wethContract: WETH9Contract; let forwarderWrapper: ForwarderWrapper; let exchangeWrapper: ExchangeWrapper; - let takerPrivateKey: Buffer; let orderWithoutFee: SignedOrder; let orderWithFee: SignedOrder; diff --git a/contracts/extensions/test/utils/balance_threshold_wrapper.ts b/contracts/extensions/test/utils/balance_threshold_wrapper.ts index 63128686c..28a4ef011 100644 --- a/contracts/extensions/test/utils/balance_threshold_wrapper.ts +++ b/contracts/extensions/test/utils/balance_threshold_wrapper.ts @@ -1,16 +1,21 @@ +import { artifacts as protocolArtifacts, ExchangeContract } from '@0x/contracts-protocol'; +import { + FillResults, + formatters, + LogDecoder, + OrderInfo, + orderUtils, + TransactionFactory, +} from '@0x/contracts-test-utils'; +import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; import { SignedOrder } from '@0x/types'; import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import { Provider, TransactionReceiptWithDecodedLogs } from 'ethereum-types'; import * as _ from 'lodash'; -import { ExchangeContract } from '@0x/contracts-protocol'; import { BalanceThresholdFilterContract } from '../../generated-wrappers/balance_threshold_filter'; - -import { formatters, LogDecoder, orderUtils, OrderInfo, TransactionFactory } from '@0x/contracts-test-utils'; import { artifacts } from '../../src/artifacts'; -import {artifacts as protocolArtifacts} from '@0x/contracts-protocol'; -import { artifacts as tokensArtifacts } from '@0x/contracts-tokens'; export class BalanceThresholdWrapper { private readonly _balanceThresholdFilter: BalanceThresholdFilterContract; @@ -18,12 +23,21 @@ export class BalanceThresholdWrapper { private readonly _exchange: ExchangeContract; private readonly _web3Wrapper: Web3Wrapper; private readonly _logDecoder: LogDecoder; - constructor(balanceThresholdFilter: BalanceThresholdFilterContract, exchangeContract: ExchangeContract, signerTransactionFactory: TransactionFactory, provider: Provider) { + constructor( + balanceThresholdFilter: BalanceThresholdFilterContract, + exchangeContract: ExchangeContract, + signerTransactionFactory: TransactionFactory, + provider: Provider, + ) { this._balanceThresholdFilter = balanceThresholdFilter; this._exchange = exchangeContract; this._signerTransactionFactory = signerTransactionFactory; this._web3Wrapper = new Web3Wrapper(provider); - this._logDecoder = new LogDecoder(this._web3Wrapper, {... artifacts, ... tokensArtifacts, ... protocolArtifacts} ); + this._logDecoder = new LogDecoder(this._web3Wrapper, { + ...artifacts, + ...tokensArtifacts, + ...protocolArtifacts, + }); } public async fillOrderAsync( signedOrder: SignedOrder, @@ -36,7 +50,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmount, params.signature, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async fillOrKillOrderAsync( @@ -50,7 +64,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmount, params.signature, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async fillOrderNoThrowAsync( @@ -64,7 +78,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmount, params.signature, ); - const txReceipt = this._executeTransaction(data, from, opts.gas); + const txReceipt = this._executeTransactionAsync(data, from, opts.gas); return txReceipt; } public async batchFillOrdersAsync( @@ -78,7 +92,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmounts, params.signatures, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async batchFillOrKillOrdersAsync( @@ -92,7 +106,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmounts, params.signatures, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async batchFillOrdersNoThrowAsync( @@ -106,7 +120,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmounts, params.signatures, ); - const txReceipt = this._executeTransaction(data, from, opts.gas); + const txReceipt = this._executeTransactionAsync(data, from, opts.gas); return txReceipt; } public async marketSellOrdersAsync( @@ -120,7 +134,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmount, params.signatures, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async marketSellOrdersNoThrowAsync( @@ -134,7 +148,7 @@ export class BalanceThresholdWrapper { params.takerAssetFillAmount, params.signatures, ); - const txReceipt = this._executeTransaction(data, from, opts.gas); + const txReceipt = this._executeTransactionAsync(data, from, opts.gas); return txReceipt; } public async marketBuyOrdersAsync( @@ -148,7 +162,7 @@ export class BalanceThresholdWrapper { params.makerAssetFillAmount, params.signatures, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async marketBuyOrdersNoThrowAsync( @@ -162,13 +176,13 @@ export class BalanceThresholdWrapper { params.makerAssetFillAmount, params.signatures, ); - const txReceipt = this._executeTransaction(data, from, opts.gas); + const txReceipt = this._executeTransactionAsync(data, from, opts.gas); return txReceipt; } public async cancelOrderAsync(signedOrder: SignedOrder, from: string): Promise<TransactionReceiptWithDecodedLogs> { const params = orderUtils.createCancel(signedOrder); const data = this._exchange.cancelOrder.getABIEncodedTransactionData(params.order); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async batchCancelOrdersAsync( @@ -177,12 +191,12 @@ export class BalanceThresholdWrapper { ): Promise<TransactionReceiptWithDecodedLogs> { const params = formatters.createBatchCancel(orders); const data = this._exchange.batchCancelOrders.getABIEncodedTransactionData(params.orders); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async cancelOrdersUpToAsync(salt: BigNumber, from: string): Promise<TransactionReceiptWithDecodedLogs> { const data = this._exchange.cancelOrdersUpTo.getABIEncodedTransactionData(salt); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { @@ -198,7 +212,7 @@ export class BalanceThresholdWrapper { return orderEpoch; } public async getOrderInfoAsync(signedOrder: SignedOrder): Promise<OrderInfo> { - const orderInfo = (await this._exchange.getOrderInfo.callAsync(signedOrder)) as OrderInfo; + const orderInfo = await this._exchange.getOrderInfo.callAsync(signedOrder); return orderInfo; } public async getOrdersInfoAsync(signedOrders: SignedOrder[]): Promise<OrderInfo[]> { @@ -215,24 +229,47 @@ export class BalanceThresholdWrapper { params.left, params.right, params.leftSignature, - params.rightSignature + params.rightSignature, ); - const txReceipt = this._executeTransaction(data, from); + const txReceipt = this._executeTransactionAsync(data, from); return txReceipt; } + public async getFillOrderResultsAsync( + signedOrder: SignedOrder, + from: string, + opts: { takerAssetFillAmount?: BigNumber } = {}, + ): Promise<FillResults> { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const fillResults = await this._exchange.fillOrder.callAsync( + params.order, + params.takerAssetFillAmount, + params.signature, + { from }, + ); + return fillResults; + } + public abiEncodeFillOrder(signedOrder: SignedOrder, opts: { takerAssetFillAmount?: BigNumber } = {}): string { + const params = orderUtils.createFill(signedOrder, opts.takerAssetFillAmount); + const data = this._exchange.fillOrder.getABIEncodedTransactionData( + params.order, + params.takerAssetFillAmount, + params.signature, + ); + return data; + } public getBalanceThresholdAddress(): string { return this._balanceThresholdFilter.address; } public getExchangeAddress(): string { return this._exchange.address; } - // Exchange functions - //abiEncodeFillOrder - //getFillOrderResultsAsync - // - private async _executeTransaction(abiEncodedExchangeTxData: string, from: string, gas?: number): Promise<TransactionReceiptWithDecodedLogs> { + private async _executeTransactionAsync( + abiEncodedExchangeTxData: string, + from: string, + gas?: number, + ): Promise<TransactionReceiptWithDecodedLogs> { const signedExchangeTx = this._signerTransactionFactory.newSignedTransaction(abiEncodedExchangeTxData); - const txOpts = _.isUndefined(gas) ? {from} : {from, gas}; + const txOpts = _.isUndefined(gas) ? { from } : { from, gas }; const txHash = await this._balanceThresholdFilter.executeTransaction.sendTransactionAsync( signedExchangeTx.salt, signedExchangeTx.signerAddress, diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index 5d3ebfe74..a303e3f5c 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -6,6 +6,10 @@ "resolveJsonModule": true }, "include": ["./src/**/*", "./test/**/*", "./generated-wrappers/**/*"], - "files": ["./generated-artifacts/BalanceThresholdFilter.json", "./generated-artifacts/DutchAuction.json", "./generated-artifacts/Forwarder.json"], + "files": [ + "./generated-artifacts/BalanceThresholdFilter.json", + "./generated-artifacts/DutchAuction.json", + "./generated-artifacts/Forwarder.json" + ], "exclude": ["./deploy/solc/solc_bin"] } -- cgit v1.2.3 From fc684ad063151f160a5bc584482aabb56df71e00 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:01:06 -0800 Subject: solhint fixes --- .../BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol | 8 ++++++-- .../BalanceThresholdFilter/interfaces/IThresholdAsset.sol | 3 ++- .../BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol | 4 +++- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 0ad8ccddf..51dbae8f3 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -53,7 +53,7 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { bytes signedExchangeTransaction, bytes signature ) - external + external { // Validate addresses. validateBalanceThresholdsOrRevert(); @@ -216,6 +216,7 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { // Step 2/2 Extract addresses to validate based on this selector. // See ../../utils/ExchangeSelectors/ExchangeSelectors.sol for selectors + // solhint-disable no-empty-blocks switch exchangeFunctionSelector case 0x297bb70b00000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrders case 0x50dde19000000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrdersNoThrow @@ -245,6 +246,7 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { // 64 -- strlen(INVALID_OR_BLOCKED_EXCHANGE_SELECTOR) rounded up to nearest 32-byte word. revert(0, 132) } + // solhint-enable no-empty-blocks ///// Validate Recorded Addresses ///// @@ -262,7 +264,9 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { // Validate addresses let thresholdAssetAddress := sload(THRESHOLD_ASSET_slot) let thresholdBalance := sload(THRESHOLD_BALANCE_slot) + // solhint-disable max-line-length for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} { + // solhint-enable max-line-length // Construct calldata for `THRESHOLD_ASSET.balanceOf` mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000) mstore(add(freeMemPtr, 0x04), mload(addressToValidate)) @@ -316,4 +320,4 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { ///// If we hit this point then all addresses are valid ///// emit ValidatedAddresses(validatedAddresses); } -} \ No newline at end of file +} diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol index 61acaba0a..3e424b9f4 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IThresholdAsset.sol @@ -18,6 +18,7 @@ pragma solidity 0.4.24; + contract IThresholdAsset { /// @param _owner The address from which the balance will be retrieved @@ -27,4 +28,4 @@ contract IThresholdAsset { view returns (uint256); -} \ No newline at end of file +} diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol index ff0042f58..af7a9453b 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol @@ -26,6 +26,7 @@ import "../interfaces/IThresholdAsset.sol"; contract MBalanceThresholdFilterCore { // Points to 0x exchange contract + // solhint-disable var-name-mixedcase IExchange internal EXCHANGE; // The asset that must be held by makers/takers @@ -33,6 +34,7 @@ contract MBalanceThresholdFilterCore { // The minimum balance of `THRESHOLD_ASSET` that must be held by makers/takers uint256 internal THRESHOLD_BALANCE; + // solhint-enable var-name-mixedcase // Addresses that hold at least `THRESHOLD_BALANCE` of `THRESHOLD_ASSET` event ValidatedAddresses ( @@ -78,4 +80,4 @@ contract MBalanceThresholdFilterCore { /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all /// of the addresses whose balance thresholds it checked. function validateBalanceThresholdsOrRevert() internal; -} \ No newline at end of file +} -- cgit v1.2.3 From dccca95c2c7a3bfcbf88db5f9182919f0e5ebf10 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:02:45 -0800 Subject: removed `only` from tests --- contracts/extensions/test/extensions/balance_threshold_filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index e4eda3354..72227090b 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -46,7 +46,7 @@ interface ValidatedAddressesLog { args: { addresses: string[] }; } -describe.only(ContractName.BalanceThresholdFilter, () => { +describe(ContractName.BalanceThresholdFilter, () => { let compliantMakerAddress: string; let compliantMakerAddress2: string; let owner: string; -- cgit v1.2.3 From d45c13bfa8a85e69bbf4476b22ad48443ae320e7 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:03:48 -0800 Subject: removed unnecessary log --- contracts/protocol/test/utils/exchange_wrapper.ts | 1 - 1 file changed, 1 deletion(-) (limited to 'contracts') diff --git a/contracts/protocol/test/utils/exchange_wrapper.ts b/contracts/protocol/test/utils/exchange_wrapper.ts index 6106e78ca..cb6dce901 100644 --- a/contracts/protocol/test/utils/exchange_wrapper.ts +++ b/contracts/protocol/test/utils/exchange_wrapper.ts @@ -214,7 +214,6 @@ export class ExchangeWrapper { { from }, ); const tx = await this._logDecoder.getTxWithDecodedLogsAsync(txHash); - console.log(JSON.stringify(tx)); return tx; } public async getTakerAssetFilledAmountAsync(orderHashHex: string): Promise<BigNumber> { -- cgit v1.2.3 From 34ff7fae9cffa7aaba5f0ba0060ed063a855f8e9 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:07:19 -0800 Subject: Removed deprecated README + comments --- contracts/extensions/test/extensions/balance_threshold_filter.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index 72227090b..63cb9de31 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -135,7 +135,7 @@ describe(ContractName.BalanceThresholdFilter, () => { // Create proxies const erc20Proxy = await erc20Wrapper.deployProxyAsync(); await erc20Wrapper.setBalancesAndAllowancesAsync(); - // Deploy Exchange congtract + // Deploy Exchange contract exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( protocolArtifacts.Exchange, provider, @@ -356,7 +356,6 @@ describe(ContractName.BalanceThresholdFilter, () => { ), ); }); - // @TODO - greater than 1 balance }); describe('batchFillOrders', () => { -- cgit v1.2.3 From a1266a3341cdc899a9090c90d6cf30b0ceb1db94 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:36:56 -0800 Subject: Cleaned up tests for balance threshold filter --- .../test/extensions/balance_threshold_filter.ts | 1097 ++++++++++---------- 1 file changed, 534 insertions(+), 563 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index 63cb9de31..ced9d6e25 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -46,13 +46,17 @@ interface ValidatedAddressesLog { args: { addresses: string[] }; } -describe(ContractName.BalanceThresholdFilter, () => { - let compliantMakerAddress: string; - let compliantMakerAddress2: string; +describe.only(ContractName.BalanceThresholdFilter, () => { + const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); + const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT); + const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT); + + let validMakerAddress: string; + let validMakerAddress2: string; let owner: string; - let compliantTakerAddress: string; + let validTakerAddress: string; let feeRecipientAddress: string; - let nonCompliantAddress: string; + let invalidAddress: string; let defaultMakerAssetAddress: string; let defaultTakerAssetAddress: string; let zrxAssetData: string; @@ -62,27 +66,21 @@ describe(ContractName.BalanceThresholdFilter, () => { let orderFactory: OrderFactory; let orderFactory2: OrderFactory; - let nonCompliantOrderFactory: OrderFactory; + let invalidOrderFactory: OrderFactory; let erc20Wrapper: ERC20Wrapper; let erc20Balances: ERC20BalancesByOwner; let erc20TakerBalanceThresholdWrapper: BalanceThresholdWrapper; let erc721TakerBalanceThresholdWrapper: BalanceThresholdWrapper; let erc721MakerBalanceThresholdWrapper: BalanceThresholdWrapper; - let erc721NonCompliantBalanceThresholdWrapper: BalanceThresholdWrapper; - - let takerTransactionFactory: TransactionFactory; - let compliantSignedOrder: SignedOrder; - let compliantSignedOrder2: SignedOrder; - let compliantSignedFillOrderTx: SignedTransaction; + let erc721NonValidBalanceThresholdWrapper: BalanceThresholdWrapper; let defaultOrderParams: Partial<Order>; + let validSignedOrder: SignedOrder; + let validSignedOrder2: SignedOrder; + let validSignedFillOrderTx: SignedTransaction; - const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); - const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT); - const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT); - - let erc721CompliantForwarderInstance: BalanceThresholdFilterContract; - let erc20CompliantForwarderInstance: BalanceThresholdFilterContract; + let erc721BalanceThresholdFilterInstance: BalanceThresholdFilterContract; + let erc20BalanceThresholdFilterInstance: BalanceThresholdFilterContract; const assertValidatedAddressesLog = async ( txReceipt: TransactionReceiptWithDecodedLogs, @@ -107,19 +105,23 @@ describe(ContractName.BalanceThresholdFilter, () => { const accounts = await web3Wrapper.getAvailableAddressesAsync(); const usedAddresses = ([ owner, - compliantMakerAddress, - compliantMakerAddress2, - compliantTakerAddress, + validMakerAddress, + validMakerAddress2, + validTakerAddress, feeRecipientAddress, - nonCompliantAddress, + invalidAddress, ] = accounts); + const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validTakerAddress)]; + const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress)]; + const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(validMakerAddress2)]; + const invalidAddressPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(invalidAddress)]; // Create wrappers erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - const compliantAddresses = _.cloneDeepWith(usedAddresses); - _.remove(compliantAddresses, (address: string) => { - return address === nonCompliantAddress; + const validAddresses = _.cloneDeepWith(usedAddresses); + _.remove(validAddresses, (address: string) => { + return address === invalidAddress; }); - const erc721Wrapper = new ERC721Wrapper(provider, compliantAddresses, owner); + const erc721Wrapper = new ERC721Wrapper(provider, validAddresses, owner); // Deploy ERC20 tokens const numDummyErc20ToDeploy = 4; let erc20TokenA: DummyERC20TokenContract; @@ -148,12 +150,13 @@ describe(ContractName.BalanceThresholdFilter, () => { await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchangeInstance.address, { from: owner, }); - // Deploy Compliant Forwarder + // Deploy Balance Threshold Filters + // One uses an ERC721 token as its balance threshold asset; the other uses an ERC20 const erc721alanceThreshold = new BigNumber(1); await erc721Wrapper.deployProxyAsync(); const [erc721BalanceThresholdAsset] = await erc721Wrapper.deployDummyTokensAsync(); await erc721Wrapper.setBalancesAndAllowancesAsync(); - erc721CompliantForwarderInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( + erc721BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( artifacts.BalanceThresholdFilter, provider, txDefaults, @@ -162,7 +165,7 @@ describe(ContractName.BalanceThresholdFilter, () => { erc721alanceThreshold, ); const erc20BalanceThreshold = Web3Wrapper.toBaseUnitAmount(new BigNumber(1), 10); - erc20CompliantForwarderInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( + erc20BalanceThresholdFilterInstance = await BalanceThresholdFilterContract.deployFrom0xArtifactAsync( artifacts.BalanceThresholdFilter, provider, txDefaults, @@ -170,7 +173,6 @@ describe(ContractName.BalanceThresholdFilter, () => { erc20BalanceThresholdAsset.address, erc20BalanceThreshold, ); - // Default order parameters defaultOrderParams = { exchangeAddress: exchangeInstance.address, @@ -181,68 +183,51 @@ describe(ContractName.BalanceThresholdFilter, () => { takerAssetAmount, makerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), DECIMALS_DEFAULT), takerFee: Web3Wrapper.toBaseUnitAmount(new BigNumber(150), DECIMALS_DEFAULT), - senderAddress: erc721CompliantForwarderInstance.address, + senderAddress: erc721BalanceThresholdFilterInstance.address, }; + // Create two order factories with valid makers (who meet the threshold balance), and + // one factory for an invalid address (that does not meet the threshold balance) + // Valid order factory #1 const defaultOrderParams1 = { - makerAddress: compliantMakerAddress, + makerAddress: validMakerAddress, ...defaultOrderParams, }; - const makerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress)]; - takerTransactionFactory = new TransactionFactory(makerPrivateKey, exchangeInstance.address); orderFactory = new OrderFactory(makerPrivateKey, defaultOrderParams1); + // Valid order factory #2 const defaultOrderParams2 = { - makerAddress: compliantMakerAddress2, + makerAddress: validMakerAddress2, ...defaultOrderParams, }; - const secondMakerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantMakerAddress2)]; orderFactory2 = new OrderFactory(secondMakerPrivateKey, defaultOrderParams2); - - const nonCompliantPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(nonCompliantAddress)]; - const defaultNonCompliantOrderParams = { - makerAddress: nonCompliantAddress, + // Invalid order factory + const defaultNonValidOrderParams = { + makerAddress: invalidAddress, ...defaultOrderParams, }; - nonCompliantOrderFactory = new OrderFactory(nonCompliantPrivateKey, defaultNonCompliantOrderParams); - // Create Valid/Invalid orders - const takerPrivateKey = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(compliantTakerAddress)]; - takerTransactionFactory = new TransactionFactory(takerPrivateKey, exchangeInstance.address); - compliantSignedOrder = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - }); - const compliantSignedOrderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress( - compliantSignedOrder, - ); - const compliantSignedOrderWithoutExchangeAddressData = exchangeInstance.fillOrder.getABIEncodedTransactionData( - compliantSignedOrderWithoutExchangeAddress, - takerAssetFillAmount, - compliantSignedOrder.signature, - ); - compliantSignedFillOrderTx = takerTransactionFactory.newSignedTransaction( - compliantSignedOrderWithoutExchangeAddressData, - ); - + invalidOrderFactory = new OrderFactory(invalidAddressPrivateKey, defaultNonValidOrderParams); + // Create Balance Thresold Wrappers erc20TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc20CompliantForwarderInstance, + erc20BalanceThresholdFilterInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider, ); erc721TakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721CompliantForwarderInstance, + erc721BalanceThresholdFilterInstance, exchangeInstance, new TransactionFactory(takerPrivateKey, exchangeInstance.address), provider, ); erc721MakerBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721CompliantForwarderInstance, + erc721BalanceThresholdFilterInstance, exchangeInstance, new TransactionFactory(makerPrivateKey, exchangeInstance.address), provider, ); - erc721NonCompliantBalanceThresholdWrapper = new BalanceThresholdWrapper( - erc721CompliantForwarderInstance, + erc721NonValidBalanceThresholdWrapper = new BalanceThresholdWrapper( + erc721BalanceThresholdFilterInstance, exchangeInstance, - new TransactionFactory(nonCompliantPrivateKey, exchangeInstance.address), + new TransactionFactory(invalidAddressPrivateKey, exchangeInstance.address), provider, ); }); @@ -256,104 +241,90 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('General Sanity Checks', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker exceed the balance threshold of an ERC20 token', async () => { - const compliantSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({ + const validSignedOrderERC20Sender = await orderFactory.newSignedOrderAsync({ ...defaultOrderParams, - makerAddress: compliantMakerAddress, + makerAddress: validMakerAddress, senderAddress: erc20TakerBalanceThresholdWrapper.getBalanceThresholdAddress(), }); // Execute a valid fill const txReceipt = await erc20TakerBalanceThresholdWrapper.fillOrderAsync( - compliantSignedOrderERC20Sender, - compliantTakerAddress, + validSignedOrderERC20Sender, + validTakerAddress, { takerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), ); }); - it('should revert if the signed transaction is not intended for supported', async () => { + it('should revert if the Exchange transaction function is not supported', async () => { // Create signed order without the fillOrder function selector - const txDataBuf = ethUtil.toBuffer(compliantSignedFillOrderTx.data); - const selectorLengthInBytes = 4; - const txDataBufMinusSelector = txDataBuf.slice(selectorLengthInBytes); - const badSelector = '0x00000000'; - const badSelectorBuf = ethUtil.toBuffer(badSelector); - const txDataBufWithBadSelector = Buffer.concat([badSelectorBuf, txDataBufMinusSelector]); - const txDataBufWithBadSelectorHex = ethUtil.bufferToHex(txDataBufWithBadSelector); - // Call compliant forwarder - return expectTransactionFailedWithoutReasonAsync( - erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( - compliantSignedFillOrderTx.salt, - compliantSignedFillOrderTx.signerAddress, - txDataBufWithBadSelectorHex, - compliantSignedFillOrderTx.signature, + const salt = new BigNumber(0); + const badSelectorHex = '0x00000000'; + const signatureHex = '0x'; + // Call valid forwarder + return expectTransactionFailedAsync( + erc721BalanceThresholdFilterInstance.executeTransaction.sendTransactionAsync( + salt, + validTakerAddress, + badSelectorHex, + signatureHex, ), + RevertReason.InvalidOrBlockedExchangeSelector ); }); - it('should revert if senderAddress is not set to the compliant forwarding contract', async () => { + it('should revert if senderAddress is not set to the valid forwarding contract', async () => { // Create signed order with incorrect senderAddress const notBalanceThresholdFilterAddress = zrxToken.address; const signedOrderWithBadSenderAddress = await orderFactory.newSignedOrderAsync({ senderAddress: notBalanceThresholdFilterAddress, }); - const signedOrderWithoutExchangeAddress = orderUtils.getOrderWithoutExchangeAddress( - signedOrderWithBadSenderAddress, - ); - const signedOrderWithoutExchangeAddressData = exchangeInstance.fillOrder.getABIEncodedTransactionData( - signedOrderWithoutExchangeAddress, - takerAssetFillAmount, - compliantSignedOrder.signature, - ); - const signedFillOrderTx = takerTransactionFactory.newSignedTransaction( - signedOrderWithoutExchangeAddressData, - ); - // Call compliant forwarder - return expectTransactionFailedWithoutReasonAsync( - erc721CompliantForwarderInstance.executeTransaction.sendTransactionAsync( - signedFillOrderTx.salt, - signedFillOrderTx.signerAddress, - signedFillOrderTx.data, - signedFillOrderTx.signature, + // Call valid forwarder + return expectTransactionFailedAsync( + erc721TakerBalanceThresholdWrapper.fillOrderAsync( + signedOrderWithBadSenderAddress, + validTakerAddress, + { takerAssetFillAmount }, ), + RevertReason.FailedExecution ); }); }); @@ -361,68 +332,68 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('batchFillOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync( orders, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmounts }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.makerAssetAmount) .times(2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( @@ -430,25 +401,25 @@ describe(ContractName.BalanceThresholdFilter, () => { ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.batchFillOrdersAsync(orders, invalidAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -459,68 +430,68 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('batchFillOrdersNoThrow', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( orders, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmounts }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.makerAssetAmount) .times(2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( @@ -528,25 +499,25 @@ describe(ContractName.BalanceThresholdFilter, () => { ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, validTakerAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.batchFillOrdersNoThrowAsync(orders, invalidAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -557,68 +528,68 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('batchFillOrKillOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync( orders, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmounts }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const cumulativeTakerAssetFillAmount = takerAssetFillAmount.times(2); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.makerAssetAmount) .times(2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount.times(2)), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( @@ -626,36 +597,36 @@ describe(ContractName.BalanceThresholdFilter, () => { ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, invalidAddress, { takerAssetFillAmounts, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if one takerAssetFillAmount is not fully filled', async () => { - const tooBigTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.times(2); - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2); + const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, tooBigTakerAssetFillAmount]; return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.batchFillOrKillOrdersAsync(orders, validTakerAddress, { takerAssetFillAmounts, }), RevertReason.FailedExecution, @@ -666,65 +637,65 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('fillOrder', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderAsync( - compliantSignedOrder, - compliantTakerAddress, + validSignedOrder, + validTakerAddress, { takerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), ); }); it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-compliant maker address + // Create signed order with non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress, + senderAddress: erc721BalanceThresholdFilterInstance.address, + makerAddress: invalidAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrderAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmount }, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -732,7 +703,7 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.fillOrderAsync(compliantSignedOrder, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.fillOrderAsync(validSignedOrder, invalidAddress, { takerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -743,65 +714,65 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('fillOrderNoThrow', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( - compliantSignedOrder, - compliantTakerAddress, + validSignedOrder, + validTakerAddress, { takerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), ); }); it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-compliant maker address + // Create signed order with non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress, + senderAddress: erc721BalanceThresholdFilterInstance.address, + makerAddress: invalidAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmount }, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -809,9 +780,9 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.fillOrderNoThrowAsync( - compliantSignedOrder, - nonCompliantAddress, + erc721NonValidBalanceThresholdWrapper.fillOrderNoThrowAsync( + validSignedOrder, + invalidAddress, { takerAssetFillAmount }, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -822,66 +793,66 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('fillOrKillOrder', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both maker/taker when both maker and taker meet the balance threshold', async () => { // Execute a valid fill - const takerAssetFillAmount_ = compliantSignedOrder.takerAssetAmount; + const takerAssetFillAmount_ = validSignedOrder.takerAssetAmount; const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( - compliantSignedOrder, - compliantTakerAddress, + validSignedOrder, + validTakerAddress, { takerAssetFillAmount: takerAssetFillAmount_ }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount = takerAssetFillAmount_ - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid = compliantSignedOrder.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid = validSignedOrder.makerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee .times(makerAssetFillAmount) - .dividedToIntegerBy(compliantSignedOrder.makerAssetAmount); - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), + .dividedToIntegerBy(validSignedOrder.makerAssetAmount); + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(makerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount_), + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(takerAssetFillAmount_), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(makerFeePaid), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(makerFeePaid), ); - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount_), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(takerAssetFillAmount_), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(makerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address].add(makerFeePaid.add(takerFeePaid)), ); }); it('should revert if maker does not meet the balance threshold', async () => { - // Create signed order with non-compliant maker address + // Create signed order with non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress, + senderAddress: erc721BalanceThresholdFilterInstance.address, + makerAddress: invalidAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync( signedOrderWithBadMakerAddress, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmount }, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -889,18 +860,18 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.fillOrKillOrderAsync( - compliantSignedOrder, - nonCompliantAddress, + erc721NonValidBalanceThresholdWrapper.fillOrKillOrderAsync( + validSignedOrder, + invalidAddress, { takerAssetFillAmount }, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if takerAssetFillAmount is not fully filled', async () => { - const tooBigTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.times(2); + const tooBigTakerAssetFillAmount = validSignedOrder.takerAssetAmount.times(2); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(compliantSignedOrder, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, validTakerAddress, { takerAssetFillAmount: tooBigTakerAssetFillAmount, }), RevertReason.FailedExecution, @@ -911,98 +882,98 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('marketSellOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; - const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const orders = [validSignedOrder, validSignedOrder2]; + const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync( orders, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid2 = validSignedOrder2.makerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid2 = compliantSignedOrder2.takerFee + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid2 = validSignedOrder2.takerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); - const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); + const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( - compliantSignedOrder.makerAssetAmount, + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( + validSignedOrder.makerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( - compliantSignedOrder.takerAssetAmount, + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( + validSignedOrder.takerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address] - .add(compliantSignedOrder.makerFee) + .add(validSignedOrder.makerFee) .add(makerFeePaid2) .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.marketSellOrdersAsync(orders, validTakerAddress, { takerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.marketSellOrdersAsync(orders, invalidAddress, { takerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -1013,98 +984,98 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('marketSellOrdersNoThrow', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; - const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const orders = [validSignedOrder, validSignedOrder2]; + const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( orders, - compliantTakerAddress, + validTakerAddress, { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const makerFeePaid2 = compliantSignedOrder2.makerFee + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const makerFeePaid2 = validSignedOrder2.makerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid2 = compliantSignedOrder2.takerFee + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid2 = validSignedOrder2.takerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); - const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); + const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( - compliantSignedOrder.makerAssetAmount, + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( + validSignedOrder.makerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( - compliantSignedOrder.takerAssetAmount, + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( + validSignedOrder.takerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address] - .add(compliantSignedOrder.makerFee) + .add(validSignedOrder.makerFee) .add(makerFeePaid2) .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, validTakerAddress, { takerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.marketSellOrdersNoThrowAsync(orders, invalidAddress, { takerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -1115,100 +1086,100 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('marketBuyOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; - const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const orders = [validSignedOrder, validSignedOrder2]; + const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync( orders, - compliantTakerAddress, + validTakerAddress, { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerFeePaid2 = compliantSignedOrder2.makerFee + const makerFeePaid2 = validSignedOrder2.makerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid2 = compliantSignedOrder2.takerFee + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid2 = validSignedOrder2.takerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( - compliantSignedOrder.makerAssetAmount, + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( + validSignedOrder.makerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( - compliantSignedOrder.takerAssetAmount, + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( + validSignedOrder.takerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address] - .add(compliantSignedOrder.makerFee) + .add(validSignedOrder.makerFee) .add(makerFeePaid2) .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, { makerAssetFillAmount: dummyMakerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.marketBuyOrdersAsync(orders, invalidAddress, { makerAssetFillAmount: dummyMakerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -1219,100 +1190,100 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('marketBuyOrdersNoThrowAsync', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('should transfer the correct amounts and validate both makers/taker when both makers and taker meet the balance threshold', async () => { // Execute a valid fill - const orders = [compliantSignedOrder, compliantSignedOrder2]; - const cumulativeTakerAssetFillAmount = compliantSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); + const orders = [validSignedOrder, validSignedOrder2]; + const cumulativeTakerAssetFillAmount = validSignedOrder.takerAssetAmount.plus(takerAssetFillAmount); const makerAssetFillAmount2 = takerAssetFillAmount - .times(compliantSignedOrder.makerAssetAmount) - .dividedToIntegerBy(compliantSignedOrder.takerAssetAmount); - const cumulativeMakerAssetFillAmount = compliantSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); + .times(validSignedOrder.makerAssetAmount) + .dividedToIntegerBy(validSignedOrder.takerAssetAmount); + const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( orders, - compliantTakerAddress, + validTakerAddress, { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, ); // Assert validated addresses const expectedValidatedAddresseses = [ - compliantSignedOrder.makerAddress, - compliantSignedOrder2.makerAddress, - compliantSignedFillOrderTx.signerAddress, + validSignedOrder.makerAddress, + validSignedOrder2.makerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); - const makerFeePaid2 = compliantSignedOrder2.makerFee + const makerFeePaid2 = validSignedOrder2.makerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid2 = compliantSignedOrder2.takerFee + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid2 = validSignedOrder2.takerFee .times(makerAssetFillAmount2) - .dividedToIntegerBy(compliantSignedOrder2.makerAssetAmount); - const takerFeePaid = compliantSignedOrder.takerFee.plus(takerFeePaid2); + .dividedToIntegerBy(validSignedOrder2.makerAssetAmount); + const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 - expect(newBalances[compliantMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultMakerAssetAddress].minus( - compliantSignedOrder.makerAssetAmount, + expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( + validSignedOrder.makerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][defaultTakerAssetAddress].add( - compliantSignedOrder.takerAssetAmount, + expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( + validSignedOrder.takerAssetAmount, ), ); - expect(newBalances[compliantMakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress][zrxToken.address].minus(compliantSignedOrder.makerFee), + expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), ); // Maker #2 - expect(newBalances[compliantMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), + expect(newBalances[validMakerAddress2][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultMakerAssetAddress].minus(makerAssetFillAmount2), ); - expect(newBalances[compliantMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), + expect(newBalances[validMakerAddress2][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][defaultTakerAssetAddress].add(takerAssetFillAmount), ); - expect(newBalances[compliantMakerAddress2][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantMakerAddress2][zrxToken.address].minus(makerFeePaid2), + expect(newBalances[validMakerAddress2][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validMakerAddress2][zrxToken.address].minus(makerFeePaid2), ); // Taker - expect(newBalances[compliantTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultTakerAssetAddress].minus(cumulativeTakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), + expect(newBalances[validTakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add(cumulativeMakerAssetFillAmount), ); - expect(newBalances[compliantTakerAddress][zrxToken.address]).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address].minus(takerFeePaid), + expect(newBalances[validTakerAddress][zrxToken.address]).to.be.bignumber.equal( + erc20Balances[validTakerAddress][zrxToken.address].minus(takerFeePaid), ); // Fee recipient expect(newBalances[feeRecipientAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[feeRecipientAddress][zrxToken.address] - .add(compliantSignedOrder.makerFee) + .add(validSignedOrder.makerFee) .add(makerFeePaid2) .add(takerFeePaid), ); }); it('should revert if one maker does not meet the balance threshold', async () => { - // Create order set with one non-compliant maker address + // Create order set with one non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - makerAddress: nonCompliantAddress, + makerAddress: invalidAddress, }); - const orders = [compliantSignedOrder, signedOrderWithBadMakerAddress]; + const orders = [validSignedOrder, signedOrderWithBadMakerAddress]; // Execute transaction const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, compliantTakerAddress, { + erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, validTakerAddress, { makerAssetFillAmount: dummyMakerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { - const orders = [compliantSignedOrder, compliantSignedOrder2]; + const orders = [validSignedOrder, validSignedOrder2]; const dummyMakerAssetFillAmount = new BigNumber(0); return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, nonCompliantAddress, { + erc721NonValidBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync(orders, invalidAddress, { makerAssetFillAmount: dummyMakerAssetFillAmount, }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, @@ -1323,8 +1294,8 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('matchOrders', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('Should transfer correct amounts when both makers and taker meet the balance threshold', async () => { // Test values/results taken from Match Orders test: @@ -1364,13 +1335,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const txReceipt = await erc721TakerBalanceThresholdWrapper.matchOrdersAsync( signedOrderLeft, signedOrderRight, - compliantTakerAddress, + validTakerAddress, ); // Assert validated addresses const expectedValidatedAddresseses = [ signedOrderLeft.makerAddress, signedOrderRight.makerAddress, - compliantTakerAddress, + validTakerAddress, ]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances @@ -1392,10 +1363,10 @@ describe(ContractName.BalanceThresholdFilter, () => { ), ); expect( - newBalances[compliantTakerAddress][defaultMakerAssetAddress], + newBalances[validTakerAddress][defaultMakerAssetAddress], 'Checking taker ingress ERC20 account balance', ).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][defaultMakerAssetAddress].add( + erc20Balances[validTakerAddress][defaultMakerAssetAddress].add( expectedTransferAmounts.amountReceivedByTaker, ), ); @@ -1433,10 +1404,10 @@ describe(ContractName.BalanceThresholdFilter, () => { ), ); expect( - newBalances[compliantTakerAddress][zrxToken.address], + newBalances[validTakerAddress][zrxToken.address], 'Checking taker egress ERC20 account fees', ).to.be.bignumber.equal( - erc20Balances[compliantTakerAddress][zrxToken.address] + erc20Balances[validTakerAddress][zrxToken.address] .minus(expectedTransferAmounts.feePaidByTakerLeft) .sub(expectedTransferAmounts.feePaidByTakerRight), ); @@ -1453,43 +1424,43 @@ describe(ContractName.BalanceThresholdFilter, () => { ); }); it('should revert if left maker does not meet the balance threshold', async () => { - // Create signed order with non-compliant maker address + // Create signed order with non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress, + senderAddress: erc721BalanceThresholdFilterInstance.address, + makerAddress: invalidAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.matchOrdersAsync( - compliantSignedOrder, + validSignedOrder, signedOrderWithBadMakerAddress, - compliantTakerAddress, + validTakerAddress, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if right maker does not meet the balance threshold', async () => { - // Create signed order with non-compliant maker address + // Create signed order with non-valid maker address const signedOrderWithBadMakerAddress = await orderFactory.newSignedOrderAsync({ - senderAddress: erc721CompliantForwarderInstance.address, - makerAddress: nonCompliantAddress, + senderAddress: erc721BalanceThresholdFilterInstance.address, + makerAddress: invalidAddress, }); // Execute transaction return expectTransactionFailedAsync( erc721TakerBalanceThresholdWrapper.matchOrdersAsync( signedOrderWithBadMakerAddress, - compliantSignedOrder, - compliantTakerAddress, + validSignedOrder, + validTakerAddress, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonCompliantBalanceThresholdWrapper.matchOrdersAsync( - compliantSignedOrder, - compliantSignedOrder, - nonCompliantAddress, + erc721NonValidBalanceThresholdWrapper.matchOrdersAsync( + validSignedOrder, + validSignedOrder, + invalidAddress, ), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); @@ -1499,39 +1470,39 @@ describe(ContractName.BalanceThresholdFilter, () => { describe('cancelOrder', () => { beforeEach(async () => { erc20Balances = await erc20Wrapper.getBalancesAsync(); - compliantSignedOrder = await orderFactory.newSignedOrderAsync(); - compliantSignedOrder2 = await orderFactory2.newSignedOrderAsync(); + validSignedOrder = await orderFactory.newSignedOrderAsync(); + validSignedOrder2 = await orderFactory2.newSignedOrderAsync(); }); it('Should successfully cancel order if maker meets balance threshold', async () => { // Verify order is not cancelled const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - compliantSignedOrder, + validSignedOrder, ); expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); // Cancel const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrderAsync( - compliantSignedOrder, - compliantSignedOrder.makerAddress, + validSignedOrder, + validSignedOrder.makerAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( - compliantSignedOrder, + validSignedOrder, ); expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); }); it('Should successfully cancel order if maker does not meet balance threshold', async () => { // Create order where maker does not meet balance threshold - const signedOrderWithBadMakerAddress = await nonCompliantOrderFactory.newSignedOrderAsync({}); + const signedOrderWithBadMakerAddress = await invalidOrderFactory.newSignedOrderAsync({}); // Verify order is not cancelled - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( signedOrderWithBadMakerAddress, ); expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); // Cancel - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrderAsync( + const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrderAsync( signedOrderWithBadMakerAddress, signedOrderWithBadMakerAddress.makerAddress, ); @@ -1552,13 +1523,13 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('Should successfully batch cancel orders if maker meets balance threshold', async () => { // Create orders to cancel - const compliantSignedOrders = [ + const validSignedOrders = [ await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync(), await orderFactory.newSignedOrderAsync(), ]; // Verify orders are not cancelled - _.each(compliantSignedOrders, async signedOrder => { + _.each(validSignedOrders, async signedOrder => { const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); @@ -1566,14 +1537,14 @@ describe(ContractName.BalanceThresholdFilter, () => { }); // Cancel const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync( - compliantSignedOrders, - compliantSignedOrder.makerAddress, + validSignedOrders, + validSignedOrder.makerAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - _.each(compliantSignedOrders, async signedOrder => { + _.each(validSignedOrders, async signedOrder => { const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); @@ -1582,29 +1553,29 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { // Create orders to cancel - const nonCompliantSignedOrders = [ - await nonCompliantOrderFactory.newSignedOrderAsync(), - await nonCompliantOrderFactory.newSignedOrderAsync(), - await nonCompliantOrderFactory.newSignedOrderAsync(), + const invalidSignedOrders = [ + await invalidOrderFactory.newSignedOrderAsync(), + await invalidOrderFactory.newSignedOrderAsync(), + await invalidOrderFactory.newSignedOrderAsync(), ]; // Verify orders are not cancelled - _.each(nonCompliantSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + _.each(invalidSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.batchCancelOrdersAsync( - nonCompliantSignedOrders, - nonCompliantAddress, + const txReceipt = await erc721NonValidBalanceThresholdWrapper.batchCancelOrdersAsync( + invalidSignedOrders, + invalidAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - _.each(nonCompliantSignedOrders, async signedOrder => { - const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + _.each(invalidSignedOrders, async signedOrder => { + const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); return expect(orderInfoAfterCancelling.orderStatus).to.be.equal(OrderStatus.CANCELLED); @@ -1618,13 +1589,13 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('Should successfully batch cancel orders if maker meets balance threshold', async () => { // Create orders to cancel - const compliantSignedOrders = [ + const validSignedOrders = [ await orderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), await orderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), await orderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), ]; // Verify orders are not cancelled - _.each(compliantSignedOrders, async signedOrder => { + _.each(validSignedOrders, async signedOrder => { const orderInfoBeforeCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); @@ -1634,13 +1605,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const cancelOrdersUpToThisSalt = new BigNumber(1); const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync( cancelOrdersUpToThisSalt, - compliantSignedOrder.makerAddress, + validSignedOrder.makerAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - _.each(compliantSignedOrders, async (signedOrder, salt: number) => { + _.each(validSignedOrders, async (signedOrder, salt: number) => { const orderInfoAfterCancelling = await erc721MakerBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); @@ -1654,30 +1625,30 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('Should successfully batch cancel order if maker does not meet balance threshold', async () => { // Create orders to cancel - const nonCompliantSignedOrders = [ - await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), - await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), - await nonCompliantOrderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), + const invalidSignedOrders = [ + await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(0) }), + await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(1) }), + await invalidOrderFactory.newSignedOrderAsync({ salt: new BigNumber(2) }), ]; // Verify orders are not cancelled - _.each(nonCompliantSignedOrders, async signedOrder => { - const orderInfoBeforeCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + _.each(invalidSignedOrders, async signedOrder => { + const orderInfoBeforeCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); return expect(orderInfoBeforeCancelling.orderStatus).to.be.equal(OrderStatus.FILLABLE); }); // Cancel const cancelOrdersUpToThisSalt = new BigNumber(1); - const txReceipt = await erc721NonCompliantBalanceThresholdWrapper.cancelOrdersUpToAsync( + const txReceipt = await erc721NonValidBalanceThresholdWrapper.cancelOrdersUpToAsync( cancelOrdersUpToThisSalt, - nonCompliantAddress, + invalidAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check that order was cancelled - _.each(nonCompliantSignedOrders, async (signedOrder, salt: number) => { - const orderInfoAfterCancelling = await erc721NonCompliantBalanceThresholdWrapper.getOrderInfoAsync( + _.each(invalidSignedOrders, async (signedOrder, salt: number) => { + const orderInfoAfterCancelling = await erc721NonValidBalanceThresholdWrapper.getOrderInfoAsync( signedOrder, ); const saltAsBigNumber = new BigNumber(salt); -- cgit v1.2.3 From 22fd23643cb26ba1b79e9b13e5ef41c97ab7fe6a Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 11 Dec 2018 16:37:07 -0800 Subject: Run all tests for extensions --- contracts/extensions/test/extensions/balance_threshold_filter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index ced9d6e25..6350c019f 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -46,7 +46,7 @@ interface ValidatedAddressesLog { args: { addresses: string[] }; } -describe.only(ContractName.BalanceThresholdFilter, () => { +describe(ContractName.BalanceThresholdFilter, () => { const takerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(500), DECIMALS_DEFAULT); const makerAssetAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1000), DECIMALS_DEFAULT); const takerAssetFillAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(250), DECIMALS_DEFAULT); -- cgit v1.2.3 From f91781a0605f46ee1a40bf979d22ff510f48d464 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 13:52:20 -0800 Subject: Less Assembly. More Solidity. Less Efficiency. More Readability. --- .../BalanceThresholdFilter.sol | 10 +- .../MixinBalanceThresholdFilterCore.sol | 404 ++++++++------------- .../mixins/MBalanceThresholdFilterCore.sol | 7 +- .../utils/ExchangeSelectors/ExchangeSelectors.sol | 151 ++++++++ 4 files changed, 306 insertions(+), 266 deletions(-) create mode 100644 contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol index a68f6805d..ea248793f 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol @@ -24,21 +24,23 @@ import "./interfaces/IThresholdAsset.sol"; import "./MixinBalanceThresholdFilterCore.sol"; -contract BalanceThresholdFilter is MixinBalanceThresholdFilterCore { +contract BalanceThresholdFilter is + MixinBalanceThresholdFilterCore +{ /// @dev Constructs BalanceThresholdFilter. /// @param exchange Address of 0x exchange. /// @param thresholdAsset The asset that must be held by makers/takers. - /// @param thresholdBalance The minimum balance of `thresholdAsset` that must be held by makers/takers. + /// @param balanceThreshold The minimum balance of `thresholdAsset` that must be held by makers/takers. constructor( address exchange, address thresholdAsset, - uint256 thresholdBalance + uint256 balanceThreshold ) public { EXCHANGE = IExchange(exchange); THRESHOLD_ASSET = IThresholdAsset(thresholdAsset); - THRESHOLD_BALANCE = thresholdBalance; + BALANCE_THRESHOLD = balanceThreshold; } } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 51dbae8f3..e78f9ced8 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -6,7 +6,7 @@ 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 + http://www.apache.org/licenses/LICENSE2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -17,31 +17,36 @@ */ pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; import "./mixins/MBalanceThresholdFilterCore.sol"; +import "@0x/contracts-utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol"; +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; -contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { +contract MixinBalanceThresholdFilterCore is + MBalanceThresholdFilterCore, + LibOrder, + ExchangeSelectors +{ /// @dev Executes an Exchange transaction iff the maker and taker meet /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR /// the exchange function is a cancellation. /// Supported Exchange functions: - /// - batchFillOrders - /// - batchFillOrdersNoThrow - /// - batchFillOrKillOrders - /// - fillOrder - /// - fillOrderNoThrow - /// - fillOrKillOrder - /// - marketBuyOrders - /// - marketBuyOrdersNoThrow - /// - marketSellOrders - /// - marketSellOrdersNoThrow - /// - matchOrders - /// - cancelOrder - /// - batchCancelOrders - /// - cancelOrdersUpTo + /// batchFillOrders + /// batchFillOrdersNoThrow + /// batchFillOrKillOrders + /// fillOrder + /// fillOrderNoThrow + /// fillOrKillOrder + /// marketBuyOrders + /// marketBuyOrdersNoThrow + /// marketSellOrders + /// marketSellOrdersNoThrow + /// matchOrders + /// cancelOrder + /// batchCancelOrders + /// cancelOrdersUpTo /// Trying to call any other exchange function will throw. /// @param salt Arbitrary number to ensure uniqueness of transaction hash. /// @param signerAddress Address of transaction signer. @@ -56,7 +61,7 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { external { // Validate addresses. - validateBalanceThresholdsOrRevert(); + validateBalanceThresholdsOrRevert(signerAddress); // All addresses are valid. Execute fillOrder. EXCHANGE.executeTransaction( @@ -67,6 +72,83 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { ); } + /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, + /// which is accessed through `signedExchangeTransaction`. + /// @param offset Offset into the Exchange calldata. + /// @return value Corresponding 32 byte value stored at `offset`. + function exchangeCalldataload(uint256 offset) + internal + returns (bytes32 value) + { + assembly { + // Pointer to exchange transaction + // 0x04 for calldata selector + // 0x40 to access `signedExchangeTransaction`, which is the third parameter + let exchangeTxPtr := calldataload(0x44) + + // Offset into Exchange calldata + // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. + // 0x04 for calldata selector + // 0x20 for length field of `signedExchangeTransaction` + let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) + value := calldataload(exchangeCalldataOffset) + } + } + + /// @dev Convenience function that skips the 4 byte selector when loading + /// from the embedded Exchange calldata. + /// @param offset Offset into the Exchange calldata (minus the 4 byte selector) + /// @return value Corresponding 32 byte value stored at `offset` + 4. + function loadExchangeData(uint256 offset) + internal + returns (bytes32 value) + { + value = exchangeCalldataload(offset + 4); + } + + /// @dev A running list is maintained of addresses to validate. + /// This function records an address in this array. + /// @param addressToValidate Address to record for validation. + function recordAddressToValidate(address addressToValidate, address[] memory addressList) + internal + { + uint256 newAddressListLength = addressList.length + 1; + assembly { + // Store new array length + mstore(addressList, newAddressListLength) + mstore(0x40, add(addressList, add(0x20, mul(0x20, newAddressListLength)))) + } + addressList[newAddressListLength - 1] = addressToValidate; + } + + /// @dev Extracts the maker address from an order stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records it in + /// the running list of addresses to validate. + /// @param orderParamIndex Index of the order in the Exchange function's signature + function loadMakerAddressFromOrder(uint8 orderParamIndex) internal returns (address makerAddress) { + uint256 orderPtr = uint256(loadExchangeData(orderParamIndex * 0x20)); + makerAddress = address(loadExchangeData(orderPtr)); + } + + /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records them in + /// the running list of addresses to validate. + /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature + function loadMakerAddressesFromOrderArray(uint8 orderArrayParamIndex) + internal + returns (address[] makerAddresses) + { + uint256 orderArrayPtr = uint256(loadExchangeData(orderArrayParamIndex * 0x20)); + uint256 orderArrayLength = uint256(loadExchangeData(orderArrayPtr)); + uint256 orderArrayElementPtr = orderArrayPtr + 0x20; + uint256 orderArrayElementEndPtr = orderArrayElementPtr + (orderArrayLength * 0x20); + for(uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 0x20) { + uint256 orderPtr = uint256(loadExchangeData(orderPtrOffset)); + address makerAddress = address(loadExchangeData(orderPtr + orderArrayElementPtr)); + recordAddressToValidate(makerAddress, makerAddresses); + } + } + /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold /// then this function will revert. Which addresses are validated depends on @@ -74,250 +156,56 @@ contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore { /// No parameters are taken as this function reads arguments directly from calldata, to save gas. /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all /// of the addresses whose balance thresholds it checked. - function validateBalanceThresholdsOrRevert() + function validateBalanceThresholdsOrRevert(address signerAddress) internal { - // Addresses that are validated below. - address[] memory validatedAddresses; - - ///// Do not add variables after this point. ///// - ///// The assembly block may overwrite their values. ///// - - // Validate addresses - assembly { - /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, - /// which is accessed through `signedExchangeTransaction`. - /// @param offset - Offset into the Exchange calldata. - /// @return value - Corresponding 32 byte value stored at `offset`. - function exchangeCalldataload(offset) -> value { - // Pointer to exchange transaction - // 0x04 for calldata selector - // 0x40 to access `signedExchangeTransaction`, which is the third parameter - let exchangeTxPtr := calldataload(0x44) - - // Offset into Exchange calldata - // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. - // 0x04 for calldata selector - // 0x20 for length field of `signedExchangeTransaction` - let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) - value := calldataload(exchangeCalldataOffset) - } - - /// @dev Convenience function that skips the 4 byte selector when loading - /// from the embedded Exchange calldata. - /// @param offset - Offset into the Exchange calldata (minus the 4 byte selector) - /// @return value - Corresponding 32 byte value stored at `offset` + 4. - function loadExchangeData(offset) -> value { - value := exchangeCalldataload(add(offset, 0x4)) - } - - /// @dev A running list is maintained of addresses to validate. - /// This function records an address in this array. - /// @param addressToValidate - Address to record for validation. - function recordAddressToValidate(addressToValidate) { - // Compute `addressesToValidate` memory offset - let addressesToValidate_ := mload(0x40) - let nAddressesToValidate_ := mload(addressesToValidate_) - - // Increment length - nAddressesToValidate_ := add(mload(addressesToValidate_), 0x01) - mstore(addressesToValidate_, nAddressesToValidate_) - - // Append address to validate - let offset := mul(nAddressesToValidate_, 0x20) - mstore(add(addressesToValidate_, offset), addressToValidate) - } - - /// @dev Extracts the maker address from an order stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records it in - /// the running list of addresses to validate. - /// @param orderParamIndex - Index of the order in the Exchange function's signature - function recordMakerAddressFromOrder(orderParamIndex) { - let orderPtr := loadExchangeData(mul(orderParamIndex, 0x20)) - let makerAddress := loadExchangeData(orderPtr) - recordAddressToValidate(makerAddress) - } - - /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records them in - /// the running list of addresses to validate. - /// @param orderArrayParamIndex - Index of the order array in the Exchange function's signature - function recordMakerAddressesFromOrderArray(orderArrayParamIndex) { - let orderArrayPtr := loadExchangeData(mul(orderArrayParamIndex, 0x20)) - let orderArrayLength := loadExchangeData(orderArrayPtr) - let orderArrayElementPtr := add(orderArrayPtr, 0x20) - let orderArrayElementEndPtr := add(orderArrayElementPtr, mul(orderArrayLength, 0x20)) - for {let orderPtrOffset := orderArrayElementPtr} lt(orderPtrOffset, orderArrayElementEndPtr) {orderPtrOffset := add(orderPtrOffset, 0x20)} { - let orderPtr := loadExchangeData(orderPtrOffset) - let makerAddress := loadExchangeData(add(orderPtr, orderArrayElementPtr)) - recordAddressToValidate(makerAddress) - } - } - - /// @dev Records address of signer in the running list of addresses to validate. - /// @note: We cannot access `signerAddress` directly from within the asm function, - /// so it is loaded from the calldata. - function recordSignerAddress() { - // Load the signer address from calldata - // 0x04 for selector - // 0x20 to access `signerAddress`, which is the second parameter. - let signerAddress_ := calldataload(0x24) - recordAddressToValidate(signerAddress_) - } - - /// @dev Records addresses to be validated when Exchange transaction is a batch fill variant. - /// This is one of: batchFillOrders, batchFillOrKillOrders, batchFillNoThrow - /// Reference signature<T>: <batchFillVariant>(Order[],uint256[],bytes[]) - function recordAddressesForBatchFillVariant() { - // Record maker addresses from order array (parameter index 0) - // The signer is the taker for these orders and must also be validated. - recordMakerAddressesFromOrderArray(0) - recordSignerAddress() - } - - /// @dev Records addresses to be validated when Exchange transaction is a fill order variant. - /// This is one of: fillOrder, fillOrKillOrder, fillOrderNoThrow - /// Reference signature<T>: <fillOrderVariant>(Order,uint256,bytes) - function recordAddressesForFillOrderVariant() { - // Record maker address from the order (param index 0) - // The signer is the taker for this order and must also be validated. - recordMakerAddressFromOrder(0) - recordSignerAddress() - } - - /// @dev Records addresses to be validated when Exchange transaction is a market fill variant. - /// This is one of: marketBuyOrders, marketBuyOrdersNoThrow, marketSellOrders, marketSellOrdersNoThrow - /// Reference signature<T>: <marketFillInvariant>(Order[],uint256,bytes[]) - function recordAddressesForMarketFillVariant() { - // Record maker addresses from order array (parameter index 0) - // The signer is the taker for these orders and must also be validated. - recordMakerAddressesFromOrderArray(0) - recordSignerAddress() - } - - /// @dev Records addresses to be validated when Exchange transaction is matchOrders. - /// Reference signature: matchOrders(Order,Order) - function recordAddressesForMatchOrders() { - // Record maker address from both orders (param indices 0 & 1). - // The signer is the taker and must also be validated. - recordMakerAddressFromOrder(0) - recordMakerAddressFromOrder(1) - recordSignerAddress() - } - - ///// Record Addresses to Validate ///// - - // Addresses needing validation depends on which Exchange function is being called. - // Step 1/2 Read the exchange function selector. - let exchangeFunctionSelector := and( - exchangeCalldataload(0x0), - 0xffffffff00000000000000000000000000000000000000000000000000000000 - ) - - // Step 2/2 Extract addresses to validate based on this selector. - // See ../../utils/ExchangeSelectors/ExchangeSelectors.sol for selectors - // solhint-disable no-empty-blocks - switch exchangeFunctionSelector - case 0x297bb70b00000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrders - case 0x50dde19000000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrdersNoThrow - case 0x4d0ae54600000000000000000000000000000000000000000000000000000000 { recordAddressesForBatchFillVariant() } // batchFillOrKillOrders - case 0xb4be83d500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrder - case 0x3e228bae00000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrderNoThrow - case 0x64a3bc1500000000000000000000000000000000000000000000000000000000 { recordAddressesForFillOrderVariant() } // fillOrKillOrder - case 0xe5fa431b00000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrders - case 0xa3e2038000000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketBuyOrdersNoThrow - case 0x7e1d980800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrders - case 0xdd1c7d1800000000000000000000000000000000000000000000000000000000 { recordAddressesForMarketFillVariant() } // marketSellOrdersNoThrow - case 0x3c28d86100000000000000000000000000000000000000000000000000000000 { recordAddressesForMatchOrders() } // matchOrders - case 0xd46b02c300000000000000000000000000000000000000000000000000000000 {} // cancelOrder - case 0x4ac1478200000000000000000000000000000000000000000000000000000000 {} // batchCancelOrders - case 0x4f9559b100000000000000000000000000000000000000000000000000000000 {} // cancelOrdersUpTo - default { - // Revert with `Error("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR")` - mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(0x40, 0x00000024494e56414c49445f4f525f424c4f434b45445f45584348414e47455f) - mstore(0x60, 0x53454c4543544f52000000000000000000000000000000000000000000000000) - mstore(0x80, 0x00000000) - // Revert length calculation: - // 4 -- error selector - // 32 -- offset to string - // 32 -- string length field - // 64 -- strlen(INVALID_OR_BLOCKED_EXCHANGE_SELECTOR) rounded up to nearest 32-byte word. - revert(0, 132) - } - // solhint-enable no-empty-blocks - - ///// Validate Recorded Addresses ///// - - // Load from memory the addresses to validate - let addressesToValidate := mload(0x40) - let addressesToValidateLength := mload(addressesToValidate) - let addressesToValidateElementPtr := add(addressesToValidate, 0x20) - let addressesToValidateElementEndPtr := add(addressesToValidateElementPtr, mul(addressesToValidateLength, 0x20)) - - // Set free memory pointer to after `addressesToValidate` array. - // This is to avoid corruption when making calls in the loop below. - let freeMemPtr := addressesToValidateElementEndPtr - mstore(0x40, freeMemPtr) - - // Validate addresses - let thresholdAssetAddress := sload(THRESHOLD_ASSET_slot) - let thresholdBalance := sload(THRESHOLD_BALANCE_slot) - // solhint-disable max-line-length - for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} { - // solhint-enable max-line-length - // Construct calldata for `THRESHOLD_ASSET.balanceOf` - mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000) - mstore(add(freeMemPtr, 0x04), mload(addressToValidate)) - - // call `THRESHOLD_ASSET.balanceOf` - let success := call( - gas, // forward all gas - thresholdAssetAddress, // call address of asset proxy - 0, // don't send any ETH - freeMemPtr, // pointer to start of input - 0x24, // length of input (one padded address) - freeMemPtr, // write output to next free memory offset - 0x20 // reserve space for return balance (0x20 bytes) - ) - if eq(success, 0) { - // @TODO Revert with `Error("BALANCE_QUERY_FAILED")` - mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(0x40, 0x0000001442414c414e43455f51554552595f4641494c45440000000000000000) - mstore(0x60, 0x00000000) - // Revert length calculation: - // 4 -- error selector - // 32 -- offset to string - // 32 -- string length field - // 32 -- strlen(BALANCE_QUERY_FAILED) rounded up to nearest 32-byte word. - revert(0, 100) - } + // Extract addresses to validate from Exchange calldata + address[] memory addressesToValidate = new address[](0); + bytes4 exchangeFunctionSelector = bytes4(exchangeCalldataload(0)); + if( + exchangeFunctionSelector == batchFillOrdersSelector || + exchangeFunctionSelector == batchFillOrdersNoThrowSelector || + exchangeFunctionSelector == batchFillOrKillOrdersSelector || + exchangeFunctionSelector == marketBuyOrdersSelector || + exchangeFunctionSelector == marketBuyOrdersNoThrowSelector || + exchangeFunctionSelector == marketSellOrdersSelector || + exchangeFunctionSelector == marketSellOrdersNoThrowSelector + ) { + addressesToValidate = loadMakerAddressesFromOrderArray(0); + recordAddressToValidate(signerAddress, addressesToValidate); + } else if( + exchangeFunctionSelector == fillOrderSelector || + exchangeFunctionSelector == fillOrderNoThrowSelector || + exchangeFunctionSelector == fillOrKillOrderSelector + ) { + address makerAddress = loadMakerAddressFromOrder(0); + recordAddressToValidate(makerAddress, addressesToValidate); + recordAddressToValidate(signerAddress, addressesToValidate); + } else if(exchangeFunctionSelector == matchOrdersSelector) { + address leftOrderAddress = loadMakerAddressFromOrder(0); + recordAddressToValidate(leftOrderAddress, addressesToValidate); + address rightOrderAddress = loadMakerAddressFromOrder(1); + recordAddressToValidate(rightOrderAddress, addressesToValidate); + recordAddressToValidate(signerAddress, addressesToValidate); + } else if( + exchangeFunctionSelector == cancelOrderSelector || + exchangeFunctionSelector == batchCancelOrdersSelector || + exchangeFunctionSelector == cancelOrdersUpToSelector + ) { + // Do nothing + } else { + revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); + } - // Revert if balance not held - let addressBalance := mload(freeMemPtr) - if lt(addressBalance, thresholdBalance) { - // Revert with `Error("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD")` - mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(0x20, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(0x40, 0x0000003441545f4c454153545f4f4e455f414444524553535f444f45535f4e4f) - mstore(0x60, 0x545f4d4545545f42414c414e43455f5448524553484f4c440000000000000000) - mstore(0x80, 0x00000000) - // Revert length calculation: - // 4 -- error selector - // 32 -- offset to string - // 32 -- string length field - // 64 -- strlen(AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD) rounded up to nearest 32-byte word. - revert(0, 132) - } + // Validate account balances + uint256 balanceThreshold = BALANCE_THRESHOLD; + IThresholdAsset thresholdAsset = THRESHOLD_ASSET; + for(uint i = 0; i < addressesToValidate.length; ++i) { + uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); + if (addressBalance < balanceThreshold) { + revert("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"); } - - // Record validated addresses - validatedAddresses := addressesToValidate } - - ///// If we hit this point then all addresses are valid ///// - emit ValidatedAddresses(validatedAddresses); + emit ValidatedAddresses(addressesToValidate); } } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol index af7a9453b..6aaa729fb 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol @@ -17,7 +17,6 @@ */ pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "../interfaces/IThresholdAsset.sol"; @@ -33,10 +32,10 @@ contract MBalanceThresholdFilterCore { IThresholdAsset internal THRESHOLD_ASSET; // The minimum balance of `THRESHOLD_ASSET` that must be held by makers/takers - uint256 internal THRESHOLD_BALANCE; + uint256 internal BALANCE_THRESHOLD; // solhint-enable var-name-mixedcase - // Addresses that hold at least `THRESHOLD_BALANCE` of `THRESHOLD_ASSET` + // Addresses that hold at least `BALANCE_THRESHOLD` of `THRESHOLD_ASSET` event ValidatedAddresses ( address[] addresses ); @@ -79,5 +78,5 @@ contract MBalanceThresholdFilterCore { /// No parameters are taken as this function reads arguments directly from calldata, to save gas. /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all /// of the addresses whose balance thresholds it checked. - function validateBalanceThresholdsOrRevert() internal; + function validateBalanceThresholdsOrRevert(address signerAddress) internal; } diff --git a/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol b/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol new file mode 100644 index 000000000..c361fd075 --- /dev/null +++ b/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol @@ -0,0 +1,151 @@ +/* + + 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 ExchangeSelectors { + + // allowedValidators + bytes4 constant allowedValidatorsSelector = 0x7b8e3514; + bytes4 constant allowedValidatorsSelectorGenerator = bytes4(keccak256('allowedValidators(address,address)')); + + // assetProxies + bytes4 constant assetProxiesSelector = 0x3fd3c997; + bytes4 constant assetProxiesSelectorGenerator = bytes4(keccak256('assetProxies(bytes4)')); + + // batchCancelOrders + bytes4 constant batchCancelOrdersSelector = 0x4ac14782; + bytes4 constant batchCancelOrdersSelectorGenerator = bytes4(keccak256('batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); + + // batchFillOrKillOrders + bytes4 constant batchFillOrKillOrdersSelector = 0x4d0ae546; + bytes4 constant batchFillOrKillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // batchFillOrders + bytes4 constant batchFillOrdersSelector = 0x297bb70b; + bytes4 constant batchFillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // batchFillOrdersNoThrow + bytes4 constant batchFillOrdersNoThrowSelector = 0x50dde190; + bytes4 constant batchFillOrdersNoThrowSelectorGenerator = bytes4(keccak256('batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // cancelOrder + bytes4 constant cancelOrderSelector = 0xd46b02c3; + bytes4 constant cancelOrderSelectorGenerator = bytes4(keccak256('cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); + + // cancelOrdersUpTo + bytes4 constant cancelOrdersUpToSelector = 0x4f9559b1; + bytes4 constant cancelOrdersUpToSelectorGenerator = bytes4(keccak256('cancelOrdersUpTo(uint256)')); + + // cancelled + bytes4 constant cancelledSelector = 0x2ac12622; + bytes4 constant cancelledSelectorGenerator = bytes4(keccak256('cancelled(bytes32)')); + + // currentContextAddress + bytes4 constant currentContextAddressSelector = 0xeea086ba; + bytes4 constant currentContextAddressSelectorGenerator = bytes4(keccak256('currentContextAddress()')); + + // executeTransaction + bytes4 constant executeTransactionSelector = 0xbfc8bfce; + bytes4 constant executeTransactionSelectorGenerator = bytes4(keccak256('executeTransaction(uint256,address,bytes,bytes)')); + + // fillOrKillOrder + bytes4 constant fillOrKillOrderSelector = 0x64a3bc15; + bytes4 constant fillOrKillOrderSelectorGenerator = bytes4(keccak256('fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // fillOrder + bytes4 constant fillOrderSelector = 0xb4be83d5; + bytes4 constant fillOrderSelectorGenerator = bytes4(keccak256('fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // fillOrderNoThrow + bytes4 constant fillOrderNoThrowSelector = 0x3e228bae; + bytes4 constant fillOrderNoThrowSelectorGenerator = bytes4(keccak256('fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // filled + bytes4 constant filledSelector = 0x288cdc91; + bytes4 constant filledSelectorGenerator = bytes4(keccak256('filled(bytes32)')); + + // getAssetProxy + bytes4 constant getAssetProxySelector = 0x60704108; + bytes4 constant getAssetProxySelectorGenerator = bytes4(keccak256('getAssetProxy(bytes4)')); + + // getOrderInfo + bytes4 constant getOrderInfoSelector = 0xc75e0a81; + bytes4 constant getOrderInfoSelectorGenerator = bytes4(keccak256('getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); + + // getOrdersInfo + bytes4 constant getOrdersInfoSelector = 0x7e9d74dc; + bytes4 constant getOrdersInfoSelectorGenerator = bytes4(keccak256('getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); + + // isValidSignature + bytes4 constant isValidSignatureSelector = 0x93634702; + bytes4 constant isValidSignatureSelectorGenerator = bytes4(keccak256('isValidSignature(bytes32,address,bytes)')); + + // marketBuyOrders + bytes4 constant marketBuyOrdersSelector = 0xe5fa431b; + bytes4 constant marketBuyOrdersSelectorGenerator = bytes4(keccak256('marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketBuyOrdersNoThrow + bytes4 constant marketBuyOrdersNoThrowSelector = 0xa3e20380; + bytes4 constant marketBuyOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketSellOrders + bytes4 constant marketSellOrdersSelector = 0x7e1d9808; + bytes4 constant marketSellOrdersSelectorGenerator = bytes4(keccak256('marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketSellOrdersNoThrow + bytes4 constant marketSellOrdersNoThrowSelector = 0xdd1c7d18; + bytes4 constant marketSellOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // matchOrders + bytes4 constant matchOrdersSelector = 0x3c28d861; + bytes4 constant matchOrdersSelectorGenerator = bytes4(keccak256('matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)')); + + // orderEpoch + bytes4 constant orderEpochSelector = 0xd9bfa73e; + bytes4 constant orderEpochSelectorGenerator = bytes4(keccak256('orderEpoch(address,address)')); + + // owner + bytes4 constant ownerSelector = 0x8da5cb5b; + bytes4 constant ownerSelectorGenerator = bytes4(keccak256('owner()')); + + // preSign + bytes4 constant preSignSelector = 0x3683ef8e; + bytes4 constant preSignSelectorGenerator = bytes4(keccak256('preSign(bytes32,address,bytes)')); + + // preSigned + bytes4 constant preSignedSelector = 0x82c174d0; + bytes4 constant preSignedSelectorGenerator = bytes4(keccak256('preSigned(bytes32,address)')); + + // registerAssetProxy + bytes4 constant registerAssetProxySelector = 0xc585bb93; + bytes4 constant registerAssetProxySelectorGenerator = bytes4(keccak256('registerAssetProxy(address)')); + + // setSignatureValidatorApproval + bytes4 constant setSignatureValidatorApprovalSelector = 0x77fcce68; + bytes4 constant setSignatureValidatorApprovalSelectorGenerator = bytes4(keccak256('setSignatureValidatorApproval(address,bool)')); + + // transactions + bytes4 constant transactionsSelector = 0x642f2eaf; + bytes4 constant transactionsSelectorGenerator = bytes4(keccak256('transactions(bytes32)')); + + // transferOwnership + bytes4 constant transferOwnershipSelector = 0xf2fde38b; + bytes4 constant transferOwnershipSelectorGenerator = bytes4(keccak256('transferOwnership(address)')); +} \ No newline at end of file -- cgit v1.2.3 From f3a2e3b6f3adb75d1920779df9dabb7cf476a996 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 14:20:11 -0800 Subject: Moved exchange calldata functions to separate mixin --- .../MixinBalanceThresholdFilterCore.sol | 93 ++----------- .../utils/ExchangeSelectors/ExchangeSelectors.sol | 151 --------------------- 2 files changed, 9 insertions(+), 235 deletions(-) delete mode 100644 contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index e78f9ced8..8d15fe6c8 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -18,15 +18,17 @@ pragma solidity 0.4.24; -import "./mixins/MBalanceThresholdFilterCore.sol"; -import "@0x/contracts-utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol"; +import "@0x/contracts-libs/contracts/libs/LibExchangeSelectors.sol"; import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "./mixins/MBalanceThresholdFilterCore.sol"; +import "./MixinExchangeCalldata.sol"; contract MixinBalanceThresholdFilterCore is MBalanceThresholdFilterCore, + MixinExchangeCalldata, LibOrder, - ExchangeSelectors + LibExchangeSelectors { /// @dev Executes an Exchange transaction iff the maker and taker meet @@ -72,82 +74,7 @@ contract MixinBalanceThresholdFilterCore is ); } - /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, - /// which is accessed through `signedExchangeTransaction`. - /// @param offset Offset into the Exchange calldata. - /// @return value Corresponding 32 byte value stored at `offset`. - function exchangeCalldataload(uint256 offset) - internal - returns (bytes32 value) - { - assembly { - // Pointer to exchange transaction - // 0x04 for calldata selector - // 0x40 to access `signedExchangeTransaction`, which is the third parameter - let exchangeTxPtr := calldataload(0x44) - - // Offset into Exchange calldata - // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. - // 0x04 for calldata selector - // 0x20 for length field of `signedExchangeTransaction` - let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) - value := calldataload(exchangeCalldataOffset) - } - } - - /// @dev Convenience function that skips the 4 byte selector when loading - /// from the embedded Exchange calldata. - /// @param offset Offset into the Exchange calldata (minus the 4 byte selector) - /// @return value Corresponding 32 byte value stored at `offset` + 4. - function loadExchangeData(uint256 offset) - internal - returns (bytes32 value) - { - value = exchangeCalldataload(offset + 4); - } - - /// @dev A running list is maintained of addresses to validate. - /// This function records an address in this array. - /// @param addressToValidate Address to record for validation. - function recordAddressToValidate(address addressToValidate, address[] memory addressList) - internal - { - uint256 newAddressListLength = addressList.length + 1; - assembly { - // Store new array length - mstore(addressList, newAddressListLength) - mstore(0x40, add(addressList, add(0x20, mul(0x20, newAddressListLength)))) - } - addressList[newAddressListLength - 1] = addressToValidate; - } - - /// @dev Extracts the maker address from an order stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records it in - /// the running list of addresses to validate. - /// @param orderParamIndex Index of the order in the Exchange function's signature - function loadMakerAddressFromOrder(uint8 orderParamIndex) internal returns (address makerAddress) { - uint256 orderPtr = uint256(loadExchangeData(orderParamIndex * 0x20)); - makerAddress = address(loadExchangeData(orderPtr)); - } - - /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records them in - /// the running list of addresses to validate. - /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature - function loadMakerAddressesFromOrderArray(uint8 orderArrayParamIndex) - internal - returns (address[] makerAddresses) - { - uint256 orderArrayPtr = uint256(loadExchangeData(orderArrayParamIndex * 0x20)); - uint256 orderArrayLength = uint256(loadExchangeData(orderArrayPtr)); - uint256 orderArrayElementPtr = orderArrayPtr + 0x20; - uint256 orderArrayElementEndPtr = orderArrayElementPtr + (orderArrayLength * 0x20); - for(uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 0x20) { - uint256 orderPtr = uint256(loadExchangeData(orderPtrOffset)); - address makerAddress = address(loadExchangeData(orderPtr + orderArrayElementPtr)); - recordAddressToValidate(makerAddress, makerAddresses); - } - } + /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold @@ -188,12 +115,10 @@ contract MixinBalanceThresholdFilterCore is recordAddressToValidate(rightOrderAddress, addressesToValidate); recordAddressToValidate(signerAddress, addressesToValidate); } else if( - exchangeFunctionSelector == cancelOrderSelector || - exchangeFunctionSelector == batchCancelOrdersSelector || - exchangeFunctionSelector == cancelOrdersUpToSelector + exchangeFunctionSelector != cancelOrderSelector && + exchangeFunctionSelector != batchCancelOrdersSelector && + exchangeFunctionSelector != cancelOrdersUpToSelector ) { - // Do nothing - } else { revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); } diff --git a/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol b/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol deleted file mode 100644 index c361fd075..000000000 --- a/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol +++ /dev/null @@ -1,151 +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.24; - - -contract ExchangeSelectors { - - // allowedValidators - bytes4 constant allowedValidatorsSelector = 0x7b8e3514; - bytes4 constant allowedValidatorsSelectorGenerator = bytes4(keccak256('allowedValidators(address,address)')); - - // assetProxies - bytes4 constant assetProxiesSelector = 0x3fd3c997; - bytes4 constant assetProxiesSelectorGenerator = bytes4(keccak256('assetProxies(bytes4)')); - - // batchCancelOrders - bytes4 constant batchCancelOrdersSelector = 0x4ac14782; - bytes4 constant batchCancelOrdersSelectorGenerator = bytes4(keccak256('batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); - - // batchFillOrKillOrders - bytes4 constant batchFillOrKillOrdersSelector = 0x4d0ae546; - bytes4 constant batchFillOrKillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); - - // batchFillOrders - bytes4 constant batchFillOrdersSelector = 0x297bb70b; - bytes4 constant batchFillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); - - // batchFillOrdersNoThrow - bytes4 constant batchFillOrdersNoThrowSelector = 0x50dde190; - bytes4 constant batchFillOrdersNoThrowSelectorGenerator = bytes4(keccak256('batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); - - // cancelOrder - bytes4 constant cancelOrderSelector = 0xd46b02c3; - bytes4 constant cancelOrderSelectorGenerator = bytes4(keccak256('cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); - - // cancelOrdersUpTo - bytes4 constant cancelOrdersUpToSelector = 0x4f9559b1; - bytes4 constant cancelOrdersUpToSelectorGenerator = bytes4(keccak256('cancelOrdersUpTo(uint256)')); - - // cancelled - bytes4 constant cancelledSelector = 0x2ac12622; - bytes4 constant cancelledSelectorGenerator = bytes4(keccak256('cancelled(bytes32)')); - - // currentContextAddress - bytes4 constant currentContextAddressSelector = 0xeea086ba; - bytes4 constant currentContextAddressSelectorGenerator = bytes4(keccak256('currentContextAddress()')); - - // executeTransaction - bytes4 constant executeTransactionSelector = 0xbfc8bfce; - bytes4 constant executeTransactionSelectorGenerator = bytes4(keccak256('executeTransaction(uint256,address,bytes,bytes)')); - - // fillOrKillOrder - bytes4 constant fillOrKillOrderSelector = 0x64a3bc15; - bytes4 constant fillOrKillOrderSelectorGenerator = bytes4(keccak256('fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); - - // fillOrder - bytes4 constant fillOrderSelector = 0xb4be83d5; - bytes4 constant fillOrderSelectorGenerator = bytes4(keccak256('fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); - - // fillOrderNoThrow - bytes4 constant fillOrderNoThrowSelector = 0x3e228bae; - bytes4 constant fillOrderNoThrowSelectorGenerator = bytes4(keccak256('fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); - - // filled - bytes4 constant filledSelector = 0x288cdc91; - bytes4 constant filledSelectorGenerator = bytes4(keccak256('filled(bytes32)')); - - // getAssetProxy - bytes4 constant getAssetProxySelector = 0x60704108; - bytes4 constant getAssetProxySelectorGenerator = bytes4(keccak256('getAssetProxy(bytes4)')); - - // getOrderInfo - bytes4 constant getOrderInfoSelector = 0xc75e0a81; - bytes4 constant getOrderInfoSelectorGenerator = bytes4(keccak256('getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); - - // getOrdersInfo - bytes4 constant getOrdersInfoSelector = 0x7e9d74dc; - bytes4 constant getOrdersInfoSelectorGenerator = bytes4(keccak256('getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); - - // isValidSignature - bytes4 constant isValidSignatureSelector = 0x93634702; - bytes4 constant isValidSignatureSelectorGenerator = bytes4(keccak256('isValidSignature(bytes32,address,bytes)')); - - // marketBuyOrders - bytes4 constant marketBuyOrdersSelector = 0xe5fa431b; - bytes4 constant marketBuyOrdersSelectorGenerator = bytes4(keccak256('marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketBuyOrdersNoThrow - bytes4 constant marketBuyOrdersNoThrowSelector = 0xa3e20380; - bytes4 constant marketBuyOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketSellOrders - bytes4 constant marketSellOrdersSelector = 0x7e1d9808; - bytes4 constant marketSellOrdersSelectorGenerator = bytes4(keccak256('marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketSellOrdersNoThrow - bytes4 constant marketSellOrdersNoThrowSelector = 0xdd1c7d18; - bytes4 constant marketSellOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // matchOrders - bytes4 constant matchOrdersSelector = 0x3c28d861; - bytes4 constant matchOrdersSelectorGenerator = bytes4(keccak256('matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)')); - - // orderEpoch - bytes4 constant orderEpochSelector = 0xd9bfa73e; - bytes4 constant orderEpochSelectorGenerator = bytes4(keccak256('orderEpoch(address,address)')); - - // owner - bytes4 constant ownerSelector = 0x8da5cb5b; - bytes4 constant ownerSelectorGenerator = bytes4(keccak256('owner()')); - - // preSign - bytes4 constant preSignSelector = 0x3683ef8e; - bytes4 constant preSignSelectorGenerator = bytes4(keccak256('preSign(bytes32,address,bytes)')); - - // preSigned - bytes4 constant preSignedSelector = 0x82c174d0; - bytes4 constant preSignedSelectorGenerator = bytes4(keccak256('preSigned(bytes32,address)')); - - // registerAssetProxy - bytes4 constant registerAssetProxySelector = 0xc585bb93; - bytes4 constant registerAssetProxySelectorGenerator = bytes4(keccak256('registerAssetProxy(address)')); - - // setSignatureValidatorApproval - bytes4 constant setSignatureValidatorApprovalSelector = 0x77fcce68; - bytes4 constant setSignatureValidatorApprovalSelectorGenerator = bytes4(keccak256('setSignatureValidatorApproval(address,bool)')); - - // transactions - bytes4 constant transactionsSelector = 0x642f2eaf; - bytes4 constant transactionsSelectorGenerator = bytes4(keccak256('transactions(bytes32)')); - - // transferOwnership - bytes4 constant transferOwnershipSelector = 0xf2fde38b; - bytes4 constant transferOwnershipSelectorGenerator = bytes4(keccak256('transferOwnership(address)')); -} \ No newline at end of file -- cgit v1.2.3 From dea6f35b0471913551d58a08f547974701fc0057 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 14:26:11 -0800 Subject: Refactoring balance threshold filter --- .../BalanceThresholdFilter.sol | 1 - .../MixinBalanceThresholdFilterCore.sol | 63 ++++----- .../MixinExchangeCalldata.sol | 102 ++++++++++++++ .../interfaces/IBalanceThresholdFilterCore.sol | 56 ++++++++ .../mixins/MBalanceThresholdFilterCore.sol | 50 ++----- .../mixins/MExchangeCalldata.sol | 57 ++++++++ contracts/libs/contracts/libs/LibAddressArray.sol | 75 ++++++++++ .../libs/contracts/libs/LibExchangeSelectors.sol | 151 +++++++++++++++++++++ 8 files changed, 481 insertions(+), 74 deletions(-) create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol create mode 100644 contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol create mode 100644 contracts/libs/contracts/libs/LibAddressArray.sol create mode 100644 contracts/libs/contracts/libs/LibExchangeSelectors.sol (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol index ea248793f..16cacd461 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol @@ -17,7 +17,6 @@ */ pragma solidity 0.4.24; -pragma experimental ABIEncoderV2; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "./interfaces/IThresholdAsset.sol"; diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 8d15fe6c8..a8947751a 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -62,8 +62,19 @@ contract MixinBalanceThresholdFilterCore is ) external { - // Validate addresses. - validateBalanceThresholdsOrRevert(signerAddress); + // Get accounts whose balances must be validated + address[] memory addressesToValidate = getAddressesToValidate(signerAddress); + + // Validate account balances + uint256 balanceThreshold = BALANCE_THRESHOLD; + IThresholdAsset thresholdAsset = THRESHOLD_ASSET; + for(uint256 i = 0; i < addressesToValidate.length; ++i) { + uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); + if (addressBalance < balanceThreshold) { + revert("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"); + } + } + emit ValidatedAddresses(addressesToValidate); // All addresses are valid. Execute fillOrder. EXCHANGE.executeTransaction( @@ -74,20 +85,15 @@ contract MixinBalanceThresholdFilterCore is ); } - - - /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` - /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold - /// then this function will revert. Which addresses are validated depends on - /// which Exchange function is to be called (defined by `signedExchangeTransaction` above). - /// No parameters are taken as this function reads arguments directly from calldata, to save gas. - /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all - /// of the addresses whose balance thresholds it checked. - function validateBalanceThresholdsOrRevert(address signerAddress) - internal + /// @dev Constructs an array of addresses to be validated. + /// Addresses depend on which Exchange function is to be called + /// (defined by `signedExchangeTransaction` above). + /// @param signerAddress Address of transaction signer. + /// @return addressesToValidate Array of addresses to validate. + function getAddressesToValidate(address signerAddress) + internal pure + returns (address[] memory addressesToValidate) { - // Extract addresses to validate from Exchange calldata - address[] memory addressesToValidate = new address[](0); bytes4 exchangeFunctionSelector = bytes4(exchangeCalldataload(0)); if( exchangeFunctionSelector == batchFillOrdersSelector || @@ -99,21 +105,21 @@ contract MixinBalanceThresholdFilterCore is exchangeFunctionSelector == marketSellOrdersNoThrowSelector ) { addressesToValidate = loadMakerAddressesFromOrderArray(0); - recordAddressToValidate(signerAddress, addressesToValidate); + addressesToValidate = addressesToValidate.append(signerAddress); } else if( exchangeFunctionSelector == fillOrderSelector || exchangeFunctionSelector == fillOrderNoThrowSelector || exchangeFunctionSelector == fillOrKillOrderSelector ) { address makerAddress = loadMakerAddressFromOrder(0); - recordAddressToValidate(makerAddress, addressesToValidate); - recordAddressToValidate(signerAddress, addressesToValidate); + addressesToValidate = addressesToValidate.append(makerAddress); + addressesToValidate = addressesToValidate.append(signerAddress); } else if(exchangeFunctionSelector == matchOrdersSelector) { - address leftOrderAddress = loadMakerAddressFromOrder(0); - recordAddressToValidate(leftOrderAddress, addressesToValidate); - address rightOrderAddress = loadMakerAddressFromOrder(1); - recordAddressToValidate(rightOrderAddress, addressesToValidate); - recordAddressToValidate(signerAddress, addressesToValidate); + address leftMakerAddress = loadMakerAddressFromOrder(0); + addressesToValidate = addressesToValidate.append(leftMakerAddress); + address rightMakerAddress = loadMakerAddressFromOrder(1); + addressesToValidate = addressesToValidate.append(rightMakerAddress); + addressesToValidate = addressesToValidate.append(signerAddress); } else if( exchangeFunctionSelector != cancelOrderSelector && exchangeFunctionSelector != batchCancelOrdersSelector && @@ -121,16 +127,5 @@ contract MixinBalanceThresholdFilterCore is ) { revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); } - - // Validate account balances - uint256 balanceThreshold = BALANCE_THRESHOLD; - IThresholdAsset thresholdAsset = THRESHOLD_ASSET; - for(uint i = 0; i < addressesToValidate.length; ++i) { - uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); - if (addressBalance < balanceThreshold) { - revert("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"); - } - } - emit ValidatedAddresses(addressesToValidate); } } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol new file mode 100644 index 000000000..12f601dea --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol @@ -0,0 +1,102 @@ + + /* + + 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/LICENSE2.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; + +import "./mixins/MExchangeCalldata.sol"; +import "@0x/contracts-libs/contracts/libs/LibAddressArray.sol"; + + +contract MixinExchangeCalldata is + MExchangeCalldata +{ + + using LibAddressArray for address[]; + + /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, + /// which is accessed through `signedExchangeTransaction`. + /// @param offset Offset into the Exchange calldata. + /// @return value Corresponding 32 byte value stored at `offset`. + function exchangeCalldataload(uint256 offset) + internal pure + returns (bytes32 value) + { + assembly { + // Pointer to exchange transaction + // 0x04 for calldata selector + // 0x40 to access `signedExchangeTransaction`, which is the third parameter + let exchangeTxPtr := calldataload(0x44) + + // Offset into Exchange calldata + // We compute this by adding 0x24 to the `exchangeTxPtr` computed above. + // 0x04 for calldata selector + // 0x20 for length field of `signedExchangeTransaction` + let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) + value := calldataload(exchangeCalldataOffset) + } + } + + /// @dev Convenience function that skips the 4 byte selector when loading + /// from the embedded Exchange calldata. + /// @param offset Offset into the Exchange calldata (minus the 4 byte selector) + /// @return value Corresponding 32 byte value stored at `offset` + 4. + function loadExchangeData(uint256 offset) + internal pure + returns (bytes32 value) + { + value = exchangeCalldataload(offset + 4); + } + + /// @dev Extracts the maker address from an order stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`). + /// @param orderParamIndex Index of the order in the Exchange function's signature. + /// @return makerAddress The extracted maker address. + function loadMakerAddressFromOrder(uint256 orderParamIndex) + internal pure + returns (address makerAddress) + { + uint256 orderOffsetInBytes = orderParamIndex * 32; + uint256 orderPtr = uint256(loadExchangeData(orderOffsetInBytes)); + makerAddress = address(loadExchangeData(orderPtr)); + return makerAddress; + } + + /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records them in + /// the running list of addresses to validate. + /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature + /// @return makerAddresses The extracted maker addresses. + function loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex) + internal pure + returns (address[] makerAddresses) + { + uint256 orderArrayOffsetInBytes = orderArrayParamIndex * 32; + uint256 orderArrayPtr = uint256(loadExchangeData(orderArrayOffsetInBytes)); + uint256 orderArrayLength = uint256(loadExchangeData(orderArrayPtr)); + uint256 orderArrayLengthInBytes = orderArrayLength * 32; + uint256 orderArrayElementPtr = orderArrayPtr + 32; + uint256 orderArrayElementEndPtr = orderArrayElementPtr + orderArrayLengthInBytes; + for(uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 32) { + uint256 orderPtr = uint256(loadExchangeData(orderPtrOffset)); + address makerAddress = address(loadExchangeData(orderPtr + orderArrayElementPtr)); + makerAddresses = makerAddresses.append(makerAddress); + } + return makerAddresses; + } +} diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol new file mode 100644 index 000000000..37e607be1 --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol @@ -0,0 +1,56 @@ + +/* + + 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 IBalanceThresholdFilterCore { + + /// @dev Executes an Exchange transaction iff the maker and taker meet + /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR + /// the exchange function is a cancellation. + /// Supported Exchange functions: + /// - batchFillOrders + /// - batchFillOrdersNoThrow + /// - batchFillOrKillOrders + /// - fillOrder + /// - fillOrderNoThrow + /// - fillOrKillOrder + /// - marketBuyOrders + /// - marketBuyOrdersNoThrow + /// - marketSellOrders + /// - marketSellOrdersNoThrow + /// - matchOrders + /// - cancelOrder + /// - batchCancelOrders + /// - cancelOrdersUpTo + /// Trying to call any other exchange function will throw. + /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @param signerAddress Address of transaction signer. + /// @param signedExchangeTransaction AbiV2 encoded calldata. + /// @param signature Proof of signer transaction by signer. + function executeTransaction( + uint256 salt, + address signerAddress, + bytes signedExchangeTransaction, + bytes signature + ) + external; +} \ No newline at end of file diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol index 6aaa729fb..b8b67e6ee 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol @@ -20,9 +20,12 @@ pragma solidity 0.4.24; import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; import "../interfaces/IThresholdAsset.sol"; +import "../interfaces/IBalanceThresholdFilterCore.sol"; -contract MBalanceThresholdFilterCore { +contract MBalanceThresholdFilterCore is + IBalanceThresholdFilterCore +{ // Points to 0x exchange contract // solhint-disable var-name-mixedcase @@ -40,43 +43,12 @@ contract MBalanceThresholdFilterCore { address[] addresses ); - /// @dev Executes an Exchange transaction iff the maker and taker meet - /// the hold at least `BALANCE_THRESHOLD` of the asset `THRESHOLD_ASSET` OR - /// the exchange function is a cancellation. - /// Supported Exchange functions: - /// - batchFillOrders - /// - batchFillOrdersNoThrow - /// - batchFillOrKillOrders - /// - fillOrder - /// - fillOrderNoThrow - /// - fillOrKillOrder - /// - marketBuyOrders - /// - marketBuyOrdersNoThrow - /// - marketSellOrders - /// - marketSellOrdersNoThrow - /// - matchOrders - /// - cancelOrder - /// - batchCancelOrders - /// - cancelOrdersUpTo - /// Trying to call any other exchange function will throw. - /// @param salt Arbitrary number to ensure uniqueness of transaction hash. + /// @dev Constructs an array of addresses to be validated. + /// Addresses depend on which Exchange function is to be called + /// (defined by `signedExchangeTransaction` above). /// @param signerAddress Address of transaction signer. - /// @param signedExchangeTransaction AbiV2 encoded calldata. - /// @param signature Proof of signer transaction by signer. - function executeTransaction( - uint256 salt, - address signerAddress, - bytes signedExchangeTransaction, - bytes signature - ) - external; - - /// @dev Validates addresses meet the balance threshold specified by `BALANCE_THRESHOLD` - /// for the asset `THRESHOLD_ASSET`. If one address does not meet the thresold - /// then this function will revert. Which addresses are validated depends on - /// which Exchange function is to be called (defined by `signedExchangeTransaction` above). - /// No parameters are taken as this function reads arguments directly from calldata, to save gas. - /// If all addresses are valid then this function emits a ValidatedAddresses event, listing all - /// of the addresses whose balance thresholds it checked. - function validateBalanceThresholdsOrRevert(address signerAddress) internal; + /// @return addressesToValidate Array of addresses to validate. + function getAddressesToValidate(address signerAddress) + internal pure + returns (address[] memory addressesToValidate); } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol new file mode 100644 index 000000000..8e0414c17 --- /dev/null +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol @@ -0,0 +1,57 @@ + + /* + + 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/LICENSE2.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 MExchangeCalldata { + + /// @dev Emulates the `calldataload` opcode on the embedded Exchange calldata, + /// which is accessed through `signedExchangeTransaction`. + /// @param offset Offset into the Exchange calldata. + /// @return value Corresponding 32 byte value stored at `offset`. + function exchangeCalldataload(uint256 offset) + internal pure + returns (bytes32 value); + + /// @dev Convenience function that skips the 4 byte selector when loading + /// from the embedded Exchange calldata. + /// @param offset Offset into the Exchange calldata (minus the 4 byte selector) + /// @return value Corresponding 32 byte value stored at `offset` + 4. + function loadExchangeData(uint256 offset) + internal pure + returns (bytes32 value); + + /// @dev Extracts the maker address from an order stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`). + /// @param orderParamIndex Index of the order in the Exchange function's signature. + /// @return makerAddress The extracted maker address. + function loadMakerAddressFromOrder(uint256 orderParamIndex) + internal pure + returns (address makerAddress); + + /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata + /// (which is embedded in `signedExchangeTransaction`), and records them in + /// the running list of addresses to validate. + /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature + /// @return makerAddresses The extracted maker addresses. + function loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex) + internal pure + returns (address[] makerAddresses); +} diff --git a/contracts/libs/contracts/libs/LibAddressArray.sol b/contracts/libs/contracts/libs/LibAddressArray.sol new file mode 100644 index 000000000..db76ddedb --- /dev/null +++ b/contracts/libs/contracts/libs/LibAddressArray.sol @@ -0,0 +1,75 @@ +/* + + 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; + +import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; + + +library LibAddressArray { + + /// @dev Append a new address to an array of addresses. + /// The `addressArray` may need to be reallocated to make space + /// for the new address. Because of this we return the resulting + /// memory location of `addressArray`. + /// @param addressToAppend Address to append. + /// @return Array of addresses: [... addressArray, addressToAppend] + function append(address[] memory addressArray, address addressToAppend) + internal pure + returns (address[]) + { + // Get stats on address array and free memory + uint256 freeMemPtr = 0; + uint256 addressArrayBeginPtr = 0; + uint256 addressArrayEndPtr = 0; + uint256 addressArrayLength = addressArray.length; + uint256 addressArrayMemSizeInBytes = 32 + (32 * addressArrayLength); + assembly { + freeMemPtr := mload(0x40) + addressArrayBeginPtr := addressArray + addressArrayEndPtr := add(addressArray, addressArrayMemSizeInBytes) + } + + // If free memory begins at the end of `addressArray` + // then we can append `addressToAppend` directly. + // Otherwise, we must copy the array to free memory + // before appending new values to it. + if (freeMemPtr != addressArrayEndPtr) { + LibBytes.memCopy(freeMemPtr, addressArrayBeginPtr, addressArrayMemSizeInBytes); + assembly { + addressArray := freeMemPtr + addressArrayBeginPtr := addressArray + } + } + + // Append `addressToAppend` + addressArrayLength += 1; + addressArrayMemSizeInBytes += 32; + addressArrayEndPtr = addressArrayBeginPtr + addressArrayMemSizeInBytes; + freeMemPtr = addressArrayEndPtr; + assembly { + // Store new array length + mstore(addressArray, addressArrayLength) + + // Update `freeMemPtr` + mstore(0x40, freeMemPtr) + } + addressArray[addressArrayLength - 1] = addressToAppend; + return addressArray; + } +} diff --git a/contracts/libs/contracts/libs/LibExchangeSelectors.sol b/contracts/libs/contracts/libs/LibExchangeSelectors.sol new file mode 100644 index 000000000..6a5344340 --- /dev/null +++ b/contracts/libs/contracts/libs/LibExchangeSelectors.sol @@ -0,0 +1,151 @@ +/* + + 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 LibExchangeSelectors { + + // allowedValidators + bytes4 constant allowedValidatorsSelector = 0x7b8e3514; + bytes4 constant allowedValidatorsSelectorGenerator = bytes4(keccak256('allowedValidators(address,address)')); + + // assetProxies + bytes4 constant assetProxiesSelector = 0x3fd3c997; + bytes4 constant assetProxiesSelectorGenerator = bytes4(keccak256('assetProxies(bytes4)')); + + // batchCancelOrders + bytes4 constant batchCancelOrdersSelector = 0x4ac14782; + bytes4 constant batchCancelOrdersSelectorGenerator = bytes4(keccak256('batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); + + // batchFillOrKillOrders + bytes4 constant batchFillOrKillOrdersSelector = 0x4d0ae546; + bytes4 constant batchFillOrKillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // batchFillOrders + bytes4 constant batchFillOrdersSelector = 0x297bb70b; + bytes4 constant batchFillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // batchFillOrdersNoThrow + bytes4 constant batchFillOrdersNoThrowSelector = 0x50dde190; + bytes4 constant batchFillOrdersNoThrowSelectorGenerator = bytes4(keccak256('batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + + // cancelOrder + bytes4 constant cancelOrderSelector = 0xd46b02c3; + bytes4 constant cancelOrderSelectorGenerator = bytes4(keccak256('cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); + + // cancelOrdersUpTo + bytes4 constant cancelOrdersUpToSelector = 0x4f9559b1; + bytes4 constant cancelOrdersUpToSelectorGenerator = bytes4(keccak256('cancelOrdersUpTo(uint256)')); + + // cancelled + bytes4 constant cancelledSelector = 0x2ac12622; + bytes4 constant cancelledSelectorGenerator = bytes4(keccak256('cancelled(bytes32)')); + + // currentContextAddress + bytes4 constant currentContextAddressSelector = 0xeea086ba; + bytes4 constant currentContextAddressSelectorGenerator = bytes4(keccak256('currentContextAddress()')); + + // executeTransaction + bytes4 constant executeTransactionSelector = 0xbfc8bfce; + bytes4 constant executeTransactionSelectorGenerator = bytes4(keccak256('executeTransaction(uint256,address,bytes,bytes)')); + + // fillOrKillOrder + bytes4 constant fillOrKillOrderSelector = 0x64a3bc15; + bytes4 constant fillOrKillOrderSelectorGenerator = bytes4(keccak256('fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // fillOrder + bytes4 constant fillOrderSelector = 0xb4be83d5; + bytes4 constant fillOrderSelectorGenerator = bytes4(keccak256('fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // fillOrderNoThrow + bytes4 constant fillOrderNoThrowSelector = 0x3e228bae; + bytes4 constant fillOrderNoThrowSelectorGenerator = bytes4(keccak256('fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + + // filled + bytes4 constant filledSelector = 0x288cdc91; + bytes4 constant filledSelectorGenerator = bytes4(keccak256('filled(bytes32)')); + + // getAssetProxy + bytes4 constant getAssetProxySelector = 0x60704108; + bytes4 constant getAssetProxySelectorGenerator = bytes4(keccak256('getAssetProxy(bytes4)')); + + // getOrderInfo + bytes4 constant getOrderInfoSelector = 0xc75e0a81; + bytes4 constant getOrderInfoSelectorGenerator = bytes4(keccak256('getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); + + // getOrdersInfo + bytes4 constant getOrdersInfoSelector = 0x7e9d74dc; + bytes4 constant getOrdersInfoSelectorGenerator = bytes4(keccak256('getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); + + // isValidSignature + bytes4 constant isValidSignatureSelector = 0x93634702; + bytes4 constant isValidSignatureSelectorGenerator = bytes4(keccak256('isValidSignature(bytes32,address,bytes)')); + + // marketBuyOrders + bytes4 constant marketBuyOrdersSelector = 0xe5fa431b; + bytes4 constant marketBuyOrdersSelectorGenerator = bytes4(keccak256('marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketBuyOrdersNoThrow + bytes4 constant marketBuyOrdersNoThrowSelector = 0xa3e20380; + bytes4 constant marketBuyOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketSellOrders + bytes4 constant marketSellOrdersSelector = 0x7e1d9808; + bytes4 constant marketSellOrdersSelectorGenerator = bytes4(keccak256('marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // marketSellOrdersNoThrow + bytes4 constant marketSellOrdersNoThrowSelector = 0xdd1c7d18; + bytes4 constant marketSellOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); + + // matchOrders + bytes4 constant matchOrdersSelector = 0x3c28d861; + bytes4 constant matchOrdersSelectorGenerator = bytes4(keccak256('matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)')); + + // orderEpoch + bytes4 constant orderEpochSelector = 0xd9bfa73e; + bytes4 constant orderEpochSelectorGenerator = bytes4(keccak256('orderEpoch(address,address)')); + + // owner + bytes4 constant ownerSelector = 0x8da5cb5b; + bytes4 constant ownerSelectorGenerator = bytes4(keccak256('owner()')); + + // preSign + bytes4 constant preSignSelector = 0x3683ef8e; + bytes4 constant preSignSelectorGenerator = bytes4(keccak256('preSign(bytes32,address,bytes)')); + + // preSigned + bytes4 constant preSignedSelector = 0x82c174d0; + bytes4 constant preSignedSelectorGenerator = bytes4(keccak256('preSigned(bytes32,address)')); + + // registerAssetProxy + bytes4 constant registerAssetProxySelector = 0xc585bb93; + bytes4 constant registerAssetProxySelectorGenerator = bytes4(keccak256('registerAssetProxy(address)')); + + // setSignatureValidatorApproval + bytes4 constant setSignatureValidatorApprovalSelector = 0x77fcce68; + bytes4 constant setSignatureValidatorApprovalSelectorGenerator = bytes4(keccak256('setSignatureValidatorApproval(address,bool)')); + + // transactions + bytes4 constant transactionsSelector = 0x642f2eaf; + bytes4 constant transactionsSelectorGenerator = bytes4(keccak256('transactions(bytes32)')); + + // transferOwnership + bytes4 constant transferOwnershipSelector = 0xf2fde38b; + bytes4 constant transferOwnershipSelectorGenerator = bytes4(keccak256('transferOwnership(address)')); +} \ No newline at end of file -- cgit v1.2.3 From b524ac7af2ebf0f00c50ae3c1ad7cd85ab8b6462 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 15:58:58 -0800 Subject: Prettier / Linter on contracts + TS --- .../MixinBalanceThresholdFilterCore.sol | 14 +-- .../MixinExchangeCalldata.sol | 5 +- .../interfaces/IBalanceThresholdFilterCore.sol | 3 +- .../mixins/MExchangeCalldata.sol | 3 +- .../test/extensions/balance_threshold_filter.ts | 109 ++++++--------------- 5 files changed, 44 insertions(+), 90 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index a8947751a..27767ad98 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -68,7 +68,7 @@ contract MixinBalanceThresholdFilterCore is // Validate account balances uint256 balanceThreshold = BALANCE_THRESHOLD; IThresholdAsset thresholdAsset = THRESHOLD_ASSET; - for(uint256 i = 0; i < addressesToValidate.length; ++i) { + for (uint256 i = 0; i < addressesToValidate.length; ++i) { uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); if (addressBalance < balanceThreshold) { revert("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"); @@ -95,8 +95,9 @@ contract MixinBalanceThresholdFilterCore is returns (address[] memory addressesToValidate) { bytes4 exchangeFunctionSelector = bytes4(exchangeCalldataload(0)); - if( - exchangeFunctionSelector == batchFillOrdersSelector || + // solhint-disable expression-indent + if ( + exchangeFunctionSelector == batchFillOrdersSelector || exchangeFunctionSelector == batchFillOrdersNoThrowSelector || exchangeFunctionSelector == batchFillOrKillOrdersSelector || exchangeFunctionSelector == marketBuyOrdersSelector || @@ -106,7 +107,7 @@ contract MixinBalanceThresholdFilterCore is ) { addressesToValidate = loadMakerAddressesFromOrderArray(0); addressesToValidate = addressesToValidate.append(signerAddress); - } else if( + } else if ( exchangeFunctionSelector == fillOrderSelector || exchangeFunctionSelector == fillOrderNoThrowSelector || exchangeFunctionSelector == fillOrKillOrderSelector @@ -114,18 +115,19 @@ contract MixinBalanceThresholdFilterCore is address makerAddress = loadMakerAddressFromOrder(0); addressesToValidate = addressesToValidate.append(makerAddress); addressesToValidate = addressesToValidate.append(signerAddress); - } else if(exchangeFunctionSelector == matchOrdersSelector) { + } else if (exchangeFunctionSelector == matchOrdersSelector) { address leftMakerAddress = loadMakerAddressFromOrder(0); addressesToValidate = addressesToValidate.append(leftMakerAddress); address rightMakerAddress = loadMakerAddressFromOrder(1); addressesToValidate = addressesToValidate.append(rightMakerAddress); addressesToValidate = addressesToValidate.append(signerAddress); - } else if( + } else if ( exchangeFunctionSelector != cancelOrderSelector && exchangeFunctionSelector != batchCancelOrdersSelector && exchangeFunctionSelector != cancelOrdersUpToSelector ) { revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); } + // solhint-enable expression-indent } } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol index 12f601dea..d49b7123f 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol @@ -78,8 +78,7 @@ contract MixinExchangeCalldata is } /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records them in - /// the running list of addresses to validate. + /// (which is embedded in `signedExchangeTransaction`). /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature /// @return makerAddresses The extracted maker addresses. function loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex) @@ -92,7 +91,7 @@ contract MixinExchangeCalldata is uint256 orderArrayLengthInBytes = orderArrayLength * 32; uint256 orderArrayElementPtr = orderArrayPtr + 32; uint256 orderArrayElementEndPtr = orderArrayElementPtr + orderArrayLengthInBytes; - for(uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 32) { + for (uint orderPtrOffset = orderArrayElementPtr; orderPtrOffset < orderArrayElementEndPtr; orderPtrOffset += 32) { uint256 orderPtr = uint256(loadExchangeData(orderPtrOffset)); address makerAddress = address(loadExchangeData(orderPtr + orderArrayElementPtr)); makerAddresses = makerAddresses.append(makerAddress); diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol index 37e607be1..3d8e2bbd1 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/interfaces/IBalanceThresholdFilterCore.sol @@ -20,7 +20,6 @@ pragma solidity 0.4.24; - contract IBalanceThresholdFilterCore { /// @dev Executes an Exchange transaction iff the maker and taker meet @@ -53,4 +52,4 @@ contract IBalanceThresholdFilterCore { bytes signature ) external; -} \ No newline at end of file +} diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol index 8e0414c17..bf2940fe1 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/mixins/MExchangeCalldata.sol @@ -47,8 +47,7 @@ contract MExchangeCalldata { returns (address makerAddress); /// @dev Extracts the maker addresses from an array of orders stored in the Exchange calldata - /// (which is embedded in `signedExchangeTransaction`), and records them in - /// the running list of addresses to validate. + /// (which is embedded in `signedExchangeTransaction`). /// @param orderArrayParamIndex Index of the order array in the Exchange function's signature /// @return makerAddresses The extracted maker addresses. function loadMakerAddressesFromOrderArray(uint256 orderArrayParamIndex) diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index 6350c019f..20397b14f 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -5,7 +5,6 @@ import { BigNumber } from '@0x/utils'; import { Web3Wrapper } from '@0x/web3-wrapper'; import * as chai from 'chai'; import { TransactionReceiptWithDecodedLogs } from 'ethereum-types'; -import * as ethUtil from 'ethereumjs-util'; import * as _ from 'lodash'; import { @@ -21,12 +20,9 @@ import { ContractName, ERC20BalancesByOwner, expectTransactionFailedAsync, - expectTransactionFailedWithoutReasonAsync, OrderFactory, OrderStatus, - orderUtils, provider, - SignedTransaction, TransactionFactory, txDefaults, web3Wrapper, @@ -77,7 +73,6 @@ describe(ContractName.BalanceThresholdFilter, () => { let defaultOrderParams: Partial<Order>; let validSignedOrder: SignedOrder; let validSignedOrder2: SignedOrder; - let validSignedFillOrderTx: SignedTransaction; let erc721BalanceThresholdFilterInstance: BalanceThresholdFilterContract; let erc20BalanceThresholdFilterInstance: BalanceThresholdFilterContract; @@ -257,10 +252,7 @@ describe(ContractName.BalanceThresholdFilter, () => { { takerAssetFillAmount }, ); // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validTakerAddress, - ]; + const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -308,7 +300,7 @@ describe(ContractName.BalanceThresholdFilter, () => { badSelectorHex, signatureHex, ), - RevertReason.InvalidOrBlockedExchangeSelector + RevertReason.InvalidOrBlockedExchangeSelector, ); }); it('should revert if senderAddress is not set to the valid forwarding contract', async () => { @@ -319,12 +311,10 @@ describe(ContractName.BalanceThresholdFilter, () => { }); // Call valid forwarder return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.fillOrderAsync( - signedOrderWithBadSenderAddress, - validTakerAddress, - { takerAssetFillAmount }, - ), - RevertReason.FailedExecution + erc721TakerBalanceThresholdWrapper.fillOrderAsync(signedOrderWithBadSenderAddress, validTakerAddress, { + takerAssetFillAmount, + }), + RevertReason.FailedExecution, ); }); }); @@ -339,11 +329,9 @@ describe(ContractName.BalanceThresholdFilter, () => { // Execute a valid fill const orders = [validSignedOrder, validSignedOrder2]; const takerAssetFillAmounts = [takerAssetFillAmount, takerAssetFillAmount]; - const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync( - orders, - validTakerAddress, - { takerAssetFillAmounts }, - ); + const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersAsync(orders, validTakerAddress, { + takerAssetFillAmounts, + }); // Assert validated addresses const expectedValidatedAddresseses = [ validSignedOrder.makerAddress, @@ -647,10 +635,7 @@ describe(ContractName.BalanceThresholdFilter, () => { { takerAssetFillAmount }, ); // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validTakerAddress, - ]; + const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -693,11 +678,9 @@ describe(ContractName.BalanceThresholdFilter, () => { }); // Execute transaction return expectTransactionFailedAsync( - erc721TakerBalanceThresholdWrapper.fillOrderAsync( - signedOrderWithBadMakerAddress, - validTakerAddress, - { takerAssetFillAmount }, - ), + erc721TakerBalanceThresholdWrapper.fillOrderAsync(signedOrderWithBadMakerAddress, validTakerAddress, { + takerAssetFillAmount, + }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); @@ -724,10 +707,7 @@ describe(ContractName.BalanceThresholdFilter, () => { { takerAssetFillAmount }, ); // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validTakerAddress, - ]; + const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -780,11 +760,9 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonValidBalanceThresholdWrapper.fillOrderNoThrowAsync( - validSignedOrder, - invalidAddress, - { takerAssetFillAmount }, - ), + erc721NonValidBalanceThresholdWrapper.fillOrderNoThrowAsync(validSignedOrder, invalidAddress, { + takerAssetFillAmount, + }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); @@ -804,10 +782,7 @@ describe(ContractName.BalanceThresholdFilter, () => { { takerAssetFillAmount: takerAssetFillAmount_ }, ); // Assert validated addresses - const expectedValidatedAddresseses = [ - validSignedOrder.makerAddress, - validTakerAddress, - ]; + const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; await assertValidatedAddressesLog(txReceipt, expectedValidatedAddresseses); // Check balances const newBalances = await erc20Wrapper.getBalancesAsync(); @@ -860,11 +835,9 @@ describe(ContractName.BalanceThresholdFilter, () => { }); it('should revert if taker does not meet the balance threshold', async () => { return expectTransactionFailedAsync( - erc721NonValidBalanceThresholdWrapper.fillOrKillOrderAsync( - validSignedOrder, - invalidAddress, - { takerAssetFillAmount }, - ), + erc721NonValidBalanceThresholdWrapper.fillOrKillOrderAsync(validSignedOrder, invalidAddress, { + takerAssetFillAmount, + }), RevertReason.AtLeastOneAddressDoesNotMeetBalanceThreshold, ); }); @@ -916,14 +889,10 @@ describe(ContractName.BalanceThresholdFilter, () => { const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( - validSignedOrder.makerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), ); expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( - validSignedOrder.takerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(validSignedOrder.takerAssetAmount), ); expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), @@ -1018,14 +987,10 @@ describe(ContractName.BalanceThresholdFilter, () => { const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); // Maker #1 expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( - validSignedOrder.makerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), ); expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( - validSignedOrder.takerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(validSignedOrder.takerAssetAmount), ); expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), @@ -1097,11 +1062,9 @@ describe(ContractName.BalanceThresholdFilter, () => { .times(validSignedOrder.makerAssetAmount) .dividedToIntegerBy(validSignedOrder.takerAssetAmount); const cumulativeMakerAssetFillAmount = validSignedOrder.makerAssetAmount.plus(makerAssetFillAmount2); - const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync( - orders, - validTakerAddress, - { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, - ); + const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersAsync(orders, validTakerAddress, { + makerAssetFillAmount: cumulativeMakerAssetFillAmount, + }); // Assert validated addresses const expectedValidatedAddresseses = [ validSignedOrder.makerAddress, @@ -1120,14 +1083,10 @@ describe(ContractName.BalanceThresholdFilter, () => { const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( - validSignedOrder.makerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), ); expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( - validSignedOrder.takerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(validSignedOrder.takerAssetAmount), ); expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), @@ -1224,14 +1183,10 @@ describe(ContractName.BalanceThresholdFilter, () => { const takerFeePaid = validSignedOrder.takerFee.plus(takerFeePaid2); // Maker #1 expect(newBalances[validMakerAddress][defaultMakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus( - validSignedOrder.makerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultMakerAssetAddress].minus(validSignedOrder.makerAssetAmount), ); expect(newBalances[validMakerAddress][defaultTakerAssetAddress]).to.be.bignumber.equal( - erc20Balances[validMakerAddress][defaultTakerAssetAddress].add( - validSignedOrder.takerAssetAmount, - ), + erc20Balances[validMakerAddress][defaultTakerAssetAddress].add(validSignedOrder.takerAssetAmount), ); expect(newBalances[validMakerAddress][zrxToken.address]).to.be.bignumber.equal( erc20Balances[validMakerAddress][zrxToken.address].minus(validSignedOrder.makerFee), -- cgit v1.2.3 From bb4ce9b3ad5658b16027c53cb4bad07149dcff4e Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 16:02:13 -0800 Subject: Explicit returns --- .../BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol | 1 + .../contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol | 2 ++ 2 files changed, 3 insertions(+) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 27767ad98..3e292c035 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -129,5 +129,6 @@ contract MixinBalanceThresholdFilterCore is revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); } // solhint-enable expression-indent + return addressesToValidate; } } diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol index d49b7123f..bd26a468f 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinExchangeCalldata.sol @@ -50,6 +50,7 @@ contract MixinExchangeCalldata is let exchangeCalldataOffset := add(exchangeTxPtr, add(0x24, offset)) value := calldataload(exchangeCalldataOffset) } + return value; } /// @dev Convenience function that skips the 4 byte selector when loading @@ -61,6 +62,7 @@ contract MixinExchangeCalldata is returns (bytes32 value) { value = exchangeCalldataload(offset + 4); + return value; } /// @dev Extracts the maker address from an order stored in the Exchange calldata -- cgit v1.2.3 From 25722d81549382617df1c70e833748782ad3e3ac Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 16:10:40 -0800 Subject: Updated comment `Execute fillOrder` -> `Execute exchange function` --- .../BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 3e292c035..2917403bd 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -76,7 +76,7 @@ contract MixinBalanceThresholdFilterCore is } emit ValidatedAddresses(addressesToValidate); - // All addresses are valid. Execute fillOrder. + // All addresses are valid. Execute exchange function. EXCHANGE.executeTransaction( salt, signerAddress, -- cgit v1.2.3 From 1de92659ee5d3cad687919bd3054d24e31461bfa Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Thu, 13 Dec 2018 18:07:29 -0800 Subject: Added Changelog for new Extensions --- contracts/extensions/CHANGELOG.json | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'contracts') diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index 19ac770af..99bc0265e 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1544753227, + "version": "1.0.3", + "changes": [ + { + "note": "Added Balance Threshold Filter" + } + ] + }, { "timestamp": 1544741676, "version": "1.0.2", -- cgit v1.2.3 From 00f5b94d0a585ccf4b1df4c9bddb4c19eb86cfe2 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Fri, 14 Dec 2018 11:22:52 -0800 Subject: Added `gas` field so tests pass on Geth; --- .../test/extensions/balance_threshold_filter.ts | 36 ++++++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/balance_threshold_filter.ts b/contracts/extensions/test/extensions/balance_threshold_filter.ts index 20397b14f..07199d60b 100644 --- a/contracts/extensions/test/extensions/balance_threshold_filter.ts +++ b/contracts/extensions/test/extensions/balance_threshold_filter.ts @@ -428,7 +428,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const txReceipt = await erc721TakerBalanceThresholdWrapper.batchFillOrdersNoThrowAsync( orders, validTakerAddress, - { takerAssetFillAmounts }, + { + takerAssetFillAmounts, + // HACK(albrow): We need to hardcode the gas estimate here because + // the Geth gas estimator doesn't work with the way we use + // delegatecall and swallow errors. + gas: 600000, + }, ); // Assert validated addresses const expectedValidatedAddresseses = [ @@ -704,7 +710,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const txReceipt = await erc721TakerBalanceThresholdWrapper.fillOrderNoThrowAsync( validSignedOrder, validTakerAddress, - { takerAssetFillAmount }, + { + takerAssetFillAmount, + // HACK(albrow): We need to hardcode the gas estimate here because + // the Geth gas estimator doesn't work with the way we use + // delegatecall and swallow errors. + gas: 600000, + }, ); // Assert validated addresses const expectedValidatedAddresseses = [validSignedOrder.makerAddress, validTakerAddress]; @@ -963,7 +975,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const txReceipt = await erc721TakerBalanceThresholdWrapper.marketSellOrdersNoThrowAsync( orders, validTakerAddress, - { takerAssetFillAmount: cumulativeTakerAssetFillAmount }, + { + takerAssetFillAmount: cumulativeTakerAssetFillAmount, + // HACK(albrow): We need to hardcode the gas estimate here because + // the Geth gas estimator doesn't work with the way we use + // delegatecall and swallow errors. + gas: 600000, + }, ); // Assert validated addresses const expectedValidatedAddresseses = [ @@ -1163,7 +1181,13 @@ describe(ContractName.BalanceThresholdFilter, () => { const txReceipt = await erc721TakerBalanceThresholdWrapper.marketBuyOrdersNoThrowAsync( orders, validTakerAddress, - { makerAssetFillAmount: cumulativeMakerAssetFillAmount }, + { + makerAssetFillAmount: cumulativeMakerAssetFillAmount, + // HACK(albrow): We need to hardcode the gas estimate here because + // the Geth gas estimator doesn't work with the way we use + // delegatecall and swallow errors. + gas: 600000, + }, ); // Assert validated addresses const expectedValidatedAddresseses = [ @@ -1493,7 +1517,7 @@ describe(ContractName.BalanceThresholdFilter, () => { // Cancel const txReceipt = await erc721MakerBalanceThresholdWrapper.batchCancelOrdersAsync( validSignedOrders, - validSignedOrder.makerAddress, + validSignedOrders[0].makerAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; @@ -1560,7 +1584,7 @@ describe(ContractName.BalanceThresholdFilter, () => { const cancelOrdersUpToThisSalt = new BigNumber(1); const txReceipt = await erc721MakerBalanceThresholdWrapper.cancelOrdersUpToAsync( cancelOrdersUpToThisSalt, - validSignedOrder.makerAddress, + validSignedOrders[0].makerAddress, ); // Assert validated addresses const expectedValidatedAddresseses: string[] = []; -- cgit v1.2.3 From afe200c4e1bd76d5f2dbfcb7898ca025a7bb3dd6 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Fri, 14 Dec 2018 11:26:38 -0800 Subject: Updated changelogs for new contracts --- contracts/extensions/CHANGELOG.json | 6 +++--- contracts/protocol/CHANGELOG.json | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index 99bc0265e..da4d9c2ba 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -1,10 +1,10 @@ [ { - "timestamp": 1544753227, - "version": "1.0.3", + "version": "1.1.0", "changes": [ { - "note": "Added Balance Threshold Filter" + "note": "Added Balance Threshold Filter", + "pr": 1383 } ] }, diff --git a/contracts/protocol/CHANGELOG.json b/contracts/protocol/CHANGELOG.json index 5c3798a69..2dca9794a 100644 --- a/contracts/protocol/CHANGELOG.json +++ b/contracts/protocol/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "version": "2.2.0", + "changes": [ + { + "note": "Added LibAddressArray", + "pr": 1383 + } + ] + }, { "timestamp": 1544741676, "version": "2.1.59", -- cgit v1.2.3 From d2a4fd570622de34a3d8e8a25735b025e3ac5f77 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 18 Dec 2018 11:36:05 -0800 Subject: Added documentation to `LibAddressArray.append` and switched `if` to `require` smt --- .../MixinBalanceThresholdFilterCore.sol | 7 ++++--- contracts/libs/contracts/libs/LibAddressArray.sol | 11 ++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index 2917403bd..ab6989115 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -70,9 +70,10 @@ contract MixinBalanceThresholdFilterCore is IThresholdAsset thresholdAsset = THRESHOLD_ASSET; for (uint256 i = 0; i < addressesToValidate.length; ++i) { uint256 addressBalance = thresholdAsset.balanceOf(addressesToValidate[i]); - if (addressBalance < balanceThreshold) { - revert("AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD"); - } + require( + addressBalance >= balanceThreshold, + "AT_LEAST_ONE_ADDRESS_DOES_NOT_MEET_BALANCE_THRESHOLD" + ); } emit ValidatedAddresses(addressesToValidate); diff --git a/contracts/libs/contracts/libs/LibAddressArray.sol b/contracts/libs/contracts/libs/LibAddressArray.sol index db76ddedb..ccae2ac5f 100644 --- a/contracts/libs/contracts/libs/LibAddressArray.sol +++ b/contracts/libs/contracts/libs/LibAddressArray.sol @@ -45,11 +45,20 @@ library LibAddressArray { addressArrayEndPtr := add(addressArray, addressArrayMemSizeInBytes) } + // Cases for `freeMemPtr`: + // `freeMemPtr` == `addressArrayEndPtr`: Nothing occupies memory after `addressArray` + // `freeMemPtr` > `addressArrayEndPtr`: Some value occupies memory after `addressArray` + // `freeMemPtr` < `addressArrayEndPtr`: Memory has not been managed properly. + require( + freeMemPtr >= addressArrayEndPtr, + "INVALID_FREE_MEMORY_PTR" + ); + // If free memory begins at the end of `addressArray` // then we can append `addressToAppend` directly. // Otherwise, we must copy the array to free memory // before appending new values to it. - if (freeMemPtr != addressArrayEndPtr) { + if (freeMemPtr > addressArrayEndPtr) { LibBytes.memCopy(freeMemPtr, addressArrayBeginPtr, addressArrayMemSizeInBytes); assembly { addressArray := freeMemPtr -- cgit v1.2.3 From ca0ab385218d588aee10502e3d75ade30ad0b5d7 Mon Sep 17 00:00:00 2001 From: Greg Hysen <greg.hysen@gmail.com> Date: Tue, 18 Dec 2018 11:53:41 -0800 Subject: Fixed solhint errors --- .../MixinBalanceThresholdFilterCore.sol | 28 +-- .../libs/contracts/libs/LibExchangeSelectors.sol | 231 +++++++++++---------- 2 files changed, 130 insertions(+), 129 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol index ab6989115..df830f36e 100644 --- a/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol +++ b/contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -98,34 +98,34 @@ contract MixinBalanceThresholdFilterCore is bytes4 exchangeFunctionSelector = bytes4(exchangeCalldataload(0)); // solhint-disable expression-indent if ( - exchangeFunctionSelector == batchFillOrdersSelector || - exchangeFunctionSelector == batchFillOrdersNoThrowSelector || - exchangeFunctionSelector == batchFillOrKillOrdersSelector || - exchangeFunctionSelector == marketBuyOrdersSelector || - exchangeFunctionSelector == marketBuyOrdersNoThrowSelector || - exchangeFunctionSelector == marketSellOrdersSelector || - exchangeFunctionSelector == marketSellOrdersNoThrowSelector + exchangeFunctionSelector == BATCH_FILL_ORDERS_SELECTOR || + exchangeFunctionSelector == BATCH_FILL_ORDERS_NO_THROW_SELECTOR || + exchangeFunctionSelector == BATCH_FILL_OR_KILL_ORDERS_SELECTOR || + exchangeFunctionSelector == MARKET_BUY_ORDERS_SELECTOR || + exchangeFunctionSelector == MARKET_BUY_ORDERS_NO_THROW_SELECTOR || + exchangeFunctionSelector == MARKET_SELL_ORDERS_SELECTOR || + exchangeFunctionSelector == MARKET_SELL_ORDERS_NO_THROW_SELECTOR ) { addressesToValidate = loadMakerAddressesFromOrderArray(0); addressesToValidate = addressesToValidate.append(signerAddress); } else if ( - exchangeFunctionSelector == fillOrderSelector || - exchangeFunctionSelector == fillOrderNoThrowSelector || - exchangeFunctionSelector == fillOrKillOrderSelector + exchangeFunctionSelector == FILL_ORDER_SELECTOR || + exchangeFunctionSelector == FILL_ORDER_NO_THROW_SELECTOR || + exchangeFunctionSelector == FILL_OR_KILL_ORDER_SELECTOR ) { address makerAddress = loadMakerAddressFromOrder(0); addressesToValidate = addressesToValidate.append(makerAddress); addressesToValidate = addressesToValidate.append(signerAddress); - } else if (exchangeFunctionSelector == matchOrdersSelector) { + } else if (exchangeFunctionSelector == MATCH_ORDERS_SELECTOR) { address leftMakerAddress = loadMakerAddressFromOrder(0); addressesToValidate = addressesToValidate.append(leftMakerAddress); address rightMakerAddress = loadMakerAddressFromOrder(1); addressesToValidate = addressesToValidate.append(rightMakerAddress); addressesToValidate = addressesToValidate.append(signerAddress); } else if ( - exchangeFunctionSelector != cancelOrderSelector && - exchangeFunctionSelector != batchCancelOrdersSelector && - exchangeFunctionSelector != cancelOrdersUpToSelector + exchangeFunctionSelector != CANCEL_ORDER_SELECTOR && + exchangeFunctionSelector != BATCH_CANCEL_ORDERS_SELECTOR && + exchangeFunctionSelector != CANCEL_ORDERS_UP_TO_SELECTOR ) { revert("INVALID_OR_BLOCKED_EXCHANGE_SELECTOR"); } diff --git a/contracts/libs/contracts/libs/LibExchangeSelectors.sol b/contracts/libs/contracts/libs/LibExchangeSelectors.sol index 6a5344340..edb4f9cbd 100644 --- a/contracts/libs/contracts/libs/LibExchangeSelectors.sol +++ b/contracts/libs/contracts/libs/LibExchangeSelectors.sol @@ -21,131 +21,132 @@ pragma solidity 0.4.24; contract LibExchangeSelectors { - // allowedValidators - bytes4 constant allowedValidatorsSelector = 0x7b8e3514; - bytes4 constant allowedValidatorsSelectorGenerator = bytes4(keccak256('allowedValidators(address,address)')); + // solhint-disable max-line-length + // allowedValidators + bytes4 constant public ALLOWED_VALIDATORS_SELECTOR = 0x7b8e3514; + bytes4 constant public ALLOWED_VALIDATORS_SELECTOR_GENERATOR = bytes4(keccak256("allowedValidators(address,address)")); - // assetProxies - bytes4 constant assetProxiesSelector = 0x3fd3c997; - bytes4 constant assetProxiesSelectorGenerator = bytes4(keccak256('assetProxies(bytes4)')); + // assetProxies + bytes4 constant public ASSET_PROXIES_SELECTOR = 0x3fd3c997; + bytes4 constant public ASSET_PROXIES_SELECTOR_GENERATOR = bytes4(keccak256("assetProxies(bytes4)")); - // batchCancelOrders - bytes4 constant batchCancelOrdersSelector = 0x4ac14782; - bytes4 constant batchCancelOrdersSelectorGenerator = bytes4(keccak256('batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); + // batchCancelOrders + bytes4 constant public BATCH_CANCEL_ORDERS_SELECTOR = 0x4ac14782; + bytes4 constant public BATCH_CANCEL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchCancelOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])")); - // batchFillOrKillOrders - bytes4 constant batchFillOrKillOrdersSelector = 0x4d0ae546; - bytes4 constant batchFillOrKillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + // batchFillOrKillOrders + bytes4 constant public BATCH_FILL_OR_KILL_ORDERS_SELECTOR = 0x4d0ae546; + bytes4 constant public BATCH_FILL_OR_KILL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrKillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])")); - // batchFillOrders - bytes4 constant batchFillOrdersSelector = 0x297bb70b; - bytes4 constant batchFillOrdersSelectorGenerator = bytes4(keccak256('batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + // batchFillOrders + bytes4 constant public BATCH_FILL_ORDERS_SELECTOR = 0x297bb70b; + bytes4 constant public BATCH_FILL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])")); - // batchFillOrdersNoThrow - bytes4 constant batchFillOrdersNoThrowSelector = 0x50dde190; - bytes4 constant batchFillOrdersNoThrowSelectorGenerator = bytes4(keccak256('batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])')); + // batchFillOrdersNoThrow + bytes4 constant public BATCH_FILL_ORDERS_NO_THROW_SELECTOR = 0x50dde190; + bytes4 constant public BATCH_FILL_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("batchFillOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256[],bytes[])")); - // cancelOrder - bytes4 constant cancelOrderSelector = 0xd46b02c3; - bytes4 constant cancelOrderSelectorGenerator = bytes4(keccak256('cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); + // cancelOrder + bytes4 constant public CANCEL_ORDER_SELECTOR = 0xd46b02c3; + bytes4 constant public CANCEL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("cancelOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))")); + + // cancelOrdersUpTo + bytes4 constant public CANCEL_ORDERS_UP_TO_SELECTOR = 0x4f9559b1; + bytes4 constant public CANCEL_ORDERS_UP_TO_SELECTOR_GENERATOR = bytes4(keccak256("cancelOrdersUpTo(uint256)")); + + // cancelled + bytes4 constant public CANCELLED_SELECTOR = 0x2ac12622; + bytes4 constant public CANCELLED_SELECTOR_GENERATOR = bytes4(keccak256("cancelled(bytes32)")); + + // currentContextAddress + bytes4 constant public CURRENT_CONTEXT_ADDRESS_SELECTOR = 0xeea086ba; + bytes4 constant public CURRENT_CONTEXT_ADDRESS_SELECTOR_GENERATOR = bytes4(keccak256("currentContextAddress()")); + + // executeTransaction + bytes4 constant public EXECUTE_TRANSACTION_SELECTOR = 0xbfc8bfce; + bytes4 constant public EXECUTE_TRANSACTION_SELECTOR_GENERATOR = bytes4(keccak256("executeTransaction(uint256,address,bytes,bytes)")); + + // fillOrKillOrder + bytes4 constant public FILL_OR_KILL_ORDER_SELECTOR = 0x64a3bc15; + bytes4 constant public FILL_OR_KILL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)")); + + // fillOrder + bytes4 constant public FILL_ORDER_SELECTOR = 0xb4be83d5; + bytes4 constant public FILL_ORDER_SELECTOR_GENERATOR = bytes4(keccak256("fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)")); + + // fillOrderNoThrow + bytes4 constant public FILL_ORDER_NO_THROW_SELECTOR = 0x3e228bae; + bytes4 constant public FILL_ORDER_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)")); + + // filled + bytes4 constant public FILLED_SELECTOR = 0x288cdc91; + bytes4 constant public FILLED_SELECTOR_GENERATOR = bytes4(keccak256("filled(bytes32)")); + + // getAssetProxy + bytes4 constant public GET_ASSET_PROXY_SELECTOR = 0x60704108; + bytes4 constant public GET_ASSET_PROXY_SELECTOR_GENERATOR = bytes4(keccak256("getAssetProxy(bytes4)")); + + // getOrderInfo + bytes4 constant public GET_ORDER_INFO_SELECTOR = 0xc75e0a81; + bytes4 constant public GET_ORDER_INFO_SELECTOR_GENERATOR = bytes4(keccak256("getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))")); + + // getOrdersInfo + bytes4 constant public GET_ORDERS_INFO_SELECTOR = 0x7e9d74dc; + bytes4 constant public GET_ORDERS_INFO_SELECTOR_GENERATOR = bytes4(keccak256("getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])")); + + // isValidSignature + bytes4 constant public IS_VALID_SIGNATURE_SELECTOR = 0x93634702; + bytes4 constant public IS_VALID_SIGNATURE_SELECTOR_GENERATOR = bytes4(keccak256("isValidSignature(bytes32,address,bytes)")); + + // marketBuyOrders + bytes4 constant public MARKET_BUY_ORDERS_SELECTOR = 0xe5fa431b; + bytes4 constant public MARKET_BUY_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])")); + + // marketBuyOrdersNoThrow + bytes4 constant public MARKET_BUY_ORDERS_NO_THROW_SELECTOR = 0xa3e20380; + bytes4 constant public MARKET_BUY_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])")); + + // marketSellOrders + bytes4 constant public MARKET_SELL_ORDERS_SELECTOR = 0x7e1d9808; + bytes4 constant public MARKET_SELL_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])")); + + // marketSellOrdersNoThrow + bytes4 constant public MARKET_SELL_ORDERS_NO_THROW_SELECTOR = 0xdd1c7d18; + bytes4 constant public MARKET_SELL_ORDERS_NO_THROW_SELECTOR_GENERATOR = bytes4(keccak256("marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])")); + + // matchOrders + bytes4 constant public MATCH_ORDERS_SELECTOR = 0x3c28d861; + bytes4 constant public MATCH_ORDERS_SELECTOR_GENERATOR = bytes4(keccak256("matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)")); + + // orderEpoch + bytes4 constant public ORDER_EPOCH_SELECTOR = 0xd9bfa73e; + bytes4 constant public ORDER_EPOCH_SELECTOR_GENERATOR = bytes4(keccak256("orderEpoch(address,address)")); + + // owner + bytes4 constant public OWNER_SELECTOR = 0x8da5cb5b; + bytes4 constant public OWNER_SELECTOR_GENERATOR = bytes4(keccak256("owner()")); - // cancelOrdersUpTo - bytes4 constant cancelOrdersUpToSelector = 0x4f9559b1; - bytes4 constant cancelOrdersUpToSelectorGenerator = bytes4(keccak256('cancelOrdersUpTo(uint256)')); + // preSign + bytes4 constant public PRE_SIGN_SELECTOR = 0x3683ef8e; + bytes4 constant public PRE_SIGN_SELECTOR_GENERATOR = bytes4(keccak256("preSign(bytes32,address,bytes)")); - // cancelled - bytes4 constant cancelledSelector = 0x2ac12622; - bytes4 constant cancelledSelectorGenerator = bytes4(keccak256('cancelled(bytes32)')); + // preSigned + bytes4 constant public PRE_SIGNED_SELECTOR = 0x82c174d0; + bytes4 constant public PRE_SIGNED_SELECTOR_GENERATOR = bytes4(keccak256("preSigned(bytes32,address)")); - // currentContextAddress - bytes4 constant currentContextAddressSelector = 0xeea086ba; - bytes4 constant currentContextAddressSelectorGenerator = bytes4(keccak256('currentContextAddress()')); + // registerAssetProxy + bytes4 constant public REGISTER_ASSET_PROXY_SELECTOR = 0xc585bb93; + bytes4 constant public REGISTER_ASSET_PROXY_SELECTOR_GENERATOR = bytes4(keccak256("registerAssetProxy(address)")); - // executeTransaction - bytes4 constant executeTransactionSelector = 0xbfc8bfce; - bytes4 constant executeTransactionSelectorGenerator = bytes4(keccak256('executeTransaction(uint256,address,bytes,bytes)')); + // setSignatureValidatorApproval + bytes4 constant public SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR = 0x77fcce68; + bytes4 constant public SET_SIGNATURE_VALIDATOR_APPROVAL_SELECTOR_GENERATOR = bytes4(keccak256("setSignatureValidatorApproval(address,bool)")); - // fillOrKillOrder - bytes4 constant fillOrKillOrderSelector = 0x64a3bc15; - bytes4 constant fillOrKillOrderSelectorGenerator = bytes4(keccak256('fillOrKillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); + // transactions + bytes4 constant public TRANSACTIONS_SELECTOR = 0x642f2eaf; + bytes4 constant public TRANSACTIONS_SELECTOR_GENERATOR = bytes4(keccak256("transactions(bytes32)")); - // fillOrder - bytes4 constant fillOrderSelector = 0xb4be83d5; - bytes4 constant fillOrderSelectorGenerator = bytes4(keccak256('fillOrder((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); - - // fillOrderNoThrow - bytes4 constant fillOrderNoThrowSelector = 0x3e228bae; - bytes4 constant fillOrderNoThrowSelectorGenerator = bytes4(keccak256('fillOrderNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),uint256,bytes)')); - - // filled - bytes4 constant filledSelector = 0x288cdc91; - bytes4 constant filledSelectorGenerator = bytes4(keccak256('filled(bytes32)')); - - // getAssetProxy - bytes4 constant getAssetProxySelector = 0x60704108; - bytes4 constant getAssetProxySelectorGenerator = bytes4(keccak256('getAssetProxy(bytes4)')); - - // getOrderInfo - bytes4 constant getOrderInfoSelector = 0xc75e0a81; - bytes4 constant getOrderInfoSelectorGenerator = bytes4(keccak256('getOrderInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes))')); - - // getOrdersInfo - bytes4 constant getOrdersInfoSelector = 0x7e9d74dc; - bytes4 constant getOrdersInfoSelectorGenerator = bytes4(keccak256('getOrdersInfo((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[])')); - - // isValidSignature - bytes4 constant isValidSignatureSelector = 0x93634702; - bytes4 constant isValidSignatureSelectorGenerator = bytes4(keccak256('isValidSignature(bytes32,address,bytes)')); - - // marketBuyOrders - bytes4 constant marketBuyOrdersSelector = 0xe5fa431b; - bytes4 constant marketBuyOrdersSelectorGenerator = bytes4(keccak256('marketBuyOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketBuyOrdersNoThrow - bytes4 constant marketBuyOrdersNoThrowSelector = 0xa3e20380; - bytes4 constant marketBuyOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketBuyOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketSellOrders - bytes4 constant marketSellOrdersSelector = 0x7e1d9808; - bytes4 constant marketSellOrdersSelectorGenerator = bytes4(keccak256('marketSellOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // marketSellOrdersNoThrow - bytes4 constant marketSellOrdersNoThrowSelector = 0xdd1c7d18; - bytes4 constant marketSellOrdersNoThrowSelectorGenerator = bytes4(keccak256('marketSellOrdersNoThrow((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes)[],uint256,bytes[])')); - - // matchOrders - bytes4 constant matchOrdersSelector = 0x3c28d861; - bytes4 constant matchOrdersSelectorGenerator = bytes4(keccak256('matchOrders((address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes,bytes),bytes,bytes)')); - - // orderEpoch - bytes4 constant orderEpochSelector = 0xd9bfa73e; - bytes4 constant orderEpochSelectorGenerator = bytes4(keccak256('orderEpoch(address,address)')); - - // owner - bytes4 constant ownerSelector = 0x8da5cb5b; - bytes4 constant ownerSelectorGenerator = bytes4(keccak256('owner()')); - - // preSign - bytes4 constant preSignSelector = 0x3683ef8e; - bytes4 constant preSignSelectorGenerator = bytes4(keccak256('preSign(bytes32,address,bytes)')); - - // preSigned - bytes4 constant preSignedSelector = 0x82c174d0; - bytes4 constant preSignedSelectorGenerator = bytes4(keccak256('preSigned(bytes32,address)')); - - // registerAssetProxy - bytes4 constant registerAssetProxySelector = 0xc585bb93; - bytes4 constant registerAssetProxySelectorGenerator = bytes4(keccak256('registerAssetProxy(address)')); - - // setSignatureValidatorApproval - bytes4 constant setSignatureValidatorApprovalSelector = 0x77fcce68; - bytes4 constant setSignatureValidatorApprovalSelectorGenerator = bytes4(keccak256('setSignatureValidatorApproval(address,bool)')); - - // transactions - bytes4 constant transactionsSelector = 0x642f2eaf; - bytes4 constant transactionsSelectorGenerator = bytes4(keccak256('transactions(bytes32)')); - - // transferOwnership - bytes4 constant transferOwnershipSelector = 0xf2fde38b; - bytes4 constant transferOwnershipSelectorGenerator = bytes4(keccak256('transferOwnership(address)')); + // transferOwnership + bytes4 constant public TRANSFER_OWNERSHIP_SELECTOR = 0xf2fde38b; + bytes4 constant public TRANSFER_OWNERSHIP_SELECTOR_GENERATOR = bytes4(keccak256("transferOwnership(address)")); } \ No newline at end of file -- cgit v1.2.3 From a650d695cede55c61aeb3a24dade828f6cc57844 Mon Sep 17 00:00:00 2001 From: Alex Browne <stephenalexbrowne@gmail.com> Date: Tue, 18 Dec 2018 16:26:10 -0800 Subject: Fix test-publish failure in contracts packages --- contracts/multisig/src/index.ts | 2 ++ contracts/protocol/src/index.ts | 1 - contracts/protocol/test/exchange/order_validator.ts | 5 ++--- 3 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 contracts/multisig/src/index.ts (limited to 'contracts') diff --git a/contracts/multisig/src/index.ts b/contracts/multisig/src/index.ts new file mode 100644 index 000000000..d55f08ea2 --- /dev/null +++ b/contracts/multisig/src/index.ts @@ -0,0 +1,2 @@ +export * from './artifacts'; +export * from './wrappers'; diff --git a/contracts/protocol/src/index.ts b/contracts/protocol/src/index.ts index ba813e7ca..d55f08ea2 100644 --- a/contracts/protocol/src/index.ts +++ b/contracts/protocol/src/index.ts @@ -1,3 +1,2 @@ export * from './artifacts'; export * from './wrappers'; -export * from '../test/utils'; diff --git a/contracts/protocol/test/exchange/order_validator.ts b/contracts/protocol/test/exchange/order_validator.ts index 8f53426db..39b173fdd 100644 --- a/contracts/protocol/test/exchange/order_validator.ts +++ b/contracts/protocol/test/exchange/order_validator.ts @@ -18,14 +18,13 @@ import * as _ from 'lodash'; import { artifacts, ERC20ProxyContract, - ERC20Wrapper, ERC721ProxyContract, - ERC721Wrapper, ExchangeContract, - ExchangeWrapper, OrderValidatorContract, } from '../../src'; +import { ERC20Wrapper, ERC721Wrapper, ExchangeWrapper } from '../utils'; + chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -- cgit v1.2.3 From 768e1d541c445dd84395afbd0cb04a1163e3f1c7 Mon Sep 17 00:00:00 2001 From: Alex Browne <stephenalexbrowne@gmail.com> Date: Tue, 18 Dec 2018 18:22:54 -0800 Subject: Make @0x/contracts-test-utils a dependency instead of a devDependency --- contracts/protocol/package.json | 2 +- contracts/protocol/src/index.ts | 1 + contracts/protocol/test/exchange/order_validator.ts | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'contracts') diff --git a/contracts/protocol/package.json b/contracts/protocol/package.json index 838189371..122ce07c3 100644 --- a/contracts/protocol/package.json +++ b/contracts/protocol/package.json @@ -44,7 +44,6 @@ "homepage": "https://github.com/0xProject/0x-monorepo/contracts/protocol/README.md", "devDependencies": { "@0x/abi-gen": "^1.0.19", - "@0x/contracts-test-utils": "^1.0.2", "@0x/dev-utils": "^1.0.21", "@0x/sol-compiler": "^1.1.16", "@0x/sol-cov": "^2.1.16", @@ -75,6 +74,7 @@ "@0x/contracts-interfaces": "^1.0.2", "@0x/contracts-libs": "^1.0.2", "@0x/contracts-multisig": "^1.0.2", + "@0x/contracts-test-utils": "^1.0.2", "@0x/contracts-tokens": "^1.0.2", "@0x/contracts-utils": "^1.0.2", "@0x/order-utils": "^3.0.7", diff --git a/contracts/protocol/src/index.ts b/contracts/protocol/src/index.ts index d55f08ea2..ba813e7ca 100644 --- a/contracts/protocol/src/index.ts +++ b/contracts/protocol/src/index.ts @@ -1,2 +1,3 @@ export * from './artifacts'; export * from './wrappers'; +export * from '../test/utils'; diff --git a/contracts/protocol/test/exchange/order_validator.ts b/contracts/protocol/test/exchange/order_validator.ts index 39b173fdd..8f53426db 100644 --- a/contracts/protocol/test/exchange/order_validator.ts +++ b/contracts/protocol/test/exchange/order_validator.ts @@ -18,13 +18,14 @@ import * as _ from 'lodash'; import { artifacts, ERC20ProxyContract, + ERC20Wrapper, ERC721ProxyContract, + ERC721Wrapper, ExchangeContract, + ExchangeWrapper, OrderValidatorContract, } from '../../src'; -import { ERC20Wrapper, ERC721Wrapper, ExchangeWrapper } from '../utils'; - chaiSetup.configure(); const expect = chai.expect; const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -- cgit v1.2.3 From b2edd84b0e935edeadd660a44df3dbceab49e8fe Mon Sep 17 00:00:00 2001 From: Leonid Logvinov <logvinov.leon@gmail.com> Date: Wed, 19 Dec 2018 13:29:25 +0100 Subject: Add missing contract libs to compiler.json config --- contracts/libs/compiler.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/libs/compiler.json b/contracts/libs/compiler.json index cf7c52a73..31ae13fef 100644 --- a/contracts/libs/compiler.json +++ b/contracts/libs/compiler.json @@ -18,5 +18,5 @@ } } }, - "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712"] + "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712", "LibAssetProxyErrors", "LibConstants"] } -- cgit v1.2.3 From abfb358250200f93323851bbb56120f7fdf15dc6 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov <logvinov.leon@gmail.com> Date: Wed, 19 Dec 2018 14:13:35 +0100 Subject: Add the sol-compiler watch command to all packages --- contracts/examples/package.json | 3 ++- contracts/extensions/package.json | 3 ++- contracts/interfaces/package.json | 3 ++- contracts/libs/package.json | 3 ++- contracts/multisig/package.json | 3 ++- contracts/protocol/package.json | 3 ++- contracts/tokens/package.json | 3 ++- contracts/utils/package.json | 3 ++- 8 files changed, 16 insertions(+), 8 deletions(-) (limited to 'contracts') diff --git a/contracts/examples/package.json b/contracts/examples/package.json index 77846241e..37b73f98c 100644 --- a/contracts/examples/package.json +++ b/contracts/examples/package.json @@ -13,7 +13,8 @@ "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s compile generate_contract_wrappers", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index 2d9ed4dcd..aa5cef462 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/interfaces/package.json b/contracts/interfaces/package.json index 4d3e4b7f9..15385a154 100644 --- a/contracts/interfaces/package.json +++ b/contracts/interfaces/package.json @@ -10,7 +10,8 @@ "build": "yarn pre_build && tsc -b", "build:ci": "yarn build", "pre_build": "run-s compile generate_contract_wrappers", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/libs/package.json b/contracts/libs/package.json index fa4b6e523..ce7b97502 100644 --- a/contracts/libs/package.json +++ b/contracts/libs/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/multisig/package.json b/contracts/multisig/package.json index b338f67f7..2d7b4aa05 100644 --- a/contracts/multisig/package.json +++ b/contracts/multisig/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../packages/abi-gen-templates/contract.handlebars --partials '../../packages/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/protocol/package.json b/contracts/protocol/package.json index 122ce07c3..d37a0302a 100644 --- a/contracts/protocol/package.json +++ b/contracts/protocol/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/tokens/package.json b/contracts/tokens/package.json index 6f8a366dd..79afc4820 100644 --- a/contracts/tokens/package.json +++ b/contracts/tokens/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", diff --git a/contracts/utils/package.json b/contracts/utils/package.json index a776bdfbb..cf94af0f6 100644 --- a/contracts/utils/package.json +++ b/contracts/utils/package.json @@ -19,7 +19,8 @@ "test:profiler": "SOLIDITY_PROFILER=true run-s build run_mocha profiler:report:html", "test:trace": "SOLIDITY_REVERT_TRACE=true run-s build run_mocha", "run_mocha": "mocha --require source-map-support/register --require make-promises-safe 'lib/test/**/*.js' --timeout 100000 --bail --exit", - "compile": "sol-compiler --contracts-dir contracts", + "compile": "sol-compiler", + "watch": "sol-compiler -w", "clean": "shx rm -rf lib generated-artifacts generated-wrappers", "generate_contract_wrappers": "abi-gen --abis ${npm_package_config_abis} --template ../../node_modules/@0x/abi-gen-templates/contract.handlebars --partials '../../node_modules/@0x/abi-gen-templates/partials/**/*.handlebars' --output generated-wrappers --backend ethers", "lint": "tslint --format stylish --project . --exclude ./generated-wrappers/**/* --exclude ./generated-artifacts/**/* --exclude **/lib/**/* && yarn lint-contracts", -- cgit v1.2.3 From f832e035da0b2b4b4e08cbe9028121a93a2ed0a9 Mon Sep 17 00:00:00 2001 From: Leonid Logvinov <logvinov.leon@gmail.com> Date: Wed, 19 Dec 2018 15:40:16 +0100 Subject: Run prettier --- contracts/libs/compiler.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/libs/compiler.json b/contracts/libs/compiler.json index 31ae13fef..349d3063b 100644 --- a/contracts/libs/compiler.json +++ b/contracts/libs/compiler.json @@ -18,5 +18,14 @@ } } }, - "contracts": ["TestLibs", "LibOrder", "LibMath", "LibFillResults", "LibAbiEncoder", "LibEIP712", "LibAssetProxyErrors", "LibConstants"] + "contracts": [ + "TestLibs", + "LibOrder", + "LibMath", + "LibFillResults", + "LibAbiEncoder", + "LibEIP712", + "LibAssetProxyErrors", + "LibConstants" + ] } -- cgit v1.2.3 From a7c3996627aaedfcd0ba37e7e15895480a0b065c Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Tue, 18 Dec 2018 12:08:11 -0800 Subject: Check if amount == 0 before doing division --- .../protocol/AssetProxy/MultiAssetProxy.sol | 5 +++- contracts/protocol/test/asset_proxy/proxies.ts | 32 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) (limited to 'contracts') diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 42231e73b..b42aa79ee 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -182,7 +182,10 @@ contract MultiAssetProxy is let totalAmount := mul(amountsElement, amount) // Revert if multiplication resulted in an overflow - if iszero(eq(div(totalAmount, amount), amountsElement)) { + if and( + gt(amount, 0), + iszero(eq(div(totalAmount, amount), amountsElement)) + ) { // Revert with `Error("UINT256_OVERFLOW")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) diff --git a/contracts/protocol/test/asset_proxy/proxies.ts b/contracts/protocol/test/asset_proxy/proxies.ts index c4bd95905..89c8b390c 100644 --- a/contracts/protocol/test/asset_proxy/proxies.ts +++ b/contracts/protocol/test/asset_proxy/proxies.ts @@ -12,6 +12,7 @@ import { import { artifacts as tokensArtifacts, DummyERC20TokenContract, + DummyERC20TokenTransferEventArgs, DummyERC721ReceiverContract, DummyERC721TokenContract, DummyMultipleReturnERC20TokenContract, @@ -22,6 +23,7 @@ import { assetDataUtils } from '@0x/order-utils'; import { RevertReason } from '@0x/types'; import { BigNumber } from '@0x/utils'; import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; @@ -738,6 +740,36 @@ describe('Asset Transfer Proxies', () => { erc20Balances[toAddress][erc20TokenA.address].add(totalAmount), ); }); + it('should dispatch an ERC20 transfer when input amount is 0', async () => { + const inputAmount = constants.ZERO_AMOUNT; + const erc20Amount = new BigNumber(10); + const erc20AssetData = assetDataUtils.encodeERC20AssetData(erc20TokenA.address); + const amounts = [erc20Amount]; + const nestedAssetData = [erc20AssetData]; + const assetData = assetDataInterface.MultiAsset.getABIEncodedTransactionData(amounts, nestedAssetData); + const data = assetProxyInterface.transferFrom.getABIEncodedTransactionData( + assetData, + fromAddress, + toAddress, + inputAmount, + ); + const erc20Balances = await erc20Wrapper.getBalancesAsync(); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokensArtifacts }); + const tx = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + to: multiAssetProxy.address, + data, + from: authorized, + }), + ); + expect(tx.logs.length).to.be.equal(1); + const log = tx.logs[0] as LogWithDecodedArgs<DummyERC20TokenTransferEventArgs>; + const transferEventName = 'Transfer'; + expect(log.event).to.equal(transferEventName); + expect(log.args._value).to.be.bignumber.equal(constants.ZERO_AMOUNT); + const newBalances = await erc20Wrapper.getBalancesAsync(); + expect(newBalances).to.deep.equal(erc20Balances); + }); it('should successfully transfer multiple of the same ERC20 token', async () => { const inputAmount = new BigNumber(1); const erc20Amount1 = new BigNumber(10); -- cgit v1.2.3 From 2a2260de45925bc309b6d6baf990d5ea6a171a90 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Tue, 18 Dec 2018 15:55:24 -0800 Subject: Use more efficient check for overflow --- .../protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'contracts') diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol index b42aa79ee..4285725d0 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -181,11 +181,11 @@ contract MultiAssetProxy is let amountsElement := calldataload(add(amountsContentsStart, i)) let totalAmount := mul(amountsElement, amount) - // Revert if multiplication resulted in an overflow - if and( - gt(amount, 0), - iszero(eq(div(totalAmount, amount), amountsElement)) - ) { + // Revert if `amount` != 0 and multiplication resulted in an overflow + if iszero(or( + iszero(amount), + eq(div(totalAmount, amount), amountsElement) + )) { // Revert with `Error("UINT256_OVERFLOW")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) -- cgit v1.2.3 From 04729c44b451bcf4818048621c890960bb7f8afb Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 19 Dec 2018 11:27:10 -0800 Subject: Add note about input validation --- contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol | 3 +++ 1 file changed, 3 insertions(+) (limited to 'contracts') diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 4285725d0..5bc32c214 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -33,6 +33,9 @@ contract MultiAssetProxy is function () external { + // NOTE: The below assembly assumes that clients do some input validation and that the input is properly encoded according to the AbiV2 specification. + // It is technically possible for inputs with very large lengths and offsets to cause overflows. However, this would make the calldata prohibitively expensive + // and we therefore do not check for overflows in these scenarios. assembly { // The first 4 bytes of calldata holds the function selector let selector := and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000) -- cgit v1.2.3 From 99e32869e602e9b01d74048af23594986aa639a1 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 19 Dec 2018 14:35:28 -0800 Subject: Use more efficient equality checks --- contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'contracts') diff --git a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol index 5bc32c214..377325384 100644 --- a/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol +++ b/contracts/protocol/contracts/protocol/AssetProxy/MultiAssetProxy.sol @@ -148,7 +148,7 @@ contract MultiAssetProxy is let nestedAssetDataLen := calldataload(sub(nestedAssetDataContentsStart, 32)) // Revert if number of elements in `amounts` differs from number of elements in `nestedAssetData` - if iszero(eq(amountsLen, nestedAssetDataLen)) { + if sub(amountsLen, nestedAssetDataLen) { // Revert with `Error("LENGTH_MISMATCH")` mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) @@ -236,7 +236,7 @@ contract MultiAssetProxy is // Only load `assetProxy` if `currentAssetProxyId` does not equal `assetProxyId` // We do not need to check if `currentAssetProxyId` is 0 since `assetProxy` is also initialized to 0 - if iszero(eq(currentAssetProxyId, assetProxyId)) { + if sub(currentAssetProxyId, assetProxyId) { // Update `assetProxyId` assetProxyId := currentAssetProxyId // To lookup a value in a mapping, we load from the storage location keccak256(k, p), -- cgit v1.2.3 From 8e5f0f9c5cffd44108b427585e571bec9eeb1b40 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Thu, 20 Dec 2018 09:11:13 -0800 Subject: Update CHANGELOG --- contracts/protocol/CHANGELOG.json | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contracts') diff --git a/contracts/protocol/CHANGELOG.json b/contracts/protocol/CHANGELOG.json index 2dca9794a..be374d892 100644 --- a/contracts/protocol/CHANGELOG.json +++ b/contracts/protocol/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Added LibAddressArray", "pr": 1383 + }, + { + "note": "Add validation and comments to MultiAssetProxy", + "pr": 1455 } ] }, -- cgit v1.2.3 From b786836db61de22fa290d94ac3f12fe5342aecc7 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Tue, 2 Oct 2018 16:22:35 -0700 Subject: feat: Add OrderMatcher contract that takes spread in multiple assets by calling `matchOrders` followed by `fillOrder` --- contracts/extensions/compiler.json | 2 +- .../contracts/OrderMatcher/MixinAssets.sol | 192 ++++++++++++ .../contracts/OrderMatcher/MixinMatchOrders.sol | 323 +++++++++++++++++++++ .../contracts/OrderMatcher/OrderMatcher.sol | 37 +++ .../contracts/OrderMatcher/interfaces/IAssets.sol | 43 +++ .../OrderMatcher/interfaces/IMatchOrders.sol | 43 +++ .../OrderMatcher/interfaces/IOrderMatcher.sol | 31 ++ .../contracts/OrderMatcher/libs/LibConstants.sol | 53 ++++ .../contracts/OrderMatcher/mixins/MAssets.sol | 71 +++++ contracts/protocol/src/artifacts/index.ts | 2 + 10 files changed, 796 insertions(+), 1 deletion(-) create mode 100644 contracts/extensions/contracts/OrderMatcher/MixinAssets.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol create mode 100644 contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol (limited to 'contracts') diff --git a/contracts/extensions/compiler.json b/contracts/extensions/compiler.json index e6ed0c215..1e21e6e6a 100644 --- a/contracts/extensions/compiler.json +++ b/contracts/extensions/compiler.json @@ -18,5 +18,5 @@ } } }, - "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder"] + "contracts": ["BalanceThresholdFilter", "DutchAuction", "Forwarder", "OrderMatcher"] } diff --git a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol new file mode 100644 index 000000000..a2bc95e4b --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol @@ -0,0 +1,192 @@ +/* + + 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; + +import "../../utils/LibBytes/LibBytes.sol"; +import "../../utils/Ownable/Ownable.sol"; +import "../../tokens/ERC20Token/IERC20Token.sol"; +import "../../tokens/ERC721Token/IERC721Token.sol"; +import "./mixins/MAssets.sol"; +import "./libs/LibConstants.sol"; + + +contract MixinAssets is + MAssets, + Ownable, + LibConstants +{ + using LibBytes for bytes; + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + transferAssetToSender(assetData, amount); + } + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external + onlyOwner + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + approveERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + approveERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal + { + bytes4 proxyId = assetData.readBytes4(0); + + if (proxyId == ERC20_DATA_ID) { + transferERC20Token(assetData, amount); + } else if (proxyId == ERC721_DATA_ID) { + transferERC721Token(assetData, amount); + } else { + revert("UNSUPPORTED_ASSET_PROXY"); + } + } + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + + // Transfer tokens. + // We do a raw call so we can check the success separate + // from the return data. + bool success = token.call(abi.encodeWithSelector( + ERC20_TRANSFER_SELECTOR, + msg.sender, + 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 Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + require( + amount == 1, + "INVALID_AMOUNT" + ); + // Decode asset data. + address token = assetData.readAddress(16); + uint256 tokenId = assetData.readUint256(36); + + // Perform transfer. + IERC721Token(token).transferFrom( + address(this), + msg.sender, + tokenId + ); + } + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + require( + IERC20Token(token).approve(ERC20_PROXY_ADDRESS, amount), + "APPROVAL_FAILED" + ); + } + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal + { + address token = assetData.readAddress(16); + bool approval = amount >= 1; + IERC721Token(token).setApprovalForAll(ERC721_PROXY_ADDRESS, approval); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol new file mode 100644 index 000000000..1c7cce675 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -0,0 +1,323 @@ +/* + + 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; + +import "../../utils/Ownable/Ownable.sol"; +import "./libs/LibConstants.sol"; + + +contract MixinMatchOrders is + Ownable, + LibConstants +{ + // The below assembly in the fallback function is functionaly equivalent to the following Solidity code: + /* + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + onlyOwner + { + // Match orders, maximally filling `leftOrder` + LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( + leftOrder, + rightOrder, + leftSignature, + rightSignature + ); + + // If a spread was taken, use the spread to fill remaining amount of `rightOrder` + // Only attempt to fill `rightOrder` if a spread was taken and if not already completely filled + uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; + if (leftMakerAssetSpreadAmount > 0 && matchedFillResults.right.takerAssetFilledAmount < rightOrder.takerAssetAmount) { + // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. + rightOrder.makerAssetData = leftOrder.takerAssetData; + rightOrder.takerAssetData = leftOrder.makerAssetData; + + // We do not need to pass in a signature since it was already validated in the `matchOrders` call + EXCHANGE.fillOrder( + rightOrder, + leftMakerAssetSpreadAmount, + "" + ); + } + } + */ + // solhint-disable-next-line payable-fallback + function () + external + { + assembly { + // The first 4 bytes of calldata holds the function selector + // `matchOrders` selector = 0x3c28d861 + if eq( + and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000), + 0x3c28d86100000000000000000000000000000000000000000000000000000000 + ) { + + // Load address of `owner` + let owner := sload(owner_slot) + + // Revert if `msg.sender` != `owner` + if iszero(eq(owner, caller)) { + // Revert with `Error("ONLY_CONTRACT_OWNER")` + mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) + mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) + mstore(64, 0x000000134f4e4c595f434f4e54524143545f4f574e4552000000000000000000) + mstore(96, 0) + revert(0, 100) + } + + // Load address of Exchange contract + let exchange := sload(EXCHANGE_slot) + + // Copy calldata to memory + // The calldata should be identical to the the ABIv2 encoded data required to call `EXCHANGE.matchOrders` + calldatacopy(0, 0, calldatasize()) + + // At this point, calldata and memory have the following layout: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 0 | 4 | `matchOrders` function selector | + // | | 4 * 32 | function parameters: | + // | 4 | | 1. offset to leftOrder (*) | + // | 36 | | 2. offset to rightOrder (*) | + // | 68 | | 3. offset to leftSignature (*) | + // | 100 | | 4. offset to rightSignature (*) | + // | | 12 * 32 | leftOrder: | + // | 132 | | 1. senderAddress | + // | 164 | | 2. makerAddress | + // | 196 | | 3. takerAddress | + // | 228 | | 4. feeRecipientAddress | + // | 260 | | 5. makerAssetAmount | + // | 292 | | 6. takerAssetAmount | + // | 324 | | 7. makerFeeAmount | + // | 356 | | 8. takerFeeAmount | + // | 388 | | 9. expirationTimeSeconds | + // | 420 | | 10. salt | + // | 452 | | 11. offset to leftMakerAssetData (*) | + // | 484 | | 12. offset to leftTakerAssetData (*) | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + // | 900 | 32 | leftMakerAssetData Length | + // | 932 | a | leftMakerAssetData Contents | + // | 932 + a | 32 | leftTakerAssetData Length | + // | 964 + a | b | leftTakerAssetData Contents | + // | 964 + a + b | 32 | rightMakerAssetData Length | + // | 996 + a + b | c | rightMakerAssetData Contents | + // | 996 + a + b + c | 32 | rightTakerAssetData Length | + // | 1028 + a + b + c | d | rightTakerAssetData Contents | + // | 1028 + a + b + c + d | 32 | leftSignature Length | + // | 1060 + a + b + c + d | e | leftSignature Contents | + // | 1060 + a + b + c + d + e | 32 | rightSignature Length | + // | 1092 + a + b + c + d + e | f | rightSignature Contents | + + // Call `EXCHANGE.matchOrders` + let matchOrdersSuccess := call( + gas, // forward all gas + exchange, // call address of Exchange contract + 0, // transfer 0 wei + 0, // input starts at 0 + calldatasize(), // length of input + 0, // write output over output + 288 // length of output is 288 bytes + ) + + if iszero(matchOrdersSuccess) { + // Revert with reason if `matchOrders` call was unsuccessful + revert(0, returndatasize()) + } + + // After calling `matchOrders`, the relevant parts of memory are: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | | 9 * 32 | matchedFillResults | + // | 0 | | 1. left.makerAssetFilledAmount | + // | 32 | | 2. left.takerAssetFilledAmount | + // | 64 | | 3. left.makerFeePaid | + // | 96 | | 4. left.takerFeePaid | + // | 128 | | 5. right.makerAssetFilledAmount | + // | 160 | | 6. right.takerAssetFilledAmount | + // | 192 | | 7. right.makerFeePaid | + // | 224 | | 8. right.takerFeePaid | + // | 256 | | 9. leftMakerAssetSpreadAmount | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + + let rightOrderStart := add(calldataload(36), 4) + + // Only call `fillOrder` if a spread was taken and `rightOrder` has not been completely filled + if and( + gt(mload(256), 0), // gt(leftMakerAssetSpreadAmount, 0) + lt(mload(160), calldataload(add(rightOrderStart, 160))) // lt(rightOrderTakerAssetFilledAmount, rightOrderTakerAssetAmount) + ) { + + // We want the following layout in memory before calling `fillOrder`: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 416 | 4 | `fillOrder` function selector | + // | | 3 * 32 | function parameters: | + // | 420 | | 1. offset to rightOrder (*) | + // | 452 | | 2. takerAssetFillAmount | + // | 484 | | 3. offset to rightSignature (*) | + // | | 12 * 32 | rightOrder: | + // | 516 | | 1. senderAddress | + // | 548 | | 2. makerAddress | + // | 580 | | 3. takerAddress | + // | 612 | | 4. feeRecipientAddress | + // | 644 | | 5. makerAssetAmount | + // | 676 | | 6. takerAssetAmount | + // | 708 | | 7. makerFeeAmount | + // | 740 | | 8. takerFeeAmount | + // | 772 | | 9. expirationTimeSeconds | + // | 804 | | 10. salt | + // | 836 | | 11. offset to rightMakerAssetData (*) | + // | 868 | | 12. offset to rightTakerAssetData (*) | + // | 900 | 32 | rightMakerAssetData Length | + // | 932 | a | rightMakerAssetData Contents | + // | 932 + a | 32 | rightTakerAssetData Length | + // | 964 | b | rightTakerAssetData Contents | + // | 964 + b | 32 | rightSigature Length (always 0) | + + // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` + // `EXCHANGE.matchOrders` already makes this assumption, so it is likely + // that the `rightMakerAssetData` and `rightTakerAssetData` in calldata are empty + + let leftOrderStart := add(calldataload(4), 4) + + // Calculate locations of `leftMakerAssetData` and `leftTakerAssetData` in calldata + let leftMakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 320)) // offset to `leftMakerAssetData` + ) + let leftTakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 352)) // offset to `leftTakerAssetData` + ) + + // Load lengths of `leftMakerAssetData` and `leftTakerAssetData` + let leftMakerAssetDataLen := calldataload(leftMakerAssetDataStart) + let leftTakerAssetDataLen := calldataload(leftTakerAssetDataStart) + + // Write offset to `rightMakerAssetData` + mstore(add(rightOrderStart, 320), 384) + + // Write offset to `rightTakerAssetData` + let rightTakerAssetDataOffset := add(416, leftTakerAssetDataLen) + mstore(add(rightOrderStart, 352), rightTakerAssetDataOffset) + + // Copy `leftTakerAssetData` from calldata onto `rightMakerAssetData` in memory + calldatacopy( + add(rightOrderStart, 384), // `rightMakerAssetDataStart` + leftTakerAssetDataStart, + add(leftTakerAssetDataLen, 32) + ) + + // Copy `leftMakerAssetData` from calldata onto `rightTakerAssetData` in memory + calldatacopy( + add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` + leftMakerAssetDataStart, + add(leftMakerAssetDataLen, 32) + ) + + // Write length of signature (always 0 since signature was previously validated) + let rightSignatureStart := add( + add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` + add(leftMakerAssetDataLen, 32) + ) + mstore(rightSignatureStart, 0) + + let cdStart := sub(rightOrderStart, 100) + + // `fillOrder` selector = 0xb4be83d5 + mstore(cdStart, 0xb4be83d500000000000000000000000000000000000000000000000000000000) + + // Write offset to `rightOrder` + mstore(add(cdStart, 4), 96) + + // Write `takerAssetFillAmount` + mstore(add(cdStart, 36), mload(256)) + + // Write offset to `rightSignature` + mstore(add(cdStart, 68), sub(rightSignatureStart, add(cdStart, 4))) + + let fillOrderSuccess := call( + gas, // forward all gas + exchange, // call address of Exchange contract + 0, // transfer 0 wei + cdStart, // start of input + sub(add(rightSignatureStart, 32), cdStart), // length of input is end - start + 0, // write output over input + 128 // length of output is 128 bytes + ) + + if fillOrderSuccess { + return(0, 0) + } + + // Revert with reason if `fillOrder` call was unsuccessful + revert(0, returndatasize()) + } + + // Return if `matchOrders` call successful and `fillOrder` was not called + return(0, 0) + } + + // Revert if undefined function is called + revert(0, 0) + } + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol new file mode 100644 index 000000000..b6f1566cf --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol @@ -0,0 +1,37 @@ +/* + + 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; + +import "../../utils/Ownable/Ownable.sol"; +import "./libs/LibConstants.sol"; +import "./MixinMatchOrders.sol"; +import "./MixinAssets.sol"; + + +// solhint-disable no-empty-blocks +contract OrderMatcher is + MixinMatchOrders, + MixinAssets +{ + constructor (address _exchange) + public + LibConstants(_exchange) + Ownable() + {} +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol new file mode 100644 index 000000000..a0b3aa4c7 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IAssets.sol @@ -0,0 +1,43 @@ +/* + + 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 IAssets { + + /// @dev Withdraws assets from this contract. The contract requires a ZRX balance in order to + /// function optimally, and this function allows the ZRX to be withdrawn by owner. It may also be + /// used to withdraw assets that were accidentally sent to this contract. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to withdraw. + function withdrawAsset( + bytes assetData, + uint256 amount + ) + external; + + /// @dev Approves or disapproves an AssetProxy to spend asset. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveAssetProxy( + bytes assetData, + uint256 amount + ) + external; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol new file mode 100644 index 000000000..455259238 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol @@ -0,0 +1,43 @@ +/* + + 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/libs/LibOrder.sol"; + + +contract IMatchOrders { + + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public; +} diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol new file mode 100644 index 000000000..ed4aee925 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol @@ -0,0 +1,31 @@ +/* + + 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; + +import "../../../utils/Ownable/IOwnable.sol"; +import "./IMatchOrders.sol"; +import "./IAssets.sol"; + + +// solhint-disable no-empty-blocks +contract IOrderMatcher is + IOwnable, + IMatchOrders, + IAssets +{} diff --git a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol new file mode 100644 index 000000000..a7c170b42 --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol @@ -0,0 +1,53 @@ +/* + + Copyright 2018 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.24; + +import "../../../protocol/Exchange/interfaces/IExchange.sol"; + + +contract LibConstants { + + bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); + bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); + bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); + + // solhint-disable var-name-mixedcase + IExchange internal EXCHANGE; + address internal ERC20_PROXY_ADDRESS; + address internal ERC721_PROXY_ADDRESS; + // solhint-enable var-name-mixedcase + + constructor (address _exchange) + public + { + EXCHANGE = IExchange(_exchange); + + ERC20_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC20_DATA_ID); + require( + ERC20_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + + ERC721_PROXY_ADDRESS = EXCHANGE.getAssetProxy(ERC721_DATA_ID); + require( + ERC721_PROXY_ADDRESS != address(0), + "UNREGISTERED_ASSET_PROXY" + ); + } +} diff --git a/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol new file mode 100644 index 000000000..32cfddf1c --- /dev/null +++ b/contracts/extensions/contracts/OrderMatcher/mixins/MAssets.sol @@ -0,0 +1,71 @@ +/* + + 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; + +import "../interfaces/IAssets.sol"; + + +contract MAssets is + IAssets +{ + /// @dev Transfers given amount of asset to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferAssetToSender( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC20 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Decodes ERC721 assetData and transfers given amount to sender. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to transfer to sender. + function transferERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC20 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC20Token( + bytes memory assetData, + uint256 amount + ) + internal; + + /// @dev Sets approval for ERC721 AssetProxy. + /// @param assetData Byte array encoded for the respective asset proxy. + /// @param amount Amount of asset to approve for respective proxy. + function approveERC721Token( + bytes memory assetData, + uint256 amount + ) + internal; +} diff --git a/contracts/protocol/src/artifacts/index.ts b/contracts/protocol/src/artifacts/index.ts index 1d53ceb04..2a9de78d2 100644 --- a/contracts/protocol/src/artifacts/index.ts +++ b/contracts/protocol/src/artifacts/index.ts @@ -6,6 +6,7 @@ import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; +import * as OrderMatcher from '../../generated-artifacts/OrderMatcher.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; @@ -20,6 +21,7 @@ export const artifacts = { Exchange: Exchange as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, + OrderMatcher: OrderMatcher as ContractArtifact, OrderValidator: OrderValidator as ContractArtifact, TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, -- cgit v1.2.3 From 2fa8b8d1d054015bfac49401c4f6eb5af9309603 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 17 Oct 2018 13:10:51 -0700 Subject: Add OrderMatcher tests --- contracts/extensions/test/extensions/forwarder.ts | 3 +-- contracts/protocol/src/wrappers/index.ts | 1 + contracts/protocol/tsconfig.json | 1 + contracts/test-utils/src/constants.ts | 1 + 4 files changed, 4 insertions(+), 2 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/test/extensions/forwarder.ts b/contracts/extensions/test/extensions/forwarder.ts index 4027f493d..69939ed04 100644 --- a/contracts/extensions/test/extensions/forwarder.ts +++ b/contracts/extensions/test/extensions/forwarder.ts @@ -48,7 +48,6 @@ describe(ContractName.Forwarder, () => { let owner: string; let takerAddress: string; let feeRecipientAddress: string; - let otherAddress: string; let defaultMakerAssetAddress: string; let zrxAssetData: string; let wethAssetData: string; @@ -78,7 +77,7 @@ describe(ContractName.Forwarder, () => { before(async () => { await blockchainLifecycle.startAsync(); const accounts = await web3Wrapper.getAvailableAddressesAsync(); - const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress, otherAddress] = accounts); + const usedAddresses = ([owner, makerAddress, takerAddress, feeRecipientAddress] = accounts); const txHash = await web3Wrapper.sendTransactionAsync({ from: accounts[0], to: accounts[0], value: 0 }); const transaction = await web3Wrapper.getTransactionByHashAsync(txHash); diff --git a/contracts/protocol/src/wrappers/index.ts b/contracts/protocol/src/wrappers/index.ts index ac951d269..13f16f44f 100644 --- a/contracts/protocol/src/wrappers/index.ts +++ b/contracts/protocol/src/wrappers/index.ts @@ -3,6 +3,7 @@ export * from '../../generated-wrappers/erc20_proxy'; export * from '../../generated-wrappers/erc721_proxy'; export * from '../../generated-wrappers/exchange'; export * from '../../generated-wrappers/mixin_authorizable'; +export * from '../../generated-wrappers/order_matcher'; export * from '../../generated-wrappers/order_validator'; export * from '../../generated-wrappers/test_asset_proxy_dispatcher'; export * from '../../generated-wrappers/test_asset_proxy_owner'; diff --git a/contracts/protocol/tsconfig.json b/contracts/protocol/tsconfig.json index 989d3ef2b..7e4ce5eb4 100644 --- a/contracts/protocol/tsconfig.json +++ b/contracts/protocol/tsconfig.json @@ -13,6 +13,7 @@ "./generated-artifacts/Exchange.json", "./generated-artifacts/MixinAuthorizable.json", "./generated-artifacts/MultiAssetProxy.json", + "./generated-artifacts/OrderMatcher.json", "./generated-artifacts/OrderValidator.json", "./generated-artifacts/TestAssetProxyDispatcher.json", "./generated-artifacts/TestAssetProxyOwner.json", diff --git a/contracts/test-utils/src/constants.ts b/contracts/test-utils/src/constants.ts index d2c3ab512..f631dc81a 100644 --- a/contracts/test-utils/src/constants.ts +++ b/contracts/test-utils/src/constants.ts @@ -29,6 +29,7 @@ export const constants = { MAX_TOKEN_TRANSFERFROM_GAS: 80000, MAX_TOKEN_APPROVE_GAS: 60000, MAX_TRANSFER_FROM_GAS: 150000, + MAX_MATCH_ORDERS_GAS: 400000, DUMMY_TOKEN_NAME: '', DUMMY_TOKEN_SYMBOL: '', DUMMY_TOKEN_DECIMALS: new BigNumber(18), -- cgit v1.2.3 From b6f4c5c7da7d0afe65c0e3dba97aa64e11934595 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Sun, 9 Dec 2018 15:03:45 -0800 Subject: Fix build and add back tests --- contracts/core/test/extensions/order_matcher.ts | 772 +++++++++++++++++++++ .../contracts/OrderMatcher/MixinAssets.sol | 4 +- .../contracts/OrderMatcher/MixinMatchOrders.sol | 2 +- .../contracts/OrderMatcher/OrderMatcher.sol | 2 +- 4 files changed, 776 insertions(+), 4 deletions(-) create mode 100644 contracts/core/test/extensions/order_matcher.ts (limited to 'contracts') diff --git a/contracts/core/test/extensions/order_matcher.ts b/contracts/core/test/extensions/order_matcher.ts new file mode 100644 index 000000000..e61d0f8e0 --- /dev/null +++ b/contracts/core/test/extensions/order_matcher.ts @@ -0,0 +1,772 @@ +import { + chaiSetup, + constants, + ERC20BalancesByOwner, + expectContractCreationFailedAsync, + expectTransactionFailedAsync, + LogDecoder, + OrderFactory, + provider, + sendTransactionResult, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; +import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; +import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; +import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; +import { ExchangeContract, ExchangeFillEventArgs } from '../../generated-wrappers/exchange'; +import { OrderMatcherContract } from '../../generated-wrappers/order_matcher'; +import { artifacts } from '../../src/artifacts'; +import { ERC20Wrapper } from '../utils/erc20_wrapper'; +import { ExchangeWrapper } from '../utils/exchange_wrapper'; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +chaiSetup.configure(); +const expect = chai.expect; +// tslint:disable:no-unnecessary-type-assertion +describe('OrderMatcher', () => { + let makerAddressLeft: string; + let makerAddressRight: string; + let owner: string; + let takerAddress: string; + let feeRecipientAddressLeft: string; + let feeRecipientAddressRight: string; + + let erc20TokenA: DummyERC20TokenContract; + let erc20TokenB: DummyERC20TokenContract; + let zrxToken: DummyERC20TokenContract; + let exchange: ExchangeContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + let orderMatcher: OrderMatcherContract; + + let erc20BalancesByOwner: ERC20BalancesByOwner; + let exchangeWrapper: ExchangeWrapper; + let erc20Wrapper: ERC20Wrapper; + let orderFactoryLeft: OrderFactory; + let orderFactoryRight: OrderFactory; + + let leftMakerAssetData: string; + let leftTakerAssetData: string; + let defaultERC20MakerAssetAddress: string; + let defaultERC20TakerAssetAddress: string; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + // Create accounts + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + // Hack(albrow): Both Prettier and TSLint insert a trailing comma below + // but that is invalid syntax as of TypeScript version >= 2.8. We don't + // have the right fine-grained configuration options in TSLint, + // Prettier, or TypeScript, to reconcile this, so we will just have to + // wait for them to sort it out. We disable TSLint and Prettier for + // this part of the code for now. This occurs several times in this + // file. See https://github.com/prettier/prettier/issues/4624. + // prettier-ignore + const usedAddresses = ([ + owner, + makerAddressLeft, + makerAddressRight, + takerAddress, + feeRecipientAddressLeft, + // tslint:disable-next-line:trailing-comma + feeRecipientAddressRight + ] = _.slice(accounts, 0, 6)); + // Create wrappers + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + // Deploy ERC20 token & ERC20 proxy + const numDummyErc20ToDeploy = 3; + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, + ); + erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + // Deploy ERC721 proxy + erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(artifacts.ERC721Proxy, provider, txDefaults); + // Depoy exchange + exchange = await ExchangeContract.deployFrom0xArtifactAsync( + artifacts.Exchange, + provider, + txDefaults, + assetDataUtils.encodeERC20AssetData(zrxToken.address), + ); + exchangeWrapper = new ExchangeWrapper(exchange, provider); + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + // Authorize ERC20 trades by exchange + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Deploy OrderMatcher + orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchange.address, + ); + // Set default addresses + defaultERC20MakerAssetAddress = erc20TokenA.address; + defaultERC20TakerAssetAddress = erc20TokenB.address; + leftMakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress); + leftTakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress); + // Set OrderMatcher balances and allowances + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenA.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenB.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftMakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftTakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Create default order parameters + const defaultOrderParamsLeft = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressLeft, + exchangeAddress: exchange.address, + makerAssetData: leftMakerAssetData, + takerAssetData: leftTakerAssetData, + feeRecipientAddress: feeRecipientAddressLeft, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const defaultOrderParamsRight = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressRight, + exchangeAddress: exchange.address, + makerAssetData: leftTakerAssetData, + takerAssetData: leftMakerAssetData, + feeRecipientAddress: feeRecipientAddressRight, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; + orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); + const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; + orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('constructor', () => { + it('should revert if assetProxy is unregistered', async () => { + const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( + artifacts.Exchange, + provider, + txDefaults, + constants.NULL_BYTES, + ); + return expectContractCreationFailedAsync( + (OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchangeInstance.address, + ) as any) as sendTransactionResult, + RevertReason.UnregisteredAssetProxy, + ); + }); + }); + describe('matchOrders', () => { + beforeEach(async () => { + erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); + }); + it('should revert if not called by owner', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: takerAddress, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.OnlyContractOwner, + ); + }); + it('should transfer the correct amounts when orders completely fill each other', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + }); + it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance); + }); + it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should not call fillOrder when rightOrder is completely filled after matchOrders call', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + const logDecoder = new LogDecoder(web3Wrapper, artifacts); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + ); + const fillLogs = _.filter( + txReceipt.logs, + log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', + ); + expect(fillLogs.length).to.be.equal(2); + }); + it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18), + }); + const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18); + const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount + .times(signedOrderRight.makerAssetAmount) + .dividedToIntegerBy(signedOrderRight.takerAssetAmount); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount), + amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT, + leftTakerAssetSpreadAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + signedOrderRight.makerAssetData = constants.NULL_BYTES; + signedOrderRight.takerAssetData = constants.NULL_BYTES; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should revert with the correct reason if matchOrders call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.InvalidOrderSignature, + ); + }); + it('should revert with the correct reason if fillOrder call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Matcher will not have enough allowance to fill rightOrder + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, constants.ZERO_AMOUNT, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.TransferFailed, + ); + }); + }); + describe('withdrawAsset', () => { + it('should allow owner to withdraw ERC20 tokens', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: owner, + }), + ); + const newBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should allow owner to withdraw ERC721 tokens', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const tokenId = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Token.mint.sendTransactionAsync(orderMatcher.address, tokenId, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); + const withdrawAmount = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(assetData, withdrawAmount, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const erc721Owner = await erc721Token.ownerOf.callAsync(tokenId); + expect(erc721Owner).to.be.equal(owner); + }); + it('should revert if not called by owner', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await expectTransactionFailedAsync( + orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); + describe('approveAssetProxy', () => { + it('should be able to set an allowance for ERC20 tokens', async () => { + const allowance = new BigNumber(55465465426546); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, allowance, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newAllowance = await erc20TokenA.allowance.callAsync(orderMatcher.address, erc20Proxy.address); + expect(newAllowance).to.be.bignumber.equal(allowance); + }); + it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + artifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(2); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should revert if not called by owner', async () => { + const approval = new BigNumber(1); + await expectTransactionFailedAsync( + orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, approval, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); +}); +// tslint:disable:max-file-line-count +// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol index a2bc95e4b..43e19c19e 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol @@ -18,8 +18,8 @@ pragma solidity 0.4.24; -import "../../utils/LibBytes/LibBytes.sol"; -import "../../utils/Ownable/Ownable.sol"; +import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "../../tokens/ERC20Token/IERC20Token.sol"; import "../../tokens/ERC721Token/IERC721Token.sol"; import "./mixins/MAssets.sol"; diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol index 1c7cce675..ffe037ee7 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -18,8 +18,8 @@ pragma solidity 0.4.24; -import "../../utils/Ownable/Ownable.sol"; import "./libs/LibConstants.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; contract MixinMatchOrders is diff --git a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol index b6f1566cf..7136c8c82 100644 --- a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol +++ b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../utils/Ownable/Ownable.sol"; +import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "./libs/LibConstants.sol"; import "./MixinMatchOrders.sol"; import "./MixinAssets.sol"; -- cgit v1.2.3 From 0a5ecec3e222ae0c5e70e0d3092e57df5dfb75cd Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 12 Dec 2018 16:44:28 -0800 Subject: update comments --- contracts/core/test/extensions/order_matcher.ts | 12 ++++++------ contracts/extensions/contracts/OrderMatcher/MixinAssets.sol | 7 +++++-- .../extensions/contracts/OrderMatcher/MixinMatchOrders.sol | 5 +++-- .../contracts/OrderMatcher/interfaces/IMatchOrders.sol | 2 +- .../contracts/OrderMatcher/interfaces/IOrderMatcher.sol | 2 +- .../extensions/contracts/OrderMatcher/libs/LibConstants.sol | 2 +- 6 files changed, 17 insertions(+), 13 deletions(-) (limited to 'contracts') diff --git a/contracts/core/test/extensions/order_matcher.ts b/contracts/core/test/extensions/order_matcher.ts index e61d0f8e0..4ea95bc49 100644 --- a/contracts/core/test/extensions/order_matcher.ts +++ b/contracts/core/test/extensions/order_matcher.ts @@ -11,6 +11,7 @@ import { txDefaults, web3Wrapper, } from '@0x/contracts-test-utils'; +import { artifacts as tokenArtifacts, DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens'; import { BlockchainLifecycle } from '@0x/dev-utils'; import { assetDataUtils } from '@0x/order-utils'; import { RevertReason } from '@0x/types'; @@ -20,8 +21,6 @@ import * as chai from 'chai'; import { LogWithDecodedArgs } from 'ethereum-types'; import * as _ from 'lodash'; -import { DummyERC20TokenContract } from '../../generated-wrappers/dummy_erc20_token'; -import { DummyERC721TokenContract } from '../../generated-wrappers/dummy_erc721_token'; import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; import { ExchangeContract, ExchangeFillEventArgs } from '../../generated-wrappers/exchange'; @@ -444,7 +443,7 @@ describe('OrderMatcher', () => { signedOrderLeft.signature, signedOrderRight.signature, ); - const logDecoder = new LogDecoder(web3Wrapper, artifacts); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts }); const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( await web3Wrapper.sendTransactionAsync({ data, @@ -457,6 +456,7 @@ describe('OrderMatcher', () => { txReceipt.logs, log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', ); + // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. expect(fillLogs.length).to.be.equal(2); }); it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { @@ -680,7 +680,7 @@ describe('OrderMatcher', () => { }); it('should allow owner to withdraw ERC721 tokens', async () => { const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, + tokenArtifacts.DummyERC721Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, @@ -725,7 +725,7 @@ describe('OrderMatcher', () => { }); it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, + tokenArtifacts.DummyERC721Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, @@ -742,7 +742,7 @@ describe('OrderMatcher', () => { }); it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - artifacts.DummyERC721Token, + tokenArtifacts.DummyERC721Token, provider, txDefaults, constants.DUMMY_TOKEN_NAME, diff --git a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol index 43e19c19e..323998705 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinAssets.sol @@ -20,8 +20,8 @@ pragma solidity 0.4.24; import "@0x/contracts-utils/contracts/utils/LibBytes/LibBytes.sol"; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; -import "../../tokens/ERC20Token/IERC20Token.sol"; -import "../../tokens/ERC721Token/IERC721Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC20Token/IERC20Token.sol"; +import "@0x/contracts-tokens/contracts/tokens/ERC721Token/IERC721Token.sol"; import "./mixins/MAssets.sol"; import "./libs/LibConstants.sol"; @@ -98,6 +98,7 @@ contract MixinAssets is ) internal { + // 4 byte id + 12 0 bytes before ABI encoded token address. address token = assetData.readAddress(16); // Transfer tokens. @@ -149,7 +150,9 @@ contract MixinAssets is "INVALID_AMOUNT" ); // Decode asset data. + // 4 byte id + 12 0 bytes before ABI encoded token address. address token = assetData.readAddress(16); + // 4 byte id + 32 byte ABI encoded token address before token id. uint256 tokenId = assetData.readUint256(36); // Perform transfer. diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol index ffe037ee7..866523190 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -230,7 +230,7 @@ contract MixinMatchOrders is // | 932 | a | rightMakerAssetData Contents | // | 932 + a | 32 | rightTakerAssetData Length | // | 964 | b | rightTakerAssetData Contents | - // | 964 + b | 32 | rightSigature Length (always 0) | + // | 964 + b | 32 | rightSignature Length (always 0) | // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` // `EXCHANGE.matchOrders` already makes this assumption, so it is likely @@ -280,6 +280,7 @@ contract MixinMatchOrders is ) mstore(rightSignatureStart, 0) + // function selector (4 bytes) + 3 params (3 * 32 bytes) must be stored before `rightOrderStart` let cdStart := sub(rightOrderStart, 100) // `fillOrder` selector = 0xb4be83d5 @@ -288,7 +289,7 @@ contract MixinMatchOrders is // Write offset to `rightOrder` mstore(add(cdStart, 4), 96) - // Write `takerAssetFillAmount` + // Write `takerAssetFillAmount`, which will be the `leftMakerAssetSpreadAmount` received from the `matchOrders` call mstore(add(cdStart, 36), mload(256)) // Write offset to `rightSignature` diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol index 455259238..19bcbb326 100644 --- a/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IMatchOrders.sol @@ -19,7 +19,7 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; -import "../../../protocol/Exchange/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; contract IMatchOrders { diff --git a/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol index ed4aee925..9b6ea26d8 100644 --- a/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol +++ b/contracts/extensions/contracts/OrderMatcher/interfaces/IOrderMatcher.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../../utils/Ownable/IOwnable.sol"; +import "@0x/contract-utils/contracts/utils/Ownable/IOwnable.sol"; import "./IMatchOrders.sol"; import "./IAssets.sol"; diff --git a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol index a7c170b42..43bbdeec2 100644 --- a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol +++ b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.24; -import "../../../protocol/Exchange/interfaces/IExchange.sol"; +import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; contract LibConstants { -- cgit v1.2.3 From 6b5b8fe8e05e497b6f4db4d3b0aa675d0bbdff50 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 12 Dec 2018 17:26:46 -0800 Subject: Fix build after rebase --- contracts/core/test/extensions/order_matcher.ts | 772 -------------------- contracts/extensions/package.json | 2 +- contracts/extensions/src/artifacts/index.ts | 2 + contracts/extensions/src/wrappers/index.ts | 1 + .../extensions/test/extensions/order_matcher.ts | 780 +++++++++++++++++++++ contracts/extensions/tsconfig.json | 3 +- contracts/protocol/src/artifacts/index.ts | 2 - contracts/protocol/src/wrappers/index.ts | 1 - contracts/protocol/tsconfig.json | 1 - 9 files changed, 786 insertions(+), 778 deletions(-) delete mode 100644 contracts/core/test/extensions/order_matcher.ts create mode 100644 contracts/extensions/test/extensions/order_matcher.ts (limited to 'contracts') diff --git a/contracts/core/test/extensions/order_matcher.ts b/contracts/core/test/extensions/order_matcher.ts deleted file mode 100644 index 4ea95bc49..000000000 --- a/contracts/core/test/extensions/order_matcher.ts +++ /dev/null @@ -1,772 +0,0 @@ -import { - chaiSetup, - constants, - ERC20BalancesByOwner, - expectContractCreationFailedAsync, - expectTransactionFailedAsync, - LogDecoder, - OrderFactory, - provider, - sendTransactionResult, - txDefaults, - web3Wrapper, -} from '@0x/contracts-test-utils'; -import { artifacts as tokenArtifacts, DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens'; -import { BlockchainLifecycle } from '@0x/dev-utils'; -import { assetDataUtils } from '@0x/order-utils'; -import { RevertReason } from '@0x/types'; -import { BigNumber } from '@0x/utils'; -import { Web3Wrapper } from '@0x/web3-wrapper'; -import * as chai from 'chai'; -import { LogWithDecodedArgs } from 'ethereum-types'; -import * as _ from 'lodash'; - -import { ERC20ProxyContract } from '../../generated-wrappers/erc20_proxy'; -import { ERC721ProxyContract } from '../../generated-wrappers/erc721_proxy'; -import { ExchangeContract, ExchangeFillEventArgs } from '../../generated-wrappers/exchange'; -import { OrderMatcherContract } from '../../generated-wrappers/order_matcher'; -import { artifacts } from '../../src/artifacts'; -import { ERC20Wrapper } from '../utils/erc20_wrapper'; -import { ExchangeWrapper } from '../utils/exchange_wrapper'; - -const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); -chaiSetup.configure(); -const expect = chai.expect; -// tslint:disable:no-unnecessary-type-assertion -describe('OrderMatcher', () => { - let makerAddressLeft: string; - let makerAddressRight: string; - let owner: string; - let takerAddress: string; - let feeRecipientAddressLeft: string; - let feeRecipientAddressRight: string; - - let erc20TokenA: DummyERC20TokenContract; - let erc20TokenB: DummyERC20TokenContract; - let zrxToken: DummyERC20TokenContract; - let exchange: ExchangeContract; - let erc20Proxy: ERC20ProxyContract; - let erc721Proxy: ERC721ProxyContract; - let orderMatcher: OrderMatcherContract; - - let erc20BalancesByOwner: ERC20BalancesByOwner; - let exchangeWrapper: ExchangeWrapper; - let erc20Wrapper: ERC20Wrapper; - let orderFactoryLeft: OrderFactory; - let orderFactoryRight: OrderFactory; - - let leftMakerAssetData: string; - let leftTakerAssetData: string; - let defaultERC20MakerAssetAddress: string; - let defaultERC20TakerAssetAddress: string; - - before(async () => { - await blockchainLifecycle.startAsync(); - }); - after(async () => { - await blockchainLifecycle.revertAsync(); - }); - before(async () => { - // Create accounts - const accounts = await web3Wrapper.getAvailableAddressesAsync(); - // Hack(albrow): Both Prettier and TSLint insert a trailing comma below - // but that is invalid syntax as of TypeScript version >= 2.8. We don't - // have the right fine-grained configuration options in TSLint, - // Prettier, or TypeScript, to reconcile this, so we will just have to - // wait for them to sort it out. We disable TSLint and Prettier for - // this part of the code for now. This occurs several times in this - // file. See https://github.com/prettier/prettier/issues/4624. - // prettier-ignore - const usedAddresses = ([ - owner, - makerAddressLeft, - makerAddressRight, - takerAddress, - feeRecipientAddressLeft, - // tslint:disable-next-line:trailing-comma - feeRecipientAddressRight - ] = _.slice(accounts, 0, 6)); - // Create wrappers - erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); - // Deploy ERC20 token & ERC20 proxy - const numDummyErc20ToDeploy = 3; - [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( - numDummyErc20ToDeploy, - constants.DUMMY_TOKEN_DECIMALS, - ); - erc20Proxy = await erc20Wrapper.deployProxyAsync(); - await erc20Wrapper.setBalancesAndAllowancesAsync(); - // Deploy ERC721 proxy - erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync(artifacts.ERC721Proxy, provider, txDefaults); - // Depoy exchange - exchange = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - assetDataUtils.encodeERC20AssetData(zrxToken.address), - ); - exchangeWrapper = new ExchangeWrapper(exchange, provider); - await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); - await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); - // Authorize ERC20 trades by exchange - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Deploy OrderMatcher - orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync( - artifacts.OrderMatcher, - provider, - txDefaults, - exchange.address, - ); - // Set default addresses - defaultERC20MakerAssetAddress = erc20TokenA.address; - defaultERC20TakerAssetAddress = erc20TokenB.address; - leftMakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress); - leftTakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress); - // Set OrderMatcher balances and allowances - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenA.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc20TokenB.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync( - leftMakerAssetData, - constants.INITIAL_ERC20_ALLOWANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync( - leftTakerAssetData, - constants.INITIAL_ERC20_ALLOWANCE, - ), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - // Create default order parameters - const defaultOrderParamsLeft = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressLeft, - exchangeAddress: exchange.address, - makerAssetData: leftMakerAssetData, - takerAssetData: leftTakerAssetData, - feeRecipientAddress: feeRecipientAddressLeft, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }; - const defaultOrderParamsRight = { - ...constants.STATIC_ORDER_PARAMS, - makerAddress: makerAddressRight, - exchangeAddress: exchange.address, - makerAssetData: leftTakerAssetData, - takerAssetData: leftMakerAssetData, - feeRecipientAddress: feeRecipientAddressRight, - makerFee: constants.ZERO_AMOUNT, - takerFee: constants.ZERO_AMOUNT, - }; - const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; - orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); - const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; - orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); - }); - beforeEach(async () => { - await blockchainLifecycle.startAsync(); - }); - afterEach(async () => { - await blockchainLifecycle.revertAsync(); - }); - describe('constructor', () => { - it('should revert if assetProxy is unregistered', async () => { - const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( - artifacts.Exchange, - provider, - txDefaults, - constants.NULL_BYTES, - ); - return expectContractCreationFailedAsync( - (OrderMatcherContract.deployFrom0xArtifactAsync( - artifacts.OrderMatcher, - provider, - txDefaults, - exchangeInstance.address, - ) as any) as sendTransactionResult, - RevertReason.UnregisteredAssetProxy, - ); - }); - }); - describe('matchOrders', () => { - beforeEach(async () => { - erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); - }); - it('should revert if not called by owner', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: takerAddress, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - RevertReason.OnlyContractOwner, - ); - }); - it('should transfer the correct amounts when orders completely fill each other', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - }); - it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance); - }); - it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - // Match signedOrderLeft with signedOrderRight - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it('should not call fillOrder when rightOrder is completely filled after matchOrders call', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts }); - const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - ); - const fillLogs = _.filter( - txReceipt.logs, - log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', - ); - // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. - expect(fillLogs.length).to.be.equal(2); - }); - it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18), - }); - const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18); - const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount - .times(signedOrderRight.makerAssetAmount) - .dividedToIntegerBy(signedOrderRight.takerAssetAmount); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount), - amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT, - leftTakerAssetSpreadAmount, - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - // Match signedOrderLeft with signedOrderRight - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Match signedOrderLeft with signedOrderRight - const expectedTransferAmounts = { - // Left Maker - amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, - amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, - // Right Maker - amountSoldByRightMaker: signedOrderRight.makerAssetAmount, - amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, - // Taker - leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), - leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), - }; - const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - // Match signedOrderLeft with signedOrderRight - signedOrderRight.makerAssetData = constants.NULL_BYTES; - signedOrderRight.takerAssetData = constants.NULL_BYTES; - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await web3Wrapper.awaitTransactionSuccessAsync( - await web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); - const newErc20Balances = await erc20Wrapper.getBalancesAsync(); - expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( - expectedTransferAmounts.amountSoldByRightMaker, - ), - ); - expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByLeftMaker, - ), - ); - expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( - erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( - expectedTransferAmounts.amountBoughtByRightMaker, - ), - ); - expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), - ); - expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( - initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), - ); - }); - it('should revert with the correct reason if matchOrders call reverts', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - }); - signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`; - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - RevertReason.InvalidOrderSignature, - ); - }); - it('should revert with the correct reason if fillOrder call reverts', async () => { - // Create orders to match - const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - }); - const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ - makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), - }); - // Matcher will not have enough allowance to fill rightOrder - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, constants.ZERO_AMOUNT, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const data = exchange.matchOrders.getABIEncodedTransactionData( - signedOrderLeft, - signedOrderRight, - signedOrderLeft.signature, - signedOrderRight.signature, - ); - await expectTransactionFailedAsync( - web3Wrapper.sendTransactionAsync({ - data, - to: orderMatcher.address, - from: owner, - gas: constants.MAX_MATCH_ORDERS_GAS, - }), - RevertReason.TransferFailed, - ); - }); - }); - describe('withdrawAsset', () => { - it('should allow owner to withdraw ERC20 tokens', async () => { - const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { - from: owner, - }), - ); - const newBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); - }); - it('should allow owner to withdraw ERC721 tokens', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - tokenArtifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const tokenId = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await erc721Token.mint.sendTransactionAsync(orderMatcher.address, tokenId, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); - const withdrawAmount = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.withdrawAsset.sendTransactionAsync(assetData, withdrawAmount, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const erc721Owner = await erc721Token.ownerOf.callAsync(tokenId); - expect(erc721Owner).to.be.equal(owner); - }); - it('should revert if not called by owner', async () => { - const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); - expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); - await expectTransactionFailedAsync( - orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { - from: takerAddress, - }), - RevertReason.OnlyContractOwner, - ); - }); - }); - describe('approveAssetProxy', () => { - it('should be able to set an allowance for ERC20 tokens', async () => { - const allowance = new BigNumber(55465465426546); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, allowance, { - from: owner, - }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const newAllowance = await erc20TokenA.allowance.callAsync(orderMatcher.address, erc20Proxy.address); - expect(newAllowance).to.be.bignumber.equal(allowance); - }); - it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - tokenArtifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); - const allowance = new BigNumber(1); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); - expect(isApproved).to.be.equal(true); - }); - it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { - const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( - tokenArtifacts.DummyERC721Token, - provider, - txDefaults, - constants.DUMMY_TOKEN_NAME, - constants.DUMMY_TOKEN_SYMBOL, - ); - const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); - const allowance = new BigNumber(2); - await web3Wrapper.awaitTransactionSuccessAsync( - await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), - constants.AWAIT_TRANSACTION_MINED_MS, - ); - const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); - expect(isApproved).to.be.equal(true); - }); - it('should revert if not called by owner', async () => { - const approval = new BigNumber(1); - await expectTransactionFailedAsync( - orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, approval, { - from: takerAddress, - }), - RevertReason.OnlyContractOwner, - ); - }); - }); -}); -// tslint:disable:max-file-line-count -// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/package.json b/contracts/extensions/package.json index aa5cef462..d0caa030e 100644 --- a/contracts/extensions/package.json +++ b/contracts/extensions/package.json @@ -32,7 +32,7 @@ "lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol" }, "config": { - "abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder).json" + "abis": "generated-artifacts/@(BalanceThresholdFilter|DutchAuction|Forwarder|OrderMatcher).json" }, "repository": { "type": "git", diff --git a/contracts/extensions/src/artifacts/index.ts b/contracts/extensions/src/artifacts/index.ts index ebf0b8050..a8bd34b4e 100644 --- a/contracts/extensions/src/artifacts/index.ts +++ b/contracts/extensions/src/artifacts/index.ts @@ -3,9 +3,11 @@ import { ContractArtifact } from 'ethereum-types'; import * as BalanceThresholdFilter from '../../generated-artifacts/BalanceThresholdFilter.json'; import * as DutchAuction from '../../generated-artifacts/DutchAuction.json'; import * as Forwarder from '../../generated-artifacts/Forwarder.json'; +import * as OrderMatcher from '../../generated-artifacts/OrderMatcher.json'; export const artifacts = { BalanceThresholdFilter: BalanceThresholdFilter as ContractArtifact, DutchAuction: DutchAuction as ContractArtifact, Forwarder: Forwarder as ContractArtifact, + OrderMatcher: OrderMatcher as ContractArtifact, }; diff --git a/contracts/extensions/src/wrappers/index.ts b/contracts/extensions/src/wrappers/index.ts index 8a8122caa..4d075ee13 100644 --- a/contracts/extensions/src/wrappers/index.ts +++ b/contracts/extensions/src/wrappers/index.ts @@ -1,3 +1,4 @@ export * from '../../generated-wrappers/balance_threshold_filter'; export * from '../../generated-wrappers/dutch_auction'; export * from '../../generated-wrappers/forwarder'; +export * from '../../generated-wrappers/order_matcher'; diff --git a/contracts/extensions/test/extensions/order_matcher.ts b/contracts/extensions/test/extensions/order_matcher.ts new file mode 100644 index 000000000..45002b324 --- /dev/null +++ b/contracts/extensions/test/extensions/order_matcher.ts @@ -0,0 +1,780 @@ +import { + artifacts as protocolArtifacts, + ERC20ProxyContract, + ERC20Wrapper, + ERC721ProxyContract, + ExchangeContract, + ExchangeFillEventArgs, + ExchangeWrapper, +} from '@0x/contracts-protocol'; +import { + chaiSetup, + constants, + ERC20BalancesByOwner, + expectContractCreationFailedAsync, + expectTransactionFailedAsync, + LogDecoder, + OrderFactory, + provider, + sendTransactionResult, + txDefaults, + web3Wrapper, +} from '@0x/contracts-test-utils'; +import { artifacts as tokenArtifacts, DummyERC20TokenContract, DummyERC721TokenContract } from '@0x/contracts-tokens'; +import { BlockchainLifecycle } from '@0x/dev-utils'; +import { assetDataUtils } from '@0x/order-utils'; +import { RevertReason } from '@0x/types'; +import { BigNumber } from '@0x/utils'; +import { Web3Wrapper } from '@0x/web3-wrapper'; +import * as chai from 'chai'; +import { LogWithDecodedArgs } from 'ethereum-types'; +import * as _ from 'lodash'; + +import { OrderMatcherContract } from '../../generated-wrappers/order_matcher'; +import { artifacts } from '../../src/artifacts'; + +const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper); +chaiSetup.configure(); +const expect = chai.expect; +// tslint:disable:no-unnecessary-type-assertion +describe('OrderMatcher', () => { + let makerAddressLeft: string; + let makerAddressRight: string; + let owner: string; + let takerAddress: string; + let feeRecipientAddressLeft: string; + let feeRecipientAddressRight: string; + + let erc20TokenA: DummyERC20TokenContract; + let erc20TokenB: DummyERC20TokenContract; + let zrxToken: DummyERC20TokenContract; + let exchange: ExchangeContract; + let erc20Proxy: ERC20ProxyContract; + let erc721Proxy: ERC721ProxyContract; + let orderMatcher: OrderMatcherContract; + + let erc20BalancesByOwner: ERC20BalancesByOwner; + let exchangeWrapper: ExchangeWrapper; + let erc20Wrapper: ERC20Wrapper; + let orderFactoryLeft: OrderFactory; + let orderFactoryRight: OrderFactory; + + let leftMakerAssetData: string; + let leftTakerAssetData: string; + let defaultERC20MakerAssetAddress: string; + let defaultERC20TakerAssetAddress: string; + + before(async () => { + await blockchainLifecycle.startAsync(); + }); + after(async () => { + await blockchainLifecycle.revertAsync(); + }); + before(async () => { + // Create accounts + const accounts = await web3Wrapper.getAvailableAddressesAsync(); + // Hack(albrow): Both Prettier and TSLint insert a trailing comma below + // but that is invalid syntax as of TypeScript version >= 2.8. We don't + // have the right fine-grained configuration options in TSLint, + // Prettier, or TypeScript, to reconcile this, so we will just have to + // wait for them to sort it out. We disable TSLint and Prettier for + // this part of the code for now. This occurs several times in this + // file. See https://github.com/prettier/prettier/issues/4624. + // prettier-ignore + const usedAddresses = ([ + owner, + makerAddressLeft, + makerAddressRight, + takerAddress, + feeRecipientAddressLeft, + // tslint:disable-next-line:trailing-comma + feeRecipientAddressRight + ] = _.slice(accounts, 0, 6)); + // Create wrappers + erc20Wrapper = new ERC20Wrapper(provider, usedAddresses, owner); + // Deploy ERC20 token & ERC20 proxy + const numDummyErc20ToDeploy = 3; + [erc20TokenA, erc20TokenB, zrxToken] = await erc20Wrapper.deployDummyTokensAsync( + numDummyErc20ToDeploy, + constants.DUMMY_TOKEN_DECIMALS, + ); + erc20Proxy = await erc20Wrapper.deployProxyAsync(); + await erc20Wrapper.setBalancesAndAllowancesAsync(); + // Deploy ERC721 proxy + erc721Proxy = await ERC721ProxyContract.deployFrom0xArtifactAsync( + protocolArtifacts.ERC721Proxy, + provider, + txDefaults, + ); + // Depoy exchange + exchange = await ExchangeContract.deployFrom0xArtifactAsync( + protocolArtifacts.Exchange, + provider, + txDefaults, + assetDataUtils.encodeERC20AssetData(zrxToken.address), + ); + exchangeWrapper = new ExchangeWrapper(exchange, provider); + await exchangeWrapper.registerAssetProxyAsync(erc20Proxy.address, owner); + await exchangeWrapper.registerAssetProxyAsync(erc721Proxy.address, owner); + // Authorize ERC20 trades by exchange + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20Proxy.addAuthorizedAddress.sendTransactionAsync(exchange.address, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Deploy OrderMatcher + orderMatcher = await OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchange.address, + ); + // Set default addresses + defaultERC20MakerAssetAddress = erc20TokenA.address; + defaultERC20TakerAssetAddress = erc20TokenB.address; + leftMakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20MakerAssetAddress); + leftTakerAssetData = assetDataUtils.encodeERC20AssetData(defaultERC20TakerAssetAddress); + // Set OrderMatcher balances and allowances + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenA.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc20TokenB.setBalance.sendTransactionAsync(orderMatcher.address, constants.INITIAL_ERC20_BALANCE, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftMakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync( + leftTakerAssetData, + constants.INITIAL_ERC20_ALLOWANCE, + ), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + // Create default order parameters + const defaultOrderParamsLeft = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressLeft, + exchangeAddress: exchange.address, + makerAssetData: leftMakerAssetData, + takerAssetData: leftTakerAssetData, + feeRecipientAddress: feeRecipientAddressLeft, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const defaultOrderParamsRight = { + ...constants.STATIC_ORDER_PARAMS, + makerAddress: makerAddressRight, + exchangeAddress: exchange.address, + makerAssetData: leftTakerAssetData, + takerAssetData: leftMakerAssetData, + feeRecipientAddress: feeRecipientAddressRight, + makerFee: constants.ZERO_AMOUNT, + takerFee: constants.ZERO_AMOUNT, + }; + const privateKeyLeft = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressLeft)]; + orderFactoryLeft = new OrderFactory(privateKeyLeft, defaultOrderParamsLeft); + const privateKeyRight = constants.TESTRPC_PRIVATE_KEYS[accounts.indexOf(makerAddressRight)]; + orderFactoryRight = new OrderFactory(privateKeyRight, defaultOrderParamsRight); + }); + beforeEach(async () => { + await blockchainLifecycle.startAsync(); + }); + afterEach(async () => { + await blockchainLifecycle.revertAsync(); + }); + describe('constructor', () => { + it('should revert if assetProxy is unregistered', async () => { + const exchangeInstance = await ExchangeContract.deployFrom0xArtifactAsync( + protocolArtifacts.Exchange, + provider, + txDefaults, + constants.NULL_BYTES, + ); + return expectContractCreationFailedAsync( + (OrderMatcherContract.deployFrom0xArtifactAsync( + artifacts.OrderMatcher, + provider, + txDefaults, + exchangeInstance.address, + ) as any) as sendTransactionResult, + RevertReason.UnregisteredAssetProxy, + ); + }); + }); + describe('matchOrders', () => { + beforeEach(async () => { + erc20BalancesByOwner = await erc20Wrapper.getBalancesAsync(); + }); + it('should revert if not called by owner', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: takerAddress, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.OnlyContractOwner, + ); + }); + it('should transfer the correct amounts when orders completely fill each other', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + }); + it('should transfer the correct amounts when orders completely fill each other and taker doesnt take a profit', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal(initialLeftMakerAssetTakerBalance); + }); + it('should transfer the correct amounts when left order is completely filled and right order would be partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should not call fillOrder when rightOrder is completely filled after matchOrders call', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts }); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + ); + const fillLogs = _.filter( + txReceipt.logs, + log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', + ); + // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. + expect(fillLogs.length).to.be.equal(2); + }); + it('should only take a spread in rightMakerAsset if entire leftMakerAssetSpread amount can be used to fill rightOrder after matchOrders call', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(0.9), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(100), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(990), 18), + }); + const initialLeftMakerAssetSpreadAmount = Web3Wrapper.toBaseUnitAmount(new BigNumber(1.09), 18); + const leftTakerAssetSpreadAmount = initialLeftMakerAssetSpreadAmount + .times(signedOrderRight.makerAssetAmount) + .dividedToIntegerBy(signedOrderRight.takerAssetAmount); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderLeft.takerAssetAmount.plus(leftTakerAssetSpreadAmount), + amountBoughtByRightMaker: signedOrderLeft.makerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: constants.ZERO_AMOUNT, + leftTakerAssetSpreadAmount, + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it("should succeed if rightOrder's makerAssetData and takerAssetData are not provided", async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Match signedOrderLeft with signedOrderRight + const expectedTransferAmounts = { + // Left Maker + amountSoldByLeftMaker: signedOrderLeft.makerAssetAmount, + amountBoughtByLeftMaker: signedOrderLeft.takerAssetAmount, + // Right Maker + amountSoldByRightMaker: signedOrderRight.makerAssetAmount, + amountBoughtByRightMaker: signedOrderRight.takerAssetAmount, + // Taker + leftMakerAssetSpreadAmount: signedOrderLeft.makerAssetAmount.minus(signedOrderRight.takerAssetAmount), + leftTakerAssetSpreadAmount: signedOrderRight.makerAssetAmount.minus(signedOrderLeft.takerAssetAmount), + }; + const initialLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const initialLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + // Match signedOrderLeft with signedOrderRight + signedOrderRight.makerAssetData = constants.NULL_BYTES; + signedOrderRight.takerAssetData = constants.NULL_BYTES; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await web3Wrapper.awaitTransactionSuccessAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newLeftMakerAssetTakerBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + const newLeftTakerAssetTakerBalance = await erc20TokenB.balanceOf.callAsync(orderMatcher.address); + const newErc20Balances = await erc20Wrapper.getBalancesAsync(); + expect(newErc20Balances[makerAddressLeft][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20MakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20TakerAssetAddress].minus( + expectedTransferAmounts.amountSoldByRightMaker, + ), + ); + expect(newErc20Balances[makerAddressLeft][defaultERC20TakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressLeft][defaultERC20TakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByLeftMaker, + ), + ); + expect(newErc20Balances[makerAddressRight][defaultERC20MakerAssetAddress]).to.be.bignumber.equal( + erc20BalancesByOwner[makerAddressRight][defaultERC20MakerAssetAddress].plus( + expectedTransferAmounts.amountBoughtByRightMaker, + ), + ); + expect(newLeftMakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftMakerAssetTakerBalance.plus(expectedTransferAmounts.leftMakerAssetSpreadAmount), + ); + expect(newLeftTakerAssetTakerBalance).to.be.bignumber.equal( + initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), + ); + }); + it('should revert with the correct reason if matchOrders call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + }); + signedOrderRight.signature = `0xff${signedOrderRight.signature.slice(4)}`; + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.InvalidOrderSignature, + ); + }); + it('should revert with the correct reason if fillOrder call reverts', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(20), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(4), 18), + }); + // Matcher will not have enough allowance to fill rightOrder + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, constants.ZERO_AMOUNT, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + await expectTransactionFailedAsync( + web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + RevertReason.TransferFailed, + ); + }); + }); + describe('withdrawAsset', () => { + it('should allow owner to withdraw ERC20 tokens', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: owner, + }), + ); + const newBalance = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(newBalance).to.be.bignumber.equal(constants.ZERO_AMOUNT); + }); + it('should allow owner to withdraw ERC721 tokens', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const tokenId = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await erc721Token.mint.sendTransactionAsync(orderMatcher.address, tokenId, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, tokenId); + const withdrawAmount = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.withdrawAsset.sendTransactionAsync(assetData, withdrawAmount, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const erc721Owner = await erc721Token.ownerOf.callAsync(tokenId); + expect(erc721Owner).to.be.equal(owner); + }); + it('should revert if not called by owner', async () => { + const erc20AWithdrawAmount = await erc20TokenA.balanceOf.callAsync(orderMatcher.address); + expect(erc20AWithdrawAmount).to.be.bignumber.gt(constants.ZERO_AMOUNT); + await expectTransactionFailedAsync( + orderMatcher.withdrawAsset.sendTransactionAsync(leftMakerAssetData, erc20AWithdrawAmount, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); + describe('approveAssetProxy', () => { + it('should be able to set an allowance for ERC20 tokens', async () => { + const allowance = new BigNumber(55465465426546); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, allowance, { + from: owner, + }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const newAllowance = await erc20TokenA.allowance.callAsync(orderMatcher.address, erc20Proxy.address); + expect(newAllowance).to.be.bignumber.equal(allowance); + }); + it('should be able to approve an ERC721 token by passing in allowance = 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(1); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should be able to approve an ERC721 token by passing in allowance > 1', async () => { + const erc721Token = await DummyERC721TokenContract.deployFrom0xArtifactAsync( + tokenArtifacts.DummyERC721Token, + provider, + txDefaults, + constants.DUMMY_TOKEN_NAME, + constants.DUMMY_TOKEN_SYMBOL, + ); + const assetData = assetDataUtils.encodeERC721AssetData(erc721Token.address, constants.ZERO_AMOUNT); + const allowance = new BigNumber(2); + await web3Wrapper.awaitTransactionSuccessAsync( + await orderMatcher.approveAssetProxy.sendTransactionAsync(assetData, allowance, { from: owner }), + constants.AWAIT_TRANSACTION_MINED_MS, + ); + const isApproved = await erc721Token.isApprovedForAll.callAsync(orderMatcher.address, erc721Proxy.address); + expect(isApproved).to.be.equal(true); + }); + it('should revert if not called by owner', async () => { + const approval = new BigNumber(1); + await expectTransactionFailedAsync( + orderMatcher.approveAssetProxy.sendTransactionAsync(leftMakerAssetData, approval, { + from: takerAddress, + }), + RevertReason.OnlyContractOwner, + ); + }); + }); +}); +// tslint:disable:max-file-line-count +// tslint:enable:no-unnecessary-type-assertion diff --git a/contracts/extensions/tsconfig.json b/contracts/extensions/tsconfig.json index a303e3f5c..506c283aa 100644 --- a/contracts/extensions/tsconfig.json +++ b/contracts/extensions/tsconfig.json @@ -9,7 +9,8 @@ "files": [ "./generated-artifacts/BalanceThresholdFilter.json", "./generated-artifacts/DutchAuction.json", - "./generated-artifacts/Forwarder.json" + "./generated-artifacts/Forwarder.json", + "./generated-artifacts/OrderMatcher.json" ], "exclude": ["./deploy/solc/solc_bin"] } diff --git a/contracts/protocol/src/artifacts/index.ts b/contracts/protocol/src/artifacts/index.ts index 2a9de78d2..1d53ceb04 100644 --- a/contracts/protocol/src/artifacts/index.ts +++ b/contracts/protocol/src/artifacts/index.ts @@ -6,7 +6,6 @@ import * as ERC721Proxy from '../../generated-artifacts/ERC721Proxy.json'; import * as Exchange from '../../generated-artifacts/Exchange.json'; import * as MixinAuthorizable from '../../generated-artifacts/MixinAuthorizable.json'; import * as MultiAssetProxy from '../../generated-artifacts/MultiAssetProxy.json'; -import * as OrderMatcher from '../../generated-artifacts/OrderMatcher.json'; import * as OrderValidator from '../../generated-artifacts/OrderValidator.json'; import * as TestAssetProxyDispatcher from '../../generated-artifacts/TestAssetProxyDispatcher.json'; import * as TestAssetProxyOwner from '../../generated-artifacts/TestAssetProxyOwner.json'; @@ -21,7 +20,6 @@ export const artifacts = { Exchange: Exchange as ContractArtifact, MixinAuthorizable: MixinAuthorizable as ContractArtifact, MultiAssetProxy: MultiAssetProxy as ContractArtifact, - OrderMatcher: OrderMatcher as ContractArtifact, OrderValidator: OrderValidator as ContractArtifact, TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact, TestAssetProxyOwner: TestAssetProxyOwner as ContractArtifact, diff --git a/contracts/protocol/src/wrappers/index.ts b/contracts/protocol/src/wrappers/index.ts index 13f16f44f..ac951d269 100644 --- a/contracts/protocol/src/wrappers/index.ts +++ b/contracts/protocol/src/wrappers/index.ts @@ -3,7 +3,6 @@ export * from '../../generated-wrappers/erc20_proxy'; export * from '../../generated-wrappers/erc721_proxy'; export * from '../../generated-wrappers/exchange'; export * from '../../generated-wrappers/mixin_authorizable'; -export * from '../../generated-wrappers/order_matcher'; export * from '../../generated-wrappers/order_validator'; export * from '../../generated-wrappers/test_asset_proxy_dispatcher'; export * from '../../generated-wrappers/test_asset_proxy_owner'; diff --git a/contracts/protocol/tsconfig.json b/contracts/protocol/tsconfig.json index 7e4ce5eb4..989d3ef2b 100644 --- a/contracts/protocol/tsconfig.json +++ b/contracts/protocol/tsconfig.json @@ -13,7 +13,6 @@ "./generated-artifacts/Exchange.json", "./generated-artifacts/MixinAuthorizable.json", "./generated-artifacts/MultiAssetProxy.json", - "./generated-artifacts/OrderMatcher.json", "./generated-artifacts/OrderValidator.json", "./generated-artifacts/TestAssetProxyDispatcher.json", "./generated-artifacts/TestAssetProxyOwner.json", -- cgit v1.2.3 From e74b24bbdb1f6f53e5437ae3d3b2ebc2aa2adba6 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Mon, 17 Dec 2018 12:59:39 -0800 Subject: Update comments and hard code function selector constants --- .../contracts/OrderMatcher/MixinMatchOrders.sol | 100 ++++++++++----------- .../contracts/OrderMatcher/libs/LibConstants.sol | 9 +- 2 files changed, 56 insertions(+), 53 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol index 866523190..c691b732f 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -126,23 +126,23 @@ contract MixinMatchOrders is // | 420 | | 10. salt | // | 452 | | 11. offset to leftMakerAssetData (*) | // | 484 | | 12. offset to leftTakerAssetData (*) | + // | 516 | 32 | leftMakerAssetData Length | + // | 548 | a | leftMakerAssetData Contents | + // | 548 + a | 32 | leftTakerAssetData Length | + // | 580 + a | b | leftTakerAssetData Contents | // | | 12 * 32 | rightOrder: | - // | 516 | | 1. senderAddress | - // | 548 | | 2. makerAddress | - // | 580 | | 3. takerAddress | - // | 612 | | 4. feeRecipientAddress | - // | 644 | | 5. makerAssetAmount | - // | 676 | | 6. takerAssetAmount | - // | 708 | | 7. makerFeeAmount | - // | 740 | | 8. takerFeeAmount | - // | 772 | | 9. expirationTimeSeconds | - // | 804 | | 10. salt | - // | 836 | | 11. offset to rightMakerAssetData (*) | - // | 868 | | 12. offset to rightTakerAssetData (*) | - // | 900 | 32 | leftMakerAssetData Length | - // | 932 | a | leftMakerAssetData Contents | - // | 932 + a | 32 | leftTakerAssetData Length | - // | 964 + a | b | leftTakerAssetData Contents | + // | 580 + a + b | | 1. senderAddress | + // | 612 + a + b | | 2. makerAddress | + // | 644 + a + b | | 3. takerAddress | + // | 676 + a + b | | 4. feeRecipientAddress | + // | 708 + a + b | | 5. makerAssetAmount | + // | 740 + a + b | | 6. takerAssetAmount | + // | 772 + a + b | | 7. makerFeeAmount | + // | 804 + a + b | | 8. takerFeeAmount | + // | 836 + a + b | | 9. expirationTimeSeconds | + // | 868 + a + b | | 10. salt | + // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | + // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | // | 964 + a + b | 32 | rightMakerAssetData Length | // | 996 + a + b | c | rightMakerAssetData Contents | // | 996 + a + b + c | 32 | rightTakerAssetData Length | @@ -159,7 +159,7 @@ contract MixinMatchOrders is 0, // transfer 0 wei 0, // input starts at 0 calldatasize(), // length of input - 0, // write output over output + 0, // write output over input 288 // length of output is 288 bytes ) @@ -183,18 +183,18 @@ contract MixinMatchOrders is // | 224 | | 8. right.takerFeePaid | // | 256 | | 9. leftMakerAssetSpreadAmount | // | | 12 * 32 | rightOrder: | - // | 516 | | 1. senderAddress | - // | 548 | | 2. makerAddress | - // | 580 | | 3. takerAddress | - // | 612 | | 4. feeRecipientAddress | - // | 644 | | 5. makerAssetAmount | - // | 676 | | 6. takerAssetAmount | - // | 708 | | 7. makerFeeAmount | - // | 740 | | 8. takerFeeAmount | - // | 772 | | 9. expirationTimeSeconds | - // | 804 | | 10. salt | - // | 836 | | 11. offset to rightMakerAssetData (*) | - // | 868 | | 12. offset to rightTakerAssetData (*) | + // | 580 + a + b | | 1. senderAddress | + // | 612 + a + b | | 2. makerAddress | + // | 644 + a + b | | 3. takerAddress | + // | 676 + a + b | | 4. feeRecipientAddress | + // | 708 + a + b | | 5. makerAssetAmount | + // | 740 + a + b | | 6. takerAssetAmount | + // | 772 + a + b | | 7. makerFeeAmount | + // | 804 + a + b | | 8. takerFeeAmount | + // | 836 + a + b | | 9. expirationTimeSeconds | + // | 868 + a + b | | 10. salt | + // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | + // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | let rightOrderStart := add(calldataload(36), 4) @@ -208,29 +208,29 @@ contract MixinMatchOrders is // | Offset | Length | Contents | // |---------------------------|---------|-------------------------------------------- | - // | 416 | 4 | `fillOrder` function selector | + // | 480 + a + b | 4 | `fillOrder` function selector | // | | 3 * 32 | function parameters: | - // | 420 | | 1. offset to rightOrder (*) | - // | 452 | | 2. takerAssetFillAmount | - // | 484 | | 3. offset to rightSignature (*) | + // | 484 + a + b | | 1. offset to rightOrder (*) | + // | 516 + a + b | | 2. takerAssetFillAmount | + // | 548 + a + b | | 3. offset to rightSignature (*) | // | | 12 * 32 | rightOrder: | - // | 516 | | 1. senderAddress | - // | 548 | | 2. makerAddress | - // | 580 | | 3. takerAddress | - // | 612 | | 4. feeRecipientAddress | - // | 644 | | 5. makerAssetAmount | - // | 676 | | 6. takerAssetAmount | - // | 708 | | 7. makerFeeAmount | - // | 740 | | 8. takerFeeAmount | - // | 772 | | 9. expirationTimeSeconds | - // | 804 | | 10. salt | - // | 836 | | 11. offset to rightMakerAssetData (*) | - // | 868 | | 12. offset to rightTakerAssetData (*) | - // | 900 | 32 | rightMakerAssetData Length | - // | 932 | a | rightMakerAssetData Contents | - // | 932 + a | 32 | rightTakerAssetData Length | - // | 964 | b | rightTakerAssetData Contents | - // | 964 + b | 32 | rightSignature Length (always 0) | + // | 580 + a + b | | 1. senderAddress | + // | 612 + a + b | | 2. makerAddress | + // | 644 + a + b | | 3. takerAddress | + // | 676 + a + b | | 4. feeRecipientAddress | + // | 708 + a + b | | 5. makerAssetAmount | + // | 740 + a + b | | 6. takerAssetAmount | + // | 772 + a + b | | 7. makerFeeAmount | + // | 804 + a + b | | 8. takerFeeAmount | + // | 836 + a + b | | 9. expirationTimeSeconds | + // | 868 + a + b | | 10. salt | + // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | + // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | + // | 964 + a + b | 32 | rightMakerAssetData Length | + // | 996 + a + b | c | rightMakerAssetData Contents | + // | 996 + a + b + c | 32 | rightTakerAssetData Length | + // | 1028 + a + b + c | d | rightTakerAssetData Contents | + // | 1028 + a + b + c + d | 32 | rightSignature Length (always 0) | // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` // `EXCHANGE.matchOrders` already makes this assumption, so it is likely diff --git a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol index 43bbdeec2..bd6a5e0ee 100644 --- a/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol +++ b/contracts/extensions/contracts/OrderMatcher/libs/LibConstants.sol @@ -23,9 +23,12 @@ import "@0x/contracts-interfaces/contracts/protocol/Exchange/IExchange.sol"; contract LibConstants { - bytes4 constant internal ERC20_TRANSFER_SELECTOR = bytes4(keccak256("transfer(address,uint256)")); - bytes4 constant internal ERC20_DATA_ID = bytes4(keccak256("ERC20Token(address)")); - bytes4 constant internal ERC721_DATA_ID = bytes4(keccak256("ERC721Token(address,uint256)")); + // bytes4(keccak256("transfer(address,uint256)")) + bytes4 constant internal ERC20_TRANSFER_SELECTOR = 0xa9059cbb; + // bytes4(keccak256("ERC20Token(address)")) + bytes4 constant internal ERC20_DATA_ID = 0xf47261b0; + // bytes4(keccak256("ERC721Token(address,uint256)")) + bytes4 constant internal ERC721_DATA_ID = 0x02571792; // solhint-disable var-name-mixedcase IExchange internal EXCHANGE; -- cgit v1.2.3 From 90fcf59a3257df91653324a48e031bdc080f841b Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Mon, 17 Dec 2018 22:07:12 -0800 Subject: Add getOrderInfo check before calling fillOrder --- .../contracts/OrderMatcher/MixinMatchOrders.sol | 322 +++++++++++++-------- .../extensions/test/extensions/order_matcher.ts | 44 ++- 2 files changed, 242 insertions(+), 124 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol index c691b732f..b6bc83af9 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -25,7 +25,7 @@ import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; contract MixinMatchOrders is Ownable, LibConstants -{ +{ // The below assembly in the fallback function is functionaly equivalent to the following Solidity code: /* /// @dev Match two complementary orders that have a profitable spread. @@ -54,21 +54,33 @@ contract MixinMatchOrders is rightSignature ); - // If a spread was taken, use the spread to fill remaining amount of `rightOrder` - // Only attempt to fill `rightOrder` if a spread was taken and if not already completely filled uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; - if (leftMakerAssetSpreadAmount > 0 && matchedFillResults.right.takerAssetFilledAmount < rightOrder.takerAssetAmount) { - // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. - rightOrder.makerAssetData = leftOrder.takerAssetData; - rightOrder.takerAssetData = leftOrder.makerAssetData; - - // We do not need to pass in a signature since it was already validated in the `matchOrders` call - EXCHANGE.fillOrder( - rightOrder, - leftMakerAssetSpreadAmount, - "" - ); + uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount; + + // Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled + if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; + } + + // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. + rightOrder.makerAssetData = leftOrder.takerAssetData; + rightOrder.takerAssetData = leftOrder.makerAssetData; + + // Query `rightOrder` info to check if it has been completely filled + // We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call + OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder); + + // Do not attempt to call `fillOrder` if order has been completely filled + if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; } + + // We do not need to pass in a signature since it was already validated in the `matchOrders` call + EXCHANGE.fillOrder( + rightOrder, + leftMakerAssetSpreadAmount, + "" + ); } */ // solhint-disable-next-line payable-fallback @@ -101,7 +113,11 @@ contract MixinMatchOrders is // Copy calldata to memory // The calldata should be identical to the the ABIv2 encoded data required to call `EXCHANGE.matchOrders` - calldatacopy(0, 0, calldatasize()) + calldatacopy( + 0, // copy to memory at 0 + 0, // copy from calldata at 0 + calldatasize() // copy all calldata + ) // At this point, calldata and memory have the following layout: @@ -153,7 +169,7 @@ contract MixinMatchOrders is // | 1092 + a + b + c + d + e | f | rightSignature Contents | // Call `EXCHANGE.matchOrders` - let matchOrdersSuccess := call( + let success := call( gas, // forward all gas exchange, // call address of Exchange contract 0, // transfer 0 wei @@ -163,12 +179,17 @@ contract MixinMatchOrders is 288 // length of output is 288 bytes ) - if iszero(matchOrdersSuccess) { - // Revert with reason if `matchOrders` call was unsuccessful + if iszero(success) { + // Revert with reason if `EXCHANGE.matchOrders` call was unsuccessful + returndatacopy( + 0, // copy to memory at 0 + 0, // copy from return data at 0 + returndatasize() // copy all return data + ) revert(0, returndatasize()) } - // After calling `matchOrders`, the relevant parts of memory are: + // After calling `matchOrders`, the return data is layed out in memory as: // | Offset | Length | Contents | // |---------------------------|---------|-------------------------------------------- | @@ -182,6 +203,67 @@ contract MixinMatchOrders is // | 192 | | 7. right.makerFeePaid | // | 224 | | 8. right.takerFeePaid | // | 256 | | 9. leftMakerAssetSpreadAmount | + + let rightOrderStart := add(calldataload(36), 4) + + // If no spread was taken or if the entire `rightOrderTakerAssetAmount` has been filled, there is no need to call `EXCHANGE.fillOrder`. + if or( + iszero(mload(256)), // iszero(leftMakerAssetSpreadAmount) + eq(mload(160), calldataload(add(rightOrderStart, 160))) // eq(rightOrderTakerAssetFilledAmount, rightOrderTakerAssetAmount) + ) { + return(0, 0) + } + + // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` + // `EXCHANGE.matchOrders` already makes this assumption, so it is likely + // that the `rightMakerAssetData` and `rightTakerAssetData` in calldata are empty + + let leftOrderStart := add(calldataload(4), 4) + + // Calculate locations of `leftMakerAssetData` and `leftTakerAssetData` in calldata + let leftMakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 320)) // offset to `leftMakerAssetData` + ) + let leftTakerAssetDataStart := add( + leftOrderStart, + calldataload(add(leftOrderStart, 352)) // offset to `leftTakerAssetData` + ) + + // Load lengths of `leftMakerAssetData` and `leftTakerAssetData` + let leftMakerAssetDataLen := calldataload(leftMakerAssetDataStart) + let leftTakerAssetDataLen := calldataload(leftTakerAssetDataStart) + + // Write offset to `rightMakerAssetData` + mstore(add(rightOrderStart, 320), 384) + + // Write offset to `rightTakerAssetData` + let rightTakerAssetDataOffset := add(416, leftTakerAssetDataLen) + mstore(add(rightOrderStart, 352), rightTakerAssetDataOffset) + + // Copy `leftTakerAssetData` from calldata onto `rightMakerAssetData` in memory + calldatacopy( + add(rightOrderStart, 384), // `rightMakerAssetDataStart` + leftTakerAssetDataStart, + add(leftTakerAssetDataLen, 32) + ) + + // Copy `leftMakerAssetData` from calldata onto `rightTakerAssetData` in memory + calldatacopy( + add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` + leftMakerAssetDataStart, + add(leftMakerAssetDataLen, 32) + ) + + // We must call `EXCHANGE.getOrderInfo(rightOrder)` before calling `EXCHANGE.fillOrder` to ensure that the order + // has not already been entirely filled (which is possible if an order was partially filled before the `matchOrders` call). + // We want the following layout in memory before calling `getOrderInfo`: + + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 544 + a + b | 4 | `getOrderInfo` function selector | + // | | 3 * 32 | function parameters: | + // | 548 + a + b | | 1. offset to rightOrder (*) | // | | 12 * 32 | rightOrder: | // | 580 + a + b | | 1. senderAddress | // | 612 + a + b | | 2. makerAddress | @@ -195,126 +277,124 @@ contract MixinMatchOrders is // | 868 + a + b | | 10. salt | // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | + // | 964 + a + b | 32 | rightMakerAssetData Length | + // | 996 + a + b | c | rightMakerAssetData Contents | + // | 996 + a + b + c | 32 | rightTakerAssetData Length | + // | 1028 + a + b + c | d | rightTakerAssetData Contents | - let rightOrderStart := add(calldataload(36), 4) + // function selector (4 bytes) + 1 param (32 bytes) must be stored before `rightOrderStart` + let cdStart := sub(rightOrderStart, 36) - // Only call `fillOrder` if a spread was taken and `rightOrder` has not been completely filled - if and( - gt(mload(256), 0), // gt(leftMakerAssetSpreadAmount, 0) - lt(mload(160), calldataload(add(rightOrderStart, 160))) // lt(rightOrderTakerAssetFilledAmount, rightOrderTakerAssetAmount) - ) { - - // We want the following layout in memory before calling `fillOrder`: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | 480 + a + b | 4 | `fillOrder` function selector | - // | | 3 * 32 | function parameters: | - // | 484 + a + b | | 1. offset to rightOrder (*) | - // | 516 + a + b | | 2. takerAssetFillAmount | - // | 548 + a + b | | 3. offset to rightSignature (*) | - // | | 12 * 32 | rightOrder: | - // | 580 + a + b | | 1. senderAddress | - // | 612 + a + b | | 2. makerAddress | - // | 644 + a + b | | 3. takerAddress | - // | 676 + a + b | | 4. feeRecipientAddress | - // | 708 + a + b | | 5. makerAssetAmount | - // | 740 + a + b | | 6. takerAssetAmount | - // | 772 + a + b | | 7. makerFeeAmount | - // | 804 + a + b | | 8. takerFeeAmount | - // | 836 + a + b | | 9. expirationTimeSeconds | - // | 868 + a + b | | 10. salt | - // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | - // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | - // | 964 + a + b | 32 | rightMakerAssetData Length | - // | 996 + a + b | c | rightMakerAssetData Contents | - // | 996 + a + b + c | 32 | rightTakerAssetData Length | - // | 1028 + a + b + c | d | rightTakerAssetData Contents | - // | 1028 + a + b + c + d | 32 | rightSignature Length (always 0) | - - // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` - // `EXCHANGE.matchOrders` already makes this assumption, so it is likely - // that the `rightMakerAssetData` and `rightTakerAssetData` in calldata are empty - - let leftOrderStart := add(calldataload(4), 4) - - // Calculate locations of `leftMakerAssetData` and `leftTakerAssetData` in calldata - let leftMakerAssetDataStart := add( - leftOrderStart, - calldataload(add(leftOrderStart, 320)) // offset to `leftMakerAssetData` - ) - let leftTakerAssetDataStart := add( - leftOrderStart, - calldataload(add(leftOrderStart, 352)) // offset to `leftTakerAssetData` - ) + // `getOrderInfo` selector = 0xc75e0a81 + mstore(cdStart, 0xc75e0a8100000000000000000000000000000000000000000000000000000000) - // Load lengths of `leftMakerAssetData` and `leftTakerAssetData` - let leftMakerAssetDataLen := calldataload(leftMakerAssetDataStart) - let leftTakerAssetDataLen := calldataload(leftTakerAssetDataStart) + // Write offset to `rightOrder` + mstore(add(cdStart, 4), 32) - // Write offset to `rightMakerAssetData` - mstore(add(rightOrderStart, 320), 384) + let rightOrderEnd := add( + add(rightOrderStart, 484), + add(leftMakerAssetDataLen, leftTakerAssetDataLen) + ) - // Write offset to `rightTakerAssetData` - let rightTakerAssetDataOffset := add(416, leftTakerAssetDataLen) - mstore(add(rightOrderStart, 352), rightTakerAssetDataOffset) + // Call `EXCHANGE.getOrderInfo(rightOrder)` + success := staticcall( + gas, // forward all gas + exchange, // call address of Exchange contract + cdStart, // start of input + sub(rightOrderEnd, cdStart), // length of input + 0, // write output over old output + 96 // output is 96 bytes + ) - // Copy `leftTakerAssetData` from calldata onto `rightMakerAssetData` in memory - calldatacopy( - add(rightOrderStart, 384), // `rightMakerAssetDataStart` - leftTakerAssetDataStart, - add(leftTakerAssetDataLen, 32) - ) + // After calling `EXCHANGE.getOrderInfo`, the return data is layed out in memory as: - // Copy `leftMakerAssetData` from calldata onto `rightTakerAssetData` in memory - calldatacopy( - add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` - leftMakerAssetDataStart, - add(leftMakerAssetDataLen, 32) - ) + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | | 3 * 32 | orderInfo | + // | 0 | | 1. orderStatus | + // | 32 | | 2. orderHash | + // | 64 | | 3. orderTakerAssetFilledAmount | + + // We do not need to check if the `getOrderInfo` call was successful because it has no possibility of reverting + // If order has been entirely filled, there is no need to call `EXCHANGE.fillOrder` + // eq(orderTakerAssetFilledAmount, rightOrderTakerAssetAmount) + if eq(mload(64), calldataload(add(rightOrderStart, 160))) { + return(0, 0) + } - // Write length of signature (always 0 since signature was previously validated) - let rightSignatureStart := add( - add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` - add(leftMakerAssetDataLen, 32) - ) - mstore(rightSignatureStart, 0) + // We want the following layout in memory before calling `EXCHANGE.fillOrder`: - // function selector (4 bytes) + 3 params (3 * 32 bytes) must be stored before `rightOrderStart` - let cdStart := sub(rightOrderStart, 100) + // | Offset | Length | Contents | + // |---------------------------|---------|-------------------------------------------- | + // | 480 + a + b | 4 | `fillOrder` function selector | + // | | 3 * 32 | function parameters: | + // | 484 + a + b | | 1. offset to rightOrder (*) | + // | 516 + a + b | | 2. takerAssetFillAmount | + // | 548 + a + b | | 3. offset to rightSignature (*) | + // | | 12 * 32 | rightOrder: | + // | 580 + a + b | | 1. senderAddress | + // | 612 + a + b | | 2. makerAddress | + // | 644 + a + b | | 3. takerAddress | + // | 676 + a + b | | 4. feeRecipientAddress | + // | 708 + a + b | | 5. makerAssetAmount | + // | 740 + a + b | | 6. takerAssetAmount | + // | 772 + a + b | | 7. makerFeeAmount | + // | 804 + a + b | | 8. takerFeeAmount | + // | 836 + a + b | | 9. expirationTimeSeconds | + // | 868 + a + b | | 10. salt | + // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | + // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | + // | 964 + a + b | 32 | rightMakerAssetData Length | + // | 996 + a + b | c | rightMakerAssetData Contents | + // | 996 + a + b + c | 32 | rightTakerAssetData Length | + // | 1028 + a + b + c | d | rightTakerAssetData Contents | + // | 1028 + a + b + c + d | 32 | rightSignature Length (always 0) | - // `fillOrder` selector = 0xb4be83d5 - mstore(cdStart, 0xb4be83d500000000000000000000000000000000000000000000000000000000) + // Write length of signature (always 0 since signature was previously validated) + mstore(rightOrderEnd, 0) - // Write offset to `rightOrder` - mstore(add(cdStart, 4), 96) + // function selector (4 bytes) + 3 params (3 * 32 bytes) must be stored before `rightOrderStart` + cdStart := sub(rightOrderStart, 100) - // Write `takerAssetFillAmount`, which will be the `leftMakerAssetSpreadAmount` received from the `matchOrders` call - mstore(add(cdStart, 36), mload(256)) + // `fillOrder` selector = 0xb4be83d5 + mstore(cdStart, 0xb4be83d500000000000000000000000000000000000000000000000000000000) - // Write offset to `rightSignature` - mstore(add(cdStart, 68), sub(rightSignatureStart, add(cdStart, 4))) + // Write offset to `rightOrder` + mstore(add(cdStart, 4), 96) - let fillOrderSuccess := call( - gas, // forward all gas - exchange, // call address of Exchange contract - 0, // transfer 0 wei - cdStart, // start of input - sub(add(rightSignatureStart, 32), cdStart), // length of input is end - start - 0, // write output over input - 128 // length of output is 128 bytes - ) + // Write `takerAssetFillAmount`, which will be the `leftMakerAssetSpreadAmount` received from the `matchOrders` call + mstore(add(cdStart, 36), mload(256)) - if fillOrderSuccess { - return(0, 0) - } - - // Revert with reason if `fillOrder` call was unsuccessful + // Write offset to `rightSignature` + mstore( + add(cdStart, 68), + sub(rightOrderEnd, add(cdStart, 4)) + ) + + // Call `EXCHANGE.fillOrder(rightOrder, leftMakerAssetSpreadAmount, "")` + success := call( + gas, // forward all gas + exchange, // call address of Exchange contract + 0, // transfer 0 wei + cdStart, // start of input + add(sub(rightOrderEnd, cdStart), 32), // length of input is end - start + 0, // write output over input + 0 // do not write output to memory + ) + + if iszero(success) { + // Revert with reason if `EXCHANGE.fillOrder` call was unsuccessful + returndatacopy( + 0, // copy to memory at 0 + 0, // copy from return data at 0 + returndatasize() // copy all return data + ) revert(0, returndatasize()) } - - // Return if `matchOrders` call successful and `fillOrder` was not called + + // Return if `EXCHANGE.fillOrder` did not revert return(0, 0) + } // Revert if undefined function is called diff --git a/contracts/extensions/test/extensions/order_matcher.ts b/contracts/extensions/test/extensions/order_matcher.ts index 45002b324..acb46ced4 100644 --- a/contracts/extensions/test/extensions/order_matcher.ts +++ b/contracts/extensions/test/extensions/order_matcher.ts @@ -435,7 +435,7 @@ describe('OrderMatcher', () => { initialLeftTakerAssetTakerBalance.plus(expectedTransferAmounts.leftTakerAssetSpreadAmount), ); }); - it('should not call fillOrder when rightOrder is completely filled after matchOrders call', async () => { + it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were never partially filled', async () => { // Create orders to match const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), @@ -443,7 +443,45 @@ describe('OrderMatcher', () => { }); const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), - takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + const data = exchange.matchOrders.getABIEncodedTransactionData( + signedOrderLeft, + signedOrderRight, + signedOrderLeft.signature, + signedOrderRight.signature, + ); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts, ...protocolArtifacts }); + const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( + await web3Wrapper.sendTransactionAsync({ + data, + to: orderMatcher.address, + from: owner, + gas: constants.MAX_MATCH_ORDERS_GAS, + }), + ); + const fillLogs = _.filter( + txReceipt.logs, + log => (log as LogWithDecodedArgs<ExchangeFillEventArgs>).event === 'Fill', + ); + // Only 2 Fill logs should exist for `matchOrders` call. `fillOrder` should not have been called and should not have emitted a Fill event. + expect(fillLogs.length).to.be.equal(2); + }); + it('should not call fillOrder when rightOrder is completely filled after matchOrders call and orders were initially partially filled', async () => { + // Create orders to match + const signedOrderLeft = await orderFactoryLeft.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(5), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + }); + const signedOrderRight = await orderFactoryRight.newSignedOrderAsync({ + makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18), + takerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(2), 18), + }); + await exchangeWrapper.fillOrderAsync(signedOrderLeft, takerAddress, { + takerAssetFillAmount: signedOrderLeft.takerAssetAmount.dividedToIntegerBy(5), + }); + await exchangeWrapper.fillOrderAsync(signedOrderRight, takerAddress, { + takerAssetFillAmount: signedOrderRight.takerAssetAmount.dividedToIntegerBy(5), }); const data = exchange.matchOrders.getABIEncodedTransactionData( signedOrderLeft, @@ -451,7 +489,7 @@ describe('OrderMatcher', () => { signedOrderLeft.signature, signedOrderRight.signature, ); - const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts }); + const logDecoder = new LogDecoder(web3Wrapper, { ...artifacts, ...tokenArtifacts, ...protocolArtifacts }); const txReceipt = await logDecoder.getTxWithDecodedLogsAsync( await web3Wrapper.sendTransactionAsync({ data, -- cgit v1.2.3 From 58be23ac9798dfb0c8dd5146aa41872b03dff174 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Wed, 19 Dec 2018 14:15:25 -0800 Subject: Remove assembly version of matchOrders --- .../contracts/OrderMatcher/MixinMatchOrders.sol | 420 +++------------------ .../contracts/OrderMatcher/OrderMatcher.sol | 1 + 2 files changed, 52 insertions(+), 369 deletions(-) (limited to 'contracts') diff --git a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol index b6bc83af9..f75cecdc1 100644 --- a/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol +++ b/contracts/extensions/contracts/OrderMatcher/MixinMatchOrders.sol @@ -17,8 +17,11 @@ */ pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; import "./libs/LibConstants.sol"; +import "@0x/contracts-libs/contracts/libs/LibOrder.sol"; +import "@0x/contracts-libs/contracts/libs/LibFillResults.sol"; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; @@ -26,379 +29,58 @@ contract MixinMatchOrders is Ownable, LibConstants { - // The below assembly in the fallback function is functionaly equivalent to the following Solidity code: - /* - /// @dev Match two complementary orders that have a profitable spread. - /// Each order is filled at their respective price point. However, the calculations are - /// carried out as though the orders are both being filled at the right order's price point. - /// The profit made by the left order is then used to fill the right order as much as possible. - /// This results in a spread being taken in terms of both assets. The spread is held within this contract. - /// @param leftOrder First order to match. - /// @param rightOrder Second order to match. - /// @param leftSignature Proof that order was created by the left maker. - /// @param rightSignature Proof that order was created by the right maker. - function matchOrders( - LibOrder.Order memory leftOrder, - LibOrder.Order memory rightOrder, - bytes memory leftSignature, - bytes memory rightSignature - ) - public - onlyOwner - { - // Match orders, maximally filling `leftOrder` - LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( - leftOrder, - rightOrder, - leftSignature, - rightSignature - ); - - uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; - uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount; - - // Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled - if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) { - return; - } - - // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. - rightOrder.makerAssetData = leftOrder.takerAssetData; - rightOrder.takerAssetData = leftOrder.makerAssetData; - - // Query `rightOrder` info to check if it has been completely filled - // We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call - OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder); - - // Do not attempt to call `fillOrder` if order has been completely filled - if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) { - return; - } - - // We do not need to pass in a signature since it was already validated in the `matchOrders` call - EXCHANGE.fillOrder( - rightOrder, - leftMakerAssetSpreadAmount, - "" - ); - } - */ - // solhint-disable-next-line payable-fallback - function () - external + /// @dev Match two complementary orders that have a profitable spread. + /// Each order is filled at their respective price point. However, the calculations are + /// carried out as though the orders are both being filled at the right order's price point. + /// The profit made by the left order is then used to fill the right order as much as possible. + /// This results in a spread being taken in terms of both assets. The spread is held within this contract. + /// @param leftOrder First order to match. + /// @param rightOrder Second order to match. + /// @param leftSignature Proof that order was created by the left maker. + /// @param rightSignature Proof that order was created by the right maker. + function matchOrders( + LibOrder.Order memory leftOrder, + LibOrder.Order memory rightOrder, + bytes memory leftSignature, + bytes memory rightSignature + ) + public + onlyOwner { - assembly { - // The first 4 bytes of calldata holds the function selector - // `matchOrders` selector = 0x3c28d861 - if eq( - and(calldataload(0), 0xffffffff00000000000000000000000000000000000000000000000000000000), - 0x3c28d86100000000000000000000000000000000000000000000000000000000 - ) { - - // Load address of `owner` - let owner := sload(owner_slot) - - // Revert if `msg.sender` != `owner` - if iszero(eq(owner, caller)) { - // Revert with `Error("ONLY_CONTRACT_OWNER")` - mstore(0, 0x08c379a000000000000000000000000000000000000000000000000000000000) - mstore(32, 0x0000002000000000000000000000000000000000000000000000000000000000) - mstore(64, 0x000000134f4e4c595f434f4e54524143545f4f574e4552000000000000000000) - mstore(96, 0) - revert(0, 100) - } - - // Load address of Exchange contract - let exchange := sload(EXCHANGE_slot) - - // Copy calldata to memory - // The calldata should be identical to the the ABIv2 encoded data required to call `EXCHANGE.matchOrders` - calldatacopy( - 0, // copy to memory at 0 - 0, // copy from calldata at 0 - calldatasize() // copy all calldata - ) - - // At this point, calldata and memory have the following layout: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | 0 | 4 | `matchOrders` function selector | - // | | 4 * 32 | function parameters: | - // | 4 | | 1. offset to leftOrder (*) | - // | 36 | | 2. offset to rightOrder (*) | - // | 68 | | 3. offset to leftSignature (*) | - // | 100 | | 4. offset to rightSignature (*) | - // | | 12 * 32 | leftOrder: | - // | 132 | | 1. senderAddress | - // | 164 | | 2. makerAddress | - // | 196 | | 3. takerAddress | - // | 228 | | 4. feeRecipientAddress | - // | 260 | | 5. makerAssetAmount | - // | 292 | | 6. takerAssetAmount | - // | 324 | | 7. makerFeeAmount | - // | 356 | | 8. takerFeeAmount | - // | 388 | | 9. expirationTimeSeconds | - // | 420 | | 10. salt | - // | 452 | | 11. offset to leftMakerAssetData (*) | - // | 484 | | 12. offset to leftTakerAssetData (*) | - // | 516 | 32 | leftMakerAssetData Length | - // | 548 | a | leftMakerAssetData Contents | - // | 548 + a | 32 | leftTakerAssetData Length | - // | 580 + a | b | leftTakerAssetData Contents | - // | | 12 * 32 | rightOrder: | - // | 580 + a + b | | 1. senderAddress | - // | 612 + a + b | | 2. makerAddress | - // | 644 + a + b | | 3. takerAddress | - // | 676 + a + b | | 4. feeRecipientAddress | - // | 708 + a + b | | 5. makerAssetAmount | - // | 740 + a + b | | 6. takerAssetAmount | - // | 772 + a + b | | 7. makerFeeAmount | - // | 804 + a + b | | 8. takerFeeAmount | - // | 836 + a + b | | 9. expirationTimeSeconds | - // | 868 + a + b | | 10. salt | - // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | - // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | - // | 964 + a + b | 32 | rightMakerAssetData Length | - // | 996 + a + b | c | rightMakerAssetData Contents | - // | 996 + a + b + c | 32 | rightTakerAssetData Length | - // | 1028 + a + b + c | d | rightTakerAssetData Contents | - // | 1028 + a + b + c + d | 32 | leftSignature Length | - // | 1060 + a + b + c + d | e | leftSignature Contents | - // | 1060 + a + b + c + d + e | 32 | rightSignature Length | - // | 1092 + a + b + c + d + e | f | rightSignature Contents | - - // Call `EXCHANGE.matchOrders` - let success := call( - gas, // forward all gas - exchange, // call address of Exchange contract - 0, // transfer 0 wei - 0, // input starts at 0 - calldatasize(), // length of input - 0, // write output over input - 288 // length of output is 288 bytes - ) - - if iszero(success) { - // Revert with reason if `EXCHANGE.matchOrders` call was unsuccessful - returndatacopy( - 0, // copy to memory at 0 - 0, // copy from return data at 0 - returndatasize() // copy all return data - ) - revert(0, returndatasize()) - } - - // After calling `matchOrders`, the return data is layed out in memory as: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | | 9 * 32 | matchedFillResults | - // | 0 | | 1. left.makerAssetFilledAmount | - // | 32 | | 2. left.takerAssetFilledAmount | - // | 64 | | 3. left.makerFeePaid | - // | 96 | | 4. left.takerFeePaid | - // | 128 | | 5. right.makerAssetFilledAmount | - // | 160 | | 6. right.takerAssetFilledAmount | - // | 192 | | 7. right.makerFeePaid | - // | 224 | | 8. right.takerFeePaid | - // | 256 | | 9. leftMakerAssetSpreadAmount | - - let rightOrderStart := add(calldataload(36), 4) - - // If no spread was taken or if the entire `rightOrderTakerAssetAmount` has been filled, there is no need to call `EXCHANGE.fillOrder`. - if or( - iszero(mload(256)), // iszero(leftMakerAssetSpreadAmount) - eq(mload(160), calldataload(add(rightOrderStart, 160))) // eq(rightOrderTakerAssetFilledAmount, rightOrderTakerAssetAmount) - ) { - return(0, 0) - } - - // We assume that `leftOrder.makerAssetData == rightOrder.takerAssetData` and `leftOrder.takerAssetData == rightOrder.makerAssetData` - // `EXCHANGE.matchOrders` already makes this assumption, so it is likely - // that the `rightMakerAssetData` and `rightTakerAssetData` in calldata are empty - - let leftOrderStart := add(calldataload(4), 4) - - // Calculate locations of `leftMakerAssetData` and `leftTakerAssetData` in calldata - let leftMakerAssetDataStart := add( - leftOrderStart, - calldataload(add(leftOrderStart, 320)) // offset to `leftMakerAssetData` - ) - let leftTakerAssetDataStart := add( - leftOrderStart, - calldataload(add(leftOrderStart, 352)) // offset to `leftTakerAssetData` - ) - - // Load lengths of `leftMakerAssetData` and `leftTakerAssetData` - let leftMakerAssetDataLen := calldataload(leftMakerAssetDataStart) - let leftTakerAssetDataLen := calldataload(leftTakerAssetDataStart) - - // Write offset to `rightMakerAssetData` - mstore(add(rightOrderStart, 320), 384) - - // Write offset to `rightTakerAssetData` - let rightTakerAssetDataOffset := add(416, leftTakerAssetDataLen) - mstore(add(rightOrderStart, 352), rightTakerAssetDataOffset) - - // Copy `leftTakerAssetData` from calldata onto `rightMakerAssetData` in memory - calldatacopy( - add(rightOrderStart, 384), // `rightMakerAssetDataStart` - leftTakerAssetDataStart, - add(leftTakerAssetDataLen, 32) - ) - - // Copy `leftMakerAssetData` from calldata onto `rightTakerAssetData` in memory - calldatacopy( - add(rightOrderStart, rightTakerAssetDataOffset), // `rightTakerAssetDataStart` - leftMakerAssetDataStart, - add(leftMakerAssetDataLen, 32) - ) - - // We must call `EXCHANGE.getOrderInfo(rightOrder)` before calling `EXCHANGE.fillOrder` to ensure that the order - // has not already been entirely filled (which is possible if an order was partially filled before the `matchOrders` call). - // We want the following layout in memory before calling `getOrderInfo`: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | 544 + a + b | 4 | `getOrderInfo` function selector | - // | | 3 * 32 | function parameters: | - // | 548 + a + b | | 1. offset to rightOrder (*) | - // | | 12 * 32 | rightOrder: | - // | 580 + a + b | | 1. senderAddress | - // | 612 + a + b | | 2. makerAddress | - // | 644 + a + b | | 3. takerAddress | - // | 676 + a + b | | 4. feeRecipientAddress | - // | 708 + a + b | | 5. makerAssetAmount | - // | 740 + a + b | | 6. takerAssetAmount | - // | 772 + a + b | | 7. makerFeeAmount | - // | 804 + a + b | | 8. takerFeeAmount | - // | 836 + a + b | | 9. expirationTimeSeconds | - // | 868 + a + b | | 10. salt | - // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | - // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | - // | 964 + a + b | 32 | rightMakerAssetData Length | - // | 996 + a + b | c | rightMakerAssetData Contents | - // | 996 + a + b + c | 32 | rightTakerAssetData Length | - // | 1028 + a + b + c | d | rightTakerAssetData Contents | - - // function selector (4 bytes) + 1 param (32 bytes) must be stored before `rightOrderStart` - let cdStart := sub(rightOrderStart, 36) - - // `getOrderInfo` selector = 0xc75e0a81 - mstore(cdStart, 0xc75e0a8100000000000000000000000000000000000000000000000000000000) - - // Write offset to `rightOrder` - mstore(add(cdStart, 4), 32) - - let rightOrderEnd := add( - add(rightOrderStart, 484), - add(leftMakerAssetDataLen, leftTakerAssetDataLen) - ) - - // Call `EXCHANGE.getOrderInfo(rightOrder)` - success := staticcall( - gas, // forward all gas - exchange, // call address of Exchange contract - cdStart, // start of input - sub(rightOrderEnd, cdStart), // length of input - 0, // write output over old output - 96 // output is 96 bytes - ) - - // After calling `EXCHANGE.getOrderInfo`, the return data is layed out in memory as: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | | 3 * 32 | orderInfo | - // | 0 | | 1. orderStatus | - // | 32 | | 2. orderHash | - // | 64 | | 3. orderTakerAssetFilledAmount | - - // We do not need to check if the `getOrderInfo` call was successful because it has no possibility of reverting - // If order has been entirely filled, there is no need to call `EXCHANGE.fillOrder` - // eq(orderTakerAssetFilledAmount, rightOrderTakerAssetAmount) - if eq(mload(64), calldataload(add(rightOrderStart, 160))) { - return(0, 0) - } - - // We want the following layout in memory before calling `EXCHANGE.fillOrder`: - - // | Offset | Length | Contents | - // |---------------------------|---------|-------------------------------------------- | - // | 480 + a + b | 4 | `fillOrder` function selector | - // | | 3 * 32 | function parameters: | - // | 484 + a + b | | 1. offset to rightOrder (*) | - // | 516 + a + b | | 2. takerAssetFillAmount | - // | 548 + a + b | | 3. offset to rightSignature (*) | - // | | 12 * 32 | rightOrder: | - // | 580 + a + b | | 1. senderAddress | - // | 612 + a + b | | 2. makerAddress | - // | 644 + a + b | | 3. takerAddress | - // | 676 + a + b | | 4. feeRecipientAddress | - // | 708 + a + b | | 5. makerAssetAmount | - // | 740 + a + b | | 6. takerAssetAmount | - // | 772 + a + b | | 7. makerFeeAmount | - // | 804 + a + b | | 8. takerFeeAmount | - // | 836 + a + b | | 9. expirationTimeSeconds | - // | 868 + a + b | | 10. salt | - // | 900 + a + b | | 11. offset to rightMakerAssetData (*) | - // | 932 + a + b | | 12. offset to rightTakerAssetData (*) | - // | 964 + a + b | 32 | rightMakerAssetData Length | - // | 996 + a + b | c | rightMakerAssetData Contents | - // | 996 + a + b + c | 32 | rightTakerAssetData Length | - // | 1028 + a + b + c | d | rightTakerAssetData Contents | - // | 1028 + a + b + c + d | 32 | rightSignature Length (always 0) | - - // Write length of signature (always 0 since signature was previously validated) - mstore(rightOrderEnd, 0) - - // function selector (4 bytes) + 3 params (3 * 32 bytes) must be stored before `rightOrderStart` - cdStart := sub(rightOrderStart, 100) - - // `fillOrder` selector = 0xb4be83d5 - mstore(cdStart, 0xb4be83d500000000000000000000000000000000000000000000000000000000) - - // Write offset to `rightOrder` - mstore(add(cdStart, 4), 96) - - // Write `takerAssetFillAmount`, which will be the `leftMakerAssetSpreadAmount` received from the `matchOrders` call - mstore(add(cdStart, 36), mload(256)) - - // Write offset to `rightSignature` - mstore( - add(cdStart, 68), - sub(rightOrderEnd, add(cdStart, 4)) - ) + // Match orders, maximally filling `leftOrder` + LibFillResults.MatchedFillResults memory matchedFillResults = EXCHANGE.matchOrders( + leftOrder, + rightOrder, + leftSignature, + rightSignature + ); + + uint256 leftMakerAssetSpreadAmount = matchedFillResults.leftMakerAssetSpreadAmount; + uint256 rightOrderTakerAssetAmount = rightOrder.takerAssetAmount; + + // Do not attempt to call `fillOrder` if no spread was taken or `rightOrder` has been completely filled + if (leftMakerAssetSpreadAmount == 0 || matchedFillResults.right.takerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; + } - // Call `EXCHANGE.fillOrder(rightOrder, leftMakerAssetSpreadAmount, "")` - success := call( - gas, // forward all gas - exchange, // call address of Exchange contract - 0, // transfer 0 wei - cdStart, // start of input - add(sub(rightOrderEnd, cdStart), 32), // length of input is end - start - 0, // write output over input - 0 // do not write output to memory - ) + // The `assetData` fields of the `rightOrder` could have been null for the `matchOrders` call. We reassign them before calling `fillOrder`. + rightOrder.makerAssetData = leftOrder.takerAssetData; + rightOrder.takerAssetData = leftOrder.makerAssetData; - if iszero(success) { - // Revert with reason if `EXCHANGE.fillOrder` call was unsuccessful - returndatacopy( - 0, // copy to memory at 0 - 0, // copy from return data at 0 - returndatasize() // copy all return data - ) - revert(0, returndatasize()) - } - - // Return if `EXCHANGE.fillOrder` did not revert - return(0, 0) - - } + // Query `rightOrder` info to check if it has been completely filled + // We need to make this check in case the `rightOrder` was partially filled before the `matchOrders` call + LibOrder.OrderInfo memory orderInfo = EXCHANGE.getOrderInfo(rightOrder); - // Revert if undefined function is called - revert(0, 0) + // Do not attempt to call `fillOrder` if order has been completely filled + if (orderInfo.orderTakerAssetFilledAmount == rightOrderTakerAssetAmount) { + return; } + + // We do not need to pass in a signature since it was already validated in the `matchOrders` call + EXCHANGE.fillOrder( + rightOrder, + leftMakerAssetSpreadAmount, + "" + ); } } diff --git a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol index 7136c8c82..4879b7bca 100644 --- a/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol +++ b/contracts/extensions/contracts/OrderMatcher/OrderMatcher.sol @@ -17,6 +17,7 @@ */ pragma solidity 0.4.24; +pragma experimental ABIEncoderV2; import "@0x/contracts-utils/contracts/utils/Ownable/Ownable.sol"; import "./libs/LibConstants.sol"; -- cgit v1.2.3 From fbfb6eb45e60aa1439162559df16444344322092 Mon Sep 17 00:00:00 2001 From: Amir Bandeali <abandeali1@gmail.com> Date: Thu, 20 Dec 2018 09:13:19 -0800 Subject: Update CHANGELOG --- contracts/extensions/CHANGELOG.json | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'contracts') diff --git a/contracts/extensions/CHANGELOG.json b/contracts/extensions/CHANGELOG.json index da4d9c2ba..4f2b54988 100644 --- a/contracts/extensions/CHANGELOG.json +++ b/contracts/extensions/CHANGELOG.json @@ -5,6 +5,10 @@ { "note": "Added Balance Threshold Filter", "pr": 1383 + }, + { + "note": "Add OrderMatcher", + "pr": 1117 } ] }, -- cgit v1.2.3