aboutsummaryrefslogtreecommitdiffstats
path: root/packages/contracts/src/2.0.0/protocol
diff options
context:
space:
mode:
Diffstat (limited to 'packages/contracts/src/2.0.0/protocol')
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol68
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol3
-rw-r--r--packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol49
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol4
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol45
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol16
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol3
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol82
-rw-r--r--packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol5
9 files changed, 198 insertions, 77 deletions
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol
index 004c3892d..258443bca 100644
--- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC20Proxy.sol
@@ -18,7 +18,6 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
import "./MixinAuthorizable.sol";
@@ -59,15 +58,64 @@ contract ERC20Proxy is
mstore(96, 0)
revert(0, 100)
}
-
- /////// Token contract address ///////
- // The token address is found as follows:
- // * It is stored at offset 4 in `assetData` contents.
- // * This is stored at offset 32 from `assetData`.
- // * The offset to `assetData` from Params is stored at offset
- // 4 in calldata.
- // * The offset of Params in calldata is 4.
- // So we read location 4 and add 32 + 4 + 4 to it.
+
+ // `transferFrom`.
+ // The function is marked `external`, so no abi decodeding is done for
+ // us. Instead, we expect the `calldata` memory to contain the
+ // following:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 4 * 32 | function parameters: |
+ // | | 4 | | 1. offset to assetData (*) |
+ // | | 36 | | 2. from |
+ // | | 68 | | 3. to |
+ // | | 100 | | 4. amount |
+ // | Data | | | assetData: |
+ // | | 132 | 32 | assetData Length |
+ // | | 164 | ** | assetData Contents |
+ //
+ // (*): offset is computed from start of function parameters, so offset
+ // by an additional 4 bytes in the calldata.
+ //
+ // (**): see table below to compute length of assetData Contents
+ //
+ // WARNING: The ABIv2 specification allows additional padding between
+ // the Params and Data section. This will result in a larger
+ // offset to assetData.
+
+ // Asset data itself is encoded as follows:
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 1 * 32 | function parameters: |
+ // | | 4 | 12 + 20 | 1. token address |
+
+ // We construct calldata for the `token.transferFrom` ABI.
+ // The layout of this calldata is in the table below.
+ //
+ // | Area | Offset | Length | Contents |
+ // |----------|--------|---------|-------------------------------------|
+ // | Header | 0 | 4 | function selector |
+ // | Params | | 3 * 32 | function parameters: |
+ // | | 4 | | 1. from |
+ // | | 36 | | 2. to |
+ // | | 68 | | 3. amount |
+
+ /////// Read token address from calldata ///////
+ // * The token address is stored in `assetData`.
+ //
+ // * The "offset to assetData" is stored at offset 4 in the calldata (table 1).
+ // [assetDataOffsetFromParams = calldataload(4)]
+ //
+ // * Notes that the "offset to assetData" is relative to the "Params" area of calldata;
+ // add 4 bytes to account for the length of the "Header" area (table 1).
+ // [assetDataOffsetFromHeader = assetDataOffsetFromParams + 4]
+ //
+ // * The "token address" is offset 32+4=36 bytes into "assetData" (tables 1 & 2).
+ // [tokenOffset = assetDataOffsetFromHeader + 36 = calldataload(4) + 4 + 36]
let token := calldataload(add(calldataload(4), 40))
/////// Setup Header Area ///////
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
index 9d0bc0f74..65b664b8b 100644
--- a/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxy/ERC721Proxy.sol
@@ -18,7 +18,6 @@
pragma solidity 0.4.24;
-import "../../utils/LibBytes/LibBytes.sol";
import "./MixinAuthorizable.sol";
@@ -80,6 +79,8 @@ contract ERC721Proxy is
// (*): offset is computed from start of function parameters, so offset
// by an additional 4 bytes in the calldata.
//
+ // (**): see table below to compute length of assetData Contents
+ //
// WARNING: The ABIv2 specification allows additional padding between
// the Params and Data section. This will result in a larger
// offset to assetData.
diff --git a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol
index 8b7333646..4d00e92d3 100644
--- a/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol
+++ b/packages/contracts/src/2.0.0/protocol/AssetProxyOwner/AssetProxyOwner.sol
@@ -16,15 +16,18 @@
*/
-pragma solidity 0.4.10;
+pragma solidity 0.4.24;
import "../../multisig/MultiSigWalletWithTimeLock.sol";
+import "../../utils/LibBytes/LibBytes.sol";
contract AssetProxyOwner is
MultiSigWalletWithTimeLock
{
+ using LibBytes for bytes;
+
event AssetProxyRegistration(address assetProxyContract, bool isRegistered);
// Mapping of AssetProxy contract address =>
@@ -37,8 +40,14 @@ contract AssetProxyOwner is
/// on an approved AssetProxy contract.
modifier validRemoveAuthorizedAddressAtIndexTx(uint256 transactionId) {
Transaction storage tx = transactions[transactionId];
- require(isAssetProxyRegistered[tx.destination]);
- require(readBytes4(tx.data, 0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR);
+ require(
+ isAssetProxyRegistered[tx.destination],
+ "UNREGISTERED_ASSET_PROXY"
+ );
+ require(
+ tx.data.readBytes4(0) == REMOVE_AUTHORIZED_ADDRESS_AT_INDEX_SELECTOR,
+ "INVALID_FUNCTION_SELECTOR"
+ );
_;
}
@@ -48,7 +57,7 @@ contract AssetProxyOwner is
/// @param _assetProxyContracts Array of AssetProxy contract addresses.
/// @param _required Number of required confirmations.
/// @param _secondsTimeLocked Duration needed after a transaction is confirmed and before it becomes executable, in seconds.
- function AssetProxyOwner(
+ constructor (
address[] memory _owners,
address[] memory _assetProxyContracts,
uint256 _required,
@@ -59,7 +68,10 @@ contract AssetProxyOwner is
{
for (uint256 i = 0; i < _assetProxyContracts.length; i++) {
address assetProxy = _assetProxyContracts[i];
- require(assetProxy != address(0));
+ require(
+ assetProxy != address(0),
+ "INVALID_ASSET_PROXY"
+ );
isAssetProxyRegistered[assetProxy] = true;
}
}
@@ -74,7 +86,7 @@ contract AssetProxyOwner is
notNull(assetProxyContract)
{
isAssetProxyRegistered[assetProxyContract] = isRegistered;
- AssetProxyRegistration(assetProxyContract, isRegistered);
+ emit AssetProxyRegistration(assetProxyContract, isRegistered);
}
/// @dev Allows execution of `removeAuthorizedAddressAtIndex` without time lock.
@@ -89,31 +101,10 @@ contract AssetProxyOwner is
tx.executed = true;
// solhint-disable-next-line avoid-call-value
if (tx.destination.call.value(tx.value)(tx.data))
- Execution(transactionId);
+ emit Execution(transactionId);
else {
- ExecutionFailure(transactionId);
+ emit ExecutionFailure(transactionId);
tx.executed = false;
}
}
-
- /// @dev Reads an unpadded bytes4 value from a position in a byte array.
- /// @param b Byte array containing a bytes4 value.
- /// @param index Index in byte array of bytes4 value.
- /// @return bytes4 value from byte array.
- function readBytes4(
- bytes memory b,
- uint256 index
- )
- internal
- returns (bytes4 result)
- {
- require(b.length >= index + 4);
- assembly {
- result := mload(add(b, 32))
- // Solidity does not require us to clean the trailing bytes.
- // We do it anyway
- result := and(result, 0xFFFFFFFF00000000000000000000000000000000000000000000000000000000)
- }
- return result;
- }
}
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol
index 80475e6e3..e90b62f19 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinAssetProxyDispatcher.sol
@@ -19,7 +19,6 @@
pragma solidity 0.4.24;
import "../../utils/Ownable/Ownable.sol";
-import "../../utils/LibBytes/LibBytes.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "../AssetProxy/interfaces/IAssetProxy.sol";
@@ -28,7 +27,6 @@ contract MixinAssetProxyDispatcher is
Ownable,
MAssetProxyDispatcher
{
- using LibBytes for bytes;
// Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => IAssetProxy) public assetProxies;
@@ -90,7 +88,7 @@ contract MixinAssetProxyDispatcher is
"LENGTH_GREATER_THAN_3_REQUIRED"
);
- // Lookup assetProxy
+ // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
bytes4 assetProxyId;
assembly {
assetProxyId := and(mload(
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
index be163ec97..11bbe40fb 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinExchangeCore.sol
@@ -107,14 +107,7 @@ contract MixinExchangeCore is
public
nonReentrant
{
- // Fetch current order status
- OrderInfo memory orderInfo = getOrderInfo(order);
-
- // Validate context
- assertValidCancel(order, orderInfo);
-
- // Perform cancel
- updateCancelledState(order, orderInfo.orderHash);
+ cancelOrderInternal(order);
}
/// @dev Gets information about an order: status, hash, and amount filled.
@@ -236,6 +229,22 @@ contract MixinExchangeCore is
return fillResults;
}
+ /// @dev After calling, the order can not be filled anymore.
+ /// Throws if order is invalid or sender does not have permission to cancel.
+ /// @param order Order to cancel. Order must be OrderStatus.FILLABLE.
+ function cancelOrderInternal(Order memory order)
+ internal
+ {
+ // Fetch current order status
+ OrderInfo memory orderInfo = getOrderInfo(order);
+
+ // Validate context
+ assertValidCancel(order, orderInfo);
+
+ // Perform cancel
+ updateCancelledState(order, orderInfo.orderHash);
+ }
+
/// @dev Updates state with results of a fill order.
/// @param order that was filled.
/// @param takerAddress Address of taker who filled the order.
@@ -404,16 +413,6 @@ contract MixinExchangeCore is
safeMul(order.makerAssetAmount, takerAssetFilledAmount),
"INVALID_FILL_PRICE"
);
-
- // Validate fill order rounding
- require(
- !isRoundingErrorFloor(
- takerAssetFilledAmount,
- order.takerAssetAmount,
- order.makerAssetAmount
- ),
- "ROUNDING_ERROR"
- );
}
/// @dev Validates context for cancelOrder. Succeeds or throws.
@@ -463,17 +462,17 @@ contract MixinExchangeCore is
{
// Compute proportional transfer amounts
fillResults.takerAssetFilledAmount = takerAssetFilledAmount;
- fillResults.makerAssetFilledAmount = getPartialAmountFloor(
+ fillResults.makerAssetFilledAmount = safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.makerAssetAmount
);
- fillResults.makerFeePaid = getPartialAmountFloor(
- takerAssetFilledAmount,
- order.takerAssetAmount,
+ fillResults.makerFeePaid = safeGetPartialAmountFloor(
+ fillResults.makerAssetFilledAmount,
+ order.makerAssetAmount,
order.makerFee
);
- fillResults.takerFeePaid = getPartialAmountFloor(
+ fillResults.takerFeePaid = safeGetPartialAmountFloor(
takerAssetFilledAmount,
order.takerAssetAmount,
order.takerFee
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol
index 075a610b5..b4f6bdb26 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinMatchOrders.sol
@@ -177,13 +177,13 @@ contract MixinMatchOrders is
{
// Derive maker asset amounts for left & right orders, given store taker assert amounts
uint256 leftTakerAssetAmountRemaining = safeSub(leftOrder.takerAssetAmount, leftOrderTakerAssetFilledAmount);
- uint256 leftMakerAssetAmountRemaining = getPartialAmountFloor(
+ uint256 leftMakerAssetAmountRemaining = safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
leftTakerAssetAmountRemaining
);
uint256 rightTakerAssetAmountRemaining = safeSub(rightOrder.takerAssetAmount, rightOrderTakerAssetFilledAmount);
- uint256 rightMakerAssetAmountRemaining = getPartialAmountFloor(
+ uint256 rightMakerAssetAmountRemaining = safeGetPartialAmountFloor(
rightOrder.makerAssetAmount,
rightOrder.takerAssetAmount,
rightTakerAssetAmountRemaining
@@ -205,7 +205,7 @@ contract MixinMatchOrders is
matchedFillResults.left.takerAssetFilledAmount = matchedFillResults.right.makerAssetFilledAmount;
// Round down to ensure the maker's exchange rate does not exceed the price specified by the order.
// We favor the maker when the exchange rate must be rounded.
- matchedFillResults.left.makerAssetFilledAmount = getPartialAmountFloor(
+ matchedFillResults.left.makerAssetFilledAmount = safeGetPartialAmountFloor(
leftOrder.makerAssetAmount,
leftOrder.takerAssetAmount,
matchedFillResults.left.takerAssetFilledAmount
@@ -217,7 +217,7 @@ contract MixinMatchOrders is
matchedFillResults.right.makerAssetFilledAmount = matchedFillResults.left.takerAssetFilledAmount;
// Round up to ensure the maker's exchange rate does not exceed the price specified by the order.
// We favor the maker when the exchange rate must be rounded.
- matchedFillResults.right.takerAssetFilledAmount = getPartialAmountCeil(
+ matchedFillResults.right.takerAssetFilledAmount = safeGetPartialAmountCeil(
rightOrder.takerAssetAmount,
rightOrder.makerAssetAmount,
matchedFillResults.right.makerAssetFilledAmount
@@ -231,24 +231,24 @@ contract MixinMatchOrders is
);
// Compute fees for left order
- matchedFillResults.left.makerFeePaid = getPartialAmountFloor(
+ matchedFillResults.left.makerFeePaid = safeGetPartialAmountFloor(
matchedFillResults.left.makerAssetFilledAmount,
leftOrder.makerAssetAmount,
leftOrder.makerFee
);
- matchedFillResults.left.takerFeePaid = getPartialAmountFloor(
+ matchedFillResults.left.takerFeePaid = safeGetPartialAmountFloor(
matchedFillResults.left.takerAssetFilledAmount,
leftOrder.takerAssetAmount,
leftOrder.takerFee
);
// Compute fees for right order
- matchedFillResults.right.makerFeePaid = getPartialAmountFloor(
+ matchedFillResults.right.makerFeePaid = safeGetPartialAmountFloor(
matchedFillResults.right.makerAssetFilledAmount,
rightOrder.makerAssetAmount,
rightOrder.makerFee
);
- matchedFillResults.right.takerFeePaid = getPartialAmountFloor(
+ matchedFillResults.right.takerFeePaid = safeGetPartialAmountFloor(
matchedFillResults.right.takerAssetFilledAmount,
rightOrder.takerAssetAmount,
rightOrder.takerFee
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
index a5459a21e..a149f95c9 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/MixinWrapperFunctions.sol
@@ -377,10 +377,11 @@ contract MixinWrapperFunctions is
/// @param orders Array of order specifications.
function batchCancelOrders(LibOrder.Order[] memory orders)
public
+ nonReentrant
{
uint256 ordersLength = orders.length;
for (uint256 i = 0; i != ordersLength; i++) {
- cancelOrder(orders[i]);
+ cancelOrderInternal(orders[i]);
}
}
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
index 0e0fba5d2..57fd53f29 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/libs/LibMath.sol
@@ -26,11 +26,48 @@ contract LibMath is
{
/// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// Reverts if rounding error is >= 0.1%
/// @param numerator Numerator.
/// @param denominator Denominator.
/// @param target Value to calculate partial of.
/// @return Partial value of target rounded down.
- function getPartialAmountFloor(
+ function safeGetPartialAmountFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
+ require(
+ !isRoundingErrorFloor(
+ numerator,
+ denominator,
+ target
+ ),
+ "ROUNDING_ERROR"
+ );
+
+ partialAmount = safeDiv(
+ safeMul(numerator, target),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// Reverts if rounding error is >= 0.1%
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded up.
+ function safeGetPartialAmountCeil(
uint256 numerator,
uint256 denominator,
uint256 target
@@ -43,7 +80,48 @@ contract LibMath is
denominator > 0,
"DIVISION_BY_ZERO"
);
+
+ require(
+ !isRoundingErrorCeil(
+ numerator,
+ denominator,
+ target
+ ),
+ "ROUNDING_ERROR"
+ );
+ // safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
+ // ceil(a / b) = floor((a + b - 1) / b)
+ // To implement `ceil(a / b)` using safeDiv.
+ partialAmount = safeDiv(
+ safeAdd(
+ safeMul(numerator, target),
+ safeSub(denominator, 1)
+ ),
+ denominator
+ );
+ return partialAmount;
+ }
+
+ /// @dev Calculates partial value given a numerator and denominator rounded down.
+ /// @param numerator Numerator.
+ /// @param denominator Denominator.
+ /// @param target Value to calculate partial of.
+ /// @return Partial value of target rounded down.
+ function getPartialAmountFloor(
+ uint256 numerator,
+ uint256 denominator,
+ uint256 target
+ )
+ internal
+ pure
+ returns (uint256 partialAmount)
+ {
+ require(
+ denominator > 0,
+ "DIVISION_BY_ZERO"
+ );
+
partialAmount = safeDiv(
safeMul(numerator, target),
denominator
@@ -69,7 +147,7 @@ contract LibMath is
denominator > 0,
"DIVISION_BY_ZERO"
);
-
+
// safeDiv computes `floor(a / b)`. We use the identity (a, b integer):
// ceil(a / b) = floor((a + b - 1) / b)
// To implement `ceil(a / b)` using safeDiv.
diff --git a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
index d85913e0f..742499568 100644
--- a/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
+++ b/packages/contracts/src/2.0.0/protocol/Exchange/mixins/MExchangeCore.sol
@@ -72,6 +72,11 @@ contract MExchangeCore is
internal
returns (LibFillResults.FillResults memory fillResults);
+ /// @dev After calling, the order can not be filled anymore.
+ /// @param order Order struct containing order specifications.
+ function cancelOrderInternal(LibOrder.Order memory order)
+ internal;
+
/// @dev Updates state with results of a fill order.
/// @param order that was filled.
/// @param takerAddress Address of taker who filled the order.