aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src/2.0.0/test')
-rw-r--r--packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol182
-rw-r--r--packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol44
-rw-r--r--packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol42
3 files changed, 259 insertions, 9 deletions
diff --git a/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
new file mode 100644
index 000000000..8bfdd2e66
--- /dev/null
+++ b/packages/contracts/src/2.0.0/test/ReentrantERC20Token/ReentrantERC20Token.sol
@@ -0,0 +1,182 @@
+/*
+
+ 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 "../../utils/LibBytes/LibBytes.sol";
+import "../../tokens/ERC20Token/ERC20Token.sol";
+import "../../protocol/Exchange/interfaces/IExchange.sol";
+import "../../protocol/Exchange/libs/LibOrder.sol";
+
+
+contract ReentrantERC20Token is
+ ERC20Token
+{
+
+ using LibBytes for bytes;
+
+ // solhint-disable-next-line var-name-mixedcase
+ IExchange internal EXCHANGE;
+
+ bytes internal constant REENTRANCY_ILLEGAL_REVERT_REASON = abi.encodeWithSelector(
+ bytes4(keccak256("Error(string)")),
+ "REENTRANCY_ILLEGAL"
+ );
+
+ // All of these functions are potentially vulnerable to reentrancy
+ // We do not test any "noThrow" functions because `fillOrderNoThrow` makes a delegatecall to `fillOrder`
+ enum ExchangeFunction {
+ FILL_ORDER,
+ FILL_OR_KILL_ORDER,
+ BATCH_FILL_ORDERS,
+ BATCH_FILL_OR_KILL_ORDERS,
+ MARKET_BUY_ORDERS,
+ MARKET_SELL_ORDERS,
+ MATCH_ORDERS,
+ CANCEL_ORDER,
+ CANCEL_ORDERS_UP_TO,
+ SET_SIGNATURE_VALIDATOR_APPROVAL
+ }
+
+ uint8 internal currentFunctionId = 0;
+
+ constructor (address _exchange)
+ public
+ {
+ EXCHANGE = IExchange(_exchange);
+ }
+
+ /// @dev Set the current function that will be called when `transferFrom` is called.
+ /// @param _currentFunctionId Id that corresponds to function name.
+ function setCurrentFunction(uint8 _currentFunctionId)
+ external
+ {
+ currentFunctionId = _currentFunctionId;
+ }
+
+ /// @dev A version of `transferFrom` that attempts to reenter the Exchange contract.
+ /// @param _from The address of the sender
+ /// @param _to The address of the recipient
+ /// @param _value The amount of token to be transferred
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _value
+ )
+ external
+ returns (bool)
+ {
+ // This order would normally be invalid, but it will be used strictly for testing reentrnacy.
+ // Any reentrancy checks will happen before any other checks that invalidate the order.
+ LibOrder.Order memory order;
+
+ // Initialize remaining null parameters
+ bytes memory signature;
+ LibOrder.Order[] memory orders;
+ uint256[] memory takerAssetFillAmounts;
+ bytes[] memory signatures;
+ bytes memory calldata;
+
+ // Create calldata for function that corresponds to currentFunctionId
+ if (currentFunctionId == uint8(ExchangeFunction.FILL_ORDER)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.fillOrder.selector,
+ order,
+ 0,
+ signature
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.FILL_OR_KILL_ORDER)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.fillOrKillOrder.selector,
+ order,
+ 0,
+ signature
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_ORDERS)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.batchFillOrders.selector,
+ orders,
+ takerAssetFillAmounts,
+ signatures
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.BATCH_FILL_OR_KILL_ORDERS)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.batchFillOrKillOrders.selector,
+ orders,
+ takerAssetFillAmounts,
+ signatures
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_BUY_ORDERS)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.marketBuyOrders.selector,
+ orders,
+ 0,
+ signatures
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.MARKET_SELL_ORDERS)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.marketSellOrders.selector,
+ orders,
+ 0,
+ signatures
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.MATCH_ORDERS)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.matchOrders.selector,
+ order,
+ order,
+ signature,
+ signature
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDER)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.cancelOrder.selector,
+ order
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.CANCEL_ORDERS_UP_TO)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.cancelOrdersUpTo.selector,
+ 0
+ );
+ } else if (currentFunctionId == uint8(ExchangeFunction.SET_SIGNATURE_VALIDATOR_APPROVAL)) {
+ calldata = abi.encodeWithSelector(
+ EXCHANGE.setSignatureValidatorApproval.selector,
+ address(0),
+ false
+ );
+ }
+
+ // Call Exchange function, swallow error
+ address(EXCHANGE).call(calldata);
+
+ // Revert reason is 100 bytes
+ bytes memory returnData = new bytes(100);
+
+ // Copy return data
+ assembly {
+ returndatacopy(add(returnData, 32), 0, 100)
+ }
+
+ // Revert if function reverted with REENTRANCY_ILLEGAL error
+ require(!REENTRANCY_ILLEGAL_REVERT_REASON.equals(returnData));
+
+ // Transfer will return true if function failed for any other reason
+ return true;
+ }
+} \ No newline at end of file
diff --git a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol
index d9cec9edc..da9313e02 100644
--- a/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol
+++ b/packages/contracts/src/2.0.0/test/TestExchangeInternals/TestExchangeInternals.sol
@@ -67,7 +67,7 @@ contract TestExchangeInternals is
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target.
- function publicGetPartialAmount(
+ function publicGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
@@ -76,15 +76,49 @@ contract TestExchangeInternals is
pure
returns (uint256 partialAmount)
{
- return getPartialAmount(numerator, denominator, target);
+ return getPartialAmountFloor(numerator, denominator, target);
}
- /// @dev Checks if rounding error > 0.1%.
+ /// @dev Calculates partial value given a numerator and denominator.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target.
+ function publicGetPartialAmountCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (uint256 partialAmount)
+ {
+ return getPartialAmountCeil(numerator, denominator, target);
+ }
+
+ /// @dev Checks if rounding error >= 0.1%.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to multiply with numerator/denominator.
+ /// @return Rounding error is present.
+ function publicIsRoundingErrorFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (bool isError)
+ {
+ return isRoundingErrorFloor(numerator, denominator, target);
+ }
+
+ /// @dev Checks if rounding error >= 0.1%.
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to multiply with numerator/denominator.
/// @return Rounding error is present.
- function publicIsRoundingError(
+ function publicIsRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@@ -93,7 +127,7 @@ contract TestExchangeInternals is
pure
returns (bool isError)
{
- return isRoundingError(numerator, denominator, target);
+ return isRoundingErrorCeil(numerator, denominator, target);
}
/// @dev Updates state with results of a fill order.
diff --git a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol
index 4a99dd9c1..c8c58545f 100644
--- a/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol
+++ b/packages/contracts/src/2.0.0/test/TestLibs/TestLibs.sol
@@ -49,7 +49,7 @@ contract TestLibs is
return fillOrderCalldata;
}
- function publicGetPartialAmount(
+ function publicGetPartialAmountFloor(
uint256 numerator,
uint256 denominator,
uint256 target
@@ -58,7 +58,7 @@ contract TestLibs is
pure
returns (uint256 partialAmount)
{
- partialAmount = getPartialAmount(
+ partialAmount = getPartialAmountFloor(
numerator,
denominator,
target
@@ -66,7 +66,41 @@ contract TestLibs is
return partialAmount;
}
- function publicIsRoundingError(
+ function publicGetPartialAmountCeil(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (uint256 partialAmount)
+ {
+ partialAmount = getPartialAmountCeil(
+ numerator,
+ denominator,
+ target
+ );
+ return partialAmount;
+ }
+
+ function publicIsRoundingErrorFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ public
+ pure
+ returns (bool isError)
+ {
+ isError = isRoundingErrorFloor(
+ numerator,
+ denominator,
+ target
+ );
+ return isError;
+ }
+
+ function publicIsRoundingErrorCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@@ -75,7 +109,7 @@ contract TestLibs is
pure
returns (bool isError)
{
- isError = isRoundingError(
+ isError = isRoundingErrorCeil(
numerator,
denominator,
target