Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

[3.0] Exchange Rich Reverts (once more, with feeling!) #1761

Merged
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
96af267
Reimplementing rich reverts in the contracts.
dorothy-zbornak Mar 29, 2019
a1fded8
Convert to use rich reverts
dorothy-zbornak Mar 30, 2019
14ccab1
Change `LibRichErrors` to just `RichErrors`.
dorothy-zbornak Apr 1, 2019
12b36b7
Convert `MixinExchangeCore` to use rich errors.
dorothy-zbornak Apr 2, 2019
f7aa989
Convert `MixinMatchOrders` to use rich reverts
dorothy-zbornak Apr 2, 2019
fa2b673
Convert `MixinSignatureValidator` to use rich reverts
dorothy-zbornak Apr 2, 2019
acc1968
Convert `MixinTransactions` to use rich reverts
dorothy-zbornak Apr 2, 2019
b1c0151
Convert `exchange` `MixinWrapperFunctions` to use rich reverts
dorothy-zbornak Apr 2, 2019
d50d1ad
Add `OrderStatus` to `@0x/types`
dorothy-zbornak Apr 2, 2019
ee2387a
Add `chaiSetup` function wtih rich revert support to `@0x/dev-utils`
dorothy-zbornak Apr 2, 2019
ea1544d
Add `RichRevertAbi` to `etherem-types`
dorothy-zbornak Apr 2, 2019
ca602f5
Inherit `chaiSetup` from `@0x/dev-utils`
dorothy-zbornak Apr 2, 2019
00f04ac
In `contract-wrappers`, inherit `chaiSetup` from `@0x/dev-utils`
dorothy-zbornak Apr 2, 2019
e538540
In `order-utils`, inherit `chaiSetup` from `@0x/dev-utils`
dorothy-zbornak Apr 2, 2019
068338f
Add `RichRevertReason` type and utilities to `@0x/utils`
dorothy-zbornak Apr 2, 2019
7f0cb06
Add Exchange rich revert types to `@0x/order-utils`
dorothy-zbornak Apr 2, 2019
93251b2
Make the chai helper for rich reverts in `dev-utils` more robust.
dorothy-zbornak Apr 3, 2019
6f9b049
Remove chai plugin dependencies from `@0x/contract-test-utils` and `@…
dorothy-zbornak Apr 3, 2019
7ab831a
Export `StandardError` from `@0x/utils`
dorothy-zbornak Apr 3, 2019
b4188fe
Ran prettier
dorothy-zbornak Apr 3, 2019
a5f98f7
Fix linter errors.
dorothy-zbornak Apr 4, 2019
c1b9b99
Rename `RichRevertReason` to `RevertError`.
dorothy-zbornak Apr 4, 2019
c61b598
Use new `RevertError` nomenclature.
dorothy-zbornak Apr 4, 2019
688cca6
Make `@0x/base-contract` `_throwIfRevertWithReasonCallResult` decode …
dorothy-zbornak Apr 4, 2019
d7b4407
In `@0x/dev-utils` fix the `RevertError` chai helper's `equal` overri…
dorothy-zbornak Apr 4, 2019
cead5f6
In `@0x/contracts-exchange`: fix contract bugs introduced by changes
dorothy-zbornak Apr 4, 2019
1ec678e
In `@0x/contracts-test-utils` add `generatePseudoRandomOrderHash()` t…
dorothy-zbornak Apr 4, 2019
912c910
In `@0x/order-utils`: Rename `ExchangeErrors` to `ExchangeRevertErrors`.
dorothy-zbornak Apr 4, 2019
b9b0e77
In `@0x/utils`: Add `AnyRevertError` type that matches with any rever…
dorothy-zbornak Apr 4, 2019
ed16086
In `@0x/dev-utils`: Break out `RevertError` helper code into a separa…
dorothy-zbornak Apr 4, 2019
0cca10c
In `@0x/contracts-test-utils`: Inherit `OrderStatus` from `@0x/types`
dorothy-zbornak Apr 4, 2019
9cdcfab
In `@0x/typescript-typings`: Add types for `@0x/dev-utils` chai helpe…
dorothy-zbornak Apr 4, 2019
0c97b3b
In `@0x/dev-utils`: Tweak equality assertion failure `actual` and `ex…
dorothy-zbornak Apr 4, 2019
99103a5
In `@0x/contracts-exchange`: Fix `dispatchTransferFrom` so it preserv…
dorothy-zbornak Apr 4, 2019
f52cbd7
In `@0x/order-utils`: add `AssetProxyTransferError` Exchange `RevertE…
dorothy-zbornak Apr 4, 2019
a422ec2
In `@0x/contracts-exchange`: Fix busted `TestAssetProxyDispatcher.sol`
dorothy-zbornak Apr 4, 2019
4e1da8d
In `@0x/contracts-exchange`: upgrading tests...
dorothy-zbornak Apr 4, 2019
719d557
In `@0x/dev-utils`: swap order of equality check in `RevertError` cha…
dorothy-zbornak Apr 5, 2019
61e1f93
In `@0x/utils` add `encode()` method to `RevertError`
dorothy-zbornak Apr 5, 2019
1867af0
In `@0x/order-utils`: Rename Exchange `RevertError` error codes.
dorothy-zbornak Apr 5, 2019
0ec8026
In `@0x/contract-wrappers`: Update tests
dorothy-zbornak Apr 5, 2019
6132759
In `@0x/contracts-exchange`: Update tests for rich reverts
dorothy-zbornak Apr 5, 2019
eca0618
In `@0x/contract-wrappers`: Update CHANGELOG
dorothy-zbornak Apr 5, 2019
1d7d5c8
Ran prettier and linter
dorothy-zbornak Apr 5, 2019
ac2032d
In `@0x/contracts-extensions`: Upgrade tests for rich reverts
dorothy-zbornak Apr 5, 2019
d8944d5
Fix linter errors
dorothy-zbornak Apr 5, 2019
54b59aa
In `@0x/contracts-test-utils`: Update CHANGELOG
dorothy-zbornak Apr 5, 2019
d3a06ca
Add PRs to changelogs
dorothy-zbornak Apr 5, 2019
e01f8ee
In `@0x/contracts-utils` add natspec comments `RichErrors.StandardError`
dorothy-zbornak Apr 11, 2019
ecd60e1
In `@0x/contracts-utils` switch from `encodePacked` to `encodeWithSel…
dorothy-zbornak Apr 11, 2019
ad5792a
In `@0x/contracts-exchange`: More efficient revert string extraction …
dorothy-zbornak Apr 11, 2019
e172a87
Switch order of parameters in some rich reverts for easier dirty pars…
dorothy-zbornak Apr 11, 2019
c18c975
In `@0x/order-utils`: Add `signerAddress` and `signature` to `Exchang…
dorothy-zbornak Apr 11, 2019
9c104f4
In `@0x/order-utils`: Add `TransactionSignatureError` to `ExchangeRev…
dorothy-zbornak Apr 11, 2019
db1e0e0
In `@0x/contracts-exchange`: Fix line count linter error.
dorothy-zbornak Apr 11, 2019
cb010eb
In `@0x/contracts-exchange`: Fix comments in `MixinSignatureValidator…
dorothy-zbornak Apr 12, 2019
4511d08
Update contracts/exchange/contracts/src/MixinSignatureValidator.sol
abandeali1 Apr 12, 2019
7a19280
In `@0x/order-utils`: Add `SignatureWalletError`, `SignatureValidator…
dorothy-zbornak Apr 12, 2019
fc2a26a
In `@0x/contracts-exchange`: Add `SignatureWalletError` and `Signatur…
dorothy-zbornak Apr 12, 2019
dfb43dd
Ran prettier/linter
dorothy-zbornak Apr 12, 2019
bb15955
In `@0x/contracts-exchange`: Change validator/wallet return value tes…
dorothy-zbornak Apr 12, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions contracts/exchange/CHANGELOG.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
{
"note": "Refactor example contracts that use `executeTransaction`",
"pr": 1753
},
{
"note": "Upgrade all string reverts to rich reverts",
"pr": 1761
}
]
},
Expand Down
4 changes: 3 additions & 1 deletion contracts/exchange/contracts/src/Exchange.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import "./MixinWrapperFunctions.sol";
import "./MixinAssetProxyDispatcher.sol";
import "./MixinTransactions.sol";
import "./MixinMatchOrders.sol";
import "./MixinExchangeRichErrors.sol";


// solhint-disable no-empty-blocks
Expand All @@ -35,7 +36,8 @@ contract Exchange is
MixinSignatureValidator,
MixinTransactions,
MixinAssetProxyDispatcher,
MixinWrapperFunctions
MixinWrapperFunctions,
MixinExchangeRichErrors
{
string constant public VERSION = "3.0.0";

Expand Down
106 changes: 80 additions & 26 deletions contracts/exchange/contracts/src/MixinAssetProxyDispatcher.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ pragma solidity ^0.5.5;

import "@0x/contracts-utils/contracts/src/Ownable.sol";
import "./mixins/MAssetProxyDispatcher.sol";
import "./mixins/MExchangeRichErrors.sol";
import "./interfaces/IAssetProxy.sol";


contract MixinAssetProxyDispatcher is
Ownable,
MAssetProxyDispatcher
MAssetProxyDispatcher,
MExchangeRichErrors
{
// Mapping from Asset Proxy Id's to their respective Asset Proxy
mapping (bytes4 => address) public assetProxies;
Expand All @@ -40,10 +42,9 @@ contract MixinAssetProxyDispatcher is
// Ensure that no asset proxy exists with current id.
bytes4 assetProxyId = IAssetProxy(assetProxy).getProxyId();
address currentAssetProxy = assetProxies[assetProxyId];
require(
currentAssetProxy == address(0),
"ASSET_PROXY_ALREADY_EXISTS"
);
if (currentAssetProxy != address(0)) {
rrevert(AssetProxyExistsError(currentAssetProxy));
}

// Add asset proxy and log registration.
assetProxies[assetProxyId] = assetProxy;
Expand All @@ -65,11 +66,13 @@ contract MixinAssetProxyDispatcher is
}

/// @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 from Address to transfer token from.
/// @param to Address to transfer token to.
/// @param amount Amount of token to transfer.
function dispatchTransferFrom(
bytes32 orderHash,
bytes memory assetData,
address from,
address to,
Expand All @@ -80,11 +83,14 @@ contract MixinAssetProxyDispatcher is
// Do nothing if no amount should be transferred.
if (amount > 0 && from != to) {
// Ensure assetData length is valid
require(
assetData.length > 3,
"LENGTH_GREATER_THAN_3_REQUIRED"
);

if (assetData.length <= 3) {
rrevert(AssetProxyDispatchError(
orderHash,
assetData,
AssetProxyDispatchErrorCodes.INVALID_ASSET_DATA_LENGTH
));
}

// Lookup assetProxy. We do not use `LibBytes.readBytes4` for gas efficiency reasons.
bytes4 assetProxyId;
assembly {
Expand All @@ -96,14 +102,22 @@ contract MixinAssetProxyDispatcher is
address assetProxy = assetProxies[assetProxyId];

// Ensure that assetProxy exists
require(
assetProxy != address(0),
"ASSET_PROXY_DOES_NOT_EXIST"
);

if (assetProxy == address(0)) {
rrevert(AssetProxyDispatchError(
orderHash,
assetData,
AssetProxyDispatchErrorCodes.UNKNOWN_ASSET_PROXY
));
}

// Whether the AssetProxy transfer succeeded.
bool didSucceed;
// On failure, the revert message returned by the asset proxy.
bytes memory revertMessage;

// We construct calldata for the `assetProxy.transferFrom` ABI.
// The layout of this calldata is in the table below.
//
//
// | Area | Offset | Length | Contents |
// | -------- |--------|---------|-------------------------------------------- |
// | Header | 0 | 4 | function selector |
Expand All @@ -120,19 +134,21 @@ contract MixinAssetProxyDispatcher is
/////// Setup State ///////
// `cdStart` is the start of the calldata for `assetProxy.transferFrom` (equal to free memory ptr).
let cdStart := mload(64)
// We reserve 288 bytes from `cdStart` because we'll reuse it later for return data.
mstore(64, add(cdStart, 288))
dorothy-zbornak marked this conversation as resolved.
Show resolved Hide resolved
// `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,
// and includes 32-bytes for length.
let dataAreaLength := and(add(mload(assetData), 63), 0xFFFFFFFFFFFE0)
// `cdEnd` is the end of the calldata for `assetProxy.transferFrom`.
let cdEnd := add(cdStart, add(132, dataAreaLength))


/////// Setup Header Area ///////
// This area holds the 4-byte `transferFromSelector`.
// bytes4(keccak256("transferFrom(bytes,address,address,uint256)")) = 0xa85e59e4
mstore(cdStart, 0xa85e59e400000000000000000000000000000000000000000000000000000000)

/////// Setup Params Area ///////
// Each parameter is padded to 32-bytes. The entire Params Area is 128 bytes.
// Notes:
Expand All @@ -142,31 +158,69 @@ contract MixinAssetProxyDispatcher is
mstore(add(cdStart, 36), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(cdStart, 68), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(cdStart, 100), amount)

/////// Setup Data Area ///////
// This area holds `assetData`.
let dataArea := add(cdStart, 132)
let assetDataArea := assetData
// solhint-disable-next-line no-empty-blocks
for {} lt(dataArea, cdEnd) {} {
mstore(dataArea, mload(assetData))
mstore(dataArea, mload(assetDataArea))
dataArea := add(dataArea, 32)
assetData := add(assetData, 32)
assetDataArea := add(assetDataArea, 32)
}

/////// Call `assetProxy.transferFrom` using the constructed calldata ///////
let success := call(
didSucceed := call(
gas, // forward all gas
assetProxy, // call address of asset proxy
0, // don't send any ETH
cdStart, // pointer to start of input
sub(cdEnd, cdStart), // length of input
sub(cdEnd, cdStart), // length of input
cdStart, // write output over input
512 // reserve 512 bytes for output
288 // reserve 288 bytes for output
dorothy-zbornak marked this conversation as resolved.
Show resolved Hide resolved
)
if iszero(success) {
revert(cdStart, returndatasize())

if iszero(didSucceed) { // Call reverted.
dorothy-zbornak marked this conversation as resolved.
Show resolved Hide resolved
// Attempt to to decode standard revert messages with the
// signature `Error(string)` so we can upgrade them to
// a rich revert.

// Standard revert errors have the following memory layout:
// | Area | Offset | Length | Contents |
// --------------------------------------------------------------------------------
// | Selector | 0 | 4 | bytes4 function selector (0x08c379a0) |
// | Params | | | |
// | | 4 | 32 | uint256 offset to data (o) |
// | Data | | | |
// | | 4 + o | 32 | uint256 length of data (n |
// | | 32 + o | n | Left-aligned message bytes |

// Make sure the return value is long enough.
if gt(returndatasize(), 67) {
dorothy-zbornak marked this conversation as resolved.
Show resolved Hide resolved
// Check that the selector matches for a standard revert error.
let selector := and(mload(sub(cdStart, 28)), 0xffffffff)
cdStart := add(cdStart, 4)
if eq(selector, 0x08c379a0) {
// Set revertMessage to the start of the reason string.
revertMessage := add(cdStart, mload(cdStart))
// Truncate the data length if it's larger than our buffer
// size (288 - 32 = 256)
if gt(mload(revertMessage), 256) {
dorothy-zbornak marked this conversation as resolved.
Show resolved Hide resolved
mstore(revertMessage, 256)
}
}
}
}
}

if (!didSucceed) {
rrevert(AssetProxyTransferError(
orderHash,
assetData,
string(revertMessage)
));
}
}
}
}
Loading