aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Hysen <greg.hysen@gmail.com>2018-12-14 05:52:20 +0800
committerGreg Hysen <greg.hysen@gmail.com>2018-12-19 05:36:05 +0800
commitf91781a0605f46ee1a40bf979d22ff510f48d464 (patch)
tree0e3198872ed89bda0431d7fe29878e2cbd47449b
parent22fd23643cb26ba1b79e9b13e5ef41c97ab7fe6a (diff)
downloaddexon-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.sol10
-rw-r--r--contracts/extensions/contracts/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol404
-rw-r--r--contracts/extensions/contracts/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol7
-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