Convert to use rich reverts

This commit is contained in:
Lawrence Forman 2019-03-30 13:55:54 -04:00 committed by Amir Bandeali
parent 34e0345b29
commit 7277fb3d93
5 changed files with 80 additions and 28 deletions

View File

@ -25,7 +25,8 @@ import "./interfaces/IAssetProxy.sol";
contract MixinAssetProxyDispatcher is contract MixinAssetProxyDispatcher is
Ownable, Ownable,
MAssetProxyDispatcher MAssetProxyDispatcher,
MRichErrors
{ {
// Mapping from Asset Proxy Id's to their respective Asset Proxy // Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => address) public assetProxies; mapping (bytes4 => address) public assetProxies;
@ -64,17 +65,17 @@ contract MixinAssetProxyDispatcher is
} }
/// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws. /// @dev Forwards arguments to assetProxy and calls `transferFrom`. Either succeeds or throws.
/// @param orderHash Hash of the order associated with this transfer.
/// @param assetData Byte array encoded for the asset. /// @param assetData Byte array encoded for the asset.
/// @param from Address to transfer token from. /// @param from Address to transfer token from.
/// @param to Address to transfer token to. /// @param to Address to transfer token to.
/// @param amount Amount of token to transfer. /// @param amount Amount of token to transfer.
/// @param orderHash Order hash, used for rich reverts.
function dispatchTransferFrom( function dispatchTransferFrom(
bytes32 orderHash,
bytes memory assetData, bytes memory assetData,
address from, address from,
address to, address to,
uint256 amount, uint256 amount
bytes32 orderHash
) )
internal internal
{ {
@ -82,14 +83,13 @@ contract MixinAssetProxyDispatcher is
if (amount > 0 && from != to) { if (amount > 0 && from != to) {
// Ensure assetData length is valid // Ensure assetData length is valid
if (assetData.length <= 3) { if (assetData.length <= 3) {
rrevert(AssetProxyDispatchError(AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH)); rrevert(AssetProxyDispatchError(
orderHash,
assetData,
AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH
));
} }
require(
assetData.length > 3,
"LENGTH_GREATER_THAN_3_REQUIRED"
);
// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons. // Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
bytes4 assetProxyId; bytes4 assetProxyId;
assembly { assembly {
@ -102,13 +102,17 @@ contract MixinAssetProxyDispatcher is
// Ensure that assetProxy exists // Ensure that assetProxy exists
if (assetProxy == address(0)) { if (assetProxy == address(0)) {
rrevert(AssetProxyDispatchError(AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY)); rrevert(AssetProxyDispatchError(
orderHash,
assetData,
AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY
));
} }
// Whether the AssetProxy transfer succeeded. // Whether the AssetProxy transfer succeeded.
bool didSucceed; bool didSucceed;
// On failure, the revert message returned by the asset proxy. // On failure, the revert message returned by the asset proxy.
bytes revertMessage = new bytes(0); bytes revertMessage;
// We construct calldata for the `assetProxy.transferFrom` ABI. // We construct calldata for the `assetProxy.transferFrom` ABI.
// The layout of this calldata is in the table below. // The layout of this calldata is in the table below.
@ -129,8 +133,8 @@ contract MixinAssetProxyDispatcher is
/////// Setup State /////// /////// Setup State ///////
// `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr). // `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).
let cdStart := mload(64) let cdStart := mload(64)
// We reserve 256 bytes from `cdStart` because we'll reuse it later for return data. // We reserve 288 bytes from `cdStart` because we'll reuse it later for return data.
mstore(64, add(cdStart, 256)) mstore(64, add(cdStart, 288))
// `dataAreaLength` is the total number of words needed to store `assetData` // `dataAreaLength` is the total number of words needed to store `assetData`
// As-per the ABI spec, this value is padded up to the nearest multiple of 32, // As-per the ABI spec, this value is padded up to the nearest multiple of 32,
// and includes 32-bytes for length. // and includes 32-bytes for length.
@ -172,7 +176,7 @@ contract MixinAssetProxyDispatcher is
cdStart, // pointer to start of input cdStart, // pointer to start of input
sub(cdEnd, cdStart), // length of input sub(cdEnd, cdStart), // length of input
cdStart, // write output over input cdStart, // write output over input
256 // reserve 256 bytes for output 288 // reserve 288 bytes for output
) )
if iszero(didSucceed) { // Call reverted. if iszero(didSucceed) { // Call reverted.
@ -196,12 +200,12 @@ contract MixinAssetProxyDispatcher is
let selector := and(mload(sub(cdStart, 28), 0xffffffff) let selector := and(mload(sub(cdStart, 28), 0xffffffff)
cdStart := add(cdStart, 4) cdStart := add(cdStart, 4)
if eq(selector, 0x08c379a) { if eq(selector, 0x08c379a) {
let dataLength := mload(cdStart); // Set revertMessage to the start of Data.
revertMessage := add(cdStart, mload(cdStart)) revertMessage := add(cdStart, mload(cdStart))
// Truncate the data length if it's larger than our buffer // Truncate the data length if it's larger than our buffer
// size (256 - 32 = 224) // size (288 - 32 = 256)
if gt(dataLength, 224) { if gt(mload(revertMessage), 256) {
mstore(revertMessage, 224) mstore(revertMessage, 256)
} }
} }
} }

View File

@ -135,7 +135,7 @@ contract MixinRichErrors is
} }
function AssetProxyExistsError( function AssetProxyExistsError(
address proxy address proxyAddress
) )
internal internal
pure pure
@ -143,11 +143,12 @@ contract MixinRichErrors is
{ {
return abi.encodeWithSelector( return abi.encodeWithSelector(
ASSET_PROXY_EXISTS_ERROR_SELECTOR, ASSET_PROXY_EXISTS_ERROR_SELECTOR,
proxy proxyAddress
); );
} }
function AssetProxyDispatchError( function AssetProxyDispatchError(
bytes32 orderHash,
bytes memory assetData, bytes memory assetData,
AssetProxyDispatchErrorCodes error AssetProxyDispatchErrorCodes error
) )
@ -157,6 +158,7 @@ contract MixinRichErrors is
{ {
return abi.encodeWithSelector( return abi.encodeWithSelector(
ASSET_PROXY_DISPATCH_ERROR_SELECTOR, ASSET_PROXY_DISPATCH_ERROR_SELECTOR,
orderHash,
assetData, assetData,
uint8(error) uint8(error)
); );

View File

@ -18,9 +18,12 @@
pragma solidity ^0.5.5; pragma solidity ^0.5.5;
import "@0x/contracts-utils/contracs/src/mixins/MLibRichErrors.sol";
contract MRichErrors {
contract MRichErrors is
MLibRichErrors
{
enum FillErrorCodes { enum FillErrorCodes {
INVALID_TAKER_AMOUNT, INVALID_TAKER_AMOUNT,
TAKER_OVERPAY, TAKER_OVERPAY,
@ -139,9 +142,10 @@ contract MRichErrors {
returns (bytes memory); returns (bytes memory);
bytes4 internal constant ASSET_PROXY_DISPATCH_ERROR_SELECTOR = bytes4 internal constant ASSET_PROXY_DISPATCH_ERROR_SELECTOR =
bytes4(keccak256("AssetProxyDispatchError(bytes,uint8)")); bytes4(keccak256("AssetProxyDispatchError(bytes32,bytes,uint8)"));
function AssetProxyDispatchError( function AssetProxyDispatchError(
bytes32 orderHash,
bytes memory assetData, bytes memory assetData,
AssetProxyDispatchErrorCodes error AssetProxyDispatchErrorCodes error
) )

View File

@ -18,12 +18,13 @@
pragma solidity ^0.5.5; pragma solidity ^0.5.5;
contract LibRichErrors { import "./mixins/MLibRichErrors.sol";
contract LibRichErrors is
MLibRichErrors
{
// solhint-disable func-name-mixedcase // solhint-disable func-name-mixedcase
bytes4 private constant STANDARD_ERROR_SELECTOR =
bytes4(keccak256("Error(string)"));
function StandardError( function StandardError(
string memory message string memory message
) )

View File

@ -0,0 +1,41 @@
/*
Copyright 2019 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.5.5;
contract MRichErrors {
// solhint-disable func-name-mixedcase
bytes4 internal constant STANDARD_ERROR_SELECTOR =
bytes4(keccak256("Error(string)"));
function StandardError(
string memory message
)
internal
pure
returns (bytes memory);
// solhint-enable func-name-mixedcase
/// @dev Reverts an encoded rich revert reason `errorData`.
/// @param errorData ABI encoded error data.
function rrevert(bytes memory errorData)
internal
pure;
}