@0x/contracts-exchange: Always check OrderValidator and WalletOrderValidator signature types on every fill
`@0x/contracts-exchange`: Add `validatorAddress` field to `SignatureValidatorError` and `SignatureOrderValidatorError` rich reverts `@0x/contracts-exchange`: Add separate `SignatureOrderValidatorNotApprovedError` for `OrderValidator` signatures `@0x/contracts-exchange`: Consolidate Wallet and Validator test contracts into a single configurable `TestValidatorWallet` contract. `@0x/contracts-exchange`: Rewrite many tests in `signature_validator.ts` for brevity.
This commit is contained in:
committed by
Amir Bandeali
parent
5f8ebc3601
commit
42f7b7cc19
@@ -25,8 +25,6 @@
|
||||
},
|
||||
"contracts": [
|
||||
"examples/ExchangeWrapper.sol",
|
||||
"examples/Validator.sol",
|
||||
"examples/Wallet.sol",
|
||||
"examples/Whitelist.sol",
|
||||
"src/Exchange.sol",
|
||||
"src/interfaces/IAssetProxyDispatcher.sol",
|
||||
@@ -43,8 +41,7 @@
|
||||
"test/TestAssetProxyDispatcher.sol",
|
||||
"test/TestExchangeInternals.sol",
|
||||
"test/TestLibExchangeRichErrorDecoder.sol",
|
||||
"test/TestRevertReceiver.sol",
|
||||
"test/TestSignatureValidator.sol",
|
||||
"test/TestStaticCallReceiver.sol"
|
||||
"test/TestValidatorWallet.sol"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "../src/interfaces/IValidator.sol";
|
||||
import "../src/interfaces/IOrderValidator.sol";
|
||||
|
||||
|
||||
contract Validator is
|
||||
IValidator,
|
||||
IOrderValidator
|
||||
{
|
||||
|
||||
// The single valid signer for this validator.
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
address internal VALID_SIGNER;
|
||||
|
||||
/// @dev constructs a new `Validator` with a single valid signer.
|
||||
/// @param validSigner The sole, valid signer.
|
||||
constructor (address validSigner) public {
|
||||
VALID_SIGNER = validSigner;
|
||||
}
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
/// @dev Verifies that a signature is valid. `signer` must match `VALID_SIGNER`.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signerAddress Address that should have signed the given hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
return (signerAddress == VALID_SIGNER);
|
||||
}
|
||||
// solhint-enable no-unused-vars
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
/// @dev Verifies that an order and signature is valid. `signer` must match `VALID_SIGNER`.
|
||||
/// @param order The order.
|
||||
/// @param orderHash The order hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of signature.
|
||||
function isValidOrderSignature(
|
||||
LibOrder.Order calldata order,
|
||||
bytes32 orderHash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
return (order.makerAddress == VALID_SIGNER);
|
||||
}
|
||||
// solhint-enable no-unused-vars
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "../src/interfaces/IWallet.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
|
||||
|
||||
contract Wallet is
|
||||
IWallet
|
||||
{
|
||||
using LibBytes for bytes;
|
||||
|
||||
// The owner of this wallet.
|
||||
// solhint-disable-next-line var-name-mixedcase
|
||||
address internal WALLET_OWNER;
|
||||
|
||||
/// @dev constructs a new `Wallet` with a single owner.
|
||||
/// @param walletOwner The owner of this wallet.
|
||||
constructor (address walletOwner) public {
|
||||
WALLET_OWNER = walletOwner;
|
||||
}
|
||||
|
||||
/// @dev Validates an EIP712 signature.
|
||||
/// The signer must match the owner of this wallet.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
require(
|
||||
signature.length == 65,
|
||||
"LENGTH_65_REQUIRED"
|
||||
);
|
||||
|
||||
return _validateEIP712Signature(hash, signature);
|
||||
}
|
||||
|
||||
/// @dev Validates an order AND EIP712 signature.
|
||||
/// The signer must match the owner of this wallet.
|
||||
/// @param order The order.
|
||||
/// @param orderHash The order hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order and signature.
|
||||
function isValidOrderSignature(
|
||||
LibOrder.Order calldata order,
|
||||
bytes32 orderHash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
// Ensure order hash is correct.
|
||||
require(
|
||||
order.makerAddress == WALLET_OWNER,
|
||||
"INVALID_ORDER_MAKER"
|
||||
);
|
||||
return _validateEIP712Signature(orderHash, signature);
|
||||
}
|
||||
|
||||
function _validateEIP712Signature(
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
)
|
||||
private
|
||||
view
|
||||
returns (bool isValid)
|
||||
{
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
address recoveredAddress = ecrecover(hash, v, r, s);
|
||||
return WALLET_OWNER == recoveredAddress;
|
||||
}
|
||||
}
|
||||
@@ -47,9 +47,40 @@ contract MixinExchangeRichErrors is
|
||||
);
|
||||
}
|
||||
|
||||
function SignatureValidatorNotApprovedError(
|
||||
address signerAddress,
|
||||
address validatorAddress
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR,
|
||||
signerAddress,
|
||||
validatorAddress
|
||||
);
|
||||
}
|
||||
|
||||
function SignatureOrderValidatorNotApprovedError(
|
||||
address signerAddress,
|
||||
address validatorAddress
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bytes memory)
|
||||
{
|
||||
return abi.encodeWithSelector(
|
||||
SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR,
|
||||
signerAddress,
|
||||
validatorAddress
|
||||
);
|
||||
}
|
||||
|
||||
function SignatureValidatorError(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
address validatorAddress,
|
||||
bytes memory signature,
|
||||
bytes memory errorData
|
||||
)
|
||||
@@ -61,6 +92,7 @@ contract MixinExchangeRichErrors is
|
||||
SIGNATURE_VALIDATOR_ERROR_SELECTOR,
|
||||
hash,
|
||||
signerAddress,
|
||||
validatorAddress,
|
||||
signature,
|
||||
errorData
|
||||
);
|
||||
@@ -88,6 +120,7 @@ contract MixinExchangeRichErrors is
|
||||
function SignatureOrderValidatorError(
|
||||
bytes32 orderHash,
|
||||
address signerAddress,
|
||||
address validatorAddress,
|
||||
bytes memory signature,
|
||||
bytes memory errorData
|
||||
)
|
||||
@@ -99,6 +132,7 @@ contract MixinExchangeRichErrors is
|
||||
SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR,
|
||||
orderHash,
|
||||
signerAddress,
|
||||
validatorAddress,
|
||||
signature,
|
||||
errorData
|
||||
);
|
||||
|
||||
@@ -360,7 +360,10 @@ contract MixinSignatureValidator is
|
||||
address validatorAddress = signature.readAddress(signatureLength - 21);
|
||||
// Ensure signer has approved validator.
|
||||
if (!allowedValidators[signerAddress][validatorAddress]) {
|
||||
return false;
|
||||
_rrevert(SignatureValidatorNotApprovedError(
|
||||
signerAddress,
|
||||
validatorAddress
|
||||
));
|
||||
}
|
||||
// Shave the validator address and signature type from the signature.
|
||||
assembly {
|
||||
@@ -387,6 +390,7 @@ contract MixinSignatureValidator is
|
||||
_rrevert(SignatureValidatorError(
|
||||
hash,
|
||||
signerAddress,
|
||||
validatorAddress,
|
||||
signature,
|
||||
returnData
|
||||
));
|
||||
@@ -468,7 +472,10 @@ contract MixinSignatureValidator is
|
||||
address validatorAddress = signature.readAddress(signatureLength - 21);
|
||||
// Ensure signer has approved validator.
|
||||
if (!allowedOrderValidators[signerAddress][validatorAddress]) {
|
||||
return false;
|
||||
_rrevert(SignatureOrderValidatorNotApprovedError(
|
||||
signerAddress,
|
||||
validatorAddress
|
||||
));
|
||||
}
|
||||
// Shave the validator address and signature type from the signature.
|
||||
assembly {
|
||||
@@ -495,6 +502,7 @@ contract MixinSignatureValidator is
|
||||
_rrevert(SignatureOrderValidatorError(
|
||||
orderHash,
|
||||
signerAddress,
|
||||
validatorAddress,
|
||||
signature,
|
||||
returnData
|
||||
));
|
||||
|
||||
@@ -33,17 +33,25 @@ contract IExchangeRichErrors {
|
||||
bytes4 internal constant SIGNATURE_ERROR_SELECTOR =
|
||||
0x7e5a2318;
|
||||
|
||||
// bytes4(keccak256("SignatureValidatorError(bytes32,address,bytes,bytes)"))
|
||||
// bytes4(keccak256("SignatureValidatorNotApprovedError(address,address)"))
|
||||
bytes4 internal constant SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR =
|
||||
0xa15c0d06;
|
||||
|
||||
// bytes4(keccak256("SignatureOrderValidatorNotApprovedError(address,address)"))
|
||||
bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR =
|
||||
0x6d273c5a;
|
||||
|
||||
// bytes4(keccak256("SignatureValidatorError(bytes32,address,address,bytes,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_VALIDATOR_ERROR_SELECTOR =
|
||||
0x169fad8c;
|
||||
0xa23838b8;
|
||||
|
||||
// bytes4(keccak256("SignatureWalletError(bytes32,address,bytes,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_WALLET_ERROR_SELECTOR =
|
||||
0x1b8388f7;
|
||||
|
||||
// bytes4(keccak256("SignatureOrderValidatorError(bytes32,address,bytes,bytes)"))
|
||||
// bytes4(keccak256("SignatureOrderValidatorError(bytes32,address,address,bytes,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR =
|
||||
0xfabf4577;
|
||||
0xf45375b7;
|
||||
|
||||
// bytes4(keccak256("SignatureWalletOrderValidatorError(bytes32,address,bytes,bytes)"))
|
||||
bytes4 internal constant SIGNATURE_WALLET_ORDER_VALIDATOR_ERROR_SELECTOR =
|
||||
|
||||
@@ -23,7 +23,7 @@ import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
import "../interfaces/IExchangeRichErrors.sol";
|
||||
|
||||
|
||||
contract LibExchangeRichErrorDecoder is
|
||||
contract LibExchangeRichErrorDecoder is
|
||||
IExchangeRichErrors
|
||||
{
|
||||
/// @dev Decompose an ABI-encoded SignatureError.
|
||||
@@ -59,6 +59,7 @@ contract LibExchangeRichErrorDecoder is
|
||||
returns (
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
address validatorAddress,
|
||||
bytes memory signature,
|
||||
bytes memory errorData
|
||||
)
|
||||
@@ -66,8 +67,43 @@ contract LibExchangeRichErrorDecoder is
|
||||
_assertSelectorBytes(encoded, SIGNATURE_VALIDATOR_ERROR_SELECTOR);
|
||||
hash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
signature = _readErrorParameterAsBytes(encoded, 2);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 3);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 2);
|
||||
signature = _readErrorParameterAsBytes(encoded, 3);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 4);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureValidatorNotApprovedError.
|
||||
/// @param encoded ABI-encoded revert error.
|
||||
/// @return signerAddress The expected signer of the hash.
|
||||
/// @return validatorAddress The expected validator.
|
||||
function decodeSignatureValidatorNotApprovedError(bytes memory encoded)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
address signerAddress,
|
||||
address validatorAddress
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, SIGNATURE_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 0);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureOrderValidatorNotApprovedError.
|
||||
/// @param encoded ABI-encoded revert error.
|
||||
/// @return signerAddress The expected signer of the hash.
|
||||
/// @return validatorAddress The expected validator.
|
||||
function decodeSignatureOrderValidatorNotApprovedError(bytes memory encoded)
|
||||
public
|
||||
pure
|
||||
returns (
|
||||
address signerAddress,
|
||||
address validatorAddress
|
||||
)
|
||||
{
|
||||
_assertSelectorBytes(encoded, SIGNATURE_ORDER_VALIDATOR_NOT_APPROVED_ERROR_SELECTOR);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 0);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureWalletError.
|
||||
@@ -105,6 +141,7 @@ contract LibExchangeRichErrorDecoder is
|
||||
returns (
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
address validatorAddress,
|
||||
bytes memory signature,
|
||||
bytes memory errorData
|
||||
)
|
||||
@@ -112,8 +149,9 @@ contract LibExchangeRichErrorDecoder is
|
||||
_assertSelectorBytes(encoded, SIGNATURE_ORDER_VALIDATOR_ERROR_SELECTOR);
|
||||
hash = _readErrorParameterAsBytes32(encoded, 0);
|
||||
signerAddress = _readErrorParameterAsAddress(encoded, 1);
|
||||
signature = _readErrorParameterAsBytes(encoded, 2);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 3);
|
||||
validatorAddress = _readErrorParameterAsAddress(encoded, 2);
|
||||
signature = _readErrorParameterAsBytes(encoded, 3);
|
||||
errorData = _readErrorParameterAsBytes(encoded, 4);
|
||||
}
|
||||
|
||||
/// @dev Decompose an ABI-encoded SignatureWalletOrderValidatorError.
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
contract TestRevertReceiver {
|
||||
|
||||
string constant public REVERT_REASON = "you shall not pass";
|
||||
|
||||
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `Validator` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signerAddress Address that should have signed the given hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
revert(REVERT_REASON);
|
||||
}
|
||||
|
||||
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `Wallet` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
revert(REVERT_REASON);
|
||||
}
|
||||
|
||||
/// @dev Reverts with `REVERT_REASON`. Intended to be used with `OrderValidator` signature type.
|
||||
/// @param order The order.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidOrderSignature(
|
||||
LibOrder.Order calldata order,
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
revert(REVERT_REASON);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
contract TestStaticCallReceiver {
|
||||
|
||||
uint256 internal state = 1;
|
||||
|
||||
/// @dev Updates state and returns true. Intended to be used with `Validator` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signerAddress Address that should have signed the given hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
_updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Updates state and returns true. Intended to be used with `Wallet` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
_updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Updates state and returns true. Intended to be used with `OrderValidator` signature type.
|
||||
/// @param order The order.
|
||||
/// @param orderHash The order hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidOrderSignature(
|
||||
LibOrder.Order calldata order,
|
||||
bytes32 orderHash,
|
||||
bytes calldata signature
|
||||
)
|
||||
external
|
||||
returns (bool isValid)
|
||||
{
|
||||
_updateState();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @dev Approves an ERC20 token to spend tokens from this address.
|
||||
/// @param token Address of ERC20 token.
|
||||
/// @param spender Address that will spend tokens.
|
||||
/// @param value Amount of tokens spender is approved to spend.
|
||||
function approveERC20(
|
||||
address token,
|
||||
address spender,
|
||||
uint256 value
|
||||
)
|
||||
external
|
||||
{
|
||||
IERC20Token(token).approve(spender, value);
|
||||
}
|
||||
|
||||
/// @dev Increments state variable.
|
||||
function _updateState()
|
||||
internal
|
||||
{
|
||||
state++;
|
||||
}
|
||||
}
|
||||
200
contracts/exchange/contracts/test/TestValidatorWallet.sol
Normal file
200
contracts/exchange/contracts/test/TestValidatorWallet.sol
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
|
||||
Copyright 2018 ZeroEx Intl.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
pragma solidity ^0.5.5;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import "@0x/contracts-exchange-libs/contracts/src/LibOrder.sol";
|
||||
import "@0x/contracts-erc20/contracts/src/interfaces/IERC20Token.sol";
|
||||
import "@0x/contracts-utils/contracts/src/LibBytes.sol";
|
||||
|
||||
// solhint-disable no-unused-vars
|
||||
contract TestValidatorWallet {
|
||||
using LibBytes for bytes;
|
||||
|
||||
string constant public REVERT_REASON = "you shall not pass";
|
||||
|
||||
enum ValidatorAction {
|
||||
// Return false (default)
|
||||
Reject,
|
||||
// Return true
|
||||
Allow,
|
||||
// Revert
|
||||
Revert,
|
||||
// Update state
|
||||
UpdateState,
|
||||
// Validate signature
|
||||
ValidateSignature,
|
||||
NTypes
|
||||
}
|
||||
/// @dev Internal state to modify.
|
||||
uint256 internal _state = 1;
|
||||
/// @dev What action to execute when a hash is validated .
|
||||
mapping (bytes32=>ValidatorAction) internal _hashActions;
|
||||
/// @dev Allowed signers for `ValidateSignature` actions using `Wallet` signature types.
|
||||
mapping (bytes32=>address) internal _hashWalletSigners;
|
||||
|
||||
/// @dev Set the action to take when validating a hash.
|
||||
/// @param hash The hash.
|
||||
/// @param action action to take.
|
||||
/// @param allowedSigner Signer that must be recovered with
|
||||
/// `ValidateSignature` action type and `Wallet` or
|
||||
/// `OrderWallet` signature types.
|
||||
function setValidateAction(
|
||||
bytes32 hash,
|
||||
ValidatorAction action,
|
||||
address allowedSigner
|
||||
)
|
||||
external
|
||||
{
|
||||
if (uint8(action) >= uint8(ValidatorAction.NTypes)) {
|
||||
revert('UNSUPPORTED_VALIDATE_ACTION');
|
||||
}
|
||||
_hashActions[hash] = action;
|
||||
_hashWalletSigners[hash] = allowedSigner;
|
||||
}
|
||||
|
||||
/// @dev Validates a hash with the `Validator` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signerAddress Address that should have signed the given hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
address signerAddress,
|
||||
bytes memory signature
|
||||
)
|
||||
public
|
||||
returns (bool isValid)
|
||||
{
|
||||
ValidatorAction action = _hashActions[hash];
|
||||
if (action == ValidatorAction.Reject) {
|
||||
isValid = false;
|
||||
} else if (action == ValidatorAction.Allow) {
|
||||
isValid = true;
|
||||
} else if (action == ValidatorAction.Revert) {
|
||||
revert(REVERT_REASON);
|
||||
} else if (action == ValidatorAction.ValidateSignature) {
|
||||
isValid = _isSignedBy(hash, signature, signerAddress);
|
||||
} else { // if (action == ValidatorAction.UpdateState) {
|
||||
_updateState();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Validates a hash with the `Wallet` signature type.
|
||||
/// @param hash Message hash that is signed.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidSignature(
|
||||
bytes32 hash,
|
||||
bytes memory signature
|
||||
)
|
||||
public
|
||||
returns (bool isValid)
|
||||
{
|
||||
ValidatorAction action = _hashActions[hash];
|
||||
if (action == ValidatorAction.Reject) {
|
||||
isValid = false;
|
||||
} else if (action == ValidatorAction.Allow) {
|
||||
isValid = true;
|
||||
} else if (action == ValidatorAction.Revert) {
|
||||
revert(REVERT_REASON);
|
||||
} else if (action == ValidatorAction.ValidateSignature) {
|
||||
isValid = _isSignedBy(hash, signature, _hashWalletSigners[hash]);
|
||||
} else { // if (action == ValidatorAction.UpdateState) {
|
||||
_updateState();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Validates a hash with the `OrderValidator` signature type.
|
||||
/// @param order The order.
|
||||
/// @param orderHash The order hash.
|
||||
/// @param signature Proof of signing.
|
||||
/// @return Validity of order signature.
|
||||
function isValidOrderSignature(
|
||||
LibOrder.Order memory order,
|
||||
bytes32 orderHash,
|
||||
bytes memory signature
|
||||
)
|
||||
public
|
||||
returns (bool isValid)
|
||||
{
|
||||
ValidatorAction action = _hashActions[orderHash];
|
||||
if (action == ValidatorAction.Reject) {
|
||||
isValid = false;
|
||||
} else if (action == ValidatorAction.Allow) {
|
||||
isValid = true;
|
||||
} else if (action == ValidatorAction.Revert) {
|
||||
revert(REVERT_REASON);
|
||||
} else if (action == ValidatorAction.ValidateSignature) {
|
||||
isValid = _isSignedBy(orderHash, signature, order.makerAddress);
|
||||
} else { // if (action == ValidatorAction.UpdateState) {
|
||||
_updateState();
|
||||
}
|
||||
}
|
||||
|
||||
/// @dev Approves an ERC20 token to spend tokens from this address.
|
||||
/// @param token Address of ERC20 token.
|
||||
/// @param spender Address that will spend tokens.
|
||||
/// @param value Amount of tokens spender is approved to spend.
|
||||
function approveERC20(
|
||||
address token,
|
||||
address spender,
|
||||
uint256 value
|
||||
)
|
||||
external
|
||||
{
|
||||
IERC20Token(token).approve(spender, value);
|
||||
}
|
||||
|
||||
/// @dev Increments state variable.
|
||||
function _updateState()
|
||||
internal
|
||||
{
|
||||
_state++;
|
||||
}
|
||||
|
||||
/// @dev Verifies the signer of a hash is correct.
|
||||
function _isSignedBy(
|
||||
bytes32 hash,
|
||||
bytes memory signature,
|
||||
address signerAddress
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (bool isSignedBy)
|
||||
{
|
||||
require(signature.length == 65, "LENGTH_65_REQUIRED");
|
||||
uint8 v = uint8(signature[0]);
|
||||
bytes32 r = signature.readBytes32(1);
|
||||
bytes32 s = signature.readBytes32(33);
|
||||
// Try a naked signature.
|
||||
address recovered = ecrecover(hash, v, r, s);
|
||||
if (recovered != signerAddress) {
|
||||
// Try an eth_sign signature.
|
||||
bytes32 ethSignHash = keccak256(
|
||||
abi.encodePacked(
|
||||
"\x19Ethereum Signed Message:\n32",
|
||||
hash
|
||||
)
|
||||
);
|
||||
recovered = ecrecover(ethSignHash, v, r, s);
|
||||
}
|
||||
isSignedBy = recovered == signerAddress;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"lint-contracts": "solhint -c ../.solhint.json contracts/**/**/**/**/*.sol"
|
||||
},
|
||||
"config": {
|
||||
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IExchange|IExchangeCore|IMatchOrders|IOrderValidator|ISignatureValidator|ITransactions|IValidator|IWallet|IWrapperFunctions|ReentrantERC20Token|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestRevertReceiver|TestSignatureValidator|TestStaticCallReceiver|Validator|Wallet|Whitelist).json",
|
||||
"abis": "./generated-artifacts/@(Exchange|ExchangeWrapper|IAssetProxyDispatcher|IExchange|IExchangeCore|IMatchOrders|IOrderValidator|ISignatureValidator|ITransactions|IValidator|IWallet|IWrapperFunctions|ReentrantERC20Token|TestAssetProxyDispatcher|TestExchangeInternals|TestLibExchangeRichErrorDecoder|TestSignatureValidator|TestValidatorWallet|Whitelist).json",
|
||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually."
|
||||
},
|
||||
"repository": {
|
||||
|
||||
@@ -21,16 +21,11 @@ import * as ReentrantERC20Token from '../generated-artifacts/ReentrantERC20Token
|
||||
import * as TestAssetProxyDispatcher from '../generated-artifacts/TestAssetProxyDispatcher.json';
|
||||
import * as TestExchangeInternals from '../generated-artifacts/TestExchangeInternals.json';
|
||||
import * as TestLibExchangeRichErrorDecoder from '../generated-artifacts/TestLibExchangeRichErrorDecoder.json';
|
||||
import * as TestRevertReceiver from '../generated-artifacts/TestRevertReceiver.json';
|
||||
import * as TestSignatureValidator from '../generated-artifacts/TestSignatureValidator.json';
|
||||
import * as TestStaticCallReceiver from '../generated-artifacts/TestStaticCallReceiver.json';
|
||||
import * as Validator from '../generated-artifacts/Validator.json';
|
||||
import * as Wallet from '../generated-artifacts/Wallet.json';
|
||||
import * as TestValidatorWallet from '../generated-artifacts/TestValidatorWallet.json';
|
||||
import * as Whitelist from '../generated-artifacts/Whitelist.json';
|
||||
export const artifacts = {
|
||||
ExchangeWrapper: ExchangeWrapper as ContractArtifact,
|
||||
Validator: Validator as ContractArtifact,
|
||||
Wallet: Wallet as ContractArtifact,
|
||||
Whitelist: Whitelist as ContractArtifact,
|
||||
Exchange: Exchange as ContractArtifact,
|
||||
IAssetProxyDispatcher: IAssetProxyDispatcher as ContractArtifact,
|
||||
@@ -47,7 +42,6 @@ export const artifacts = {
|
||||
TestAssetProxyDispatcher: TestAssetProxyDispatcher as ContractArtifact,
|
||||
TestExchangeInternals: TestExchangeInternals as ContractArtifact,
|
||||
TestLibExchangeRichErrorDecoder: TestLibExchangeRichErrorDecoder as ContractArtifact,
|
||||
TestRevertReceiver: TestRevertReceiver as ContractArtifact,
|
||||
TestSignatureValidator: TestSignatureValidator as ContractArtifact,
|
||||
TestStaticCallReceiver: TestStaticCallReceiver as ContractArtifact,
|
||||
TestValidatorWallet: TestValidatorWallet as ContractArtifact,
|
||||
};
|
||||
|
||||
@@ -19,9 +19,6 @@ export * from '../generated-wrappers/reentrant_erc20_token';
|
||||
export * from '../generated-wrappers/test_asset_proxy_dispatcher';
|
||||
export * from '../generated-wrappers/test_exchange_internals';
|
||||
export * from '../generated-wrappers/test_lib_exchange_rich_error_decoder';
|
||||
export * from '../generated-wrappers/test_revert_receiver';
|
||||
export * from '../generated-wrappers/test_signature_validator';
|
||||
export * from '../generated-wrappers/test_static_call_receiver';
|
||||
export * from '../generated-wrappers/validator';
|
||||
export * from '../generated-wrappers/wallet';
|
||||
export * from '../generated-wrappers/test_validator_wallet';
|
||||
export * from '../generated-wrappers/whitelist';
|
||||
|
||||
@@ -47,7 +47,8 @@ import {
|
||||
ExchangeContract,
|
||||
ExchangeWrapper,
|
||||
ReentrantERC20TokenContract,
|
||||
TestStaticCallReceiverContract,
|
||||
TestValidatorWalletContract,
|
||||
ValidatorWalletAction,
|
||||
} from '../src';
|
||||
|
||||
chaiSetup.configure();
|
||||
@@ -73,10 +74,7 @@ describe('Exchange core', () => {
|
||||
let erc721Proxy: ERC721ProxyContract;
|
||||
let erc1155Proxy: ERC721ProxyContract;
|
||||
let multiAssetProxy: MultiAssetProxyContract;
|
||||
let staticCallProxy: StaticCallProxyContract;
|
||||
let staticCallTarget: TestStaticCallTargetContract;
|
||||
let maliciousWallet: TestStaticCallReceiverContract;
|
||||
let maliciousValidator: TestStaticCallReceiverContract;
|
||||
let validatorWallet: TestValidatorWalletContract;
|
||||
let erc1155Contract: ERC1155MintableContract;
|
||||
|
||||
let signedOrder: SignedOrder;
|
||||
@@ -141,8 +139,8 @@ describe('Exchange core', () => {
|
||||
txDefaults,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallReceiver,
|
||||
validatorWallet = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestValidatorWallet,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
@@ -251,27 +249,143 @@ describe('Exchange core', () => {
|
||||
};
|
||||
describe('fillOrder reentrancy tests', () => reentrancyTest(exchangeConstants.FUNCTIONS_WITH_MUTEX));
|
||||
|
||||
it('should throw if signature is invalid', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
describe('signatures', () => {
|
||||
beforeEach(async () => {
|
||||
// Approve the ERC20 proxy with the test validator wallet.
|
||||
await validatorWallet.approveERC20.awaitTransactionSuccessAsync(
|
||||
erc20TokenA.address,
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
);
|
||||
// Mint some ERC20 tokens to the test validator wallet.
|
||||
await erc20TokenA.setBalance.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
);
|
||||
// Approve the validator.
|
||||
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
true,
|
||||
{ from: makerAddress },
|
||||
);
|
||||
});
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
|
||||
const v = ethUtil.toBuffer(signedOrder.signature.slice(0, 4));
|
||||
const invalidR = ethUtil.sha3('invalidR');
|
||||
const invalidS = ethUtil.sha3('invalidS');
|
||||
const signatureType = ethUtil.toBuffer(`0x${signedOrder.signature.slice(-2)}`);
|
||||
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
|
||||
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
|
||||
signedOrder.signature = invalidSigHex;
|
||||
const expectedError = new ExchangeRevertErrors.SignatureError(
|
||||
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
invalidSigHex,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
it('should revert if signature is invalid', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAssetAmount: Web3Wrapper.toBaseUnitAmount(new BigNumber(10), 18),
|
||||
});
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
|
||||
const v = ethUtil.toBuffer(signedOrder.signature.slice(0, 4));
|
||||
const invalidR = ethUtil.sha3('invalidR');
|
||||
const invalidS = ethUtil.sha3('invalidS');
|
||||
const signatureType = ethUtil.toBuffer(`0x${signedOrder.signature.slice(-2)}`);
|
||||
const invalidSigBuff = Buffer.concat([v, invalidR, invalidS, signatureType]);
|
||||
const invalidSigHex = `0x${invalidSigBuff.toString('hex')}`;
|
||||
signedOrder.signature = invalidSigHex;
|
||||
const expectedError = new ExchangeRevertErrors.SignatureError(
|
||||
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
invalidSigHex,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if validator tries to update state when SignatureType=Wallet', async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAddress: validatorWallet.address,
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
signedOrder.signature = `0x0${SignatureType.Wallet}`;
|
||||
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
makerAddress,
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if validator tries to update state when SignatureType=Validator', async () => {
|
||||
signedOrder.signature = `${validatorWallet.address}0${SignatureType.Validator}`;
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
makerAddress,
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if validator is not approved when SignatureType=Validator', async () => {
|
||||
signedOrder.signature = `${validatorWallet.address}0${SignatureType.Validator}`;
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
ValidatorWalletAction.Allow,
|
||||
makerAddress,
|
||||
);
|
||||
// Disapprove the validator.
|
||||
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
false,
|
||||
{ from: makerAddress },
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if `OrderValidator` signature type rejects during a second fill', async () => {
|
||||
signedOrder.signature = `${validatorWallet.address}0${SignatureType.OrderValidator}`;
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
ValidatorWalletAction.Allow,
|
||||
makerAddress,
|
||||
);
|
||||
const fillAmount = signedOrder.takerAssetAmount.div(10);
|
||||
await exchangeWrapper.fillOrderAsync(
|
||||
signedOrder,
|
||||
takerAddress,
|
||||
{ takerAssetFillAmount: fillAmount },
|
||||
);
|
||||
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
ValidatorWalletAction.Reject,
|
||||
makerAddress,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(
|
||||
signedOrder,
|
||||
takerAddress,
|
||||
{ takerAssetFillAmount: fillAmount },
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureError(
|
||||
ExchangeRevertErrors.SignatureErrorCode.BadSignature,
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw if fully filled', async () => {
|
||||
@@ -283,52 +397,6 @@ describe('Exchange core', () => {
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if `isValidSignature` tries to update state when SignatureType=Wallet', async () => {
|
||||
const maliciousMakerAddress = maliciousWallet.address;
|
||||
await erc20TokenA.setBalance.awaitTransactionSuccessAsync(
|
||||
maliciousMakerAddress,
|
||||
constants.INITIAL_ERC20_BALANCE,
|
||||
);
|
||||
await maliciousWallet.approveERC20.awaitTransactionSuccessAsync(
|
||||
erc20TokenA.address,
|
||||
erc20Proxy.address,
|
||||
constants.INITIAL_ERC20_ALLOWANCE,
|
||||
);
|
||||
signedOrder = await orderFactory.newSignedOrderAsync({
|
||||
makerAddress: maliciousMakerAddress,
|
||||
makerFee: constants.ZERO_AMOUNT,
|
||||
});
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
signedOrder.signature = `0x0${SignatureType.Wallet}`;
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert if `isValidSignature` tries to update state when SignatureType=Validator', async () => {
|
||||
const isApproved = true;
|
||||
await exchange.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
maliciousValidator.address,
|
||||
isApproved,
|
||||
{ from: makerAddress },
|
||||
);
|
||||
signedOrder.signature = `${maliciousValidator.address}0${SignatureType.Validator}`;
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signedOrder.signature,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = exchangeWrapper.fillOrderAsync(signedOrder, takerAddress);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should not emit transfer events for transfers where from == to', async () => {
|
||||
const txReceipt = await exchangeWrapper.fillOrderAsync(signedOrder, makerAddress);
|
||||
const logs = txReceipt.logs;
|
||||
|
||||
@@ -75,12 +75,15 @@ describe('LibExchangeRichErrorDecoder', () => {
|
||||
const errorCode = ExchangeRevertErrors.SignatureErrorCode.Illegal;
|
||||
const orderHash = orderUtils.generatePseudoRandomOrderHash();
|
||||
const signer = addressUtils.generatePseudoRandomAddress();
|
||||
const validator = addressUtils.generatePseudoRandomAddress();
|
||||
const signature = generateRandomBytes(SIGNATURE_LENGTH);
|
||||
const errorData = generateRandomBytes(ERROR_DATA_LENGTH);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureError, [errorCode, orderHash, signer, signature]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureValidatorError, [orderHash, signer, signature, errorData]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureValidatorNotApprovedError, [signer, validator]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorNotApprovedError, [signer, validator]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureValidatorError, [orderHash, signer, validator, signature, errorData]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureWalletError, [orderHash, signer, signature, errorData]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorError, [orderHash, signer, signature, errorData]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureOrderValidatorError, [orderHash, signer, validator, signature, errorData]);
|
||||
createDecodeTest(ExchangeRevertErrors.SignatureWalletOrderValidatorError, [
|
||||
orderHash,
|
||||
signer,
|
||||
|
||||
@@ -13,41 +13,37 @@ import { assetDataUtils, ExchangeRevertErrors, orderHashUtils, signatureUtils }
|
||||
import { SignatureType, SignedOrder } from '@0x/types';
|
||||
import { BigNumber, providerUtils, StringRevertError } from '@0x/utils';
|
||||
import * as chai from 'chai';
|
||||
import * as crypto from 'crypto';
|
||||
import { LogWithDecodedArgs, TransactionReceiptWithDecodedLogs } from 'ethereum-types';
|
||||
import ethUtil = require('ethereumjs-util');
|
||||
|
||||
import {
|
||||
artifacts,
|
||||
TestRevertReceiverContract,
|
||||
TestSignatureValidatorContract,
|
||||
TestSignatureValidatorSignatureValidatorApprovalEventArgs,
|
||||
TestStaticCallReceiverContract,
|
||||
ValidatorContract,
|
||||
WalletContract,
|
||||
TestValidatorWalletContract,
|
||||
} from '../src';
|
||||
|
||||
import { ValidatorWalletAction } from './utils';
|
||||
|
||||
chaiSetup.configure();
|
||||
const expect = chai.expect;
|
||||
|
||||
const blockchainLifecycle = new BlockchainLifecycle(web3Wrapper);
|
||||
// tslint:disable:no-unnecessary-type-assertion
|
||||
describe.only('MixinSignatureValidator', () => {
|
||||
const SIGNATURE_LENGTH = 65;
|
||||
let chainId: number;
|
||||
let signedOrder: SignedOrder;
|
||||
let orderFactory: OrderFactory;
|
||||
let signatureValidator: TestSignatureValidatorContract;
|
||||
let testWallet: WalletContract;
|
||||
let testValidator: ValidatorContract;
|
||||
let maliciousWallet: TestStaticCallReceiverContract;
|
||||
let maliciousValidator: TestStaticCallReceiverContract;
|
||||
let revertingWallet: TestRevertReceiverContract;
|
||||
let revertingValidator: TestRevertReceiverContract;
|
||||
let externalRevertReason: string;
|
||||
let validatorWallet: TestValidatorWalletContract;
|
||||
let validator: TestValidatorWalletContract;
|
||||
let validatorWalletRevertReason: string;
|
||||
let signerAddress: string;
|
||||
let signerPrivateKey: Buffer;
|
||||
let notSignerAddress: string;
|
||||
let notSignerPrivateKey: Buffer;
|
||||
let signatureValidatorLogDecoder: LogDecoder;
|
||||
|
||||
before(async () => {
|
||||
await blockchainLifecycle.startAsync();
|
||||
@@ -67,42 +63,29 @@ describe.only('MixinSignatureValidator', () => {
|
||||
txDefaults,
|
||||
new BigNumber(chainId),
|
||||
);
|
||||
testWallet = await WalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Wallet,
|
||||
provider,
|
||||
txDefaults,
|
||||
signerAddress,
|
||||
);
|
||||
testValidator = await ValidatorContract.deployFrom0xArtifactAsync(
|
||||
artifacts.Validator,
|
||||
provider,
|
||||
txDefaults,
|
||||
signerAddress,
|
||||
);
|
||||
maliciousWallet = maliciousValidator = await TestStaticCallReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestStaticCallReceiver,
|
||||
validatorWallet = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestValidatorWallet,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
revertingWallet = revertingValidator = await TestRevertReceiverContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestRevertReceiver,
|
||||
validator = await TestValidatorWalletContract.deployFrom0xArtifactAsync(
|
||||
artifacts.TestValidatorWallet,
|
||||
provider,
|
||||
txDefaults,
|
||||
);
|
||||
externalRevertReason = await revertingWallet.REVERT_REASON.callAsync();
|
||||
validatorWalletRevertReason = await validator.REVERT_REASON.callAsync();
|
||||
|
||||
signatureValidatorLogDecoder = new LogDecoder(web3Wrapper, artifacts);
|
||||
const approveValidator = async (validatorAddress: string) => {
|
||||
type SendApproveTx = (address: string, approved: boolean, txData: { from: string }) => Promise<any>;
|
||||
const sendTx = async (fn: { awaitTransactionSuccessAsync: SendApproveTx }) => {
|
||||
return fn.awaitTransactionSuccessAsync(validatorAddress, true, { from: signerAddress }) as Promise<any>;
|
||||
};
|
||||
await sendTx(signatureValidator.setSignatureValidatorApproval);
|
||||
await sendTx(signatureValidator.setOrderValidatorApproval);
|
||||
};
|
||||
await approveValidator(testValidator.address);
|
||||
await approveValidator(maliciousValidator.address);
|
||||
await approveValidator(revertingValidator.address);
|
||||
// Approve the validator.
|
||||
signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
true,
|
||||
{ from: signerAddress },
|
||||
);
|
||||
signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
true,
|
||||
{ from: signerAddress },
|
||||
);
|
||||
|
||||
const defaultOrderParams = {
|
||||
...constants.STATIC_ORDER_PARAMS,
|
||||
@@ -129,7 +112,12 @@ describe.only('MixinSignatureValidator', () => {
|
||||
await blockchainLifecycle.revertAsync();
|
||||
});
|
||||
|
||||
type ValidateCallAsync = (order: SignedOrder, signerAddress: string, ignatureHex: string) => Promise<any>;
|
||||
type ValidateCallAsync = (
|
||||
order: SignedOrder,
|
||||
signerAddress: string,
|
||||
signatureHex: string,
|
||||
validatorAction?: ValidatorWalletAction,
|
||||
) => Promise<any>;
|
||||
|
||||
const createHashSignatureTests = (validateCallAsync: ValidateCallAsync) => {
|
||||
it('should revert when signature is empty', async () => {
|
||||
@@ -141,7 +129,11 @@ describe.only('MixinSignatureValidator', () => {
|
||||
signedOrder.makerAddress,
|
||||
emptySignature,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, emptySignature);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
emptySignature,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
@@ -155,7 +147,11 @@ describe.only('MixinSignatureValidator', () => {
|
||||
signedOrder.makerAddress,
|
||||
unsupportedSignatureHex,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, unsupportedSignatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
unsupportedSignatureHex,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
@@ -168,13 +164,21 @@ describe.only('MixinSignatureValidator', () => {
|
||||
signedOrder.makerAddress,
|
||||
illegalSignatureHex,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, illegalSignatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
illegalSignatureHex,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=Invalid and signature has a length of zero', async () => {
|
||||
const signatureHex = `0x${Buffer.from([SignatureType.Invalid]).toString('hex')}`;
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
@@ -190,44 +194,37 @@ describe.only('MixinSignatureValidator', () => {
|
||||
signedOrder.makerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=EIP712 and signature is valid', async () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.EIP712}`),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signedOrder.signature,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=EIP712 and signature is invalid', async () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.EIP712}`),
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.EIP712]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature.
|
||||
// This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress`
|
||||
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
notSignerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
@@ -242,183 +239,211 @@ describe.only('MixinSignatureValidator', () => {
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.EthSign}`),
|
||||
ethUtil.toBuffer([SignatureType.EthSign]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=EthSign and signature is invalid', async () => {
|
||||
// Create EthSign signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashWithEthSignPrefixHex = signatureUtils.addSignedMessagePrefix(orderHashHex);
|
||||
const orderHashWithEthSignPrefixBuffer = ethUtil.toBuffer(orderHashWithEthSignPrefixHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashWithEthSignPrefixBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EthSign signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.EthSign}`),
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.EthSign]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature.
|
||||
// This will fail because `signerAddress` signed the message, but we're passing in `notSignerAddress`
|
||||
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=Wallet and signature is valid', async () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.Wallet]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=Wallet and signature is invalid', async () => {
|
||||
// Create EIP712 signature using a private key that does not belong to the wallet owner.
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const notWalletOwnerPrivateKey = notSignerPrivateKey;
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, notWalletOwnerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.Wallet]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` attempts to update state and SignatureType=Wallet', async () => {
|
||||
// Create EIP712 signature
|
||||
it('should revert when validator attempts to update state and SignatureType=Wallet', async () => {
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.Wallet]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
|
||||
orderHashHex,
|
||||
maliciousWallet.address,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, maliciousWallet.address, signatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` reverts and SignatureType=Wallet', async () => {
|
||||
// Create EIP712 signature
|
||||
it('should revert when validator reverts and SignatureType=Wallet', async () => {
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.Wallet}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer([SignatureType.Wallet]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletError(
|
||||
orderHashHex,
|
||||
revertingWallet.address,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
new StringRevertError(externalRevertReason).encode(),
|
||||
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||
);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.Revert,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, revertingWallet.address, signatureHex);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=Validator, signature is valid and validator is approved', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.Validator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.Allow,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=Validator, signature is invalid and validator is approved', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// This will return false because the validator requires `signerAddress`
|
||||
// to be the signer.
|
||||
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
|
||||
const signature = Buffer.concat([
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.Validator]),
|
||||
]);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
ethUtil.bufferToHex(signature),
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` attempts to update state and SignatureType=Validator', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
it('should revert when validator attempts to update state and SignatureType=Validator', async () => {
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.Validator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signerAddress,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` reverts and SignatureType=Validator', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${revertingValidator.address}`);
|
||||
it('should revert when validator reverts and SignatureType=Validator', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${validatorWallet.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signerAddress,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
new StringRevertError(externalRevertReason).encode(),
|
||||
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||
);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.Revert,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=Validator, signature is valid and validator is not approved', async () => {
|
||||
it('should revert when SignatureType=Validator, signature is valid and validator is not approved', async () => {
|
||||
// Set approval of signature validator to false
|
||||
await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
testValidator.address,
|
||||
validatorWallet.address,
|
||||
false,
|
||||
{ from: signerAddress },
|
||||
);
|
||||
// Validate signature
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const validatorAddress = ethUtil.toBuffer(`${validatorWallet.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.Validator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
expect(isValidSignature).to.be.false();
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureValidatorNotApprovedError(
|
||||
signerAddress,
|
||||
validatorWallet.address,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=Presigned and signer has presigned hash', async () => {
|
||||
@@ -428,21 +453,41 @@ describe.only('MixinSignatureValidator', () => {
|
||||
// Validate presigned signature
|
||||
const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=Presigned and signer has not presigned hash', async () => {
|
||||
const signature = ethUtil.toBuffer(`0x${SignatureType.PreSigned}`);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signedOrder.makerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signedOrder.makerAddress,
|
||||
signatureHex,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
};
|
||||
|
||||
describe('isValidHashSignature', () => {
|
||||
const validateCallAsync = async (order: SignedOrder, signer: string, signatureHex: string) => {
|
||||
const validateCallAsync = async (
|
||||
order: SignedOrder,
|
||||
signer: string,
|
||||
signatureHex: string,
|
||||
validatorAction?: ValidatorWalletAction,
|
||||
) => {
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(order);
|
||||
if (validatorAction !== undefined) {
|
||||
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
validatorAction,
|
||||
order.makerAddress,
|
||||
);
|
||||
}
|
||||
return signatureValidator.isValidHashSignature.callAsync(orderHashHex, signer, signatureHex);
|
||||
};
|
||||
|
||||
@@ -459,7 +504,12 @@ describe.only('MixinSignatureValidator', () => {
|
||||
signedOrder.makerAddress,
|
||||
inappropriateSignatureHex,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signerAddress, inappropriateSignatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
inappropriateSignatureHex,
|
||||
ValidatorWalletAction.Allow,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
@@ -503,162 +553,212 @@ describe.only('MixinSignatureValidator', () => {
|
||||
});
|
||||
|
||||
describe('isValidOrderSignature', () => {
|
||||
const validateCallAsync = async (order: SignedOrder, signer: string, signatureHex: string) => {
|
||||
const validateCallAsync = async (
|
||||
order: SignedOrder,
|
||||
signer: string,
|
||||
signatureHex: string,
|
||||
validatorAction?: ValidatorWalletAction,
|
||||
) => {
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(order);
|
||||
if (validatorAction !== undefined) {
|
||||
await validatorWallet.setValidateAction.awaitTransactionSuccessAsync(
|
||||
orderHashHex,
|
||||
validatorAction,
|
||||
order.makerAddress,
|
||||
);
|
||||
}
|
||||
return signatureValidator.isValidOrderSignature.callAsync(order, signer, signatureHex);
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
signedOrder = await orderFactory.newSignedOrderAsync();
|
||||
// Set up allowances
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=OrderValidator, signature is valid and validator is approved', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=OrderValidator, signature is invalid and validator is approved', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// This will return false because the validator requires `signerAddress`
|
||||
// to be the signer.
|
||||
const isValidSignature = await validateCallAsync(signedOrder, notSignerAddress, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert when `isValidOrderSignature` attempts to update state and SignatureType=OrderValidator', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${maliciousValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert when `isValidOrderSignature` reverts and SignatureType=OrderValidator', async () => {
|
||||
const validatorAddress = ethUtil.toBuffer(`${revertingValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorError(
|
||||
orderHashHex,
|
||||
signedOrder.makerAddress,
|
||||
signerAddress,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
new StringRevertError(externalRevertReason).encode(),
|
||||
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||
);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.Revert,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=OrderValidator, signature is valid and validator is not approved', async () => {
|
||||
it('should throw when SignatureType=OrderValidator, signature is valid and validator is not approved', async () => {
|
||||
// Set approval of signature validator to false
|
||||
await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
|
||||
testValidator.address,
|
||||
validatorWallet.address,
|
||||
false,
|
||||
{ from: signerAddress },
|
||||
);
|
||||
// Validate signature
|
||||
const validatorAddress = ethUtil.toBuffer(`${testValidator.address}`);
|
||||
const signatureType = ethUtil.toBuffer(`0x${SignatureType.OrderValidator}`);
|
||||
const signature = Buffer.concat([validatorAddress, signatureType]);
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const isValidSignature = await validateCallAsync(signedOrder, signerAddress, signatureHex);
|
||||
expect(isValidSignature).to.be.false();
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureOrderValidatorNotApprovedError(
|
||||
signerAddress,
|
||||
validatorWallet.address,
|
||||
);
|
||||
return expect(tx).to.rejectedWith(expectedError);
|
||||
});
|
||||
|
||||
it('should return true when SignatureType=WalletOrderValidator and signature is valid', async () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.true();
|
||||
});
|
||||
|
||||
it('should return false when SignatureType=WalletOrderValidator and signature is invalid', async () => {
|
||||
// Create EIP712 signature using a private key that does not belong to the wallet owner.
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const notWalletOwnerPrivateKey = notSignerPrivateKey;
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, notWalletOwnerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
|
||||
crypto.randomBytes(SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
// Validate signature
|
||||
const isValidSignature = await validateCallAsync(signedOrder, testWallet.address, signatureHex);
|
||||
const isValidSignature = await validateCallAsync(
|
||||
signedOrder,
|
||||
signerAddress,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.ValidateSignature,
|
||||
);
|
||||
expect(isValidSignature).to.be.false();
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` attempts to update state and SignatureType=WalletOrderValidator', async () => {
|
||||
// Create EIP712 signature
|
||||
it('should revert when validator attempts to update state and SignatureType=WalletOrderValidator', async () => {
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
|
||||
orderHashHex,
|
||||
maliciousWallet.address,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
constants.NULL_BYTES,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, maliciousWallet.address, signatureHex);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.UpdateState,
|
||||
);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
it('should revert when `isValidSignature` reverts and SignatureType=WalletOrderValidator', async () => {
|
||||
it('should revert when validator reverts and SignatureType=WalletOrderValidator', async () => {
|
||||
// Create EIP712 signature
|
||||
const orderHashHex = orderHashUtils.getOrderHashHex(signedOrder);
|
||||
const orderHashBuffer = ethUtil.toBuffer(orderHashHex);
|
||||
const ecSignature = ethUtil.ecsign(orderHashBuffer, signerPrivateKey);
|
||||
// Create 0x signature from EIP712 signature
|
||||
const signature = Buffer.concat([
|
||||
ethUtil.toBuffer(ecSignature.v),
|
||||
ecSignature.r,
|
||||
ecSignature.s,
|
||||
ethUtil.toBuffer(`0x${SignatureType.WalletOrderValidator}`),
|
||||
ethUtil.toBuffer(signedOrder.signature).slice(0, SIGNATURE_LENGTH),
|
||||
ethUtil.toBuffer(validatorWallet.address),
|
||||
ethUtil.toBuffer([SignatureType.OrderValidator]),
|
||||
]);
|
||||
const signatureHex = ethUtil.bufferToHex(signature);
|
||||
const expectedError = new ExchangeRevertErrors.SignatureWalletOrderValidatorError(
|
||||
orderHashHex,
|
||||
revertingWallet.address,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
new StringRevertError(externalRevertReason).encode(),
|
||||
new StringRevertError(validatorWalletRevertReason).encode(),
|
||||
);
|
||||
const tx = validateCallAsync(
|
||||
signedOrder,
|
||||
validatorWallet.address,
|
||||
signatureHex,
|
||||
ValidatorWalletAction.Revert,
|
||||
);
|
||||
const tx = validateCallAsync(signedOrder, revertingWallet.address, signatureHex);
|
||||
return expect(tx).to.revertWith(expectedError);
|
||||
});
|
||||
|
||||
@@ -668,38 +768,66 @@ describe.only('MixinSignatureValidator', () => {
|
||||
describe('setSignatureValidatorApproval', () => {
|
||||
it('should emit a SignatureValidatorApprovalSet with correct args when a validator is approved', async () => {
|
||||
const approval = true;
|
||||
const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync(
|
||||
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
|
||||
testValidator.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
),
|
||||
const res = await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
);
|
||||
expect(res.logs.length).to.equal(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
|
||||
const logArgs = log.args;
|
||||
expect(logArgs.signerAddress).to.equal(signerAddress);
|
||||
expect(logArgs.validatorAddress).to.equal(testValidator.address);
|
||||
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
|
||||
expect(logArgs.approved).to.equal(approval);
|
||||
});
|
||||
it('should emit a SignatureValidatorApprovalSet with correct args when a validator is disapproved', async () => {
|
||||
const approval = false;
|
||||
const res = await signatureValidatorLogDecoder.getTxWithDecodedLogsAsync(
|
||||
await signatureValidator.setSignatureValidatorApproval.sendTransactionAsync(
|
||||
testValidator.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
),
|
||||
const res = await signatureValidator.setSignatureValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
);
|
||||
expect(res.logs.length).to.equal(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
|
||||
const logArgs = log.args;
|
||||
expect(logArgs.signerAddress).to.equal(signerAddress);
|
||||
expect(logArgs.validatorAddress).to.equal(testValidator.address);
|
||||
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
|
||||
expect(logArgs.approved).to.equal(approval);
|
||||
});
|
||||
it('should emit a SignatureValidatorApprovalSet with correct args when an order validator is approved', async () => {
|
||||
const approval = true;
|
||||
const res = await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
);
|
||||
expect(res.logs.length).to.equal(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
|
||||
const logArgs = log.args;
|
||||
expect(logArgs.signerAddress).to.equal(signerAddress);
|
||||
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
|
||||
expect(logArgs.approved).to.equal(approval);
|
||||
});
|
||||
it('should emit a SignatureValidatorApprovalSet with correct args when am order validator is disapproved', async () => {
|
||||
const approval = false;
|
||||
const res = await signatureValidator.setOrderValidatorApproval.awaitTransactionSuccessAsync(
|
||||
validatorWallet.address,
|
||||
approval,
|
||||
{
|
||||
from: signerAddress,
|
||||
},
|
||||
);
|
||||
expect(res.logs.length).to.equal(1);
|
||||
const log = res.logs[0] as LogWithDecodedArgs<TestSignatureValidatorSignatureValidatorApprovalEventArgs>;
|
||||
const logArgs = log.args;
|
||||
expect(logArgs.signerAddress).to.equal(signerAddress);
|
||||
expect(logArgs.validatorAddress).to.equal(validatorWallet.address);
|
||||
expect(logArgs.approved).to.equal(approval);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,3 +33,12 @@ export const constants = {
|
||||
ExchangeFunctionName.MarketSellOrdersNoThrow,
|
||||
],
|
||||
};
|
||||
|
||||
export enum ValidatorWalletAction {
|
||||
Reject = 0,
|
||||
Allow = 1,
|
||||
Revert = 2,
|
||||
UpdateState = 3,
|
||||
ValidateSignature = 4,
|
||||
NTypes = 5,
|
||||
}
|
||||
|
||||
@@ -19,11 +19,8 @@
|
||||
"generated-artifacts/TestAssetProxyDispatcher.json",
|
||||
"generated-artifacts/TestExchangeInternals.json",
|
||||
"generated-artifacts/TestLibExchangeRichErrorDecoder.json",
|
||||
"generated-artifacts/TestRevertReceiver.json",
|
||||
"generated-artifacts/TestSignatureValidator.json",
|
||||
"generated-artifacts/TestStaticCallReceiver.json",
|
||||
"generated-artifacts/Validator.json",
|
||||
"generated-artifacts/Wallet.json",
|
||||
"generated-artifacts/TestValidatorWallet.json",
|
||||
"generated-artifacts/Whitelist.json"
|
||||
],
|
||||
"exclude": ["./deploy/solc/solc_bin"]
|
||||
|
||||
Reference in New Issue
Block a user