@0x/contracts-zero-ex
: Add reentrancy guard to mtx functions
`@0x/contracts-zero-ex`: Add refund mechanism to mtxs `@0x/contracts-zero-ex`: Pass sender to transfomers. `@0x/contracts-zero-ex`: Refund protocol fees to `refundReceiver` in FQT. `@0x/utils`: Add EP flavor of `IllegalReentrancyError` `@0x/order-utils`: Add `refundReceiver` to FQT transform data. `@0x/asset-swapper`: Add `refundReceiver` support to EP swap quote consumer.
This commit is contained in:
committed by
Lawrence Forman
parent
5f47ad3363
commit
5dd686f22f
@@ -1,4 +1,25 @@
|
||||
[
|
||||
{
|
||||
"version": "0.3.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Internal audit fixes",
|
||||
"pr": 2657
|
||||
},
|
||||
{
|
||||
"note": "Add refund mechanism to meta-transactions",
|
||||
"pr": 2657
|
||||
},
|
||||
{
|
||||
"note": "Pass sender address to transformers",
|
||||
"pr": 2657
|
||||
},
|
||||
{
|
||||
"note": "Refund unused protocol fees to `refundReceiver` in FQT",
|
||||
"pr": 2657
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"changes": [
|
||||
|
@@ -34,13 +34,15 @@ library LibCommonRichErrors {
|
||||
);
|
||||
}
|
||||
|
||||
function IllegalReentrancyError()
|
||||
function IllegalReentrancyError(bytes4 selector, uint256 reentrancyFlags)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
bytes4(keccak256("IllegalReentrancyError()"))
|
||||
bytes4(keccak256("IllegalReentrancyError(bytes4,uint256)")),
|
||||
selector,
|
||||
reentrancyFlags
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -21,8 +21,10 @@ pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol";
|
||||
import "../errors/LibMetaTransactionsRichErrors.sol";
|
||||
import "../fixins/FixinCommon.sol";
|
||||
import "../fixins/FixinReentrancyGuard.sol";
|
||||
import "../fixins/FixinEIP712.sol";
|
||||
import "../migrations/LibMigrate.sol";
|
||||
import "../storage/LibMetaTransactionsStorage.sol";
|
||||
@@ -39,6 +41,7 @@ contract MetaTransactions is
|
||||
IFeature,
|
||||
IMetaTransactions,
|
||||
FixinCommon,
|
||||
FixinReentrancyGuard,
|
||||
FixinEIP712
|
||||
{
|
||||
using LibBytesV06 for bytes;
|
||||
@@ -92,6 +95,16 @@ contract MetaTransactions is
|
||||
")"
|
||||
);
|
||||
|
||||
/// @dev Refunds up to `msg.value` leftover ETH at the end of the call.
|
||||
modifier refundsAttachedEth() {
|
||||
_;
|
||||
uint256 remainingBalance =
|
||||
LibSafeMathV06.min256(msg.value, address(this).balance);
|
||||
if (remainingBalance > 0) {
|
||||
msg.sender.transfer(remainingBalance);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(address zeroExAddress)
|
||||
public
|
||||
FixinCommon()
|
||||
@@ -127,9 +140,11 @@ contract MetaTransactions is
|
||||
public
|
||||
payable
|
||||
override
|
||||
nonReentrant(REENTRANCY_MTX)
|
||||
refundsAttachedEth
|
||||
returns (bytes memory returnResult)
|
||||
{
|
||||
return _executeMetaTransactionPrivate(
|
||||
returnResult = _executeMetaTransactionPrivate(
|
||||
msg.sender,
|
||||
mtx,
|
||||
signature
|
||||
@@ -147,6 +162,8 @@ contract MetaTransactions is
|
||||
public
|
||||
payable
|
||||
override
|
||||
nonReentrant(REENTRANCY_MTX)
|
||||
refundsAttachedEth
|
||||
returns (bytes[] memory returnResults)
|
||||
{
|
||||
if (mtxs.length != signatures.length) {
|
||||
@@ -255,10 +272,19 @@ contract MetaTransactions is
|
||||
_validateMetaTransaction(state);
|
||||
|
||||
// Mark the transaction executed.
|
||||
assert(block.number > 0);
|
||||
LibMetaTransactionsStorage.getStorage()
|
||||
.mtxHashToExecutedBlockNumber[state.hash] = block.number;
|
||||
|
||||
// Pay the fee to the sender.
|
||||
if (mtx.feeAmount > 0) {
|
||||
ITokenSpender(address(this))._spendERC20Tokens(
|
||||
mtx.feeToken,
|
||||
mtx.signer, // From the signer.
|
||||
sender, // To the sender.
|
||||
mtx.feeAmount
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the call based on the selector.
|
||||
state.selector = mtx.callData.readBytes4(0);
|
||||
if (state.selector == ITransformERC20.transformERC20.selector) {
|
||||
@@ -268,15 +294,6 @@ contract MetaTransactions is
|
||||
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
||||
.rrevert();
|
||||
}
|
||||
// Pay the fee to the sender.
|
||||
if (mtx.feeAmount > 0) {
|
||||
ITokenSpender(address(this))._spendERC20Tokens(
|
||||
mtx.feeToken,
|
||||
mtx.signer, // From the signer.
|
||||
sender, // To the sender.
|
||||
mtx.feeAmount
|
||||
);
|
||||
}
|
||||
emit MetaTransactionExecuted(
|
||||
state.hash,
|
||||
state.selector,
|
||||
@@ -367,7 +384,7 @@ contract MetaTransactions is
|
||||
// since decoding a single struct arg consumes far less stack space than
|
||||
// decoding multiple struct args.
|
||||
|
||||
// Where the encoding for multiple args (with the seleector ommitted)
|
||||
// Where the encoding for multiple args (with the selector ommitted)
|
||||
// would typically look like:
|
||||
// | argument | offset |
|
||||
// |--------------------------|---------|
|
||||
@@ -394,7 +411,7 @@ contract MetaTransactions is
|
||||
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
||||
// Copy the args data from the original, after the new struct offset prefix.
|
||||
bytes memory fromCallData = state.mtx.callData;
|
||||
assert(fromCallData.length >= 4);
|
||||
assert(fromCallData.length >= 160);
|
||||
uint256 fromMem;
|
||||
uint256 toMem;
|
||||
assembly {
|
||||
|
@@ -37,6 +37,13 @@ contract SignatureValidator is
|
||||
using LibBytesV06 for bytes;
|
||||
using LibRichErrorsV06 for bytes;
|
||||
|
||||
/// @dev Exclusive upper limit on ECDSA signatures 'R' values.
|
||||
/// The valid range is given by fig (282) of the yellow paper.
|
||||
uint256 private constant ECDSA_SIGNATURE_R_LIMIT =
|
||||
uint256(0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141);
|
||||
/// @dev Exclusive upper limit on ECDSA signatures 'S' values.
|
||||
/// The valid range is given by fig (283) of the yellow paper.
|
||||
uint256 private constant ECDSA_SIGNATURE_S_LIMIT = ECDSA_SIGNATURE_R_LIMIT / 2 + 1;
|
||||
/// @dev Name of this feature.
|
||||
string public constant override FEATURE_NAME = "SignatureValidator";
|
||||
/// @dev Version of this feature.
|
||||
@@ -160,12 +167,18 @@ contract SignatureValidator is
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
recovered = ecrecover(
|
||||
hash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
if (v < 27) {
|
||||
// Handle clients that encode v as 0 or 1.
|
||||
v += 27;
|
||||
}
|
||||
if (uint256(r) < ECDSA_SIGNATURE_R_LIMIT && uint256(s) < ECDSA_SIGNATURE_S_LIMIT) {
|
||||
recovered = ecrecover(
|
||||
hash,
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
}
|
||||
} else if (signatureType == SignatureType.EthSign) {
|
||||
// Signed using `eth_sign`
|
||||
if (signature.length != 66) {
|
||||
@@ -179,15 +192,21 @@ contract SignatureValidator is
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
recovered = ecrecover(
|
||||
keccak256(abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
hash
|
||||
)),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
if (v < 27) {
|
||||
// Handle clients that encode v as 0 or 1.
|
||||
v += 27;
|
||||
}
|
||||
if (uint256(r) < ECDSA_SIGNATURE_R_LIMIT && uint256(s) < ECDSA_SIGNATURE_S_LIMIT) {
|
||||
recovered = ecrecover(
|
||||
keccak256(abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
hash
|
||||
)),
|
||||
v,
|
||||
r,
|
||||
s
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// This should never happen.
|
||||
revert('SignatureValidator/ILLEGAL_CODE_PATH');
|
||||
|
@@ -360,9 +360,12 @@ contract TransformERC20 is
|
||||
// Call data.
|
||||
abi.encodeWithSelector(
|
||||
IERC20Transformer.transform.selector,
|
||||
callDataHash,
|
||||
taker,
|
||||
transformation.data
|
||||
IERC20Transformer.TransformContext({
|
||||
callDataHash: callDataHash,
|
||||
sender: msg.sender,
|
||||
taker: taker,
|
||||
data: transformation.data
|
||||
})
|
||||
)
|
||||
);
|
||||
// Ensure the transformer returned the magic bytes.
|
||||
|
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||
import "../errors/LibCommonRichErrors.sol";
|
||||
import "../storage/LibReentrancyGuardStorage.sol";
|
||||
|
||||
|
||||
/// @dev Common feature utilities.
|
||||
abstract contract FixinReentrancyGuard {
|
||||
|
||||
using LibRichErrorsV06 for bytes;
|
||||
using LibBytesV06 for bytes;
|
||||
|
||||
// Combinable reentrancy flags.
|
||||
/// @dev Reentrancy guard flag for meta-transaction functions.
|
||||
uint256 constant internal REENTRANCY_MTX = 0x1;
|
||||
|
||||
/// @dev Cannot reenter a function with the same reentrancy guard flags.
|
||||
modifier nonReentrant(uint256 reentrancyFlags) virtual {
|
||||
LibReentrancyGuardStorage.Storage storage stor =
|
||||
LibReentrancyGuardStorage.getStorage();
|
||||
{
|
||||
uint256 currentFlags = stor.reentrancyFlags;
|
||||
// Revert if any bits in `reentrancyFlags` has already been set.
|
||||
if ((currentFlags & reentrancyFlags) != 0) {
|
||||
LibCommonRichErrors.IllegalReentrancyError(
|
||||
msg.data.readBytes4(0),
|
||||
reentrancyFlags
|
||||
).rrevert();
|
||||
}
|
||||
// Update reentrancy flags.
|
||||
stor.reentrancyFlags = currentFlags | reentrancyFlags;
|
||||
}
|
||||
|
||||
_;
|
||||
|
||||
// Clear reentrancy flags.
|
||||
stor.reentrancyFlags = stor.reentrancyFlags & (~reentrancyFlags);
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
|
||||
Copyright 2020 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.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "./LibStorage.sol";
|
||||
import "../external/IFlashWallet.sol";
|
||||
|
||||
|
||||
/// @dev Storage helpers for the `FixinReentrancyGuard` mixin.
|
||||
library LibReentrancyGuardStorage {
|
||||
|
||||
/// @dev Storage bucket for this feature.
|
||||
struct Storage {
|
||||
// Reentrancy flags set whenever a non-reentrant function is entered
|
||||
// and cleared when it is exited.
|
||||
uint256 reentrancyFlags;
|
||||
}
|
||||
|
||||
/// @dev Get the storage bucket for this contract.
|
||||
function getStorage() internal pure returns (Storage storage stor) {
|
||||
uint256 storageSlot = LibStorage.getStorageSlot(
|
||||
LibStorage.StorageId.ReentrancyGuard
|
||||
);
|
||||
// Dip into assembly to change the slot pointed to by the local
|
||||
// variable `stor`.
|
||||
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
|
||||
assembly { stor_slot := storageSlot }
|
||||
}
|
||||
}
|
@@ -35,7 +35,8 @@ library LibStorage {
|
||||
Ownable,
|
||||
TokenSpender,
|
||||
TransformERC20,
|
||||
MetaTransactions
|
||||
MetaTransactions,
|
||||
ReentrancyGuard
|
||||
}
|
||||
|
||||
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
|
||||
|
@@ -58,18 +58,14 @@ contract AffiliateFeeTransformer is
|
||||
{}
|
||||
|
||||
/// @dev Transfers tokens to recipients.
|
||||
/// @param data ABI-encoded `TokenFee[]`, indicating which tokens to transfer.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(
|
||||
bytes32, // callDataHash,
|
||||
address payable, // taker,
|
||||
bytes calldata data
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TokenFee[] memory fees = abi.decode(data, (TokenFee[]));
|
||||
TokenFee[] memory fees = abi.decode(context.data, (TokenFee[]));
|
||||
|
||||
// Transfer tokens to recipients.
|
||||
for (uint256 i = 0; i < fees.length; ++i) {
|
||||
|
@@ -74,6 +74,12 @@ contract FillQuoteTransformer is
|
||||
// For sells, this may be `uint256(-1)` to sell the entire balance of
|
||||
// `sellToken`.
|
||||
uint256 fillAmount;
|
||||
// Who to transfer unused protocol fees to.
|
||||
// May be a valid address or one of:
|
||||
// `address(0)`: Stay in flash wallet.
|
||||
// `address(1)`: Send to the taker.
|
||||
// `address(2)`: Send to the sender (caller of `transformERC20()`).
|
||||
address payable refundReceiver;
|
||||
}
|
||||
|
||||
/// @dev Results of a call to `_fillOrder()`.
|
||||
@@ -111,6 +117,12 @@ contract FillQuoteTransformer is
|
||||
bytes4 private constant ERC20_BRIDGE_PROXY_ID = 0xdc1600f3;
|
||||
/// @dev Maximum uint256 value.
|
||||
uint256 private constant MAX_UINT256 = uint256(-1);
|
||||
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||
/// protocol fees will be sent to the taker.
|
||||
address private constant REFUND_RECEIVER_TAKER = address(1);
|
||||
/// @dev If `refundReceiver` is set to this address, unpsent
|
||||
/// protocol fees will be sent to the sender.
|
||||
address private constant REFUND_RECEIVER_SENDER = address(2);
|
||||
|
||||
/// @dev The Exchange contract.
|
||||
IExchange public immutable exchange;
|
||||
@@ -130,32 +142,28 @@ contract FillQuoteTransformer is
|
||||
/// @dev Sell this contract's entire balance of of `sellToken` in exchange
|
||||
/// for `buyToken` by filling `orders`. Protocol fees should be attached
|
||||
/// to this call. `buyToken` and excess ETH will be transferred back to the caller.
|
||||
/// @param data_ ABI-encoded `TransformData`.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(
|
||||
bytes32, // callDataHash,
|
||||
address payable, // taker,
|
||||
bytes calldata data_
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
freesGasTokensFromCollector
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TransformData memory data = abi.decode(data_, (TransformData));
|
||||
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||
FillState memory state;
|
||||
|
||||
// Validate data fields.
|
||||
if (data.sellToken.isTokenETH() || data.buyToken.isTokenETH()) {
|
||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
||||
data_
|
||||
context.data
|
||||
).rrevert();
|
||||
}
|
||||
if (data.orders.length != data.signatures.length) {
|
||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_ARRAY_LENGTH,
|
||||
data_
|
||||
context.data
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
@@ -249,6 +257,17 @@ contract FillQuoteTransformer is
|
||||
).rrevert();
|
||||
}
|
||||
}
|
||||
|
||||
// Refund unspent protocol fees.
|
||||
if (state.ethRemaining > 0 && data.refundReceiver != address(0)) {
|
||||
if (data.refundReceiver == REFUND_RECEIVER_TAKER) {
|
||||
context.taker.transfer(state.ethRemaining);
|
||||
} else if (data.refundReceiver == REFUND_RECEIVER_SENDER) {
|
||||
context.sender.transfer(state.ethRemaining);
|
||||
} else {
|
||||
data.refundReceiver.transfer(state.ethRemaining);
|
||||
}
|
||||
}
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
}
|
||||
|
||||
|
@@ -25,17 +25,24 @@ import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||
/// @dev A transformation callback used in `TransformERC20.transformERC20()`.
|
||||
interface IERC20Transformer {
|
||||
|
||||
/// @dev Context information to pass into `transform()` by `TransformERC20.transformERC20()`.
|
||||
struct TransformContext {
|
||||
// The hash of the `TransformERC20.transformERC20()` calldata.
|
||||
bytes32 callDataHash;
|
||||
// The caller of `TransformERC20.transformERC20()`.
|
||||
address payable sender;
|
||||
// taker The taker address, which may be distinct from `sender` in the case
|
||||
// meta-transactions.
|
||||
address payable taker;
|
||||
// Arbitrary data to pass to the transformer.
|
||||
bytes data;
|
||||
}
|
||||
|
||||
/// @dev Called from `TransformERC20.transformERC20()`. This will be
|
||||
/// delegatecalled in the context of the FlashWallet instance being used.
|
||||
/// @param callDataHash The hash of the `TransformERC20.transformERC20()` calldata.
|
||||
/// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
|
||||
/// @param data Arbitrary data to pass to the transformer.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(
|
||||
bytes32 callDataHash,
|
||||
address payable taker,
|
||||
bytes calldata data
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
returns (bytes4 success);
|
||||
}
|
||||
|
@@ -56,19 +56,14 @@ contract PayTakerTransformer is
|
||||
{}
|
||||
|
||||
/// @dev Forwards tokens to the taker.
|
||||
/// @param taker The taker address (caller of `TransformERC20.transformERC20()`).
|
||||
/// @param data_ ABI-encoded `TransformData`, indicating which tokens to transfer.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(
|
||||
bytes32, // callDataHash,
|
||||
address payable taker,
|
||||
bytes calldata data_
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TransformData memory data = abi.decode(data_, (TransformData));
|
||||
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||
|
||||
// Transfer tokens directly to the taker.
|
||||
for (uint256 i = 0; i < data.tokens.length; ++i) {
|
||||
@@ -79,7 +74,7 @@ contract PayTakerTransformer is
|
||||
amount = data.tokens[i].getTokenBalanceOf(address(this));
|
||||
}
|
||||
if (amount != 0) {
|
||||
data.tokens[i].transformerTransfer(taker, amount);
|
||||
data.tokens[i].transformerTransfer(context.taker, amount);
|
||||
}
|
||||
}
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
|
@@ -59,22 +59,18 @@ contract WethTransformer is
|
||||
}
|
||||
|
||||
/// @dev Wraps and unwraps WETH.
|
||||
/// @param data_ ABI-encoded `TransformData`, indicating which token to wrap/umwrap.
|
||||
/// @param context Context information.
|
||||
/// @return success The success bytes (`LibERC20Transformer.TRANSFORMER_SUCCESS`).
|
||||
function transform(
|
||||
bytes32, // callDataHash,
|
||||
address payable, // taker,
|
||||
bytes calldata data_
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TransformData memory data = abi.decode(data_, (TransformData));
|
||||
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||
if (!data.token.isTokenETH() && data.token != weth) {
|
||||
LibTransformERC20RichErrors.InvalidTransformDataError(
|
||||
LibTransformERC20RichErrors.InvalidTransformDataErrorCode.INVALID_TOKENS,
|
||||
data_
|
||||
context.data
|
||||
).rrevert();
|
||||
}
|
||||
|
||||
|
@@ -31,6 +31,8 @@ contract TestFillQuoteTransformerHost is
|
||||
IERC20Transformer transformer,
|
||||
TestMintableERC20Token inputToken,
|
||||
uint256 inputTokenAmount,
|
||||
address payable sender,
|
||||
address payable taker,
|
||||
bytes calldata data
|
||||
)
|
||||
external
|
||||
@@ -40,6 +42,14 @@ contract TestFillQuoteTransformerHost is
|
||||
inputToken.mint(address(this), inputTokenAmount);
|
||||
}
|
||||
// Have to make this call externally because transformers aren't payable.
|
||||
this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
|
||||
this.rawExecuteTransform(
|
||||
transformer,
|
||||
IERC20Transformer.TransformContext({
|
||||
callDataHash: bytes32(0),
|
||||
sender: sender,
|
||||
taker: taker,
|
||||
data: data
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/features/TransformERC20.sol";
|
||||
import "../src/features/IMetaTransactions.sol";
|
||||
|
||||
|
||||
contract TestMetaTransactionsTransformERC20Feature is
|
||||
@@ -48,6 +49,49 @@ contract TestMetaTransactionsTransformERC20Feature is
|
||||
revert('FAIL');
|
||||
}
|
||||
|
||||
if (msg.value == 777) {
|
||||
// Try to reenter `executeMetaTransaction()`
|
||||
IMetaTransactions(address(this)).executeMetaTransaction(
|
||||
IMetaTransactions.MetaTransactionData({
|
||||
signer: address(0),
|
||||
sender: address(0),
|
||||
minGasPrice: 0,
|
||||
maxGasPrice: 0,
|
||||
expirationTimeSeconds: 0,
|
||||
salt: 0,
|
||||
callData: "",
|
||||
value: 0,
|
||||
feeToken: IERC20TokenV06(0),
|
||||
feeAmount: 0
|
||||
}),
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
if (msg.value == 888) {
|
||||
// Try to reenter `batchExecuteMetaTransactions()`
|
||||
IMetaTransactions.MetaTransactionData[] memory mtxs =
|
||||
new IMetaTransactions.MetaTransactionData[](1);
|
||||
bytes[] memory signatures = new bytes[](1);
|
||||
mtxs[0] = IMetaTransactions.MetaTransactionData({
|
||||
signer: address(0),
|
||||
sender: address(0),
|
||||
minGasPrice: 0,
|
||||
maxGasPrice: 0,
|
||||
expirationTimeSeconds: 0,
|
||||
salt: 0,
|
||||
callData: "",
|
||||
value: 0,
|
||||
feeToken: IERC20TokenV06(0),
|
||||
feeAmount: 0
|
||||
});
|
||||
signatures[0] = "";
|
||||
IMetaTransactions(address(this)).batchExecuteMetaTransactions(
|
||||
mtxs,
|
||||
signatures
|
||||
);
|
||||
}
|
||||
|
||||
emit TransformERC20Called(
|
||||
msg.sender,
|
||||
msg.value,
|
||||
|
@@ -40,28 +40,26 @@ contract TestMintTokenERC20Transformer is
|
||||
address context,
|
||||
address caller,
|
||||
bytes32 callDataHash,
|
||||
address sender,
|
||||
address taker,
|
||||
bytes data,
|
||||
uint256 inputTokenBalance,
|
||||
uint256 ethBalance
|
||||
);
|
||||
|
||||
function transform(
|
||||
bytes32 callDataHash,
|
||||
address payable taker,
|
||||
bytes calldata data_
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
{
|
||||
TransformData memory data = abi.decode(data_, (TransformData));
|
||||
TransformData memory data = abi.decode(context.data, (TransformData));
|
||||
emit MintTransform(
|
||||
address(this),
|
||||
msg.sender,
|
||||
callDataHash,
|
||||
taker,
|
||||
data_,
|
||||
context.callDataHash,
|
||||
context.sender,
|
||||
context.taker,
|
||||
context.data,
|
||||
data.inputToken.balanceOf(address(this)),
|
||||
address(this).balance
|
||||
);
|
||||
@@ -69,14 +67,14 @@ contract TestMintTokenERC20Transformer is
|
||||
data.inputToken.transfer(address(0), data.burnAmount);
|
||||
// Mint output tokens.
|
||||
if (LibERC20Transformer.isTokenETH(IERC20TokenV06(address(data.outputToken)))) {
|
||||
taker.transfer(data.mintAmount);
|
||||
context.taker.transfer(data.mintAmount);
|
||||
} else {
|
||||
data.outputToken.mint(
|
||||
taker,
|
||||
context.taker,
|
||||
data.mintAmount
|
||||
);
|
||||
// Burn fees from output.
|
||||
data.outputToken.burn(taker, data.feeAmount);
|
||||
data.outputToken.burn(context.taker, data.feeAmount);
|
||||
}
|
||||
return LibERC20Transformer.TRANSFORMER_SUCCESS;
|
||||
}
|
||||
|
@@ -20,17 +20,15 @@ pragma solidity ^0.6.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/transformers/Transformer.sol";
|
||||
import "../src/transformers/IERC20Transformer.sol";
|
||||
import "../src/transformers/LibERC20Transformer.sol";
|
||||
|
||||
|
||||
contract TestTransformerBase is
|
||||
IERC20Transformer,
|
||||
Transformer
|
||||
{
|
||||
function transform(
|
||||
bytes32,
|
||||
address payable,
|
||||
bytes calldata
|
||||
)
|
||||
function transform(TransformContext calldata context)
|
||||
external
|
||||
override
|
||||
returns (bytes4 success)
|
||||
|
@@ -32,18 +32,14 @@ contract TestTransformerHost {
|
||||
|
||||
function rawExecuteTransform(
|
||||
IERC20Transformer transformer,
|
||||
bytes32 callDataHash,
|
||||
address taker,
|
||||
bytes calldata data
|
||||
IERC20Transformer.TransformContext calldata context
|
||||
)
|
||||
external
|
||||
{
|
||||
(bool _success, bytes memory resultData) =
|
||||
address(transformer).delegatecall(abi.encodeWithSelector(
|
||||
transformer.transform.selector,
|
||||
callDataHash,
|
||||
taker,
|
||||
data
|
||||
context
|
||||
));
|
||||
if (!_success) {
|
||||
resultData.rrevert();
|
||||
|
@@ -48,6 +48,14 @@ contract TestWethTransformerHost is
|
||||
_weth.deposit{value: wethAmount}();
|
||||
}
|
||||
// Have to make this call externally because transformers aren't payable.
|
||||
this.rawExecuteTransform(transformer, bytes32(0), msg.sender, data);
|
||||
this.rawExecuteTransform(
|
||||
transformer,
|
||||
IERC20Transformer.TransformContext({
|
||||
callDataHash: bytes32(0),
|
||||
sender: msg.sender,
|
||||
taker: msg.sender,
|
||||
data: data
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@
|
||||
"config": {
|
||||
"publicInterfaceContracts": "IZeroEx,ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnable,ISimpleFunctionRegistry,ITokenSpender,ITransformERC20,FillQuoteTransformer,PayTakerTransformer,WethTransformer,Ownable,SimpleFunctionRegistry,TransformERC20,TokenSpender,AffiliateFeeTransformer,SignatureValidator,MetaTransactions",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinGasToken|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactions|IOwnable|ISignatureValidator|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|MetaTransactions|Ownable|PayTakerTransformer|SignatureValidator|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinGasToken|FixinReentrancyGuard|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactions|IOwnable|ISignatureValidator|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|IZeroEx|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibReentrancyGuardStorage|LibSignatureRichErrors|LibSignedCallData|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|MetaTransactions|Ownable|PayTakerTransformer|SignatureValidator|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -12,6 +12,7 @@ import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTran
|
||||
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
|
||||
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
|
||||
import * as FixinGasToken from '../test/generated-artifacts/FixinGasToken.json';
|
||||
import * as FixinReentrancyGuard from '../test/generated-artifacts/FixinReentrancyGuard.json';
|
||||
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
||||
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
||||
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
||||
@@ -41,6 +42,7 @@ import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRic
|
||||
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
||||
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
||||
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
|
||||
import * as LibReentrancyGuardStorage from '../test/generated-artifacts/LibReentrancyGuardStorage.json';
|
||||
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
|
||||
import * as LibSignedCallData from '../test/generated-artifacts/LibSignedCallData.json';
|
||||
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
||||
@@ -120,6 +122,7 @@ export const artifacts = {
|
||||
FixinCommon: FixinCommon as ContractArtifact,
|
||||
FixinEIP712: FixinEIP712 as ContractArtifact,
|
||||
FixinGasToken: FixinGasToken as ContractArtifact,
|
||||
FixinReentrancyGuard: FixinReentrancyGuard as ContractArtifact,
|
||||
FullMigration: FullMigration as ContractArtifact,
|
||||
InitialMigration: InitialMigration as ContractArtifact,
|
||||
LibBootstrap: LibBootstrap as ContractArtifact,
|
||||
@@ -127,6 +130,7 @@ export const artifacts = {
|
||||
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
|
||||
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
|
||||
LibProxyStorage: LibProxyStorage as ContractArtifact,
|
||||
LibReentrancyGuardStorage: LibReentrancyGuardStorage as ContractArtifact,
|
||||
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
||||
LibStorage: LibStorage as ContractArtifact,
|
||||
LibTokenSpenderStorage: LibTokenSpenderStorage as ContractArtifact,
|
||||
|
@@ -36,6 +36,10 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
let allowanceTarget: string;
|
||||
|
||||
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
||||
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
|
||||
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
|
||||
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
|
||||
const REENTRANCY_FLAG_MTX = 0x1;
|
||||
|
||||
before(async () => {
|
||||
[owner, sender, ...signers] = await env.getAccountAddressesAsync();
|
||||
@@ -263,7 +267,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
it('fails if the translated call fails', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
value: new BigNumber(666),
|
||||
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
@@ -469,6 +473,72 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('executeMetaTransaction'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('batchExecuteMetaTransactions'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchExecuteMetaTransactions()', () => {
|
||||
@@ -526,6 +596,102 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(mtxHash, block),
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if a meta-transaction fails', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.minGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).callAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new StringRevertError('FAIL').encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('executeMetaTransaction'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('batchExecuteMetaTransactions'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMetaTransactionExecutedBlock()', () => {
|
||||
|
@@ -37,6 +37,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
const callDataSigner = ethjs.bufferToHex(ethjs.privateToAddress(ethjs.toBuffer(callDataSignerKey)));
|
||||
let owner: string;
|
||||
let taker: string;
|
||||
let sender: string;
|
||||
let transformerDeployer: string;
|
||||
let zeroEx: ZeroExContract;
|
||||
let feature: TransformERC20Contract;
|
||||
@@ -44,7 +45,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
let allowanceTarget: string;
|
||||
|
||||
before(async () => {
|
||||
[owner, taker, transformerDeployer] = await env.getAccountAddressesAsync();
|
||||
[owner, taker, sender, transformerDeployer] = await env.getAccountAddressesAsync();
|
||||
zeroEx = await fullMigrateAsync(
|
||||
owner,
|
||||
env.provider,
|
||||
@@ -59,12 +60,12 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
},
|
||||
{ transformerDeployer },
|
||||
);
|
||||
feature = new TransformERC20Contract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||
feature = new TransformERC20Contract(zeroEx.address, env.provider, { ...env.txDefaults, from: sender }, abis);
|
||||
wallet = new FlashWalletContract(await feature.getTransformWallet().callAsync(), env.provider, env.txDefaults);
|
||||
allowanceTarget = await new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults)
|
||||
.getAllowanceTarget()
|
||||
.callAsync();
|
||||
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync();
|
||||
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync({ from: owner });
|
||||
});
|
||||
|
||||
const { MAX_UINT256, ZERO_AMOUNT } = constants;
|
||||
@@ -73,7 +74,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
it('createTransformWallet() replaces the current wallet', async () => {
|
||||
const newWalletAddress = await feature.createTransformWallet().callAsync({ from: owner });
|
||||
expect(newWalletAddress).to.not.eq(wallet.address);
|
||||
await feature.createTransformWallet().awaitTransactionSuccessAsync();
|
||||
await feature.createTransformWallet().awaitTransactionSuccessAsync({ from: owner });
|
||||
return expect(feature.getTransformWallet().callAsync()).to.eventually.eq(newWalletAddress);
|
||||
});
|
||||
|
||||
@@ -264,8 +265,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -320,8 +322,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
taker,
|
||||
sender,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -379,8 +382,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -496,8 +500,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformations[0].data,
|
||||
@@ -505,8 +510,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
ethBalance: callValue,
|
||||
},
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformations[1].data,
|
||||
|
@@ -86,7 +86,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
sender: randomAddress(),
|
||||
taker: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
||||
@@ -112,7 +117,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
sender: randomAddress(),
|
||||
taker: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||
expect(await getBalancesAsync(recipients[0])).to.deep.eq({
|
||||
@@ -138,7 +148,12 @@ blockchainTests.resets('AffiliateFeeTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), randomAddress(), data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
sender: randomAddress(),
|
||||
taker: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
||||
tokenBalance: new BigNumber(1),
|
||||
|
@@ -31,6 +31,8 @@ const { NULL_ADDRESS, NULL_BYTES, MAX_UINT256, ZERO_AMOUNT } = constants;
|
||||
blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
let maker: string;
|
||||
let feeRecipient: string;
|
||||
let sender: string;
|
||||
let taker: string;
|
||||
let exchange: TestFillQuoteTransformerExchangeContract;
|
||||
let bridge: TestFillQuoteTransformerBridgeContract;
|
||||
let transformer: FillQuoteTransformerContract;
|
||||
@@ -43,7 +45,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
const GAS_PRICE = 1337;
|
||||
|
||||
before(async () => {
|
||||
[maker, feeRecipient] = await env.getAccountAddressesAsync();
|
||||
[maker, feeRecipient, sender, taker] = await env.getAccountAddressesAsync();
|
||||
exchange = await TestFillQuoteTransformerExchangeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestFillQuoteTransformerExchange,
|
||||
env.provider,
|
||||
@@ -69,7 +71,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
bridge = await TestFillQuoteTransformerBridgeContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestFillQuoteTransformerBridge,
|
||||
env.provider,
|
||||
env.txDefaults,
|
||||
{ ...env.txDefaults, from: sender },
|
||||
artifacts,
|
||||
);
|
||||
[makerToken, takerToken, takerFeeToken] = await Promise.all(
|
||||
@@ -246,6 +248,7 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
signatures: [],
|
||||
maxOrderFillAmounts: [],
|
||||
fillAmount: MAX_UINT256,
|
||||
refundReceiver: NULL_ADDRESS,
|
||||
...fields,
|
||||
});
|
||||
}
|
||||
@@ -288,6 +291,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -309,6 +314,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -333,6 +340,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -355,6 +364,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -379,6 +390,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -405,6 +418,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -437,6 +452,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -461,6 +478,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -486,6 +505,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
takerTokenBalance,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -510,6 +531,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
takerTokenBalance,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -539,6 +562,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -561,6 +586,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -583,6 +610,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -602,6 +631,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -615,6 +646,80 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
makerAssetBalance: qfr.makerAssetBought,
|
||||
});
|
||||
});
|
||||
|
||||
it('can refund unspent protocol fee to the `refundReceiver`', async () => {
|
||||
const orders = _.times(2, () => createOrder());
|
||||
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||
const refundReceiver = randomAddress();
|
||||
await host
|
||||
.executeTransform(
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
refundReceiver,
|
||||
}),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||
expect(receiverBalancer).to.bignumber.eq(1);
|
||||
});
|
||||
|
||||
it('can refund unspent protocol fee to the taker', async () => {
|
||||
const orders = _.times(2, () => createOrder());
|
||||
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||
const refundReceiver = randomAddress();
|
||||
await host
|
||||
.executeTransform(
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
refundReceiver, // taker = refundReceiver
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
// address(1) indicates taker
|
||||
refundReceiver: hexUtils.leftPad(1, 20),
|
||||
}),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||
expect(receiverBalancer).to.bignumber.eq(1);
|
||||
});
|
||||
|
||||
it('can refund unspent protocol fee to the sender', async () => {
|
||||
const orders = _.times(2, () => createOrder());
|
||||
const signatures = orders.map(() => encodeExchangeBehavior());
|
||||
const qfr = getExpectedSellQuoteFillResults(orders);
|
||||
const protocolFee = qfr.protocolFeePaid.plus(1);
|
||||
const refundReceiver = randomAddress();
|
||||
await host
|
||||
.executeTransform(
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
refundReceiver, // sender = refundReceiver
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
// address(2) indicates sender
|
||||
refundReceiver: hexUtils.leftPad(2, 20),
|
||||
}),
|
||||
)
|
||||
.awaitTransactionSuccessAsync({ value: protocolFee });
|
||||
const receiverBalancer = await env.web3Wrapper.getBalanceInWeiAsync(refundReceiver);
|
||||
expect(receiverBalancer).to.bignumber.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('buy quotes', () => {
|
||||
@@ -627,6 +732,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -650,6 +757,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -676,6 +785,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -700,6 +811,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -726,6 +839,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -749,6 +864,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -779,6 +896,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -803,6 +922,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -827,6 +948,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -848,6 +971,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -875,6 +1000,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -901,6 +1028,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -927,6 +1056,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
@@ -955,6 +1086,8 @@ blockchainTests.resets('FillQuoteTransformer', env => {
|
||||
transformer.address,
|
||||
takerToken.address,
|
||||
qfr.takerAssetSpent,
|
||||
sender,
|
||||
taker,
|
||||
encodeTransformData({
|
||||
orders,
|
||||
signatures,
|
||||
|
@@ -78,7 +78,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
taker,
|
||||
sender: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||
@@ -96,7 +101,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
taker,
|
||||
sender: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||
@@ -114,7 +124,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
taker,
|
||||
sender: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq(ZERO_BALANCES);
|
||||
expect(await getBalancesAsync(taker)).to.deep.eq({
|
||||
@@ -132,7 +147,12 @@ blockchainTests.resets('PayTakerTransformer', env => {
|
||||
await mintHostTokensAsync(amounts[0]);
|
||||
await sendEtherAsync(host.address, amounts[1]);
|
||||
await host
|
||||
.rawExecuteTransform(transformer.address, hexUtils.random(), taker, data)
|
||||
.rawExecuteTransform(transformer.address, {
|
||||
data,
|
||||
callDataHash: hexUtils.random(),
|
||||
taker,
|
||||
sender: randomAddress(),
|
||||
})
|
||||
.awaitTransactionSuccessAsync();
|
||||
expect(await getBalancesAsync(host.address)).to.deep.eq({
|
||||
tokenBalance: amounts[0].minus(amounts[0].dividedToIntegerBy(2)),
|
||||
|
@@ -10,6 +10,7 @@ export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||
export * from '../test/generated-wrappers/fixin_common';
|
||||
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
||||
export * from '../test/generated-wrappers/fixin_gas_token';
|
||||
export * from '../test/generated-wrappers/fixin_reentrancy_guard';
|
||||
export * from '../test/generated-wrappers/flash_wallet';
|
||||
export * from '../test/generated-wrappers/full_migration';
|
||||
export * from '../test/generated-wrappers/i_allowance_target';
|
||||
@@ -39,6 +40,7 @@ export * from '../test/generated-wrappers/lib_ownable_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_ownable_storage';
|
||||
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_proxy_storage';
|
||||
export * from '../test/generated-wrappers/lib_reentrancy_guard_storage';
|
||||
export * from '../test/generated-wrappers/lib_signature_rich_errors';
|
||||
export * from '../test/generated-wrappers/lib_signed_call_data';
|
||||
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
||||
|
@@ -31,6 +31,7 @@
|
||||
"test/generated-artifacts/FixinCommon.json",
|
||||
"test/generated-artifacts/FixinEIP712.json",
|
||||
"test/generated-artifacts/FixinGasToken.json",
|
||||
"test/generated-artifacts/FixinReentrancyGuard.json",
|
||||
"test/generated-artifacts/FlashWallet.json",
|
||||
"test/generated-artifacts/FullMigration.json",
|
||||
"test/generated-artifacts/IAllowanceTarget.json",
|
||||
@@ -60,6 +61,7 @@
|
||||
"test/generated-artifacts/LibOwnableStorage.json",
|
||||
"test/generated-artifacts/LibProxyRichErrors.json",
|
||||
"test/generated-artifacts/LibProxyStorage.json",
|
||||
"test/generated-artifacts/LibReentrancyGuardStorage.json",
|
||||
"test/generated-artifacts/LibSignatureRichErrors.json",
|
||||
"test/generated-artifacts/LibSignedCallData.json",
|
||||
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
||||
|
Reference in New Issue
Block a user