Convert MetaTransactionsFeature to use the new LibSignature (#31)

This commit is contained in:
Steve Marx 2020-11-23 16:14:09 -05:00 committed by GitHub
parent 85f5d32de2
commit 5306cc03e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 101 additions and 80 deletions

View File

@ -6,6 +6,7 @@ import {
artifacts as exchangeProxyArtifacts, artifacts as exchangeProxyArtifacts,
IZeroExContract, IZeroExContract,
LogMetadataTransformerContract, LogMetadataTransformerContract,
Signature,
signCallData, signCallData,
} from '@0x/contracts-zero-ex'; } from '@0x/contracts-zero-ex';
import { migrateOnceAsync } from '@0x/migrations'; import { migrateOnceAsync } from '@0x/migrations';
@ -25,6 +26,15 @@ import * as ethjs from 'ethereumjs-util';
const { MAX_UINT256, NULL_ADDRESS, NULL_BYTES, NULL_BYTES32, ZERO_AMOUNT } = constants; const { MAX_UINT256, NULL_ADDRESS, NULL_BYTES, NULL_BYTES32, ZERO_AMOUNT } = constants;
function sigstruct(signature: string): Signature {
return {
v: parseInt(hexUtils.slice(signature, 0, 1), 16),
signatureType: parseInt(hexUtils.slice(signature, 65, 66), 16),
r: hexUtils.slice(signature, 1, 33),
s: hexUtils.slice(signature, 33, 65),
};
}
blockchainTests.resets('exchange proxy - meta-transactions', env => { blockchainTests.resets('exchange proxy - meta-transactions', env => {
const quoteSignerKey = hexUtils.random(); const quoteSignerKey = hexUtils.random();
const quoteSigner = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(quoteSignerKey))); const quoteSigner = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(quoteSignerKey)));
@ -240,7 +250,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0); const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer); const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx const receipt = await zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer)) .minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
@ -276,7 +286,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee); const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer); const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx const receipt = await zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer)) .minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
@ -311,7 +321,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee); const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer); const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx const receipt = await zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer)) .minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
@ -348,7 +358,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0); const mtx = await createMetaTransactionAsync(signedSwapData, _protocolFee, 0);
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer); const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
const receipt = await zeroEx const receipt = await zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer)) .minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
@ -385,7 +395,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer); const relayerEthBalanceBefore = await env.web3Wrapper.getBalanceInWeiAsync(relayer);
await zeroEx.setQuoteSigner(NULL_ADDRESS).awaitTransactionSuccessAsync({ from: owner }); await zeroEx.setQuoteSigner(NULL_ADDRESS).awaitTransactionSuccessAsync({ from: owner });
const receipt = await zeroEx const receipt = await zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
const relayerEthRefund = relayerEthBalanceBefore const relayerEthRefund = relayerEthBalanceBefore
.minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer)) .minus(await env.web3Wrapper.getBalanceInWeiAsync(relayer))
@ -419,7 +429,7 @@ blockchainTests.resets('exchange proxy - meta-transactions', env => {
const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed. const _protocolFee = protocolFee.times(GAS_PRICE).times(swap.orders.length + 1); // Pay a little more fee than needed.
const mtx = await createMetaTransactionAsync(callData, _protocolFee, 0); const mtx = await createMetaTransactionAsync(callData, _protocolFee, 0);
const tx = zeroEx const tx = zeroEx
.executeMetaTransaction(mtx, mtx.signature) .executeMetaTransaction(mtx, sigstruct(mtx.signature))
.awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE }); .awaitTransactionSuccessAsync({ from: relayer, value: mtx.value, gasPrice: GAS_PRICE });
return expect(tx).to.revertWith(new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError()); return expect(tx).to.revertWith(new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError());
}); });

View File

@ -21,6 +21,10 @@
{ {
"note": "Add a gas limit to first `LibTokenSpender` and `UniswapFeature` transfer", "note": "Add a gas limit to first `LibTokenSpender` and `UniswapFeature` transfer",
"pr": 38 "pr": 38
},
{
"note": "Convert metatransactions to use `LibSignature`",
"pr": 31
} }
] ]
}, },

View File

@ -20,11 +20,10 @@ pragma solidity ^0.6.5;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
import "./libs/LibSignature.sol";
/// @dev Meta-transactions feature. /// @dev Meta-transactions feature.
interface IMetaTransactionsFeature { interface IMetaTransactionsFeature {
/// @dev Describes an exchange proxy meta transaction. /// @dev Describes an exchange proxy meta transaction.
struct MetaTransactionData { struct MetaTransactionData {
// Signer of meta-transaction. On whose behalf to execute the MTX. // Signer of meta-transaction. On whose behalf to execute the MTX.
@ -68,7 +67,7 @@ interface IMetaTransactionsFeature {
/// @return returnResult The ABI-encoded result of the underlying call. /// @return returnResult The ABI-encoded result of the underlying call.
function executeMetaTransaction( function executeMetaTransaction(
MetaTransactionData calldata mtx, MetaTransactionData calldata mtx,
bytes calldata signature LibSignature.Signature calldata signature
) )
external external
payable payable
@ -80,7 +79,7 @@ interface IMetaTransactionsFeature {
/// @return returnResults The ABI-encoded results of the underlying calls. /// @return returnResults The ABI-encoded results of the underlying calls.
function batchExecuteMetaTransactions( function batchExecuteMetaTransactions(
MetaTransactionData[] calldata mtxs, MetaTransactionData[] calldata mtxs,
bytes[] calldata signatures LibSignature.Signature[] calldata signatures
) )
external external
payable payable
@ -88,14 +87,14 @@ interface IMetaTransactionsFeature {
/// @dev Execute a meta-transaction via `sender`. Privileged variant. /// @dev Execute a meta-transaction via `sender`. Privileged variant.
/// Only callable from within. /// Only callable from within.
/// @param sender Who is executing the meta-transaction.. /// @param sender Who is executing the meta-transaction.
/// @param mtx The meta-transaction. /// @param mtx The meta-transaction.
/// @param signature The signature by `mtx.signer`. /// @param signature The signature by `mtx.signer`.
/// @return returnResult The ABI-encoded result of the underlying call. /// @return returnResult The ABI-encoded result of the underlying call.
function _executeMetaTransaction( function _executeMetaTransaction(
address sender, address sender,
MetaTransactionData calldata mtx, MetaTransactionData memory mtx,
bytes calldata signature LibSignature.Signature memory signature
) )
external external
payable payable

View File

@ -32,6 +32,7 @@ import "../storage/LibMetaTransactionsStorage.sol";
import "./libs/LibSignedCallData.sol"; import "./libs/LibSignedCallData.sol";
import "./IMetaTransactionsFeature.sol"; import "./IMetaTransactionsFeature.sol";
import "./ITransformERC20Feature.sol"; import "./ITransformERC20Feature.sol";
import "./libs/LibSignature.sol";
import "./ISignatureValidatorFeature.sol"; import "./ISignatureValidatorFeature.sol";
import "./IFeature.sol"; import "./IFeature.sol";
@ -47,8 +48,8 @@ contract MetaTransactionsFeature is
using LibBytesV06 for bytes; using LibBytesV06 for bytes;
using LibRichErrorsV06 for bytes; using LibRichErrorsV06 for bytes;
/// @dev Intermediate state vars used by `_executeMetaTransactionPrivate()`
/// to avoid stack overflows. /// @dev Describes the state of a meta transaction.
struct ExecuteState { struct ExecuteState {
// Sender of the meta-transaction. // Sender of the meta-transaction.
address sender; address sender;
@ -57,7 +58,7 @@ contract MetaTransactionsFeature is
// The meta-transaction data. // The meta-transaction data.
MetaTransactionData mtx; MetaTransactionData mtx;
// The meta-transaction signature (by `mtx.signer`). // The meta-transaction signature (by `mtx.signer`).
bytes signature; LibSignature.Signature signature;
// The selector of the function being called. // The selector of the function being called.
bytes4 selector; bytes4 selector;
// The ETH balance of this contract before performing the call. // The ETH balance of this contract before performing the call.
@ -136,7 +137,7 @@ contract MetaTransactionsFeature is
/// @return returnResult The ABI-encoded result of the underlying call. /// @return returnResult The ABI-encoded result of the underlying call.
function executeMetaTransaction( function executeMetaTransaction(
MetaTransactionData memory mtx, MetaTransactionData memory mtx,
bytes memory signature LibSignature.Signature memory signature
) )
public public
payable payable
@ -145,11 +146,13 @@ contract MetaTransactionsFeature is
refundsAttachedEth refundsAttachedEth
returns (bytes memory returnResult) returns (bytes memory returnResult)
{ {
returnResult = _executeMetaTransactionPrivate( ExecuteState memory state;
msg.sender, state.sender = msg.sender;
mtx, state.mtx = mtx;
signature state.hash = getMetaTransactionHash(mtx);
); state.signature = signature;
returnResult = _executeMetaTransactionPrivate(state);
} }
/// @dev Execute multiple meta-transactions. /// @dev Execute multiple meta-transactions.
@ -158,7 +161,7 @@ contract MetaTransactionsFeature is
/// @return returnResults The ABI-encoded results of the underlying calls. /// @return returnResults The ABI-encoded results of the underlying calls.
function batchExecuteMetaTransactions( function batchExecuteMetaTransactions(
MetaTransactionData[] memory mtxs, MetaTransactionData[] memory mtxs,
bytes[] memory signatures LibSignature.Signature[] memory signatures
) )
public public
payable payable
@ -175,11 +178,13 @@ contract MetaTransactionsFeature is
} }
returnResults = new bytes[](mtxs.length); returnResults = new bytes[](mtxs.length);
for (uint256 i = 0; i < mtxs.length; ++i) { for (uint256 i = 0; i < mtxs.length; ++i) {
returnResults[i] = _executeMetaTransactionPrivate( ExecuteState memory state;
msg.sender, state.sender = msg.sender;
mtxs[i], state.mtx = mtxs[i];
signatures[i] state.hash = getMetaTransactionHash(mtxs[i]);
); state.signature = signatures[i];
returnResults[i] = _executeMetaTransactionPrivate(state);
} }
} }
@ -192,7 +197,7 @@ contract MetaTransactionsFeature is
function _executeMetaTransaction( function _executeMetaTransaction(
address sender, address sender,
MetaTransactionData memory mtx, MetaTransactionData memory mtx,
bytes memory signature LibSignature.Signature memory signature
) )
public public
payable payable
@ -200,7 +205,13 @@ contract MetaTransactionsFeature is
onlySelf onlySelf
returns (bytes memory returnResult) returns (bytes memory returnResult)
{ {
return _executeMetaTransactionPrivate(sender, mtx, signature); ExecuteState memory state;
state.sender = sender;
state.mtx = mtx;
state.hash = getMetaTransactionHash(mtx);
state.signature = signature;
return _executeMetaTransactionPrivate(state);
} }
/// @dev Get the block at which a meta-transaction has been executed. /// @dev Get the block at which a meta-transaction has been executed.
@ -252,24 +263,13 @@ contract MetaTransactionsFeature is
} }
/// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant. /// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant.
/// @param sender Who is executing the meta-transaction.. /// @param state The `ExecuteState` for this metatransaction, with `sender`,
/// @param mtx The meta-transaction. /// `hash`, `mtx`, and `signature` fields filled.
/// @param signature The signature by `mtx.signer`.
/// @return returnResult The ABI-encoded result of the underlying call. /// @return returnResult The ABI-encoded result of the underlying call.
function _executeMetaTransactionPrivate( function _executeMetaTransactionPrivate(ExecuteState memory state)
address sender,
MetaTransactionData memory mtx,
bytes memory signature
)
private private
returns (bytes memory returnResult) returns (bytes memory returnResult)
{ {
ExecuteState memory state;
state.sender = sender;
state.hash = getMetaTransactionHash(mtx);
state.mtx = mtx;
state.signature = signature;
_validateMetaTransaction(state); _validateMetaTransaction(state);
// Mark the transaction executed by storing the block at which it was executed. // Mark the transaction executed by storing the block at which it was executed.
@ -279,17 +279,17 @@ contract MetaTransactionsFeature is
.mtxHashToExecutedBlockNumber[state.hash] = block.number; .mtxHashToExecutedBlockNumber[state.hash] = block.number;
// Pay the fee to the sender. // Pay the fee to the sender.
if (mtx.feeAmount > 0) { if (state.mtx.feeAmount > 0) {
_transferERC20Tokens( _transferERC20Tokens(
mtx.feeToken, state.mtx.feeToken,
mtx.signer, state.mtx.signer,
sender, state.sender,
mtx.feeAmount state.mtx.feeAmount
); );
} }
// Execute the call based on the selector. // Execute the call based on the selector.
state.selector = mtx.callData.readBytes4(0); state.selector = state.mtx.callData.readBytes4(0);
if (state.selector == ITransformERC20Feature.transformERC20.selector) { if (state.selector == ITransformERC20Feature.transformERC20.selector) {
returnResult = _executeTransformERC20Call(state); returnResult = _executeTransformERC20Call(state);
} else { } else {
@ -300,8 +300,8 @@ contract MetaTransactionsFeature is
emit MetaTransactionExecuted( emit MetaTransactionExecuted(
state.hash, state.hash,
state.selector, state.selector,
mtx.signer, state.mtx.signer,
mtx.sender state.mtx.sender
); );
} }
@ -348,18 +348,17 @@ contract MetaTransactionsFeature is
state.mtx.value state.mtx.value
).rrevert(); ).rrevert();
} }
// Must be signed by signer.
try if (LibSignature.getSignerOfHash(state.hash, state.signature) !=
ISignatureValidatorFeature(address(this)) state.mtx.signer) {
.validateHashSignature(state.hash, state.mtx.signer, state.signature) LibSignatureRichErrors.SignatureValidationError(
{} LibSignatureRichErrors.SignatureValidationErrorCodes.WRONG_SIGNER,
catch (bytes memory err) { state.hash,
LibMetaTransactionsRichErrors state.mtx.signer,
.MetaTransactionInvalidSignatureError( // TODO: Remove this field from SignatureValidationError
state.hash, // when rich reverts are part of the protocol repo.
state.signature, ""
err ).rrevert();
).rrevert();
} }
// Transaction must not have been already executed. // Transaction must not have been already executed.
state.executedBlockNumber = LibMetaTransactionsStorage state.executedBlockNumber = LibMetaTransactionsStorage

View File

@ -52,6 +52,8 @@ contract TestMetaTransactionsTransformERC20Feature is
} }
if (msg.value == 777) { if (msg.value == 777) {
LibSignature.Signature memory signature;
// Try to reenter `executeMetaTransaction()` // Try to reenter `executeMetaTransaction()`
IMetaTransactionsFeature(address(this)).executeMetaTransaction( IMetaTransactionsFeature(address(this)).executeMetaTransaction(
IMetaTransactionsFeature.MetaTransactionData({ IMetaTransactionsFeature.MetaTransactionData({
@ -66,7 +68,7 @@ contract TestMetaTransactionsTransformERC20Feature is
feeToken: IERC20TokenV06(0), feeToken: IERC20TokenV06(0),
feeAmount: 0 feeAmount: 0
}), }),
"" signature
); );
} }
@ -74,7 +76,7 @@ contract TestMetaTransactionsTransformERC20Feature is
// Try to reenter `batchExecuteMetaTransactions()` // Try to reenter `batchExecuteMetaTransactions()`
IMetaTransactionsFeature.MetaTransactionData[] memory mtxs = IMetaTransactionsFeature.MetaTransactionData[] memory mtxs =
new IMetaTransactionsFeature.MetaTransactionData[](1); new IMetaTransactionsFeature.MetaTransactionData[](1);
bytes[] memory signatures = new bytes[](1); LibSignature.Signature[] memory signatures = new LibSignature.Signature[](1);
mtxs[0] = IMetaTransactionsFeature.MetaTransactionData({ mtxs[0] = IMetaTransactionsFeature.MetaTransactionData({
signer: address(0), signer: address(0),
sender: address(0), sender: address(0),
@ -87,7 +89,6 @@ contract TestMetaTransactionsTransformERC20Feature is
feeToken: IERC20TokenV06(0), feeToken: IERC20TokenV06(0),
feeAmount: 0 feeAmount: 0
}); });
signatures[0] = "";
IMetaTransactionsFeature(address(this)).batchExecuteMetaTransactions( IMetaTransactionsFeature(address(this)).batchExecuteMetaTransactions(
mtxs, mtxs,
signatures signatures

View File

@ -11,6 +11,7 @@ import { ExchangeProxyMetaTransaction } from '@0x/types';
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils'; import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { Signature } from '../../src/signature_utils';
import { generateCallDataSignature, signCallData } from '../../src/signed_call_data'; import { generateCallDataSignature, signCallData } from '../../src/signed_call_data';
import { IZeroExContract, MetaTransactionsFeatureContract } from '../../src/wrappers'; import { IZeroExContract, MetaTransactionsFeatureContract } from '../../src/wrappers';
import { artifacts } from '../artifacts'; import { artifacts } from '../artifacts';
@ -71,6 +72,15 @@ blockchainTests.resets('MetaTransactions feature', env => {
); );
}); });
function sigstruct(signature: string): Signature {
return {
v: parseInt(hexUtils.slice(signature, 0, 1), 16),
signatureType: parseInt(hexUtils.slice(signature, 65, 66), 16),
r: hexUtils.slice(signature, 1, 33),
s: hexUtils.slice(signature, 33, 65),
};
}
function getRandomMetaTransaction( function getRandomMetaTransaction(
fields: Partial<ExchangeProxyMetaTransaction> = {}, fields: Partial<ExchangeProxyMetaTransaction> = {},
): ExchangeProxyMetaTransaction { ): ExchangeProxyMetaTransaction {
@ -93,11 +103,13 @@ blockchainTests.resets('MetaTransactions feature', env => {
}; };
} }
async function signMetaTransactionAsync(mtx: ExchangeProxyMetaTransaction, signer?: string): Promise<string> { async function signMetaTransactionAsync(mtx: ExchangeProxyMetaTransaction, signer?: string): Promise<Signature> {
return signatureUtils.ecSignHashAsync( return sigstruct(
env.provider, await signatureUtils.ecSignHashAsync(
getExchangeProxyMetaTransactionHash(mtx), env.provider,
signer || mtx.signer, getExchangeProxyMetaTransactionHash(mtx),
signer || mtx.signer,
),
); );
} }
@ -461,15 +473,11 @@ blockchainTests.resets('MetaTransactions feature', env => {
}; };
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts); const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
return expect(tx).to.revertWith( return expect(tx).to.revertWith(
new ZeroExRevertErrors.MetaTransactions.MetaTransactionInvalidSignatureError( new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
mtxHash, mtxHash,
signature, signers[0],
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError( '0x',
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
mtxHash,
signers[0],
signature,
).encode(),
), ),
); );
}); });