From 2be9b1ff082ff2753798cc0c218ab1829949661f Mon Sep 17 00:00:00 2001 From: Greg Hysen Date: Tue, 4 Dec 2018 15:47:45 -0800 Subject: Updated Balance Threshold Filter to use mixin pattern --- .../BalanceThresholdFilter.sol | 292 +------------------ .../MixinBalanceThresholdFilterCore.sol | 312 +++++++++++++++++++++ .../mixins/MBalanceThresholdFilterCore.sol | 44 +++ 3 files changed, 358 insertions(+), 290 deletions(-) create mode 100644 packages/contracts/contracts/extensions/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol create mode 100644 packages/contracts/contracts/extensions/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol (limited to 'packages') diff --git a/packages/contracts/contracts/extensions/BalanceThresholdFilter/BalanceThresholdFilter.sol b/packages/contracts/contracts/extensions/BalanceThresholdFilter/BalanceThresholdFilter.sol index 93b63eb52..ce3e925fe 100644 --- a/packages/contracts/contracts/extensions/BalanceThresholdFilter/BalanceThresholdFilter.sol +++ b/packages/contracts/contracts/extensions/BalanceThresholdFilter/BalanceThresholdFilter.sol @@ -20,20 +20,11 @@ pragma solidity 0.4.24; pragma experimental ABIEncoderV2; import "../../protocol/Exchange/interfaces/IExchange.sol"; -import "../../utils/LibBytes/LibBytes.sol"; -import "../../utils/ExchangeSelectors/ExchangeSelectors.sol"; import "./interfaces/IThresholdAsset.sol"; +import "./MixinBalanceThresholdFilterCore.sol"; -contract BalanceThresholdFilter is ExchangeSelectors { - using LibBytes for bytes; - - IExchange internal EXCHANGE; - IThresholdAsset internal THRESHOLD_ASSET; - - event ValidatedAddresses ( - address[] addresses - ); +contract BalanceThresholdFilter is MixinBalanceThresholdFilterCore { constructor(address exchange, address thresholdAsset) public @@ -41,283 +32,4 @@ contract BalanceThresholdFilter is ExchangeSelectors { EXCHANGE = IExchange(exchange); THRESHOLD_ASSET = IThresholdAsset(thresholdAsset); } - - function executeTransaction( - uint256 salt, - address signerAddress, - bytes signedExchangeTransaction, - bytes signature - ) - external - { - // 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 { - /** - * 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) - } - - /** - * 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)) - } - - /** - * A running list is maintained of addresses to validate. - * This function records an address in this array. - * @param addressToValidate - Address to record for validation. - * @note - Variables are scoped but names are not, so we append - * underscores to names that share the global namespace. - */ - 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) - } - - /** - * 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(orderParamIndex) - let makerAddress := loadExchangeData(orderPtr) - recordAddressToValidate(makerAddress) - } - - /** - * 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(0x0) - 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) - } - } - - /** - * 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_) - } - - /** - * Records addresses to be validated when Exchange transaction is a batch fill variant. - * This is one of: batchFillOrders, batchFillOrKillOrders, batchFillNoThrow - * Reference signature: (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() - } - - /** - * Records addresses to be validated when Exchange transaction is a fill order variant. - * This is one of: fillOrder, fillOrKillOrder, fillOrderNoThrow - * Reference signature: (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() - } - - /** - * Records addresses to be validated when Exchange transaction is a market fill variant. - * This is one of: marketBuyOrders, marketBuyOrdersNoThrow, marketSellOrders, marketSellOrdersNoThrow - * Reference signature: (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() - } - - /** - * 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) - for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} { - // Construct calldata for `THRESHOLD_ASSET.balanceOf` - mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000) - mstore(add(4, freeMemPtr), 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 eq(addressBalance, 0) { - // 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); - - // All addresses are valid. Execute fillOrder. - EXCHANGE.executeTransaction( - salt, - signerAddress, - signedExchangeTransaction, - signature - ); - } } \ No newline at end of file diff --git a/packages/contracts/contracts/extensions/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol b/packages/contracts/contracts/extensions/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol new file mode 100644 index 000000000..dbb352f90 --- /dev/null +++ b/packages/contracts/contracts/extensions/BalanceThresholdFilter/MixinBalanceThresholdFilterCore.sol @@ -0,0 +1,312 @@ +/* + + 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 { + + 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 + ); + } + + 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 { + /** + * 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) + } + + /** + * 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)) + } + + /** + * A running list is maintained of addresses to validate. + * This function records an address in this array. + * @param addressToValidate - Address to record for validation. + * @note - Variables are scoped but names are not, so we append + * underscores to names that share the global namespace. + */ + 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) + } + + /** + * 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(orderParamIndex) + let makerAddress := loadExchangeData(orderPtr) + recordAddressToValidate(makerAddress) + } + + /** + * 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(orderArrayParamIndex) + 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) + } + } + + /** + * 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_) + } + + /** + * Records addresses to be validated when Exchange transaction is a batch fill variant. + * This is one of: batchFillOrders, batchFillOrKillOrders, batchFillNoThrow + * Reference signature: (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() + } + + /** + * Records addresses to be validated when Exchange transaction is a fill order variant. + * This is one of: fillOrder, fillOrKillOrder, fillOrderNoThrow + * Reference signature: (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() + } + + /** + * Records addresses to be validated when Exchange transaction is a market fill variant. + * This is one of: marketBuyOrders, marketBuyOrdersNoThrow, marketSellOrders, marketSellOrdersNoThrow + * Reference signature: (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() + } + + /** + * 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) + for {let addressToValidate := addressesToValidateElementPtr} lt(addressToValidate, addressesToValidateElementEndPtr) {addressToValidate := add(addressToValidate, 0x20)} { + // Construct calldata for `THRESHOLD_ASSET.balanceOf` + mstore(freeMemPtr, 0x70a0823100000000000000000000000000000000000000000000000000000000) + mstore(add(4, freeMemPtr), 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 eq(addressBalance, 0) { + // 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/packages/contracts/contracts/extensions/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol b/packages/contracts/contracts/extensions/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.sol new file mode 100644 index 000000000..1de415f27 --- /dev/null +++ b/packages/contracts/contracts/extensions/BalanceThresholdFilter/mixins/MBalanceThresholdFilterCore.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 "../../../protocol/Exchange/interfaces/IExchange.sol"; +import "../interfaces/IThresholdAsset.sol"; + + +contract MBalanceThresholdFilterCore { + + IExchange internal EXCHANGE; + IThresholdAsset internal THRESHOLD_ASSET; + + event ValidatedAddresses ( + address[] addresses + ); + + function executeTransaction( + uint256 salt, + address signerAddress, + bytes signedExchangeTransaction, + bytes signature + ) + external; + + function validateBalanceThresholdsOrRevert() internal; +} \ No newline at end of file -- cgit