diff options
author | Greg Hysen <greg.hysen@gmail.com> | 2018-12-14 05:52:20 +0800 |
---|---|---|
committer | Greg Hysen <greg.hysen@gmail.com> | 2018-12-19 05:36:05 +0800 |
commit | f91781a0605f46ee1a40bf979d22ff510f48d464 (patch) | |
tree | 0e3198872ed89bda0431d7fe29878e2cbd47449b | |
parent | 22fd23643cb26ba1b79e9b13e5ef41c97ab7fe6a (diff) | |
download | dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar.gz dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar.bz2 dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar.lz dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar.xz dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.tar.zst dexon-sol-tools-f91781a0605f46ee1a40bf979d22ff510f48d464.zip |
Less Assembly. More Solidity. Less Efficiency. More Readability.
-rw-r--r-- | contracts/extensions/contracts/BalanceThresholdFilter/BalanceThresholdFilter.sol | 10 | ||||
-rw-r--r-- | contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol | 404 | ||||
-rw-r--r-- | contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol | 7 | ||||
-rw-r--r-- | contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol (renamed from packages/contracts/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol) | 0 |
4 files changed, 155 insertions, 266 deletions
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/packages/contracts/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol b/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol index c361fd075..c361fd075 100644 --- a/packages/contracts/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol +++ b/contracts/utils/contracts/utils/ExchangeSelectors/ExchangeSelectors.sol |