Merge pull request #2610 from 0xProject/feat/zero-ex/meta-transactions
EP: MetaTransactions
This commit is contained in:
commit
18bc701e8b
@ -9,6 +9,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Export `AffiliateFeeTransformerContract`",
|
"note": "Export `AffiliateFeeTransformerContract`",
|
||||||
"pr": 2622
|
"pr": 2622
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `MetaTransactions` and `SignatureValidator` features",
|
||||||
|
"pr": 2610
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -34,11 +34,12 @@ contract ZeroEx {
|
|||||||
|
|
||||||
/// @dev Construct this contract and register the `Bootstrap` feature.
|
/// @dev Construct this contract and register the `Bootstrap` feature.
|
||||||
/// After constructing this contract, `bootstrap()` should be called
|
/// After constructing this contract, `bootstrap()` should be called
|
||||||
/// to seed the initial feature set.
|
/// by `bootstrap()` to seed the initial feature set.
|
||||||
constructor() public {
|
/// @param bootstrapper Who can call `bootstrap()`.
|
||||||
|
constructor(address bootstrapper) public {
|
||||||
// Temporarily create and register the bootstrap feature.
|
// Temporarily create and register the bootstrap feature.
|
||||||
// It will deregister itself after `bootstrap()` has been called.
|
// It will deregister itself after `bootstrap()` has been called.
|
||||||
Bootstrap bootstrap = new Bootstrap(msg.sender);
|
Bootstrap bootstrap = new Bootstrap(bootstrapper);
|
||||||
LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
|
LibProxyStorage.getStorage().impls[bootstrap.bootstrap.selector] =
|
||||||
address(bootstrap);
|
address(bootstrap);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
|
||||||
|
|
||||||
|
library LibMetaTransactionsRichErrors {
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
|
||||||
|
function InvalidMetaTransactionsArrayLengthsError(
|
||||||
|
uint256 mtxCount,
|
||||||
|
uint256 signatureCount
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("InvalidMetaTransactionsArrayLengthsError(uint256,uint256)")),
|
||||||
|
mtxCount,
|
||||||
|
signatureCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionUnsupportedFunctionError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
bytes4 selector
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionUnsupportedFunctionError(bytes32,bytes4)")),
|
||||||
|
mtxHash,
|
||||||
|
selector
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionWrongSenderError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
address sender,
|
||||||
|
address expectedSender
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionWrongSenderError(bytes32,address,address)")),
|
||||||
|
mtxHash,
|
||||||
|
sender,
|
||||||
|
expectedSender
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionExpiredError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
uint256 time,
|
||||||
|
uint256 expirationTime
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionExpiredError(bytes32,uint256,uint256)")),
|
||||||
|
mtxHash,
|
||||||
|
time,
|
||||||
|
expirationTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionGasPriceError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
uint256 gasPrice,
|
||||||
|
uint256 minGasPrice,
|
||||||
|
uint256 maxGasPrice
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionGasPriceError(bytes32,uint256,uint256,uint256)")),
|
||||||
|
mtxHash,
|
||||||
|
gasPrice,
|
||||||
|
minGasPrice,
|
||||||
|
maxGasPrice
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionInsufficientEthError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
uint256 ethBalance,
|
||||||
|
uint256 ethRequired
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionInsufficientEthError(bytes32,uint256,uint256)")),
|
||||||
|
mtxHash,
|
||||||
|
ethBalance,
|
||||||
|
ethRequired
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionInvalidSignatureError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
bytes memory signature,
|
||||||
|
bytes memory errData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionInvalidSignatureError(bytes32,bytes,bytes)")),
|
||||||
|
mtxHash,
|
||||||
|
signature,
|
||||||
|
errData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionAlreadyExecutedError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
uint256 executedBlockNumber
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionAlreadyExecutedError(bytes32,uint256)")),
|
||||||
|
mtxHash,
|
||||||
|
executedBlockNumber
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function MetaTransactionCallFailedError(
|
||||||
|
bytes32 mtxHash,
|
||||||
|
bytes memory callData,
|
||||||
|
bytes memory returnData
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("MetaTransactionCallFailedError(bytes32,bytes,bytes)")),
|
||||||
|
mtxHash,
|
||||||
|
callData,
|
||||||
|
returnData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
|
||||||
|
|
||||||
|
library LibSignatureRichErrors {
|
||||||
|
|
||||||
|
enum SignatureValidationErrorCodes {
|
||||||
|
ALWAYS_INVALID,
|
||||||
|
INVALID_LENGTH,
|
||||||
|
UNSUPPORTED,
|
||||||
|
ILLEGAL,
|
||||||
|
WRONG_SIGNER
|
||||||
|
}
|
||||||
|
|
||||||
|
// solhint-disable func-name-mixedcase
|
||||||
|
|
||||||
|
function SignatureValidationError(
|
||||||
|
SignatureValidationErrorCodes code,
|
||||||
|
bytes32 hash,
|
||||||
|
address signerAddress,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes memory)
|
||||||
|
{
|
||||||
|
return abi.encodeWithSelector(
|
||||||
|
bytes4(keccak256("SignatureValidationError(uint8,bytes32,address,bytes)")),
|
||||||
|
code,
|
||||||
|
hash,
|
||||||
|
signerAddress,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
127
contracts/zero-ex/contracts/src/features/IMetaTransactions.sol
Normal file
127
contracts/zero-ex/contracts/src/features/IMetaTransactions.sol
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Meta-transactions feature.
|
||||||
|
interface IMetaTransactions {
|
||||||
|
|
||||||
|
/// @dev Describes an exchange proxy meta transaction.
|
||||||
|
struct MetaTransactionData {
|
||||||
|
// Signer of meta-transaction. On whose behalf to execute the MTX.
|
||||||
|
address signer;
|
||||||
|
// Required sender, or NULL for anyone.
|
||||||
|
address sender;
|
||||||
|
// Minimum gas price.
|
||||||
|
uint256 minGasPrice;
|
||||||
|
// Maximum gas price.
|
||||||
|
uint256 maxGasPrice;
|
||||||
|
// MTX is invalid after this time.
|
||||||
|
uint256 expirationTimeSeconds;
|
||||||
|
// Nonce to make this MTX unique.
|
||||||
|
uint256 salt;
|
||||||
|
// Encoded call data to a function on the exchange proxy.
|
||||||
|
bytes callData;
|
||||||
|
// Amount of ETH to attach to the call.
|
||||||
|
uint256 value;
|
||||||
|
// ERC20 fee `signer` pays `sender`.
|
||||||
|
IERC20TokenV06 feeToken;
|
||||||
|
// ERC20 fee amount.
|
||||||
|
uint256 feeAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Emitted whenever a meta-transaction is executed via
|
||||||
|
/// `executeMetaTransaction()` or `executeMetaTransactions()`.
|
||||||
|
/// @param hash The meta-transaction hash.
|
||||||
|
/// @param selector The selector of the function being executed.
|
||||||
|
/// @param signer Who to execute the meta-transaction on behalf of.
|
||||||
|
/// @param sender Who executed the meta-transaction.
|
||||||
|
event MetaTransactionExecuted(
|
||||||
|
bytes32 hash,
|
||||||
|
bytes4 indexed selector,
|
||||||
|
address signer,
|
||||||
|
address sender
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @dev Execute a single meta-transaction.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @param signature The signature by `mtx.signer`.
|
||||||
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
|
function executeMetaTransaction(
|
||||||
|
MetaTransactionData calldata mtx,
|
||||||
|
bytes calldata signature
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (bytes memory returnResult);
|
||||||
|
|
||||||
|
/// @dev Execute multiple meta-transactions.
|
||||||
|
/// @param mtxs The meta-transactions.
|
||||||
|
/// @param signatures The signature by each respective `mtx.signer`.
|
||||||
|
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||||
|
function batchExecuteMetaTransactions(
|
||||||
|
MetaTransactionData[] calldata mtxs,
|
||||||
|
bytes[] calldata signatures
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (bytes[] memory returnResults);
|
||||||
|
|
||||||
|
/// @dev Execute a meta-transaction via `sender`. Privileged variant.
|
||||||
|
/// Only callable from within.
|
||||||
|
/// @param sender Who is executing the meta-transaction..
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @param signature The signature by `mtx.signer`.
|
||||||
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
|
function _executeMetaTransaction(
|
||||||
|
address sender,
|
||||||
|
MetaTransactionData calldata mtx,
|
||||||
|
bytes calldata signature
|
||||||
|
)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (bytes memory returnResult);
|
||||||
|
|
||||||
|
/// @dev Get the block at which a meta-transaction has been executed.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||||
|
function getMetaTransactionExecutedBlock(MetaTransactionData calldata mtx)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 blockNumber);
|
||||||
|
|
||||||
|
/// @dev Get the block at which a meta-transaction hash has been executed.
|
||||||
|
/// @param mtxHash The meta-transaction hash.
|
||||||
|
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||||
|
function getMetaTransactionHashExecutedBlock(bytes32 mtxHash)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (uint256 blockNumber);
|
||||||
|
|
||||||
|
/// @dev Get the EIP712 hash of a meta-transaction.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @return mtxHash The EIP712 hash of `mtx`.
|
||||||
|
function getMetaTransactionHash(MetaTransactionData calldata mtx)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bytes32 mtxHash);
|
||||||
|
}
|
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Feature for validating signatures.
|
||||||
|
interface ISignatureValidator {
|
||||||
|
|
||||||
|
/// @dev Allowed signature types.
|
||||||
|
enum SignatureType {
|
||||||
|
Illegal, // 0x00, default value
|
||||||
|
Invalid, // 0x01
|
||||||
|
EIP712, // 0x02
|
||||||
|
EthSign, // 0x03
|
||||||
|
NSignatureTypes // 0x04, number of signature types. Always leave at end.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Validate that `hash` was signed by `signer` given `signature`.
|
||||||
|
/// Reverts otherwise.
|
||||||
|
/// @param hash The hash that was signed.
|
||||||
|
/// @param signer The signer of the hash.
|
||||||
|
/// @param signature The signature. The last byte of this signature should
|
||||||
|
/// be a member of the `SignatureType` enum.
|
||||||
|
function validateHashSignature(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes calldata signature
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view;
|
||||||
|
|
||||||
|
/// @dev Check that `hash` was signed by `signer` given `signature`.
|
||||||
|
/// @param hash The hash that was signed.
|
||||||
|
/// @param signer The signer of the hash.
|
||||||
|
/// @param signature The signature. The last byte of this signature should
|
||||||
|
/// be a member of the `SignatureType` enum.
|
||||||
|
/// @return isValid `true` on success.
|
||||||
|
function isValidHashSignature(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes calldata signature
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (bool isValid);
|
||||||
|
}
|
436
contracts/zero-ex/contracts/src/features/MetaTransactions.sol
Normal file
436
contracts/zero-ex/contracts/src/features/MetaTransactions.sol
Normal file
@ -0,0 +1,436 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||||
|
import "../errors/LibMetaTransactionsRichErrors.sol";
|
||||||
|
import "../fixins/FixinCommon.sol";
|
||||||
|
import "../fixins/FixinEIP712.sol";
|
||||||
|
import "../migrations/LibMigrate.sol";
|
||||||
|
import "../storage/LibMetaTransactionsStorage.sol";
|
||||||
|
import "./IMetaTransactions.sol";
|
||||||
|
import "./ITransformERC20.sol";
|
||||||
|
import "./ISignatureValidator.sol";
|
||||||
|
import "./ITokenSpender.sol";
|
||||||
|
import "./IFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev MetaTransactions feature.
|
||||||
|
contract MetaTransactions is
|
||||||
|
IFeature,
|
||||||
|
IMetaTransactions,
|
||||||
|
FixinCommon,
|
||||||
|
FixinEIP712
|
||||||
|
{
|
||||||
|
using LibBytesV06 for bytes;
|
||||||
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
|
/// @dev Intermediate state vars to avoid stack overflows.
|
||||||
|
struct ExecuteState {
|
||||||
|
address sender;
|
||||||
|
bytes32 hash;
|
||||||
|
MetaTransactionData mtx;
|
||||||
|
bytes signature;
|
||||||
|
bytes4 selector;
|
||||||
|
uint256 selfBalance;
|
||||||
|
uint256 executedBlockNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransformERC20Args {
|
||||||
|
IERC20TokenV06 inputToken;
|
||||||
|
IERC20TokenV06 outputToken;
|
||||||
|
uint256 inputTokenAmount;
|
||||||
|
uint256 minOutputTokenAmount;
|
||||||
|
ITransformERC20.Transformation[] transformations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Name of this feature.
|
||||||
|
string public constant override FEATURE_NAME = "MetaTransactions";
|
||||||
|
/// @dev Version of this feature.
|
||||||
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
|
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
||||||
|
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
|
||||||
|
"MetaTransactionData("
|
||||||
|
"address signer,"
|
||||||
|
"address sender,"
|
||||||
|
"uint256 minGasPrice,"
|
||||||
|
"uint256 maxGasPrice,"
|
||||||
|
"uint256 expirationTimeSeconds,"
|
||||||
|
"uint256 salt,"
|
||||||
|
"bytes callData,"
|
||||||
|
"uint256 value,"
|
||||||
|
"address feeToken,"
|
||||||
|
"uint256 feeAmount"
|
||||||
|
")"
|
||||||
|
);
|
||||||
|
|
||||||
|
constructor(address zeroExAddress)
|
||||||
|
public
|
||||||
|
FixinCommon()
|
||||||
|
FixinEIP712(zeroExAddress)
|
||||||
|
{
|
||||||
|
// solhint-disable-next-line no-empty-blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Initialize and register this feature.
|
||||||
|
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||||
|
/// @return success `LibMigrate.SUCCESS` on success.
|
||||||
|
function migrate()
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
_registerFeatureFunction(this.executeMetaTransaction.selector);
|
||||||
|
_registerFeatureFunction(this.batchExecuteMetaTransactions.selector);
|
||||||
|
_registerFeatureFunction(this._executeMetaTransaction.selector);
|
||||||
|
_registerFeatureFunction(this.getMetaTransactionExecutedBlock.selector);
|
||||||
|
_registerFeatureFunction(this.getMetaTransactionHashExecutedBlock.selector);
|
||||||
|
_registerFeatureFunction(this.getMetaTransactionHash.selector);
|
||||||
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Execute a single meta-transaction.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @param signature The signature by `mtx.signer`.
|
||||||
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
|
function executeMetaTransaction(
|
||||||
|
MetaTransactionData memory mtx,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
override
|
||||||
|
returns (bytes memory returnResult)
|
||||||
|
{
|
||||||
|
return _executeMetaTransactionPrivate(
|
||||||
|
msg.sender,
|
||||||
|
mtx,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Execute multiple meta-transactions.
|
||||||
|
/// @param mtxs The meta-transactions.
|
||||||
|
/// @param signatures The signature by each respective `mtx.signer`.
|
||||||
|
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||||
|
function batchExecuteMetaTransactions(
|
||||||
|
MetaTransactionData[] memory mtxs,
|
||||||
|
bytes[] memory signatures
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
override
|
||||||
|
returns (bytes[] memory returnResults)
|
||||||
|
{
|
||||||
|
if (mtxs.length != signatures.length) {
|
||||||
|
LibMetaTransactionsRichErrors.InvalidMetaTransactionsArrayLengthsError(
|
||||||
|
mtxs.length,
|
||||||
|
signatures.length
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
returnResults = new bytes[](mtxs.length);
|
||||||
|
for (uint256 i = 0; i < mtxs.length; ++i) {
|
||||||
|
returnResults[i] = _executeMetaTransactionPrivate(
|
||||||
|
msg.sender,
|
||||||
|
mtxs[i],
|
||||||
|
signatures[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Execute a meta-transaction via `sender`. Privileged variant.
|
||||||
|
/// Only callable from within.
|
||||||
|
/// @param sender Who is executing the meta-transaction..
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @param signature The signature by `mtx.signer`.
|
||||||
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
|
function _executeMetaTransaction(
|
||||||
|
address sender,
|
||||||
|
MetaTransactionData memory mtx,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
public
|
||||||
|
payable
|
||||||
|
override
|
||||||
|
onlySelf
|
||||||
|
returns (bytes memory returnResult)
|
||||||
|
{
|
||||||
|
return _executeMetaTransactionPrivate(sender, mtx, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Get the block at which a meta-transaction has been executed.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||||
|
function getMetaTransactionExecutedBlock(MetaTransactionData memory mtx)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
view
|
||||||
|
returns (uint256 blockNumber)
|
||||||
|
{
|
||||||
|
return getMetaTransactionHashExecutedBlock(getMetaTransactionHash(mtx));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Get the block at which a meta-transaction hash has been executed.
|
||||||
|
/// @param mtxHash The meta-transaction hash.
|
||||||
|
/// @return blockNumber The block height when the meta-transactioin was executed.
|
||||||
|
function getMetaTransactionHashExecutedBlock(bytes32 mtxHash)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
view
|
||||||
|
returns (uint256 blockNumber)
|
||||||
|
{
|
||||||
|
return LibMetaTransactionsStorage.getStorage().mtxHashToExecutedBlockNumber[mtxHash];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Get the EIP712 hash of a meta-transaction.
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @return mtxHash The EIP712 hash of `mtx`.
|
||||||
|
function getMetaTransactionHash(MetaTransactionData memory mtx)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
view
|
||||||
|
returns (bytes32 mtxHash)
|
||||||
|
{
|
||||||
|
return _getEIP712Hash(keccak256(abi.encode(
|
||||||
|
MTX_EIP712_TYPEHASH,
|
||||||
|
mtx.signer,
|
||||||
|
mtx.sender,
|
||||||
|
mtx.minGasPrice,
|
||||||
|
mtx.maxGasPrice,
|
||||||
|
mtx.expirationTimeSeconds,
|
||||||
|
mtx.salt,
|
||||||
|
keccak256(mtx.callData),
|
||||||
|
mtx.value,
|
||||||
|
mtx.feeToken,
|
||||||
|
mtx.feeAmount
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Execute a meta-transaction by `sender`. Low-level, hidden variant.
|
||||||
|
/// @param sender Who is executing the meta-transaction..
|
||||||
|
/// @param mtx The meta-transaction.
|
||||||
|
/// @param signature The signature by `mtx.signer`.
|
||||||
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
|
function _executeMetaTransactionPrivate(
|
||||||
|
address sender,
|
||||||
|
MetaTransactionData memory mtx,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
private
|
||||||
|
returns (bytes memory returnResult)
|
||||||
|
{
|
||||||
|
ExecuteState memory state;
|
||||||
|
state.sender = sender;
|
||||||
|
state.hash = getMetaTransactionHash(mtx);
|
||||||
|
state.mtx = mtx;
|
||||||
|
state.signature = signature;
|
||||||
|
|
||||||
|
_validateMetaTransaction(state);
|
||||||
|
|
||||||
|
// Mark the transaction executed.
|
||||||
|
assert(block.number > 0);
|
||||||
|
LibMetaTransactionsStorage.getStorage()
|
||||||
|
.mtxHashToExecutedBlockNumber[state.hash] = block.number;
|
||||||
|
|
||||||
|
// Execute the call based on the selector.
|
||||||
|
state.selector = mtx.callData.readBytes4(0);
|
||||||
|
if (state.selector == ITransformERC20.transformERC20.selector) {
|
||||||
|
returnResult = _executeTransformERC20Call(state);
|
||||||
|
} else {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
||||||
|
.rrevert();
|
||||||
|
}
|
||||||
|
// Pay the fee to the sender.
|
||||||
|
if (mtx.feeAmount > 0) {
|
||||||
|
ITokenSpender(address(this))._spendERC20Tokens(
|
||||||
|
mtx.feeToken,
|
||||||
|
mtx.signer, // From the signer.
|
||||||
|
sender, // To the sender.
|
||||||
|
mtx.feeAmount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
emit MetaTransactionExecuted(
|
||||||
|
state.hash,
|
||||||
|
state.selector,
|
||||||
|
mtx.signer,
|
||||||
|
mtx.sender
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Validate that a meta-transaction is executable.
|
||||||
|
function _validateMetaTransaction(ExecuteState memory state)
|
||||||
|
private
|
||||||
|
view
|
||||||
|
{
|
||||||
|
// Must be from the required sender, if set.
|
||||||
|
if (state.mtx.sender != address(0) && state.mtx.sender != state.sender) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionWrongSenderError(
|
||||||
|
state.hash,
|
||||||
|
state.sender,
|
||||||
|
state.mtx.sender
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Must not be expired.
|
||||||
|
if (state.mtx.expirationTimeSeconds <= block.timestamp) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionExpiredError(
|
||||||
|
state.hash,
|
||||||
|
block.timestamp,
|
||||||
|
state.mtx.expirationTimeSeconds
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Must have a valid gas price.
|
||||||
|
if (state.mtx.minGasPrice > tx.gasprice || state.mtx.maxGasPrice < tx.gasprice) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionGasPriceError(
|
||||||
|
state.hash,
|
||||||
|
tx.gasprice,
|
||||||
|
state.mtx.minGasPrice,
|
||||||
|
state.mtx.maxGasPrice
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Must have enough ETH.
|
||||||
|
state.selfBalance = address(this).balance;
|
||||||
|
if (state.mtx.value > state.selfBalance) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionInsufficientEthError(
|
||||||
|
state.hash,
|
||||||
|
state.selfBalance,
|
||||||
|
state.mtx.value
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Must be signed by signer.
|
||||||
|
try
|
||||||
|
ISignatureValidator(address(this))
|
||||||
|
.validateHashSignature(state.hash, state.mtx.signer, state.signature)
|
||||||
|
{}
|
||||||
|
catch (bytes memory err) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionInvalidSignatureError(
|
||||||
|
state.hash,
|
||||||
|
state.signature,
|
||||||
|
err
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
// Transaction must not have been already executed.
|
||||||
|
state.executedBlockNumber = LibMetaTransactionsStorage
|
||||||
|
.getStorage().mtxHashToExecutedBlockNumber[state.hash];
|
||||||
|
if (state.executedBlockNumber != 0) {
|
||||||
|
LibMetaTransactionsRichErrors
|
||||||
|
.MetaTransactionAlreadyExecutedError(
|
||||||
|
state.hash,
|
||||||
|
state.executedBlockNumber
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Execute a `ITransformERC20.transformERC20()` meta-transaction call
|
||||||
|
/// by decoding the call args and translating the call to the internal
|
||||||
|
/// `ITransformERC20._transformERC20()` variant, where we can override
|
||||||
|
/// the taker address.
|
||||||
|
function _executeTransformERC20Call(ExecuteState memory state)
|
||||||
|
private
|
||||||
|
returns (bytes memory returnResult)
|
||||||
|
{
|
||||||
|
// HACK(dorothy-zbornak): `abi.decode()` with the individual args
|
||||||
|
// will cause a stack overflow. But we can prefix the call data with an
|
||||||
|
// offset to transform it into the encoding for the equivalent single struct arg,
|
||||||
|
// since decoding a single struct arg consumes far less stack space than
|
||||||
|
// decoding multiple struct args.
|
||||||
|
|
||||||
|
// Where the encoding for multiple args (with the seleector ommitted)
|
||||||
|
// would typically look like:
|
||||||
|
// | argument | offset |
|
||||||
|
// |--------------------------|---------|
|
||||||
|
// | inputToken | 0 |
|
||||||
|
// | outputToken | 32 |
|
||||||
|
// | inputTokenAmount | 64 |
|
||||||
|
// | minOutputTokenAmount | 96 |
|
||||||
|
// | transformations (offset) | 128 | = 32
|
||||||
|
// | transformations (data) | 160 |
|
||||||
|
|
||||||
|
// We will ABI-decode a single struct arg copy with the layout:
|
||||||
|
// | argument | offset |
|
||||||
|
// |--------------------------|---------|
|
||||||
|
// | (arg 1 offset) | 0 | = 32
|
||||||
|
// | inputToken | 32 |
|
||||||
|
// | outputToken | 64 |
|
||||||
|
// | inputTokenAmount | 96 |
|
||||||
|
// | minOutputTokenAmount | 128 |
|
||||||
|
// | transformations (offset) | 160 | = 32
|
||||||
|
// | transformations (data) | 192 |
|
||||||
|
|
||||||
|
TransformERC20Args memory args;
|
||||||
|
{
|
||||||
|
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
||||||
|
// Copy the args data from the original, after the new struct offset prefix.
|
||||||
|
bytes memory fromCallData = state.mtx.callData;
|
||||||
|
assert(fromCallData.length >= 4);
|
||||||
|
uint256 fromMem;
|
||||||
|
uint256 toMem;
|
||||||
|
assembly {
|
||||||
|
// Prefix the original calldata with a struct offset,
|
||||||
|
// which is just one word over.
|
||||||
|
mstore(add(encodedStructArgs, 32), 32)
|
||||||
|
// Copy everything after the selector.
|
||||||
|
fromMem := add(fromCallData, 36)
|
||||||
|
// Start copying after the struct offset.
|
||||||
|
toMem := add(encodedStructArgs, 64)
|
||||||
|
}
|
||||||
|
LibBytesV06.memCopy(toMem, fromMem, fromCallData.length - 4);
|
||||||
|
// Decode call args for `ITransformERC20.transformERC20()` as a struct.
|
||||||
|
args = abi.decode(encodedStructArgs, (TransformERC20Args));
|
||||||
|
}
|
||||||
|
// Call `ITransformERC20._transformERC20()` (internal variant).
|
||||||
|
return _callSelf(
|
||||||
|
state.hash,
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
ITransformERC20._transformERC20.selector,
|
||||||
|
keccak256(state.mtx.callData),
|
||||||
|
state.mtx.signer, // taker is mtx signer
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations
|
||||||
|
),
|
||||||
|
state.mtx.value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Make an arbitrary internal, meta-transaction call.
|
||||||
|
/// Warning: Do not let unadulerated `callData` into this function.
|
||||||
|
function _callSelf(bytes32 hash, bytes memory callData, uint256 value)
|
||||||
|
private
|
||||||
|
returns (bytes memory returnResult)
|
||||||
|
{
|
||||||
|
bool success;
|
||||||
|
(success, returnResult) = address(this).call{value: value}(callData);
|
||||||
|
if (!success) {
|
||||||
|
LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(
|
||||||
|
hash,
|
||||||
|
callData,
|
||||||
|
returnResult
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -37,19 +37,15 @@ contract Ownable is
|
|||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
|
|
||||||
// solhint-disable
|
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "Ownable";
|
string public constant override FEATURE_NAME = "Ownable";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
/// @dev The deployed address of this contract.
|
|
||||||
address immutable private _implementation;
|
|
||||||
// solhint-enable
|
|
||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public FixinCommon() {
|
||||||
_implementation = address(this);
|
// solhint-disable-next-line no-empty-blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
|
/// @dev Initializes this feature. The intial owner will be set to this (ZeroEx)
|
||||||
|
260
contracts/zero-ex/contracts/src/features/SignatureValidator.sol
Normal file
260
contracts/zero-ex/contracts/src/features/SignatureValidator.sol
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/LibBytesV06.sol";
|
||||||
|
import "../errors/LibSignatureRichErrors.sol";
|
||||||
|
import "../fixins/FixinCommon.sol";
|
||||||
|
import "../migrations/LibMigrate.sol";
|
||||||
|
import "./ISignatureValidator.sol";
|
||||||
|
import "./IFeature.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Feature for validating signatures.
|
||||||
|
contract SignatureValidator is
|
||||||
|
IFeature,
|
||||||
|
ISignatureValidator,
|
||||||
|
FixinCommon
|
||||||
|
{
|
||||||
|
using LibBytesV06 for bytes;
|
||||||
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
|
/// @dev Name of this feature.
|
||||||
|
string public constant override FEATURE_NAME = "SignatureValidator";
|
||||||
|
/// @dev Version of this feature.
|
||||||
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
|
|
||||||
|
constructor() public FixinCommon() {
|
||||||
|
// solhint-disable-next-line no-empty-blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Initialize and register this feature.
|
||||||
|
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||||
|
/// @return success `LibMigrate.SUCCESS` on success.
|
||||||
|
function migrate()
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
|
_registerFeatureFunction(this.validateHashSignature.selector);
|
||||||
|
_registerFeatureFunction(this.isValidHashSignature.selector);
|
||||||
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Validate that `hash` was signed by `signer` given `signature`.
|
||||||
|
/// Reverts otherwise.
|
||||||
|
/// @param hash The hash that was signed.
|
||||||
|
/// @param signer The signer of the hash.
|
||||||
|
/// @param signature The signature. The last byte of this signature should
|
||||||
|
/// be a member of the `SignatureType` enum.
|
||||||
|
function validateHashSignature(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
view
|
||||||
|
{
|
||||||
|
SignatureType signatureType = _readValidSignatureType(
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
// TODO: When we support non-hash signature types, assert that
|
||||||
|
// `signatureType` is only `EIP712` or `EthSign` here.
|
||||||
|
|
||||||
|
_validateHashSignatureTypes(
|
||||||
|
signatureType,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Check that `hash` was signed by `signer` given `signature`.
|
||||||
|
/// @param hash The hash that was signed.
|
||||||
|
/// @param signer The signer of the hash.
|
||||||
|
/// @param signature The signature. The last byte of this signature should
|
||||||
|
/// be a member of the `SignatureType` enum.
|
||||||
|
/// @return isValid `true` on success.
|
||||||
|
function isValidHashSignature(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes calldata signature
|
||||||
|
)
|
||||||
|
external
|
||||||
|
view
|
||||||
|
override
|
||||||
|
returns (bool isValid)
|
||||||
|
{
|
||||||
|
try this.validateHashSignature(hash, signer, signature) {
|
||||||
|
isValid = true;
|
||||||
|
} catch (bytes memory) {
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Validates a hash-only signature type. Low-level, hidden variant.
|
||||||
|
/// @param signatureType The type of signature to check.
|
||||||
|
/// @param hash The hash that was signed.
|
||||||
|
/// @param signer The signer of the hash.
|
||||||
|
/// @param signature The signature. The last byte of this signature should
|
||||||
|
/// be a member of the `SignatureType` enum.
|
||||||
|
function _validateHashSignatureTypes(
|
||||||
|
SignatureType signatureType,
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
{
|
||||||
|
address recovered = address(0);
|
||||||
|
if (signatureType == SignatureType.Invalid) {
|
||||||
|
// Always invalid signature.
|
||||||
|
// Like Illegal, this is always implicitly available and therefore
|
||||||
|
// offered explicitly. It can be implicitly created by providing
|
||||||
|
// a correctly formatted but incorrect signature.
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.ALWAYS_INVALID,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
} else if (signatureType == SignatureType.EIP712) {
|
||||||
|
// Signature using EIP712
|
||||||
|
if (signature.length != 66) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
uint8 v = uint8(signature[0]);
|
||||||
|
bytes32 r = signature.readBytes32(1);
|
||||||
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
recovered = ecrecover(
|
||||||
|
hash,
|
||||||
|
v,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
);
|
||||||
|
} else if (signatureType == SignatureType.EthSign) {
|
||||||
|
// Signed using `eth_sign`
|
||||||
|
if (signature.length != 66) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
uint8 v = uint8(signature[0]);
|
||||||
|
bytes32 r = signature.readBytes32(1);
|
||||||
|
bytes32 s = signature.readBytes32(33);
|
||||||
|
recovered = ecrecover(
|
||||||
|
keccak256(abi.encodePacked(
|
||||||
|
"\x19Ethereum Signed Message:\n32",
|
||||||
|
hash
|
||||||
|
)),
|
||||||
|
v,
|
||||||
|
r,
|
||||||
|
s
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// This should never happen.
|
||||||
|
revert('SignatureValidator/ILLEGAL_CODE_PATH');
|
||||||
|
}
|
||||||
|
if (recovered == address(0) || signer != recovered) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.WRONG_SIGNER,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Reads the `SignatureType` from the end of a signature and validates it.
|
||||||
|
function _readValidSignatureType(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (SignatureType signatureType)
|
||||||
|
{
|
||||||
|
// Read the signatureType from the signature
|
||||||
|
signatureType = _readSignatureType(
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ensure signature is supported
|
||||||
|
if (uint8(signatureType) >= uint8(SignatureType.NSignatureTypes)) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.UNSUPPORTED,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always illegal signature.
|
||||||
|
// This is always an implicit option since a signer can create a
|
||||||
|
// signature array with invalid type or length. We may as well make
|
||||||
|
// it an explicit option. This aids testing and analysis. It is
|
||||||
|
// also the initialization value for the enum type.
|
||||||
|
if (signatureType == SignatureType.Illegal) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.ILLEGAL,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Reads the `SignatureType` from the end of a signature.
|
||||||
|
function _readSignatureType(
|
||||||
|
bytes32 hash,
|
||||||
|
address signer,
|
||||||
|
bytes memory signature
|
||||||
|
)
|
||||||
|
private
|
||||||
|
pure
|
||||||
|
returns (SignatureType sigType)
|
||||||
|
{
|
||||||
|
if (signature.length == 0) {
|
||||||
|
LibSignatureRichErrors.SignatureValidationError(
|
||||||
|
LibSignatureRichErrors.SignatureValidationErrorCodes.INVALID_LENGTH,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature
|
||||||
|
).rrevert();
|
||||||
|
}
|
||||||
|
return SignatureType(uint8(signature[signature.length - 1]));
|
||||||
|
}
|
||||||
|
}
|
@ -35,19 +35,15 @@ contract SimpleFunctionRegistry is
|
|||||||
ISimpleFunctionRegistry,
|
ISimpleFunctionRegistry,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
// solhint-disable
|
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "SimpleFunctionRegistry";
|
string public constant override FEATURE_NAME = "SimpleFunctionRegistry";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
/// @dev The deployed address of this contract.
|
|
||||||
address private immutable _implementation;
|
|
||||||
// solhint-enable
|
|
||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public FixinCommon() {
|
||||||
_implementation = address(this);
|
// solhint-disable-next-line no-empty-blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Initializes this feature, registering its own functions.
|
/// @dev Initializes this feature, registering its own functions.
|
||||||
|
@ -44,14 +44,12 @@ contract TokenSpender is
|
|||||||
string public constant override FEATURE_NAME = "TokenSpender";
|
string public constant override FEATURE_NAME = "TokenSpender";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 0, 0);
|
||||||
/// @dev The implementation address of this feature.
|
|
||||||
address private immutable _implementation;
|
|
||||||
// solhint-enable
|
// solhint-enable
|
||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public FixinCommon() {
|
||||||
_implementation = address(this);
|
// solhint-disable-next-line no-empty-blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Initialize and register this feature. Should be delegatecalled
|
/// @dev Initialize and register this feature. Should be delegatecalled
|
||||||
@ -59,14 +57,14 @@ contract TokenSpender is
|
|||||||
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
|
/// @param allowanceTarget An `allowanceTarget` instance, configured to have
|
||||||
/// the ZeroeEx contract as an authority.
|
/// the ZeroeEx contract as an authority.
|
||||||
/// @return success `MIGRATE_SUCCESS` on success.
|
/// @return success `MIGRATE_SUCCESS` on success.
|
||||||
function migrate(IAllowanceTarget allowanceTarget) external returns (bytes4 success) {
|
function migrate(IAllowanceTarget allowanceTarget)
|
||||||
|
external
|
||||||
|
returns (bytes4 success)
|
||||||
|
{
|
||||||
LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
|
LibTokenSpenderStorage.getStorage().allowanceTarget = allowanceTarget;
|
||||||
ISimpleFunctionRegistry(address(this))
|
_registerFeatureFunction(this.getAllowanceTarget.selector);
|
||||||
.extend(this.getAllowanceTarget.selector, _implementation);
|
_registerFeatureFunction(this._spendERC20Tokens.selector);
|
||||||
ISimpleFunctionRegistry(address(this))
|
_registerFeatureFunction(this.getSpendableERC20BalanceOf.selector);
|
||||||
.extend(this._spendERC20Tokens.selector, _implementation);
|
|
||||||
ISimpleFunctionRegistry(address(this))
|
|
||||||
.extend(this.getSpendableERC20BalanceOf.selector, _implementation);
|
|
||||||
return LibMigrate.MIGRATE_SUCCESS;
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,39 +52,32 @@ contract TransformERC20 is
|
|||||||
uint256 takerOutputTokenBalanceAfter;
|
uint256 takerOutputTokenBalanceAfter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// solhint-disable
|
|
||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "TransformERC20";
|
string public constant override FEATURE_NAME = "TransformERC20";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 1, 0);
|
||||||
/// @dev The implementation address of this feature.
|
|
||||||
address private immutable _implementation;
|
|
||||||
// solhint-enable
|
|
||||||
|
|
||||||
using LibSafeMathV06 for uint256;
|
using LibSafeMathV06 for uint256;
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
constructor() public {
|
constructor() public FixinCommon() {
|
||||||
_implementation = address(this);
|
// solhint-disable-next-line no-empty-blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Initialize and register this feature.
|
/// @dev Initialize and register this feature.
|
||||||
/// Should be delegatecalled by `Migrate.migrate()`.
|
/// Should be delegatecalled by `Migrate.migrate()`.
|
||||||
/// @param transformerDeployer The trusted deployer for transformers.
|
/// @param transformerDeployer The trusted deployer for transformers.
|
||||||
/// @return success `LibMigrate.SUCCESS` on success.
|
/// @return success `LibMigrate.SUCCESS` on success.
|
||||||
function migrate(address transformerDeployer) external returns (bytes4 success) {
|
function migrate(address transformerDeployer)
|
||||||
ISimpleFunctionRegistry(address(this))
|
external
|
||||||
.extend(this.getTransformerDeployer.selector, _implementation);
|
returns (bytes4 success)
|
||||||
ISimpleFunctionRegistry(address(this))
|
{
|
||||||
.extend(this.createTransformWallet.selector, _implementation);
|
_registerFeatureFunction(this.getTransformerDeployer.selector);
|
||||||
ISimpleFunctionRegistry(address(this))
|
_registerFeatureFunction(this.createTransformWallet.selector);
|
||||||
.extend(this.getTransformWallet.selector, _implementation);
|
_registerFeatureFunction(this.getTransformWallet.selector);
|
||||||
ISimpleFunctionRegistry(address(this))
|
_registerFeatureFunction(this.setTransformerDeployer.selector);
|
||||||
.extend(this.setTransformerDeployer.selector, _implementation);
|
_registerFeatureFunction(this.transformERC20.selector);
|
||||||
ISimpleFunctionRegistry(address(this))
|
_registerFeatureFunction(this._transformERC20.selector);
|
||||||
.extend(this.transformERC20.selector, _implementation);
|
|
||||||
ISimpleFunctionRegistry(address(this))
|
|
||||||
.extend(this._transformERC20.selector, _implementation);
|
|
||||||
this.createTransformWallet();
|
this.createTransformWallet();
|
||||||
LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
|
LibTransformERC20Storage.getStorage().transformerDeployer = transformerDeployer;
|
||||||
return LibMigrate.MIGRATE_SUCCESS;
|
return LibMigrate.MIGRATE_SUCCESS;
|
||||||
@ -191,6 +184,7 @@ contract TransformERC20 is
|
|||||||
Transformation[] memory transformations
|
Transformation[] memory transformations
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
|
virtual
|
||||||
override
|
override
|
||||||
payable
|
payable
|
||||||
onlySelf
|
onlySelf
|
||||||
|
@ -23,13 +23,17 @@ import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
|||||||
import "../errors/LibCommonRichErrors.sol";
|
import "../errors/LibCommonRichErrors.sol";
|
||||||
import "../errors/LibOwnableRichErrors.sol";
|
import "../errors/LibOwnableRichErrors.sol";
|
||||||
import "../features/IOwnable.sol";
|
import "../features/IOwnable.sol";
|
||||||
|
import "../features/ISimpleFunctionRegistry.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Common feature utilities.
|
/// @dev Common feature utilities.
|
||||||
contract FixinCommon {
|
abstract contract FixinCommon {
|
||||||
|
|
||||||
using LibRichErrorsV06 for bytes;
|
using LibRichErrorsV06 for bytes;
|
||||||
|
|
||||||
|
/// @dev The implementation address of this feature.
|
||||||
|
address internal immutable _implementation;
|
||||||
|
|
||||||
/// @dev The caller must be this contract.
|
/// @dev The caller must be this contract.
|
||||||
modifier onlySelf() virtual {
|
modifier onlySelf() virtual {
|
||||||
if (msg.sender != address(this)) {
|
if (msg.sender != address(this)) {
|
||||||
@ -52,6 +56,21 @@ contract FixinCommon {
|
|||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructor() internal {
|
||||||
|
// Remember this feature's original address.
|
||||||
|
_implementation = address(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Registers a function implemented by this feature at `_implementation`.
|
||||||
|
/// Can and should only be called within a `migrate()`.
|
||||||
|
/// @param selector The selector of the function whose implementation
|
||||||
|
/// is at `_implementation`.
|
||||||
|
function _registerFeatureFunction(bytes4 selector)
|
||||||
|
internal
|
||||||
|
{
|
||||||
|
ISimpleFunctionRegistry(address(this)).extend(selector, _implementation);
|
||||||
|
}
|
||||||
|
|
||||||
/// @dev Encode a feature version as a `uint256`.
|
/// @dev Encode a feature version as a `uint256`.
|
||||||
/// @param major The major version number of the feature.
|
/// @param major The major version number of the feature.
|
||||||
/// @param minor The minor version number of the feature.
|
/// @param minor The minor version number of the feature.
|
||||||
|
69
contracts/zero-ex/contracts/src/fixins/FixinEIP712.sol
Normal file
69
contracts/zero-ex/contracts/src/fixins/FixinEIP712.sol
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "@0x/contracts-utils/contracts/src/v06/errors/LibRichErrorsV06.sol";
|
||||||
|
import "../errors/LibCommonRichErrors.sol";
|
||||||
|
import "../errors/LibOwnableRichErrors.sol";
|
||||||
|
import "../features/IOwnable.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev EIP712 helpers for features.
|
||||||
|
abstract contract FixinEIP712 {
|
||||||
|
|
||||||
|
/// @dev The domain hash separator for the entire exchange proxy.
|
||||||
|
bytes32 public immutable EIP712_DOMAIN_SEPARATOR;
|
||||||
|
|
||||||
|
constructor(address zeroExAddress) internal {
|
||||||
|
// Compute `EIP712_DOMAIN_SEPARATOR`
|
||||||
|
{
|
||||||
|
uint256 chainId;
|
||||||
|
assembly { chainId := chainid() }
|
||||||
|
EIP712_DOMAIN_SEPARATOR = keccak256(
|
||||||
|
abi.encode(
|
||||||
|
keccak256(
|
||||||
|
"EIP712Domain("
|
||||||
|
"string name,"
|
||||||
|
"string version,"
|
||||||
|
"uint256 chainId,"
|
||||||
|
"address verifyingContract"
|
||||||
|
")"
|
||||||
|
),
|
||||||
|
keccak256("ZeroEx"),
|
||||||
|
keccak256("1.0.0"),
|
||||||
|
chainId,
|
||||||
|
zeroExAddress
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getEIP712Hash(bytes32 structHash)
|
||||||
|
internal
|
||||||
|
view
|
||||||
|
returns (bytes32 eip712Hash)
|
||||||
|
{
|
||||||
|
return keccak256(abi.encodePacked(
|
||||||
|
hex"1901",
|
||||||
|
EIP712_DOMAIN_SEPARATOR,
|
||||||
|
structHash
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
@ -23,6 +23,8 @@ import "../ZeroEx.sol";
|
|||||||
import "../features/IOwnable.sol";
|
import "../features/IOwnable.sol";
|
||||||
import "../features/TokenSpender.sol";
|
import "../features/TokenSpender.sol";
|
||||||
import "../features/TransformERC20.sol";
|
import "../features/TransformERC20.sol";
|
||||||
|
import "../features/SignatureValidator.sol";
|
||||||
|
import "../features/MetaTransactions.sol";
|
||||||
import "../external/AllowanceTarget.sol";
|
import "../external/AllowanceTarget.sol";
|
||||||
import "./InitialMigration.sol";
|
import "./InitialMigration.sol";
|
||||||
|
|
||||||
@ -38,6 +40,8 @@ contract FullMigration {
|
|||||||
Ownable ownable;
|
Ownable ownable;
|
||||||
TokenSpender tokenSpender;
|
TokenSpender tokenSpender;
|
||||||
TransformERC20 transformERC20;
|
TransformERC20 transformERC20;
|
||||||
|
SignatureValidator signatureValidator;
|
||||||
|
MetaTransactions metaTransactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Parameters needed to initialize features.
|
/// @dev Parameters needed to initialize features.
|
||||||
@ -45,42 +49,56 @@ contract FullMigration {
|
|||||||
address transformerDeployer;
|
address transformerDeployer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev The allowed caller of `deploy()`.
|
/// @dev The allowed caller of `initializeZeroEx()`.
|
||||||
address public immutable deployer;
|
address public immutable initializeCaller;
|
||||||
/// @dev The initial migration contract.
|
/// @dev The initial migration contract.
|
||||||
InitialMigration private _initialMigration;
|
InitialMigration private _initialMigration;
|
||||||
|
|
||||||
/// @dev Instantiate this contract and set the allowed caller of `deploy()`
|
/// @dev Instantiate this contract and set the allowed caller of `initializeZeroEx()`
|
||||||
/// to `deployer`.
|
/// to `initializeCaller`.
|
||||||
/// @param deployer_ The allowed caller of `deploy()`.
|
/// @param initializeCaller_ The allowed caller of `initializeZeroEx()`.
|
||||||
constructor(address payable deployer_)
|
constructor(address payable initializeCaller_)
|
||||||
public
|
public
|
||||||
{
|
{
|
||||||
deployer = deployer_;
|
initializeCaller = initializeCaller_;
|
||||||
// Create an initial migration contract with this contract set to the
|
// Create an initial migration contract with this contract set to the
|
||||||
// allowed deployer.
|
// allowed `initializeCaller`.
|
||||||
_initialMigration = new InitialMigration(address(this));
|
_initialMigration = new InitialMigration(address(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Deploy the `ZeroEx` contract with the full feature set,
|
/// @dev Retrieve the bootstrapper address to use when constructing `ZeroEx`.
|
||||||
|
/// @return bootstrapper The bootstrapper address.
|
||||||
|
function getBootstrapper()
|
||||||
|
external
|
||||||
|
view
|
||||||
|
returns (address bootstrapper)
|
||||||
|
{
|
||||||
|
return address(_initialMigration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Initialize the `ZeroEx` contract with the full feature set,
|
||||||
/// transfer ownership to `owner`, then self-destruct.
|
/// transfer ownership to `owner`, then self-destruct.
|
||||||
/// @param owner The owner of the contract.
|
/// @param owner The owner of the contract.
|
||||||
|
/// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
|
||||||
|
/// been constructed with this contract as the bootstrapper.
|
||||||
/// @param features Features to add to the proxy.
|
/// @param features Features to add to the proxy.
|
||||||
/// @return zeroEx The deployed and configured `ZeroEx` contract.
|
/// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
|
||||||
/// @param migrateOpts Parameters needed to initialize features.
|
/// @param migrateOpts Parameters needed to initialize features.
|
||||||
function deploy(
|
function initializeZeroEx(
|
||||||
address payable owner,
|
address payable owner,
|
||||||
|
ZeroEx zeroEx,
|
||||||
Features memory features,
|
Features memory features,
|
||||||
MigrateOpts memory migrateOpts
|
MigrateOpts memory migrateOpts
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
returns (ZeroEx zeroEx)
|
returns (ZeroEx _zeroEx)
|
||||||
{
|
{
|
||||||
require(msg.sender == deployer, "FullMigration/INVALID_SENDER");
|
require(msg.sender == initializeCaller, "FullMigration/INVALID_SENDER");
|
||||||
|
|
||||||
// Perform the initial migration with the owner set to this contract.
|
// Perform the initial migration with the owner set to this contract.
|
||||||
zeroEx = _initialMigration.deploy(
|
_initialMigration.initializeZeroEx(
|
||||||
address(uint160(address(this))),
|
address(uint160(address(this))),
|
||||||
|
zeroEx,
|
||||||
InitialMigration.BootstrapFeatures({
|
InitialMigration.BootstrapFeatures({
|
||||||
registry: features.registry,
|
registry: features.registry,
|
||||||
ownable: features.ownable
|
ownable: features.ownable
|
||||||
@ -95,9 +113,11 @@ contract FullMigration {
|
|||||||
|
|
||||||
// Self-destruct.
|
// Self-destruct.
|
||||||
this.die(owner);
|
this.die(owner);
|
||||||
|
|
||||||
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Destroy this contract. Only callable from ourselves (from `deploy()`).
|
/// @dev Destroy this contract. Only callable from ourselves (from `initializeZeroEx()`).
|
||||||
/// @param ethRecipient Receiver of any ETH in this contract.
|
/// @param ethRecipient Receiver of any ETH in this contract.
|
||||||
function die(address payable ethRecipient)
|
function die(address payable ethRecipient)
|
||||||
external
|
external
|
||||||
@ -153,5 +173,27 @@ contract FullMigration {
|
|||||||
address(this)
|
address(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
// SignatureValidator
|
||||||
|
{
|
||||||
|
// Register the feature.
|
||||||
|
ownable.migrate(
|
||||||
|
address(features.signatureValidator),
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
SignatureValidator.migrate.selector
|
||||||
|
),
|
||||||
|
address(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// MetaTransactions
|
||||||
|
{
|
||||||
|
// Register the feature.
|
||||||
|
ownable.migrate(
|
||||||
|
address(features.metaTransactions),
|
||||||
|
abi.encodeWithSelector(
|
||||||
|
MetaTransactions.migrate.selector
|
||||||
|
),
|
||||||
|
address(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,36 +35,39 @@ contract InitialMigration {
|
|||||||
Ownable ownable;
|
Ownable ownable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev The allowed caller of `deploy()`. In production, this would be
|
/// @dev The allowed caller of `initializeZeroEx()`. In production, this would be
|
||||||
/// the governor.
|
/// the governor.
|
||||||
address public immutable deployer;
|
address public immutable initializeCaller;
|
||||||
/// @dev The real address of this contract.
|
/// @dev The real address of this contract.
|
||||||
address private immutable _implementation;
|
address private immutable _implementation;
|
||||||
|
|
||||||
/// @dev Instantiate this contract and set the allowed caller of `deploy()`
|
/// @dev Instantiate this contract and set the allowed caller of `initializeZeroEx()`
|
||||||
/// to `deployer_`.
|
/// to `initializeCaller_`.
|
||||||
/// @param deployer_ The allowed caller of `deploy()`.
|
/// @param initializeCaller_ The allowed caller of `initializeZeroEx()`.
|
||||||
constructor(address deployer_) public {
|
constructor(address initializeCaller_) public {
|
||||||
deployer = deployer_;
|
initializeCaller = initializeCaller_;
|
||||||
_implementation = address(this);
|
_implementation = address(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Deploy the `ZeroEx` contract with the minimum feature set,
|
/// @dev Initialize the `ZeroEx` contract with the minimum feature set,
|
||||||
/// transfers ownership to `owner`, then self-destructs.
|
/// transfers ownership to `owner`, then self-destructs.
|
||||||
/// Only callable by `deployer` set in the contstructor.
|
/// Only callable by `initializeCaller` set in the contstructor.
|
||||||
/// @param owner The owner of the contract.
|
/// @param owner The owner of the contract.
|
||||||
|
/// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
|
||||||
|
/// been constructed with this contract as the bootstrapper.
|
||||||
/// @param features Features to bootstrap into the proxy.
|
/// @param features Features to bootstrap into the proxy.
|
||||||
/// @return zeroEx The deployed and configured `ZeroEx` contract.
|
/// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
|
||||||
function deploy(address payable owner, BootstrapFeatures memory features)
|
function initializeZeroEx(
|
||||||
|
address payable owner,
|
||||||
|
ZeroEx zeroEx,
|
||||||
|
BootstrapFeatures memory features
|
||||||
|
)
|
||||||
public
|
public
|
||||||
virtual
|
virtual
|
||||||
returns (ZeroEx zeroEx)
|
returns (ZeroEx _zeroEx)
|
||||||
{
|
{
|
||||||
// Must be called by the allowed deployer.
|
// Must be called by the allowed initializeCaller.
|
||||||
require(msg.sender == deployer, "InitialMigration/INVALID_SENDER");
|
require(msg.sender == initializeCaller, "InitialMigration/INVALID_SENDER");
|
||||||
|
|
||||||
// Deploy the ZeroEx contract, setting ourselves as the bootstrapper.
|
|
||||||
zeroEx = new ZeroEx();
|
|
||||||
|
|
||||||
// Bootstrap the initial feature set.
|
// Bootstrap the initial feature set.
|
||||||
IBootstrap(address(zeroEx)).bootstrap(
|
IBootstrap(address(zeroEx)).bootstrap(
|
||||||
@ -75,6 +78,8 @@ contract InitialMigration {
|
|||||||
// Self-destruct. This contract should not hold any funds but we send
|
// Self-destruct. This contract should not hold any funds but we send
|
||||||
// them to the owner just in case.
|
// them to the owner just in case.
|
||||||
this.die(owner);
|
this.die(owner);
|
||||||
|
|
||||||
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Sets up the initial state of the `ZeroEx` contract.
|
/// @dev Sets up the initial state of the `ZeroEx` contract.
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "./LibStorage.sol";
|
||||||
|
|
||||||
|
|
||||||
|
/// @dev Storage helpers for the `MetaTransactions` feature.
|
||||||
|
library LibMetaTransactionsStorage {
|
||||||
|
|
||||||
|
/// @dev Storage bucket for this feature.
|
||||||
|
struct Storage {
|
||||||
|
// The block number when a hash was executed.
|
||||||
|
mapping (bytes32 => uint256) mtxHashToExecutedBlockNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev Get the storage bucket for this contract.
|
||||||
|
function getStorage() internal pure returns (Storage storage stor) {
|
||||||
|
uint256 storageSlot = LibStorage.getStorageSlot(
|
||||||
|
LibStorage.StorageId.MetaTransactions
|
||||||
|
);
|
||||||
|
// Dip into assembly to change the slot pointed to by the local
|
||||||
|
// variable `stor`.
|
||||||
|
// See https://solidity.readthedocs.io/en/v0.6.8/assembly.html?highlight=slot#access-to-external-variables-functions-and-libraries
|
||||||
|
assembly { stor_slot := storageSlot }
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,8 @@ library LibStorage {
|
|||||||
SimpleFunctionRegistry,
|
SimpleFunctionRegistry,
|
||||||
Ownable,
|
Ownable,
|
||||||
TokenSpender,
|
TokenSpender,
|
||||||
TransformERC20
|
TransformERC20,
|
||||||
|
MetaTransactions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
|
/// @dev Get the storage slot given a storage ID. We assign unique, well-spaced
|
||||||
|
@ -23,7 +23,7 @@ import "./LibStorage.sol";
|
|||||||
import "../external/IFlashWallet.sol";
|
import "../external/IFlashWallet.sol";
|
||||||
|
|
||||||
|
|
||||||
/// @dev Storage helpers for the `TokenSpender` feature.
|
/// @dev Storage helpers for the `TransformERC20` feature.
|
||||||
library LibTransformERC20Storage {
|
library LibTransformERC20Storage {
|
||||||
|
|
||||||
/// @dev Storage bucket for this feature.
|
/// @dev Storage bucket for this feature.
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Copyright 2020 ZeroEx Intl.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
pragma solidity ^0.6.5;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import "../src/features/TransformERC20.sol";
|
||||||
|
|
||||||
|
|
||||||
|
contract TestMetaTransactionsTransformERC20Feature is
|
||||||
|
TransformERC20
|
||||||
|
{
|
||||||
|
event TransformERC20Called(
|
||||||
|
address sender,
|
||||||
|
uint256 value,
|
||||||
|
bytes32 callDataHash,
|
||||||
|
address taker,
|
||||||
|
IERC20TokenV06 inputToken,
|
||||||
|
IERC20TokenV06 outputToken,
|
||||||
|
uint256 inputTokenAmount,
|
||||||
|
uint256 minOutputTokenAmount,
|
||||||
|
Transformation[] transformations
|
||||||
|
);
|
||||||
|
|
||||||
|
function _transformERC20(
|
||||||
|
bytes32 callDataHash,
|
||||||
|
address payable taker,
|
||||||
|
IERC20TokenV06 inputToken,
|
||||||
|
IERC20TokenV06 outputToken,
|
||||||
|
uint256 inputTokenAmount,
|
||||||
|
uint256 minOutputTokenAmount,
|
||||||
|
Transformation[] memory transformations
|
||||||
|
)
|
||||||
|
public
|
||||||
|
override
|
||||||
|
payable
|
||||||
|
returns (uint256 outputTokenAmount)
|
||||||
|
{
|
||||||
|
if (msg.value == 666) {
|
||||||
|
revert('FAIL');
|
||||||
|
}
|
||||||
|
|
||||||
|
emit TransformERC20Called(
|
||||||
|
msg.sender,
|
||||||
|
msg.value,
|
||||||
|
callDataHash,
|
||||||
|
taker,
|
||||||
|
inputToken,
|
||||||
|
outputToken,
|
||||||
|
inputTokenAmount,
|
||||||
|
minOutputTokenAmount,
|
||||||
|
transformations
|
||||||
|
);
|
||||||
|
return 1337;
|
||||||
|
}
|
||||||
|
}
|
@ -39,9 +39,9 @@
|
|||||||
"publish:private": "yarn build && gitpkg publish"
|
"publish:private": "yarn build && gitpkg publish"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"publicInterfaceContracts": "ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnable,ISimpleFunctionRegistry,ITokenSpender,ITransformERC20,FillQuoteTransformer,PayTakerTransformer,WethTransformer,Ownable,SimpleFunctionRegistry,TransformERC20,TokenSpender,AffiliateFeeTransformer",
|
"publicInterfaceContracts": "ZeroEx,FullMigration,InitialMigration,IFlashWallet,IAllowanceTarget,IERC20Transformer,IOwnable,ISimpleFunctionRegistry,ITokenSpender,ITransformERC20,FillQuoteTransformer,PayTakerTransformer,WethTransformer,Ownable,SimpleFunctionRegistry,TransformERC20,TokenSpender,AffiliateFeeTransformer,SignatureValidator,MetaTransactions",
|
||||||
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
"abis:comment": "This list is auto-generated by contracts-gen. Don't edit manually.",
|
||||||
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FixinGasToken|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IOwnable|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|Ownable|PayTakerTransformer|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinGasToken|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Bridge|IERC20Transformer|IExchange|IFeature|IFlashWallet|IGasToken|IMetaTransactions|IOwnable|ISignatureValidator|ISimpleFunctionRegistry|ITestSimpleFunctionRegistryFeature|ITokenSpender|ITransformERC20|InitialMigration|LibBootstrap|LibCommonRichErrors|LibERC20Transformer|LibMetaTransactionsRichErrors|LibMetaTransactionsStorage|LibMigrate|LibOwnableRichErrors|LibOwnableStorage|LibProxyRichErrors|LibProxyStorage|LibSignatureRichErrors|LibSimpleFunctionRegistryRichErrors|LibSimpleFunctionRegistryStorage|LibSpenderRichErrors|LibStorage|LibTokenSpenderStorage|LibTransformERC20RichErrors|LibTransformERC20Storage|LibWalletRichErrors|MetaTransactions|Ownable|PayTakerTransformer|SignatureValidator|SimpleFunctionRegistry|TestCallTarget|TestDelegateCaller|TestFillQuoteTransformerBridge|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -16,8 +16,10 @@ import * as IOwnable from '../generated-artifacts/IOwnable.json';
|
|||||||
import * as ISimpleFunctionRegistry from '../generated-artifacts/ISimpleFunctionRegistry.json';
|
import * as ISimpleFunctionRegistry from '../generated-artifacts/ISimpleFunctionRegistry.json';
|
||||||
import * as ITokenSpender from '../generated-artifacts/ITokenSpender.json';
|
import * as ITokenSpender from '../generated-artifacts/ITokenSpender.json';
|
||||||
import * as ITransformERC20 from '../generated-artifacts/ITransformERC20.json';
|
import * as ITransformERC20 from '../generated-artifacts/ITransformERC20.json';
|
||||||
|
import * as MetaTransactions from '../generated-artifacts/MetaTransactions.json';
|
||||||
import * as Ownable from '../generated-artifacts/Ownable.json';
|
import * as Ownable from '../generated-artifacts/Ownable.json';
|
||||||
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../generated-artifacts/PayTakerTransformer.json';
|
||||||
|
import * as SignatureValidator from '../generated-artifacts/SignatureValidator.json';
|
||||||
import * as SimpleFunctionRegistry from '../generated-artifacts/SimpleFunctionRegistry.json';
|
import * as SimpleFunctionRegistry from '../generated-artifacts/SimpleFunctionRegistry.json';
|
||||||
import * as TokenSpender from '../generated-artifacts/TokenSpender.json';
|
import * as TokenSpender from '../generated-artifacts/TokenSpender.json';
|
||||||
import * as TransformERC20 from '../generated-artifacts/TransformERC20.json';
|
import * as TransformERC20 from '../generated-artifacts/TransformERC20.json';
|
||||||
@ -42,4 +44,6 @@ export const artifacts = {
|
|||||||
TransformERC20: TransformERC20 as ContractArtifact,
|
TransformERC20: TransformERC20 as ContractArtifact,
|
||||||
TokenSpender: TokenSpender as ContractArtifact,
|
TokenSpender: TokenSpender as ContractArtifact,
|
||||||
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
AffiliateFeeTransformer: AffiliateFeeTransformer as ContractArtifact,
|
||||||
|
SignatureValidator: SignatureValidator as ContractArtifact,
|
||||||
|
MetaTransactions: MetaTransactions as ContractArtifact,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { BaseContract } from '@0x/base-contract';
|
|
||||||
import { SupportedProvider } from '@0x/subproviders';
|
import { SupportedProvider } from '@0x/subproviders';
|
||||||
import { TxData } from 'ethereum-types';
|
import { TxData } from 'ethereum-types';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
@ -7,7 +6,9 @@ import { artifacts } from './artifacts';
|
|||||||
import {
|
import {
|
||||||
FullMigrationContract,
|
FullMigrationContract,
|
||||||
InitialMigrationContract,
|
InitialMigrationContract,
|
||||||
|
MetaTransactionsContract,
|
||||||
OwnableContract,
|
OwnableContract,
|
||||||
|
SignatureValidatorContract,
|
||||||
SimpleFunctionRegistryContract,
|
SimpleFunctionRegistryContract,
|
||||||
TokenSpenderContract,
|
TokenSpenderContract,
|
||||||
TransformERC20Contract,
|
TransformERC20Contract,
|
||||||
@ -16,11 +17,17 @@ import {
|
|||||||
|
|
||||||
// tslint:disable: completed-docs
|
// tslint:disable: completed-docs
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addresses of minimum features for a deployment of the Exchange Proxy.
|
||||||
|
*/
|
||||||
export interface BootstrapFeatures {
|
export interface BootstrapFeatures {
|
||||||
registry: SimpleFunctionRegistryContract;
|
registry: string;
|
||||||
ownable: OwnableContract;
|
ownable: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deploy the minimum features of the Exchange Proxy.
|
||||||
|
*/
|
||||||
export async function deployBootstrapFeaturesAsync(
|
export async function deployBootstrapFeaturesAsync(
|
||||||
provider: SupportedProvider,
|
provider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
@ -34,20 +41,23 @@ export async function deployBootstrapFeaturesAsync(
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)),
|
)).address,
|
||||||
ownable:
|
ownable:
|
||||||
features.ownable ||
|
features.ownable ||
|
||||||
(await OwnableContract.deployFrom0xArtifactAsync(artifacts.Ownable, provider, txDefaults, artifacts)),
|
(await OwnableContract.deployFrom0xArtifactAsync(artifacts.Ownable, provider, txDefaults, artifacts))
|
||||||
|
.address,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migrate an instance of the Exchange proxy with minimum viable features.
|
||||||
|
*/
|
||||||
export async function initialMigrateAsync(
|
export async function initialMigrateAsync(
|
||||||
owner: string,
|
owner: string,
|
||||||
provider: SupportedProvider,
|
provider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
features: Partial<BootstrapFeatures> = {},
|
features: Partial<BootstrapFeatures> = {},
|
||||||
): Promise<ZeroExContract> {
|
): Promise<ZeroExContract> {
|
||||||
const _features = await deployBootstrapFeaturesAsync(provider, txDefaults, features);
|
|
||||||
const migrator = await InitialMigrationContract.deployFrom0xArtifactAsync(
|
const migrator = await InitialMigrationContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.InitialMigration,
|
artifacts.InitialMigration,
|
||||||
provider,
|
provider,
|
||||||
@ -55,24 +65,42 @@ export async function initialMigrateAsync(
|
|||||||
artifacts,
|
artifacts,
|
||||||
txDefaults.from as string,
|
txDefaults.from as string,
|
||||||
);
|
);
|
||||||
const deployCall = migrator.deploy(owner, toFeatureAdddresses(_features));
|
const zeroEx = await ZeroExContract.deployFrom0xArtifactAsync(
|
||||||
const zeroEx = new ZeroExContract(await deployCall.callAsync(), provider, {});
|
artifacts.ZeroEx,
|
||||||
await deployCall.awaitTransactionSuccessAsync();
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
migrator.address,
|
||||||
|
);
|
||||||
|
const _features = await deployBootstrapFeaturesAsync(provider, txDefaults, features);
|
||||||
|
await migrator.initializeZeroEx(owner, zeroEx.address, _features).awaitTransactionSuccessAsync();
|
||||||
return zeroEx;
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addresses of features for a full deployment of the Exchange Proxy.
|
||||||
|
*/
|
||||||
export interface FullFeatures extends BootstrapFeatures {
|
export interface FullFeatures extends BootstrapFeatures {
|
||||||
tokenSpender: TokenSpenderContract;
|
tokenSpender: string;
|
||||||
transformERC20: TransformERC20Contract;
|
transformERC20: string;
|
||||||
|
signatureValidator: string;
|
||||||
|
metaTransactions: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extra configuration options for a full migration of the Exchange Proxy.
|
||||||
|
*/
|
||||||
export interface FullMigrationOpts {
|
export interface FullMigrationOpts {
|
||||||
transformerDeployer: string;
|
transformerDeployer: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deploy all the features for a full Exchange Proxy.
|
||||||
|
*/
|
||||||
export async function deployFullFeaturesAsync(
|
export async function deployFullFeaturesAsync(
|
||||||
provider: SupportedProvider,
|
provider: SupportedProvider,
|
||||||
txDefaults: Partial<TxData>,
|
txDefaults: Partial<TxData>,
|
||||||
|
zeroExAddress: string,
|
||||||
features: Partial<FullFeatures> = {},
|
features: Partial<FullFeatures> = {},
|
||||||
): Promise<FullFeatures> {
|
): Promise<FullFeatures> {
|
||||||
return {
|
return {
|
||||||
@ -84,7 +112,7 @@ export async function deployFullFeaturesAsync(
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)),
|
)).address,
|
||||||
transformERC20:
|
transformERC20:
|
||||||
features.transformERC20 ||
|
features.transformERC20 ||
|
||||||
(await TransformERC20Contract.deployFrom0xArtifactAsync(
|
(await TransformERC20Contract.deployFrom0xArtifactAsync(
|
||||||
@ -92,10 +120,30 @@ export async function deployFullFeaturesAsync(
|
|||||||
provider,
|
provider,
|
||||||
txDefaults,
|
txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
)),
|
)).address,
|
||||||
|
signatureValidator:
|
||||||
|
features.signatureValidator ||
|
||||||
|
(await SignatureValidatorContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.SignatureValidator,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
)).address,
|
||||||
|
metaTransactions:
|
||||||
|
features.metaTransactions ||
|
||||||
|
(await MetaTransactionsContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.MetaTransactions,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
zeroExAddress,
|
||||||
|
)).address,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deploy a fully featured instance of the Exchange Proxy.
|
||||||
|
*/
|
||||||
export async function fullMigrateAsync(
|
export async function fullMigrateAsync(
|
||||||
owner: string,
|
owner: string,
|
||||||
provider: SupportedProvider,
|
provider: SupportedProvider,
|
||||||
@ -103,7 +151,6 @@ export async function fullMigrateAsync(
|
|||||||
features: Partial<FullFeatures> = {},
|
features: Partial<FullFeatures> = {},
|
||||||
opts: Partial<FullMigrationOpts> = {},
|
opts: Partial<FullMigrationOpts> = {},
|
||||||
): Promise<ZeroExContract> {
|
): Promise<ZeroExContract> {
|
||||||
const _features = await deployFullFeaturesAsync(provider, txDefaults, features);
|
|
||||||
const migrator = await FullMigrationContract.deployFrom0xArtifactAsync(
|
const migrator = await FullMigrationContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.FullMigration,
|
artifacts.FullMigration,
|
||||||
provider,
|
provider,
|
||||||
@ -111,20 +158,18 @@ export async function fullMigrateAsync(
|
|||||||
artifacts,
|
artifacts,
|
||||||
txDefaults.from as string,
|
txDefaults.from as string,
|
||||||
);
|
);
|
||||||
|
const zeroEx = await ZeroExContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.ZeroEx,
|
||||||
|
provider,
|
||||||
|
txDefaults,
|
||||||
|
artifacts,
|
||||||
|
await migrator.getBootstrapper().callAsync(),
|
||||||
|
);
|
||||||
|
const _features = await deployFullFeaturesAsync(provider, txDefaults, zeroEx.address, features);
|
||||||
const _opts = {
|
const _opts = {
|
||||||
transformerDeployer: txDefaults.from as string,
|
transformerDeployer: txDefaults.from as string,
|
||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
const deployCall = migrator.deploy(owner, toFeatureAdddresses(_features), _opts);
|
await migrator.initializeZeroEx(owner, zeroEx.address, _features, _opts).awaitTransactionSuccessAsync();
|
||||||
const zeroEx = new ZeroExContract(await deployCall.callAsync(), provider, {});
|
|
||||||
await deployCall.awaitTransactionSuccessAsync();
|
|
||||||
return zeroEx;
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// tslint:disable:space-before-function-parent one-line
|
|
||||||
export function toFeatureAdddresses<T extends BootstrapFeatures | FullFeatures | (BootstrapFeatures & FullFeatures)>(
|
|
||||||
features: T,
|
|
||||||
): { [name in keyof T]: string } {
|
|
||||||
// TS can't figure this out.
|
|
||||||
return _.mapValues(features, (c: BaseContract) => c.address) as any;
|
|
||||||
}
|
|
||||||
|
@ -14,8 +14,10 @@ export * from '../generated-wrappers/i_simple_function_registry';
|
|||||||
export * from '../generated-wrappers/i_token_spender';
|
export * from '../generated-wrappers/i_token_spender';
|
||||||
export * from '../generated-wrappers/i_transform_erc20';
|
export * from '../generated-wrappers/i_transform_erc20';
|
||||||
export * from '../generated-wrappers/initial_migration';
|
export * from '../generated-wrappers/initial_migration';
|
||||||
|
export * from '../generated-wrappers/meta_transactions';
|
||||||
export * from '../generated-wrappers/ownable';
|
export * from '../generated-wrappers/ownable';
|
||||||
export * from '../generated-wrappers/pay_taker_transformer';
|
export * from '../generated-wrappers/pay_taker_transformer';
|
||||||
|
export * from '../generated-wrappers/signature_validator';
|
||||||
export * from '../generated-wrappers/simple_function_registry';
|
export * from '../generated-wrappers/simple_function_registry';
|
||||||
export * from '../generated-wrappers/token_spender';
|
export * from '../generated-wrappers/token_spender';
|
||||||
export * from '../generated-wrappers/transform_erc20';
|
export * from '../generated-wrappers/transform_erc20';
|
||||||
|
@ -10,6 +10,7 @@ import * as AllowanceTarget from '../test/generated-artifacts/AllowanceTarget.js
|
|||||||
import * as Bootstrap from '../test/generated-artifacts/Bootstrap.json';
|
import * as Bootstrap from '../test/generated-artifacts/Bootstrap.json';
|
||||||
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
import * as FillQuoteTransformer from '../test/generated-artifacts/FillQuoteTransformer.json';
|
||||||
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
|
import * as FixinCommon from '../test/generated-artifacts/FixinCommon.json';
|
||||||
|
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
|
||||||
import * as FixinGasToken from '../test/generated-artifacts/FixinGasToken.json';
|
import * as FixinGasToken from '../test/generated-artifacts/FixinGasToken.json';
|
||||||
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
import * as FlashWallet from '../test/generated-artifacts/FlashWallet.json';
|
||||||
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
import * as FullMigration from '../test/generated-artifacts/FullMigration.json';
|
||||||
@ -21,8 +22,10 @@ import * as IExchange from '../test/generated-artifacts/IExchange.json';
|
|||||||
import * as IFeature from '../test/generated-artifacts/IFeature.json';
|
import * as IFeature from '../test/generated-artifacts/IFeature.json';
|
||||||
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
|
import * as IFlashWallet from '../test/generated-artifacts/IFlashWallet.json';
|
||||||
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
import * as IGasToken from '../test/generated-artifacts/IGasToken.json';
|
||||||
|
import * as IMetaTransactions from '../test/generated-artifacts/IMetaTransactions.json';
|
||||||
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
|
import * as InitialMigration from '../test/generated-artifacts/InitialMigration.json';
|
||||||
import * as IOwnable from '../test/generated-artifacts/IOwnable.json';
|
import * as IOwnable from '../test/generated-artifacts/IOwnable.json';
|
||||||
|
import * as ISignatureValidator from '../test/generated-artifacts/ISignatureValidator.json';
|
||||||
import * as ISimpleFunctionRegistry from '../test/generated-artifacts/ISimpleFunctionRegistry.json';
|
import * as ISimpleFunctionRegistry from '../test/generated-artifacts/ISimpleFunctionRegistry.json';
|
||||||
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
import * as ITestSimpleFunctionRegistryFeature from '../test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json';
|
||||||
import * as ITokenSpender from '../test/generated-artifacts/ITokenSpender.json';
|
import * as ITokenSpender from '../test/generated-artifacts/ITokenSpender.json';
|
||||||
@ -30,11 +33,14 @@ import * as ITransformERC20 from '../test/generated-artifacts/ITransformERC20.js
|
|||||||
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
|
import * as LibBootstrap from '../test/generated-artifacts/LibBootstrap.json';
|
||||||
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
|
import * as LibCommonRichErrors from '../test/generated-artifacts/LibCommonRichErrors.json';
|
||||||
import * as LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.json';
|
import * as LibERC20Transformer from '../test/generated-artifacts/LibERC20Transformer.json';
|
||||||
|
import * as LibMetaTransactionsRichErrors from '../test/generated-artifacts/LibMetaTransactionsRichErrors.json';
|
||||||
|
import * as LibMetaTransactionsStorage from '../test/generated-artifacts/LibMetaTransactionsStorage.json';
|
||||||
import * as LibMigrate from '../test/generated-artifacts/LibMigrate.json';
|
import * as LibMigrate from '../test/generated-artifacts/LibMigrate.json';
|
||||||
import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRichErrors.json';
|
import * as LibOwnableRichErrors from '../test/generated-artifacts/LibOwnableRichErrors.json';
|
||||||
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
import * as LibOwnableStorage from '../test/generated-artifacts/LibOwnableStorage.json';
|
||||||
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
import * as LibProxyRichErrors from '../test/generated-artifacts/LibProxyRichErrors.json';
|
||||||
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
|
import * as LibProxyStorage from '../test/generated-artifacts/LibProxyStorage.json';
|
||||||
|
import * as LibSignatureRichErrors from '../test/generated-artifacts/LibSignatureRichErrors.json';
|
||||||
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
import * as LibSimpleFunctionRegistryRichErrors from '../test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json';
|
||||||
import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json';
|
import * as LibSimpleFunctionRegistryStorage from '../test/generated-artifacts/LibSimpleFunctionRegistryStorage.json';
|
||||||
import * as LibSpenderRichErrors from '../test/generated-artifacts/LibSpenderRichErrors.json';
|
import * as LibSpenderRichErrors from '../test/generated-artifacts/LibSpenderRichErrors.json';
|
||||||
@ -43,8 +49,10 @@ import * as LibTokenSpenderStorage from '../test/generated-artifacts/LibTokenSpe
|
|||||||
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
|
import * as LibTransformERC20RichErrors from '../test/generated-artifacts/LibTransformERC20RichErrors.json';
|
||||||
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
|
import * as LibTransformERC20Storage from '../test/generated-artifacts/LibTransformERC20Storage.json';
|
||||||
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
|
import * as LibWalletRichErrors from '../test/generated-artifacts/LibWalletRichErrors.json';
|
||||||
|
import * as MetaTransactions from '../test/generated-artifacts/MetaTransactions.json';
|
||||||
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
import * as Ownable from '../test/generated-artifacts/Ownable.json';
|
||||||
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
import * as PayTakerTransformer from '../test/generated-artifacts/PayTakerTransformer.json';
|
||||||
|
import * as SignatureValidator from '../test/generated-artifacts/SignatureValidator.json';
|
||||||
import * as SimpleFunctionRegistry from '../test/generated-artifacts/SimpleFunctionRegistry.json';
|
import * as SimpleFunctionRegistry from '../test/generated-artifacts/SimpleFunctionRegistry.json';
|
||||||
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
import * as TestCallTarget from '../test/generated-artifacts/TestCallTarget.json';
|
||||||
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
import * as TestDelegateCaller from '../test/generated-artifacts/TestDelegateCaller.json';
|
||||||
@ -53,6 +61,7 @@ import * as TestFillQuoteTransformerExchange from '../test/generated-artifacts/T
|
|||||||
import * as TestFillQuoteTransformerHost from '../test/generated-artifacts/TestFillQuoteTransformerHost.json';
|
import * as TestFillQuoteTransformerHost from '../test/generated-artifacts/TestFillQuoteTransformerHost.json';
|
||||||
import * as TestFullMigration from '../test/generated-artifacts/TestFullMigration.json';
|
import * as TestFullMigration from '../test/generated-artifacts/TestFullMigration.json';
|
||||||
import * as TestInitialMigration from '../test/generated-artifacts/TestInitialMigration.json';
|
import * as TestInitialMigration from '../test/generated-artifacts/TestInitialMigration.json';
|
||||||
|
import * as TestMetaTransactionsTransformERC20Feature from '../test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json';
|
||||||
import * as TestMigrator from '../test/generated-artifacts/TestMigrator.json';
|
import * as TestMigrator from '../test/generated-artifacts/TestMigrator.json';
|
||||||
import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json';
|
import * as TestMintableERC20Token from '../test/generated-artifacts/TestMintableERC20Token.json';
|
||||||
import * as TestMintTokenERC20Transformer from '../test/generated-artifacts/TestMintTokenERC20Transformer.json';
|
import * as TestMintTokenERC20Transformer from '../test/generated-artifacts/TestMintTokenERC20Transformer.json';
|
||||||
@ -76,8 +85,10 @@ import * as ZeroEx from '../test/generated-artifacts/ZeroEx.json';
|
|||||||
export const artifacts = {
|
export const artifacts = {
|
||||||
ZeroEx: ZeroEx as ContractArtifact,
|
ZeroEx: ZeroEx as ContractArtifact,
|
||||||
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
|
LibCommonRichErrors: LibCommonRichErrors as ContractArtifact,
|
||||||
|
LibMetaTransactionsRichErrors: LibMetaTransactionsRichErrors as ContractArtifact,
|
||||||
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
|
LibOwnableRichErrors: LibOwnableRichErrors as ContractArtifact,
|
||||||
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
|
LibProxyRichErrors: LibProxyRichErrors as ContractArtifact,
|
||||||
|
LibSignatureRichErrors: LibSignatureRichErrors as ContractArtifact,
|
||||||
LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact,
|
LibSimpleFunctionRegistryRichErrors: LibSimpleFunctionRegistryRichErrors as ContractArtifact,
|
||||||
LibSpenderRichErrors: LibSpenderRichErrors as ContractArtifact,
|
LibSpenderRichErrors: LibSpenderRichErrors as ContractArtifact,
|
||||||
LibTransformERC20RichErrors: LibTransformERC20RichErrors as ContractArtifact,
|
LibTransformERC20RichErrors: LibTransformERC20RichErrors as ContractArtifact,
|
||||||
@ -90,20 +101,26 @@ export const artifacts = {
|
|||||||
Bootstrap: Bootstrap as ContractArtifact,
|
Bootstrap: Bootstrap as ContractArtifact,
|
||||||
IBootstrap: IBootstrap as ContractArtifact,
|
IBootstrap: IBootstrap as ContractArtifact,
|
||||||
IFeature: IFeature as ContractArtifact,
|
IFeature: IFeature as ContractArtifact,
|
||||||
|
IMetaTransactions: IMetaTransactions as ContractArtifact,
|
||||||
IOwnable: IOwnable as ContractArtifact,
|
IOwnable: IOwnable as ContractArtifact,
|
||||||
|
ISignatureValidator: ISignatureValidator as ContractArtifact,
|
||||||
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
|
ISimpleFunctionRegistry: ISimpleFunctionRegistry as ContractArtifact,
|
||||||
ITokenSpender: ITokenSpender as ContractArtifact,
|
ITokenSpender: ITokenSpender as ContractArtifact,
|
||||||
ITransformERC20: ITransformERC20 as ContractArtifact,
|
ITransformERC20: ITransformERC20 as ContractArtifact,
|
||||||
|
MetaTransactions: MetaTransactions as ContractArtifact,
|
||||||
Ownable: Ownable as ContractArtifact,
|
Ownable: Ownable as ContractArtifact,
|
||||||
|
SignatureValidator: SignatureValidator as ContractArtifact,
|
||||||
SimpleFunctionRegistry: SimpleFunctionRegistry as ContractArtifact,
|
SimpleFunctionRegistry: SimpleFunctionRegistry as ContractArtifact,
|
||||||
TokenSpender: TokenSpender as ContractArtifact,
|
TokenSpender: TokenSpender as ContractArtifact,
|
||||||
TransformERC20: TransformERC20 as ContractArtifact,
|
TransformERC20: TransformERC20 as ContractArtifact,
|
||||||
FixinCommon: FixinCommon as ContractArtifact,
|
FixinCommon: FixinCommon as ContractArtifact,
|
||||||
|
FixinEIP712: FixinEIP712 as ContractArtifact,
|
||||||
FixinGasToken: FixinGasToken as ContractArtifact,
|
FixinGasToken: FixinGasToken as ContractArtifact,
|
||||||
FullMigration: FullMigration as ContractArtifact,
|
FullMigration: FullMigration as ContractArtifact,
|
||||||
InitialMigration: InitialMigration as ContractArtifact,
|
InitialMigration: InitialMigration as ContractArtifact,
|
||||||
LibBootstrap: LibBootstrap as ContractArtifact,
|
LibBootstrap: LibBootstrap as ContractArtifact,
|
||||||
LibMigrate: LibMigrate as ContractArtifact,
|
LibMigrate: LibMigrate as ContractArtifact,
|
||||||
|
LibMetaTransactionsStorage: LibMetaTransactionsStorage as ContractArtifact,
|
||||||
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
|
LibOwnableStorage: LibOwnableStorage as ContractArtifact,
|
||||||
LibProxyStorage: LibProxyStorage as ContractArtifact,
|
LibProxyStorage: LibProxyStorage as ContractArtifact,
|
||||||
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
LibSimpleFunctionRegistryStorage: LibSimpleFunctionRegistryStorage as ContractArtifact,
|
||||||
@ -128,6 +145,7 @@ export const artifacts = {
|
|||||||
TestFillQuoteTransformerHost: TestFillQuoteTransformerHost as ContractArtifact,
|
TestFillQuoteTransformerHost: TestFillQuoteTransformerHost as ContractArtifact,
|
||||||
TestFullMigration: TestFullMigration as ContractArtifact,
|
TestFullMigration: TestFullMigration as ContractArtifact,
|
||||||
TestInitialMigration: TestInitialMigration as ContractArtifact,
|
TestInitialMigration: TestInitialMigration as ContractArtifact,
|
||||||
|
TestMetaTransactionsTransformERC20Feature: TestMetaTransactionsTransformERC20Feature as ContractArtifact,
|
||||||
TestMigrator: TestMigrator as ContractArtifact,
|
TestMigrator: TestMigrator as ContractArtifact,
|
||||||
TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact,
|
TestMintTokenERC20Transformer: TestMintTokenERC20Transformer as ContractArtifact,
|
||||||
TestMintableERC20Token: TestMintableERC20Token as ContractArtifact,
|
TestMintableERC20Token: TestMintableERC20Token as ContractArtifact,
|
||||||
|
548
contracts/zero-ex/test/features/meta_transactions_test.ts
Normal file
548
contracts/zero-ex/test/features/meta_transactions_test.ts
Normal file
@ -0,0 +1,548 @@
|
|||||||
|
import {
|
||||||
|
blockchainTests,
|
||||||
|
constants,
|
||||||
|
expect,
|
||||||
|
getRandomInteger,
|
||||||
|
randomAddress,
|
||||||
|
verifyEventsFromLogs,
|
||||||
|
} from '@0x/contracts-test-utils';
|
||||||
|
import { getExchangeProxyMetaTransactionHash, signatureUtils } from '@0x/order-utils';
|
||||||
|
import { ExchangeProxyMetaTransaction } from '@0x/types';
|
||||||
|
import { BigNumber, hexUtils, StringRevertError, ZeroExRevertErrors } from '@0x/utils';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { MetaTransactionsContract, ZeroExContract } from '../../src/wrappers';
|
||||||
|
import { artifacts } from '../artifacts';
|
||||||
|
import { abis } from '../utils/abis';
|
||||||
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
|
import {
|
||||||
|
ITokenSpenderContract,
|
||||||
|
TestMetaTransactionsTransformERC20FeatureContract,
|
||||||
|
TestMetaTransactionsTransformERC20FeatureEvents,
|
||||||
|
TestMintableERC20TokenContract,
|
||||||
|
} from '../wrappers';
|
||||||
|
|
||||||
|
const { NULL_ADDRESS, ZERO_AMOUNT } = constants;
|
||||||
|
|
||||||
|
blockchainTests.resets('MetaTransactions feature', env => {
|
||||||
|
let owner: string;
|
||||||
|
let sender: string;
|
||||||
|
let signers: string[];
|
||||||
|
let zeroEx: ZeroExContract;
|
||||||
|
let feature: MetaTransactionsContract;
|
||||||
|
let feeToken: TestMintableERC20TokenContract;
|
||||||
|
let transformERC20Feature: TestMetaTransactionsTransformERC20FeatureContract;
|
||||||
|
let allowanceTarget: string;
|
||||||
|
|
||||||
|
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[owner, sender, ...signers] = await env.getAccountAddressesAsync();
|
||||||
|
transformERC20Feature = await TestMetaTransactionsTransformERC20FeatureContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestMetaTransactionsTransformERC20Feature,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||||
|
transformERC20: transformERC20Feature.address,
|
||||||
|
});
|
||||||
|
feature = new MetaTransactionsContract(zeroEx.address, env.provider, { ...env.txDefaults, from: sender }, abis);
|
||||||
|
feeToken = await TestMintableERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
artifacts.TestMintableERC20Token,
|
||||||
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
allowanceTarget = await new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults)
|
||||||
|
.getAllowanceTarget()
|
||||||
|
.callAsync();
|
||||||
|
// Fund signers with fee tokens.
|
||||||
|
await Promise.all(
|
||||||
|
signers.map(async signer => {
|
||||||
|
await feeToken.mint(signer, MAX_FEE_AMOUNT).awaitTransactionSuccessAsync();
|
||||||
|
await feeToken.approve(allowanceTarget, MAX_FEE_AMOUNT).awaitTransactionSuccessAsync({ from: signer });
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
function getRandomMetaTransaction(
|
||||||
|
fields: Partial<ExchangeProxyMetaTransaction> = {},
|
||||||
|
): ExchangeProxyMetaTransaction {
|
||||||
|
return {
|
||||||
|
signer: _.sampleSize(signers)[0],
|
||||||
|
sender,
|
||||||
|
minGasPrice: getRandomInteger('2', '1e9'),
|
||||||
|
maxGasPrice: getRandomInteger('1e9', '100e9'),
|
||||||
|
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000) + 360),
|
||||||
|
salt: new BigNumber(hexUtils.random()),
|
||||||
|
callData: hexUtils.random(4),
|
||||||
|
value: getRandomInteger(1, '1e18'),
|
||||||
|
feeToken: feeToken.address,
|
||||||
|
feeAmount: getRandomInteger(1, MAX_FEE_AMOUNT),
|
||||||
|
domain: {
|
||||||
|
chainId: 1, // Ganache's `chainid` opcode is hardcoded as 1
|
||||||
|
verifyingContract: zeroEx.address,
|
||||||
|
},
|
||||||
|
...fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function signMetaTransactionAsync(mtx: ExchangeProxyMetaTransaction, signer?: string): Promise<string> {
|
||||||
|
return signatureUtils.ecSignHashAsync(
|
||||||
|
env.provider,
|
||||||
|
getExchangeProxyMetaTransactionHash(mtx),
|
||||||
|
signer || mtx.signer,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getMetaTransactionHash()', () => {
|
||||||
|
it('generates the correct hash', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const expected = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const actual = await feature.getMetaTransactionHash(mtx).callAsync();
|
||||||
|
expect(actual).to.eq(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
interface TransformERC20Args {
|
||||||
|
inputToken: string;
|
||||||
|
outputToken: string;
|
||||||
|
inputTokenAmount: BigNumber;
|
||||||
|
minOutputTokenAmount: BigNumber;
|
||||||
|
transformations: Array<{ deploymentNonce: BigNumber; data: string }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomTransformERC20Args(fields: Partial<TransformERC20Args> = {}): TransformERC20Args {
|
||||||
|
return {
|
||||||
|
inputToken: randomAddress(),
|
||||||
|
outputToken: randomAddress(),
|
||||||
|
inputTokenAmount: getRandomInteger(1, '1e18'),
|
||||||
|
minOutputTokenAmount: getRandomInteger(1, '1e18'),
|
||||||
|
transformations: [{ deploymentNonce: new BigNumber(123), data: hexUtils.random() }],
|
||||||
|
...fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const RAW_SUCCESS_RESULT = hexUtils.leftPad(1337);
|
||||||
|
|
||||||
|
describe('executeMetaTransaction()', () => {
|
||||||
|
it('can call `TransformERC20.transformERC20()`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const rawResult = await feature.executeMetaTransaction(mtx, signature).callAsync(callOpts);
|
||||||
|
expect(rawResult).to.eq(RAW_SUCCESS_RESULT);
|
||||||
|
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
verifyEventsFromLogs(
|
||||||
|
receipt.logs,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
inputToken: args.inputToken,
|
||||||
|
outputToken: args.outputToken,
|
||||||
|
inputTokenAmount: args.inputTokenAmount,
|
||||||
|
minOutputTokenAmount: args.minOutputTokenAmount,
|
||||||
|
transformations: args.transformations,
|
||||||
|
sender: zeroEx.address,
|
||||||
|
value: mtx.value,
|
||||||
|
callDataHash: hexUtils.hash(mtx.callData),
|
||||||
|
taker: mtx.signer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
TestMetaTransactionsTransformERC20FeatureEvents.TransformERC20Called,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can call with any sender if `sender == 0`', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
sender: NULL_ADDRESS,
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
from: randomAddress(),
|
||||||
|
};
|
||||||
|
const rawResult = await feature.executeMetaTransaction(mtx, signature).callAsync(callOpts);
|
||||||
|
expect(rawResult).to.eq(RAW_SUCCESS_RESULT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works without fee', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
feeAmount: ZERO_AMOUNT,
|
||||||
|
feeToken: randomAddress(),
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const rawResult = await feature.executeMetaTransaction(mtx, signature).callAsync(callOpts);
|
||||||
|
expect(rawResult).to.eq(RAW_SUCCESS_RESULT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if the translated call fails', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
value: new BigNumber(666),
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).callAsync(callOpts);
|
||||||
|
const actualCallData = transformERC20Feature
|
||||||
|
._transformERC20(
|
||||||
|
hexUtils.hash(mtx.callData),
|
||||||
|
mtx.signer,
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||||
|
mtxHash,
|
||||||
|
actualCallData,
|
||||||
|
new StringRevertError('FAIL').encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails with unsupported function', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature.createTransformWallet().getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionUnsupportedFunctionError(
|
||||||
|
mtxHash,
|
||||||
|
hexUtils.slice(mtx.callData, 0, 4),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot execute the same mtx twice', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(
|
||||||
|
mtxHash,
|
||||||
|
receipt.blockNumber,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if not enough ETH provided', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value.minus(1),
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionInsufficientEthError(
|
||||||
|
mtxHash,
|
||||||
|
callOpts.value,
|
||||||
|
mtx.value,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if gas price too low', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice.minus(1),
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionGasPriceError(
|
||||||
|
mtxHash,
|
||||||
|
callOpts.gasPrice,
|
||||||
|
mtx.minGasPrice,
|
||||||
|
mtx.maxGasPrice,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if gas price too high', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice.plus(1),
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionGasPriceError(
|
||||||
|
mtxHash,
|
||||||
|
callOpts.gasPrice,
|
||||||
|
mtx.minGasPrice,
|
||||||
|
mtx.maxGasPrice,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if expired', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000 - 60)),
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionExpiredError(
|
||||||
|
mtxHash,
|
||||||
|
undefined,
|
||||||
|
mtx.expirationTimeSeconds,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if wrong sender', async () => {
|
||||||
|
const requiredSender = randomAddress();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
sender: requiredSender,
|
||||||
|
});
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionWrongSenderError(
|
||||||
|
mtxHash,
|
||||||
|
sender,
|
||||||
|
requiredSender,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fails if signature is wrong', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction({ signer: signers[0] });
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const signature = await signMetaTransactionAsync(mtx, signers[1]);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionInvalidSignatureError(
|
||||||
|
mtxHash,
|
||||||
|
signature,
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
|
||||||
|
mtxHash,
|
||||||
|
signers[0],
|
||||||
|
signature,
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('batchExecuteMetaTransactions()', () => {
|
||||||
|
it('can execute multiple transactions', async () => {
|
||||||
|
const mtxs = _.times(2, i => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
return getRandomMetaTransaction({
|
||||||
|
signer: signers[i],
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const signatures = await Promise.all(mtxs.map(async mtx => signMetaTransactionAsync(mtx)));
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
||||||
|
value: BigNumber.sum(...mtxs.map(mtx => mtx.value)),
|
||||||
|
};
|
||||||
|
const rawResults = await feature.batchExecuteMetaTransactions(mtxs, signatures).callAsync(callOpts);
|
||||||
|
expect(rawResults).to.eql(mtxs.map(() => RAW_SUCCESS_RESULT));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cannot execute the same transaction twice', async () => {
|
||||||
|
const mtx = (() => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
return getRandomMetaTransaction({
|
||||||
|
signer: _.sampleSize(signers, 1)[0],
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const mtxs = _.times(2, () => mtx);
|
||||||
|
const signatures = await Promise.all(mtxs.map(async m => signMetaTransactionAsync(m)));
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: BigNumber.max(...mtxs.map(m => m.minGasPrice)),
|
||||||
|
value: BigNumber.sum(...mtxs.map(m => m.value)),
|
||||||
|
};
|
||||||
|
const block = await env.web3Wrapper.getBlockNumberAsync();
|
||||||
|
const tx = feature.batchExecuteMetaTransactions(mtxs, signatures).callAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(mtxHash, block),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMetaTransactionExecutedBlock()', () => {
|
||||||
|
it('returns zero for an unexecuted mtx', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const block = await feature.getMetaTransactionExecutedBlock(mtx).callAsync();
|
||||||
|
expect(block).to.bignumber.eq(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the block it was executed in', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
const block = await feature.getMetaTransactionExecutedBlock(mtx).callAsync();
|
||||||
|
expect(block).to.bignumber.eq(receipt.blockNumber);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMetaTransactionHashExecutedBlock()', () => {
|
||||||
|
it('returns zero for an unexecuted mtx', async () => {
|
||||||
|
const mtx = getRandomMetaTransaction();
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
||||||
|
expect(block).to.bignumber.eq(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the block it was executed in', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
});
|
||||||
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.minGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
const receipt = await feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
|
const block = await feature.getMetaTransactionHashExecutedBlock(mtxHash).callAsync();
|
||||||
|
expect(block).to.bignumber.eq(receipt.blockNumber);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
231
contracts/zero-ex/test/features/signature_validator_test.ts
Normal file
231
contracts/zero-ex/test/features/signature_validator_test.ts
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
import { blockchainTests, constants, expect, randomAddress, signingUtils } from '@0x/contracts-test-utils';
|
||||||
|
import { signatureUtils } from '@0x/order-utils';
|
||||||
|
import { SignatureType } from '@0x/types';
|
||||||
|
import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
||||||
|
import * as ethjs from 'ethereumjs-util';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
import { SignatureValidatorContract, ZeroExContract } from '../../src/wrappers';
|
||||||
|
import { abis } from '../utils/abis';
|
||||||
|
import { fullMigrateAsync } from '../utils/migration';
|
||||||
|
|
||||||
|
const { NULL_BYTES } = constants;
|
||||||
|
|
||||||
|
blockchainTests.resets('SignatureValidator feature', env => {
|
||||||
|
let owner: string;
|
||||||
|
let signers: string[];
|
||||||
|
let zeroEx: ZeroExContract;
|
||||||
|
let feature: SignatureValidatorContract;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
[owner, ...signers] = await env.getAccountAddressesAsync();
|
||||||
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults);
|
||||||
|
feature = new SignatureValidatorContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('validateHashSignature()', () => {
|
||||||
|
it('can validate an eth_sign signature', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = await signatureUtils.ecSignHashAsync(env.provider, hash, signer);
|
||||||
|
await feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects a wrong eth_sign signature', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = await signatureUtils.ecSignHashAsync(env.provider, hash, signer);
|
||||||
|
const notSigner = randomAddress();
|
||||||
|
const tx = feature.validateHashSignature(hash, notSigner, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
|
||||||
|
hash,
|
||||||
|
notSigner,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an eth_sign if ecrecover() fails', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.concat(hexUtils.random(65), SignatureType.EthSign);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects a too short eth_sign signature', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 1);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.InvalidLength,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can validate an eip712 signature', async () => {
|
||||||
|
const privateKey = hexUtils.random();
|
||||||
|
const signer = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(privateKey)));
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signature = hexUtils.toHex(
|
||||||
|
signingUtils.signMessage(ethjs.toBuffer(hash), ethjs.toBuffer(privateKey), SignatureType.EIP712),
|
||||||
|
);
|
||||||
|
await feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects a wrong eip712 signature', async () => {
|
||||||
|
const privateKey = hexUtils.random();
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signature = hexUtils.toHex(
|
||||||
|
signingUtils.signMessage(ethjs.toBuffer(hash), ethjs.toBuffer(privateKey), SignatureType.EIP712),
|
||||||
|
);
|
||||||
|
const notSigner = randomAddress();
|
||||||
|
const tx = feature.validateHashSignature(hash, notSigner, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
|
||||||
|
hash,
|
||||||
|
notSigner,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an eip712 if ecrecover() fails', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.concat(hexUtils.random(65), SignatureType.EIP712);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.WrongSigner,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects a too short eip712 signature', async () => {
|
||||||
|
const privateKey = hexUtils.random();
|
||||||
|
const signer = hexUtils.toHex(ethjs.privateToAddress(ethjs.toBuffer(privateKey)));
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signature = hexUtils.slice(
|
||||||
|
hexUtils.toHex(
|
||||||
|
signingUtils.signMessage(ethjs.toBuffer(hash), ethjs.toBuffer(privateKey), SignatureType.EIP712),
|
||||||
|
),
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.InvalidLength,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an INVALID signature type', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.concat(
|
||||||
|
hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 0, -1),
|
||||||
|
SignatureType.Invalid,
|
||||||
|
);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.AlwaysInvalid,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an ILLEGAL signature type', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.concat(
|
||||||
|
hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 0, -1),
|
||||||
|
SignatureType.Illegal,
|
||||||
|
);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.Illegal,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an unsupported signature type', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = hexUtils.concat(
|
||||||
|
hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 0, -1),
|
||||||
|
SignatureType.Wallet,
|
||||||
|
);
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.Unsupported,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('rejects an empty signature type', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = NULL_BYTES;
|
||||||
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
|
return expect(tx).to.revertWith(
|
||||||
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.InvalidLength,
|
||||||
|
hash,
|
||||||
|
signer,
|
||||||
|
signature,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidHashSignature()', () => {
|
||||||
|
it('returns true on valid signature', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = await signatureUtils.ecSignHashAsync(env.provider, hash, signer);
|
||||||
|
const r = await feature.isValidHashSignature(hash, signer, signature).callAsync();
|
||||||
|
expect(r).to.eq(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns false on invalid signature', async () => {
|
||||||
|
const hash = hexUtils.random();
|
||||||
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
|
const signature = await signatureUtils.ecSignHashAsync(env.provider, hash, signer);
|
||||||
|
const r = await feature.isValidHashSignature(hash, randomAddress(), signature).callAsync();
|
||||||
|
expect(r).to.eq(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -22,12 +22,12 @@ blockchainTests.resets('TokenSpender feature', env => {
|
|||||||
before(async () => {
|
before(async () => {
|
||||||
const [owner] = await env.getAccountAddressesAsync();
|
const [owner] = await env.getAccountAddressesAsync();
|
||||||
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
zeroEx = await fullMigrateAsync(owner, env.provider, env.txDefaults, {
|
||||||
tokenSpender: await TokenSpenderContract.deployFrom0xArtifactAsync(
|
tokenSpender: (await TokenSpenderContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTokenSpender,
|
artifacts.TestTokenSpender,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
),
|
)).address,
|
||||||
});
|
});
|
||||||
feature = new TokenSpenderContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
feature = new TokenSpenderContract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||||
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
token = await TestTokenSpenderERC20TokenContract.deployFrom0xArtifactAsync(
|
||||||
|
@ -40,12 +40,12 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
|||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{
|
{
|
||||||
transformERC20: await TransformERC20Contract.deployFrom0xArtifactAsync(
|
transformERC20: (await TransformERC20Contract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestTransformERC20,
|
artifacts.TestTransformERC20,
|
||||||
env.provider,
|
env.provider,
|
||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
artifacts,
|
artifacts,
|
||||||
),
|
)).address,
|
||||||
},
|
},
|
||||||
{ transformerDeployer },
|
{ transformerDeployer },
|
||||||
);
|
);
|
||||||
|
@ -6,10 +6,12 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { abis } from './utils/abis';
|
import { abis } from './utils/abis';
|
||||||
import { deployFullFeaturesAsync, FullFeatures, toFeatureAdddresses } from './utils/migration';
|
import { deployFullFeaturesAsync, FullFeatures } from './utils/migration';
|
||||||
import {
|
import {
|
||||||
AllowanceTargetContract,
|
AllowanceTargetContract,
|
||||||
|
IMetaTransactionsContract,
|
||||||
IOwnableContract,
|
IOwnableContract,
|
||||||
|
ISignatureValidatorContract,
|
||||||
ITokenSpenderContract,
|
ITokenSpenderContract,
|
||||||
ITransformERC20Contract,
|
ITransformERC20Contract,
|
||||||
TestFullMigrationContract,
|
TestFullMigrationContract,
|
||||||
@ -27,7 +29,6 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
|
|
||||||
before(async () => {
|
before(async () => {
|
||||||
[owner] = await env.getAccountAddressesAsync();
|
[owner] = await env.getAccountAddressesAsync();
|
||||||
features = await deployFullFeaturesAsync(env.provider, env.txDefaults);
|
|
||||||
migrator = await TestFullMigrationContract.deployFrom0xArtifactAsync(
|
migrator = await TestFullMigrationContract.deployFrom0xArtifactAsync(
|
||||||
artifacts.TestFullMigration,
|
artifacts.TestFullMigration,
|
||||||
env.provider,
|
env.provider,
|
||||||
@ -35,9 +36,17 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
artifacts,
|
artifacts,
|
||||||
env.txDefaults.from as string,
|
env.txDefaults.from as string,
|
||||||
);
|
);
|
||||||
const deployCall = migrator.deploy(owner, toFeatureAdddresses(features), { transformerDeployer });
|
zeroEx = await ZeroExContract.deployFrom0xArtifactAsync(
|
||||||
zeroEx = new ZeroExContract(await deployCall.callAsync(), env.provider, env.txDefaults);
|
artifacts.ZeroEx,
|
||||||
await deployCall.awaitTransactionSuccessAsync();
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
await migrator.getBootstrapper().callAsync(),
|
||||||
|
);
|
||||||
|
features = await deployFullFeaturesAsync(env.provider, env.txDefaults, zeroEx.address);
|
||||||
|
await migrator
|
||||||
|
.initializeZeroEx(owner, zeroEx.address, features, { transformerDeployer })
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ZeroEx has the correct owner', async () => {
|
it('ZeroEx has the correct owner', async () => {
|
||||||
@ -51,10 +60,10 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
expect(dieRecipient).to.eq(owner);
|
expect(dieRecipient).to.eq(owner);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Non-deployer cannot call deploy()', async () => {
|
it('Non-deployer cannot call initializeZeroEx()', async () => {
|
||||||
const notDeployer = randomAddress();
|
const notDeployer = randomAddress();
|
||||||
const tx = migrator
|
const tx = migrator
|
||||||
.deploy(owner, toFeatureAdddresses(features), { transformerDeployer })
|
.initializeZeroEx(owner, zeroEx.address, features, { transformerDeployer })
|
||||||
.callAsync({ from: notDeployer });
|
.callAsync({ from: notDeployer });
|
||||||
return expect(tx).to.revertWith('FullMigration/INVALID_SENDER');
|
return expect(tx).to.revertWith('FullMigration/INVALID_SENDER');
|
||||||
});
|
});
|
||||||
@ -74,6 +83,21 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
'setTransformerDeployer',
|
'setTransformerDeployer',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
SignatureValidator: {
|
||||||
|
contractType: ISignatureValidatorContract,
|
||||||
|
fns: ['isValidHashSignature', 'validateHashSignature'],
|
||||||
|
},
|
||||||
|
MetaTransactions: {
|
||||||
|
contractType: IMetaTransactionsContract,
|
||||||
|
fns: [
|
||||||
|
'executeMetaTransaction',
|
||||||
|
'batchExecuteMetaTransactions',
|
||||||
|
'_executeMetaTransaction',
|
||||||
|
'getMetaTransactionExecutedBlock',
|
||||||
|
'getMetaTransactionHashExecutedBlock',
|
||||||
|
'getMetaTransactionHash',
|
||||||
|
],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createFakeInputs(inputs: DataItem[] | DataItem): any | any[] {
|
function createFakeInputs(inputs: DataItem[] | DataItem): any | any[] {
|
||||||
|
@ -2,7 +2,7 @@ import { blockchainTests, expect, randomAddress } from '@0x/contracts-test-utils
|
|||||||
import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
import { hexUtils, ZeroExRevertErrors } from '@0x/utils';
|
||||||
|
|
||||||
import { artifacts } from './artifacts';
|
import { artifacts } from './artifacts';
|
||||||
import { BootstrapFeatures, deployBootstrapFeaturesAsync, toFeatureAdddresses } from './utils/migration';
|
import { BootstrapFeatures, deployBootstrapFeaturesAsync } from './utils/migration';
|
||||||
import {
|
import {
|
||||||
IBootstrapContract,
|
IBootstrapContract,
|
||||||
InitialMigrationContract,
|
InitialMigrationContract,
|
||||||
@ -35,9 +35,14 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
env.txDefaults,
|
env.txDefaults,
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
const deployCall = migrator.deploy(owner, toFeatureAdddresses(features));
|
zeroEx = await ZeroExContract.deployFrom0xArtifactAsync(
|
||||||
zeroEx = new ZeroExContract(await deployCall.callAsync(), env.provider, env.txDefaults);
|
artifacts.ZeroEx,
|
||||||
await deployCall.awaitTransactionSuccessAsync();
|
env.provider,
|
||||||
|
env.txDefaults,
|
||||||
|
artifacts,
|
||||||
|
migrator.address,
|
||||||
|
);
|
||||||
|
await migrator.initializeZeroEx(owner, zeroEx.address, features).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Self-destructs after deployment', async () => {
|
it('Self-destructs after deployment', async () => {
|
||||||
@ -45,9 +50,9 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
expect(dieRecipient).to.eq(owner);
|
expect(dieRecipient).to.eq(owner);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Non-deployer cannot call deploy()', async () => {
|
it('Non-deployer cannot call initializeZeroEx()', async () => {
|
||||||
const notDeployer = randomAddress();
|
const notDeployer = randomAddress();
|
||||||
const tx = migrator.deploy(owner, toFeatureAdddresses(features)).callAsync({ from: notDeployer });
|
const tx = migrator.initializeZeroEx(owner, zeroEx.address, features).callAsync({ from: notDeployer });
|
||||||
return expect(tx).to.revertWith('InitialMigration/INVALID_SENDER');
|
return expect(tx).to.revertWith('InitialMigration/INVALID_SENDER');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ export {
|
|||||||
deployFullFeaturesAsync,
|
deployFullFeaturesAsync,
|
||||||
initialMigrateAsync,
|
initialMigrateAsync,
|
||||||
fullMigrateAsync,
|
fullMigrateAsync,
|
||||||
toFeatureAdddresses,
|
|
||||||
FullMigrationOpts,
|
FullMigrationOpts,
|
||||||
FullFeatures,
|
FullFeatures,
|
||||||
} from '../../src/migration';
|
} from '../../src/migration';
|
||||||
|
@ -8,6 +8,7 @@ export * from '../test/generated-wrappers/allowance_target';
|
|||||||
export * from '../test/generated-wrappers/bootstrap';
|
export * from '../test/generated-wrappers/bootstrap';
|
||||||
export * from '../test/generated-wrappers/fill_quote_transformer';
|
export * from '../test/generated-wrappers/fill_quote_transformer';
|
||||||
export * from '../test/generated-wrappers/fixin_common';
|
export * from '../test/generated-wrappers/fixin_common';
|
||||||
|
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
||||||
export * from '../test/generated-wrappers/fixin_gas_token';
|
export * from '../test/generated-wrappers/fixin_gas_token';
|
||||||
export * from '../test/generated-wrappers/flash_wallet';
|
export * from '../test/generated-wrappers/flash_wallet';
|
||||||
export * from '../test/generated-wrappers/full_migration';
|
export * from '../test/generated-wrappers/full_migration';
|
||||||
@ -19,7 +20,9 @@ export * from '../test/generated-wrappers/i_exchange';
|
|||||||
export * from '../test/generated-wrappers/i_feature';
|
export * from '../test/generated-wrappers/i_feature';
|
||||||
export * from '../test/generated-wrappers/i_flash_wallet';
|
export * from '../test/generated-wrappers/i_flash_wallet';
|
||||||
export * from '../test/generated-wrappers/i_gas_token';
|
export * from '../test/generated-wrappers/i_gas_token';
|
||||||
|
export * from '../test/generated-wrappers/i_meta_transactions';
|
||||||
export * from '../test/generated-wrappers/i_ownable';
|
export * from '../test/generated-wrappers/i_ownable';
|
||||||
|
export * from '../test/generated-wrappers/i_signature_validator';
|
||||||
export * from '../test/generated-wrappers/i_simple_function_registry';
|
export * from '../test/generated-wrappers/i_simple_function_registry';
|
||||||
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
export * from '../test/generated-wrappers/i_test_simple_function_registry_feature';
|
||||||
export * from '../test/generated-wrappers/i_token_spender';
|
export * from '../test/generated-wrappers/i_token_spender';
|
||||||
@ -28,11 +31,14 @@ export * from '../test/generated-wrappers/initial_migration';
|
|||||||
export * from '../test/generated-wrappers/lib_bootstrap';
|
export * from '../test/generated-wrappers/lib_bootstrap';
|
||||||
export * from '../test/generated-wrappers/lib_common_rich_errors';
|
export * from '../test/generated-wrappers/lib_common_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_erc20_transformer';
|
export * from '../test/generated-wrappers/lib_erc20_transformer';
|
||||||
|
export * from '../test/generated-wrappers/lib_meta_transactions_rich_errors';
|
||||||
|
export * from '../test/generated-wrappers/lib_meta_transactions_storage';
|
||||||
export * from '../test/generated-wrappers/lib_migrate';
|
export * from '../test/generated-wrappers/lib_migrate';
|
||||||
export * from '../test/generated-wrappers/lib_ownable_rich_errors';
|
export * from '../test/generated-wrappers/lib_ownable_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_ownable_storage';
|
export * from '../test/generated-wrappers/lib_ownable_storage';
|
||||||
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
export * from '../test/generated-wrappers/lib_proxy_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_proxy_storage';
|
export * from '../test/generated-wrappers/lib_proxy_storage';
|
||||||
|
export * from '../test/generated-wrappers/lib_signature_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
export * from '../test/generated-wrappers/lib_simple_function_registry_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_simple_function_registry_storage';
|
export * from '../test/generated-wrappers/lib_simple_function_registry_storage';
|
||||||
export * from '../test/generated-wrappers/lib_spender_rich_errors';
|
export * from '../test/generated-wrappers/lib_spender_rich_errors';
|
||||||
@ -41,8 +47,10 @@ export * from '../test/generated-wrappers/lib_token_spender_storage';
|
|||||||
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
|
export * from '../test/generated-wrappers/lib_transform_erc20_rich_errors';
|
||||||
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
|
export * from '../test/generated-wrappers/lib_transform_erc20_storage';
|
||||||
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
|
export * from '../test/generated-wrappers/lib_wallet_rich_errors';
|
||||||
|
export * from '../test/generated-wrappers/meta_transactions';
|
||||||
export * from '../test/generated-wrappers/ownable';
|
export * from '../test/generated-wrappers/ownable';
|
||||||
export * from '../test/generated-wrappers/pay_taker_transformer';
|
export * from '../test/generated-wrappers/pay_taker_transformer';
|
||||||
|
export * from '../test/generated-wrappers/signature_validator';
|
||||||
export * from '../test/generated-wrappers/simple_function_registry';
|
export * from '../test/generated-wrappers/simple_function_registry';
|
||||||
export * from '../test/generated-wrappers/test_call_target';
|
export * from '../test/generated-wrappers/test_call_target';
|
||||||
export * from '../test/generated-wrappers/test_delegate_caller';
|
export * from '../test/generated-wrappers/test_delegate_caller';
|
||||||
@ -51,6 +59,7 @@ export * from '../test/generated-wrappers/test_fill_quote_transformer_exchange';
|
|||||||
export * from '../test/generated-wrappers/test_fill_quote_transformer_host';
|
export * from '../test/generated-wrappers/test_fill_quote_transformer_host';
|
||||||
export * from '../test/generated-wrappers/test_full_migration';
|
export * from '../test/generated-wrappers/test_full_migration';
|
||||||
export * from '../test/generated-wrappers/test_initial_migration';
|
export * from '../test/generated-wrappers/test_initial_migration';
|
||||||
|
export * from '../test/generated-wrappers/test_meta_transactions_transform_erc20_feature';
|
||||||
export * from '../test/generated-wrappers/test_migrator';
|
export * from '../test/generated-wrappers/test_migrator';
|
||||||
export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';
|
export * from '../test/generated-wrappers/test_mint_token_erc20_transformer';
|
||||||
export * from '../test/generated-wrappers/test_mintable_erc20_token';
|
export * from '../test/generated-wrappers/test_mintable_erc20_token';
|
||||||
|
@ -14,8 +14,10 @@
|
|||||||
"generated-artifacts/ITokenSpender.json",
|
"generated-artifacts/ITokenSpender.json",
|
||||||
"generated-artifacts/ITransformERC20.json",
|
"generated-artifacts/ITransformERC20.json",
|
||||||
"generated-artifacts/InitialMigration.json",
|
"generated-artifacts/InitialMigration.json",
|
||||||
|
"generated-artifacts/MetaTransactions.json",
|
||||||
"generated-artifacts/Ownable.json",
|
"generated-artifacts/Ownable.json",
|
||||||
"generated-artifacts/PayTakerTransformer.json",
|
"generated-artifacts/PayTakerTransformer.json",
|
||||||
|
"generated-artifacts/SignatureValidator.json",
|
||||||
"generated-artifacts/SimpleFunctionRegistry.json",
|
"generated-artifacts/SimpleFunctionRegistry.json",
|
||||||
"generated-artifacts/TokenSpender.json",
|
"generated-artifacts/TokenSpender.json",
|
||||||
"generated-artifacts/TransformERC20.json",
|
"generated-artifacts/TransformERC20.json",
|
||||||
@ -26,6 +28,7 @@
|
|||||||
"test/generated-artifacts/Bootstrap.json",
|
"test/generated-artifacts/Bootstrap.json",
|
||||||
"test/generated-artifacts/FillQuoteTransformer.json",
|
"test/generated-artifacts/FillQuoteTransformer.json",
|
||||||
"test/generated-artifacts/FixinCommon.json",
|
"test/generated-artifacts/FixinCommon.json",
|
||||||
|
"test/generated-artifacts/FixinEIP712.json",
|
||||||
"test/generated-artifacts/FixinGasToken.json",
|
"test/generated-artifacts/FixinGasToken.json",
|
||||||
"test/generated-artifacts/FlashWallet.json",
|
"test/generated-artifacts/FlashWallet.json",
|
||||||
"test/generated-artifacts/FullMigration.json",
|
"test/generated-artifacts/FullMigration.json",
|
||||||
@ -37,7 +40,9 @@
|
|||||||
"test/generated-artifacts/IFeature.json",
|
"test/generated-artifacts/IFeature.json",
|
||||||
"test/generated-artifacts/IFlashWallet.json",
|
"test/generated-artifacts/IFlashWallet.json",
|
||||||
"test/generated-artifacts/IGasToken.json",
|
"test/generated-artifacts/IGasToken.json",
|
||||||
|
"test/generated-artifacts/IMetaTransactions.json",
|
||||||
"test/generated-artifacts/IOwnable.json",
|
"test/generated-artifacts/IOwnable.json",
|
||||||
|
"test/generated-artifacts/ISignatureValidator.json",
|
||||||
"test/generated-artifacts/ISimpleFunctionRegistry.json",
|
"test/generated-artifacts/ISimpleFunctionRegistry.json",
|
||||||
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
"test/generated-artifacts/ITestSimpleFunctionRegistryFeature.json",
|
||||||
"test/generated-artifacts/ITokenSpender.json",
|
"test/generated-artifacts/ITokenSpender.json",
|
||||||
@ -46,11 +51,14 @@
|
|||||||
"test/generated-artifacts/LibBootstrap.json",
|
"test/generated-artifacts/LibBootstrap.json",
|
||||||
"test/generated-artifacts/LibCommonRichErrors.json",
|
"test/generated-artifacts/LibCommonRichErrors.json",
|
||||||
"test/generated-artifacts/LibERC20Transformer.json",
|
"test/generated-artifacts/LibERC20Transformer.json",
|
||||||
|
"test/generated-artifacts/LibMetaTransactionsRichErrors.json",
|
||||||
|
"test/generated-artifacts/LibMetaTransactionsStorage.json",
|
||||||
"test/generated-artifacts/LibMigrate.json",
|
"test/generated-artifacts/LibMigrate.json",
|
||||||
"test/generated-artifacts/LibOwnableRichErrors.json",
|
"test/generated-artifacts/LibOwnableRichErrors.json",
|
||||||
"test/generated-artifacts/LibOwnableStorage.json",
|
"test/generated-artifacts/LibOwnableStorage.json",
|
||||||
"test/generated-artifacts/LibProxyRichErrors.json",
|
"test/generated-artifacts/LibProxyRichErrors.json",
|
||||||
"test/generated-artifacts/LibProxyStorage.json",
|
"test/generated-artifacts/LibProxyStorage.json",
|
||||||
|
"test/generated-artifacts/LibSignatureRichErrors.json",
|
||||||
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
"test/generated-artifacts/LibSimpleFunctionRegistryRichErrors.json",
|
||||||
"test/generated-artifacts/LibSimpleFunctionRegistryStorage.json",
|
"test/generated-artifacts/LibSimpleFunctionRegistryStorage.json",
|
||||||
"test/generated-artifacts/LibSpenderRichErrors.json",
|
"test/generated-artifacts/LibSpenderRichErrors.json",
|
||||||
@ -59,8 +67,10 @@
|
|||||||
"test/generated-artifacts/LibTransformERC20RichErrors.json",
|
"test/generated-artifacts/LibTransformERC20RichErrors.json",
|
||||||
"test/generated-artifacts/LibTransformERC20Storage.json",
|
"test/generated-artifacts/LibTransformERC20Storage.json",
|
||||||
"test/generated-artifacts/LibWalletRichErrors.json",
|
"test/generated-artifacts/LibWalletRichErrors.json",
|
||||||
|
"test/generated-artifacts/MetaTransactions.json",
|
||||||
"test/generated-artifacts/Ownable.json",
|
"test/generated-artifacts/Ownable.json",
|
||||||
"test/generated-artifacts/PayTakerTransformer.json",
|
"test/generated-artifacts/PayTakerTransformer.json",
|
||||||
|
"test/generated-artifacts/SignatureValidator.json",
|
||||||
"test/generated-artifacts/SimpleFunctionRegistry.json",
|
"test/generated-artifacts/SimpleFunctionRegistry.json",
|
||||||
"test/generated-artifacts/TestCallTarget.json",
|
"test/generated-artifacts/TestCallTarget.json",
|
||||||
"test/generated-artifacts/TestDelegateCaller.json",
|
"test/generated-artifacts/TestDelegateCaller.json",
|
||||||
@ -69,6 +79,7 @@
|
|||||||
"test/generated-artifacts/TestFillQuoteTransformerHost.json",
|
"test/generated-artifacts/TestFillQuoteTransformerHost.json",
|
||||||
"test/generated-artifacts/TestFullMigration.json",
|
"test/generated-artifacts/TestFullMigration.json",
|
||||||
"test/generated-artifacts/TestInitialMigration.json",
|
"test/generated-artifacts/TestInitialMigration.json",
|
||||||
|
"test/generated-artifacts/TestMetaTransactionsTransformERC20Feature.json",
|
||||||
"test/generated-artifacts/TestMigrator.json",
|
"test/generated-artifacts/TestMigrator.json",
|
||||||
"test/generated-artifacts/TestMintTokenERC20Transformer.json",
|
"test/generated-artifacts/TestMintTokenERC20Transformer.json",
|
||||||
"test/generated-artifacts/TestMintableERC20Token.json",
|
"test/generated-artifacts/TestMintableERC20Token.json",
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Export `GethCallOverrides` type",
|
"note": "Export `GethCallOverrides` type",
|
||||||
"pr": 2620
|
"pr": 2620
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Export `ExchangeProxyMetaTransaction` and `SignedExchangeProxyMetaTransaction`",
|
||||||
|
"pr": 2610
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -82,6 +82,8 @@ export {
|
|||||||
EventCallback,
|
EventCallback,
|
||||||
IndexedFilterValues,
|
IndexedFilterValues,
|
||||||
DecodedLogEvent,
|
DecodedLogEvent,
|
||||||
|
ExchangeProxyMetaTransaction,
|
||||||
|
SignedExchangeProxyMetaTransaction,
|
||||||
} from '@0x/types';
|
} from '@0x/types';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
@ -21,6 +21,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Redeploy DFB on kovan",
|
"note": "Redeploy DFB on kovan",
|
||||||
"pr": 2628
|
"pr": 2628
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Update ganache snapshot Exchange Proxy addresses for MetaTransactions",
|
||||||
|
"pr": 2610
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -218,15 +218,15 @@
|
|||||||
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
|
"dexForwarderBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"multiBridge": "0x0000000000000000000000000000000000000000",
|
"multiBridge": "0x0000000000000000000000000000000000000000",
|
||||||
"exchangeProxyGovernor": "0x0000000000000000000000000000000000000000",
|
"exchangeProxyGovernor": "0x0000000000000000000000000000000000000000",
|
||||||
"exchangeProxy": "0x4b8ce0fa221284de4aaa09be3e7bf6193444b786",
|
"exchangeProxy": "0x2ebb94cc79d7d0f1195300aaf191d118f53292a8",
|
||||||
"exchangeProxyAllowanceTarget": "0xd6724bf180441a89d08ea3aeded2c995180b9a04",
|
"exchangeProxyAllowanceTarget": "0x3eab3df72fd584b50184ff7d988a0d8f9328c866",
|
||||||
"exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631",
|
"exchangeProxyTransformerDeployer": "0x5409ed021d9299bf6814279a6a1411a7e866a631",
|
||||||
"exchangeProxyFlashWallet": "0xdec8629610e2f4087bd9cc441d10ca8be0c6f6c5",
|
"exchangeProxyFlashWallet": "0x8362c3ebd90041b30ec45908332e592721642637",
|
||||||
"transformers": {
|
"transformers": {
|
||||||
"wethTransformer": "0xb125995f5a4766c451cd8c34c4f5cac89b724571",
|
"wethTransformer": "0x7209185959d7227fb77274e1e88151d7c4c368d3",
|
||||||
"payTakerTransformer": "0x10a736a7b223f1fe1050264249d1abb975741e75",
|
"payTakerTransformer": "0xc6b0d3c45a6b5092808196cb00df5c357d55e1d5",
|
||||||
"fillQuoteTransformer": "0x33def1aa867be09809f3a01ce41d5ec1888846c9",
|
"fillQuoteTransformer": "0xc7124963ab16c33e5bf421d4c0090116622b3074",
|
||||||
"affiliateFeeTransformer": "0xc7124963ab16c33e5bf421d4c0090116622b3074"
|
"affiliateFeeTransformer": "0x3f16ca81691dab9184cb4606c361d73c4fd2510a"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"fix": "tslint --fix --format stylish --project .--exclude **/lib/**/*",
|
"fix": "tslint --fix --format stylish --project .--exclude **/lib/**/*",
|
||||||
"test:circleci": "run-s test:coverage",
|
"test:circleci": "run-s test:coverage",
|
||||||
"test": "yarn run_mocha",
|
"test": "yarn run_mocha",
|
||||||
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 20000 --bail --exit",
|
"run_mocha": "mocha --require source-map-support/register --require make-promises-safe lib/test/**/*_test.js lib/test/global_hooks.js --timeout 30000 --bail --exit",
|
||||||
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
"test:coverage": "nyc npm run test --all && yarn coverage:report:lcov",
|
||||||
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
"coverage:report:lcov": "nyc report --reporter=text-lcov > coverage/lcov.info",
|
||||||
"prettier": "prettier --write **/* --config ../../.prettierrc",
|
"prettier": "prettier --write **/* --config ../../.prettierrc",
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Add ERC20 Transformer utils and export useful constants.",
|
"note": "Add ERC20 Transformer utils and export useful constants.",
|
||||||
"pr": 2604
|
"pr": 2604
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add `getOrderHash()`, `getExchangeTransactionHash()`, `getExchangeProxyTransactionHash()`",
|
||||||
|
"pr": 2610
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@0x/assert": "^3.0.8",
|
"@0x/assert": "^3.0.8",
|
||||||
|
"@0x/contract-addresses": "^4.10.0",
|
||||||
"@0x/contract-wrappers": "^13.7.0",
|
"@0x/contract-wrappers": "^13.7.0",
|
||||||
"@0x/json-schemas": "^5.0.8",
|
"@0x/json-schemas": "^5.0.8",
|
||||||
"@0x/utils": "^5.5.0",
|
"@0x/utils": "^5.5.0",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { getContractAddressesForChainOrThrow } from '@0x/contract-addresses';
|
||||||
import { BigNumber, NULL_ADDRESS, NULL_BYTES } from '@0x/utils';
|
import { BigNumber, NULL_ADDRESS, NULL_BYTES } from '@0x/utils';
|
||||||
import { MethodAbi } from 'ethereum-types';
|
import { MethodAbi } from 'ethereum-types';
|
||||||
|
|
||||||
@ -150,6 +151,27 @@ export const constants = {
|
|||||||
{ name: 'transactionSignature', type: 'bytes' },
|
{ name: 'transactionSignature', type: 'bytes' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
MAINNET_EXCHANGE_PROXY_DOMAIN: {
|
||||||
|
name: 'ZeroEx',
|
||||||
|
version: '1.0.0',
|
||||||
|
chainId: 1,
|
||||||
|
verifyingContract: getContractAddressesForChainOrThrow(1).exchangeProxy,
|
||||||
|
},
|
||||||
|
EXCHANGE_PROXY_MTX_SCEHMA: {
|
||||||
|
name: 'MetaTransactionData',
|
||||||
|
parameters: [
|
||||||
|
{ name: 'signer', type: 'address' },
|
||||||
|
{ name: 'sender', type: 'address' },
|
||||||
|
{ name: 'minGasPrice', type: 'uint256' },
|
||||||
|
{ name: 'maxGasPrice', type: 'uint256' },
|
||||||
|
{ name: 'expirationTimeSeconds', type: 'uint256' },
|
||||||
|
{ name: 'salt', type: 'uint256' },
|
||||||
|
{ name: 'callData', type: 'bytes' },
|
||||||
|
{ name: 'value', type: 'uint256' },
|
||||||
|
{ name: 'feeToken', type: 'address' },
|
||||||
|
{ name: 'feeAmount', type: 'uint256' },
|
||||||
|
],
|
||||||
|
},
|
||||||
ERC20_METHOD_ABI,
|
ERC20_METHOD_ABI,
|
||||||
ERC721_METHOD_ABI,
|
ERC721_METHOD_ABI,
|
||||||
MULTI_ASSET_METHOD_ABI,
|
MULTI_ASSET_METHOD_ABI,
|
||||||
|
@ -5,11 +5,12 @@ import {
|
|||||||
EIP712Object,
|
EIP712Object,
|
||||||
EIP712TypedData,
|
EIP712TypedData,
|
||||||
EIP712Types,
|
EIP712Types,
|
||||||
|
ExchangeProxyMetaTransaction,
|
||||||
Order,
|
Order,
|
||||||
SignedZeroExTransaction,
|
SignedZeroExTransaction,
|
||||||
ZeroExTransaction,
|
ZeroExTransaction,
|
||||||
} from '@0x/types';
|
} from '@0x/types';
|
||||||
import { hexUtils, signTypedDataUtils } from '@0x/utils';
|
import { BigNumber, hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
@ -131,4 +132,21 @@ export const eip712Utils = {
|
|||||||
);
|
);
|
||||||
return typedData;
|
return typedData;
|
||||||
},
|
},
|
||||||
|
createExchangeProxyMetaTransactionTypedData(mtx: ExchangeProxyMetaTransaction): EIP712TypedData {
|
||||||
|
return eip712Utils.createTypedData(
|
||||||
|
constants.EXCHANGE_PROXY_MTX_SCEHMA.name,
|
||||||
|
{
|
||||||
|
MetaTransactionData: constants.EXCHANGE_PROXY_MTX_SCEHMA.parameters,
|
||||||
|
},
|
||||||
|
_.mapValues(
|
||||||
|
_.omit(mtx, 'domain'),
|
||||||
|
// tslint:disable-next-line: custom-no-magic-numbers
|
||||||
|
v => (BigNumber.isBigNumber(v) ? v.toString(10) : v),
|
||||||
|
) as EIP712Object,
|
||||||
|
{
|
||||||
|
...constants.MAINNET_EXCHANGE_PROXY_DOMAIN,
|
||||||
|
...mtx.domain,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
29
packages/order-utils/src/hash_utils.ts
Normal file
29
packages/order-utils/src/hash_utils.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ExchangeProxyMetaTransaction, Order, ZeroExTransaction } from '@0x/types';
|
||||||
|
import { hexUtils, signTypedDataUtils } from '@0x/utils';
|
||||||
|
|
||||||
|
import { eip712Utils } from './eip712_utils';
|
||||||
|
import { orderHashUtils } from './order_hash_utils';
|
||||||
|
import { transactionHashUtils } from './transaction_hash_utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the EIP712 hash of an order.
|
||||||
|
*/
|
||||||
|
export function getOrderHash(order: Order): string {
|
||||||
|
return orderHashUtils.getOrderHash(order);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the EIP712 hash of an Exchange meta-transaction.
|
||||||
|
*/
|
||||||
|
export function getExchangeMetaTransactionHash(tx: ZeroExTransaction): string {
|
||||||
|
return transactionHashUtils.getTransactionHash(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the EIP712 hash of an Exchange Proxy meta-transaction.
|
||||||
|
*/
|
||||||
|
export function getExchangeProxyMetaTransactionHash(mtx: ExchangeProxyMetaTransaction): string {
|
||||||
|
return hexUtils.toHex(
|
||||||
|
signTypedDataUtils.generateTypedDataHash(eip712Utils.createExchangeProxyMetaTransactionTypedData(mtx)),
|
||||||
|
);
|
||||||
|
}
|
@ -49,6 +49,8 @@ export {
|
|||||||
ZeroExTransaction,
|
ZeroExTransaction,
|
||||||
SignedZeroExTransaction,
|
SignedZeroExTransaction,
|
||||||
ValidatorSignature,
|
ValidatorSignature,
|
||||||
|
ExchangeProxyMetaTransaction,
|
||||||
|
SignedExchangeProxyMetaTransaction,
|
||||||
} from '@0x/types';
|
} from '@0x/types';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
@ -77,6 +79,8 @@ export {
|
|||||||
decodeAffiliateFeeTransformerData,
|
decodeAffiliateFeeTransformerData,
|
||||||
} from './transformer_data_encoders';
|
} from './transformer_data_encoders';
|
||||||
|
|
||||||
|
export { getOrderHash, getExchangeMetaTransactionHash, getExchangeProxyMetaTransactionHash } from './hash_utils';
|
||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
export const NULL_ADDRESS = constants.NULL_ADDRESS;
|
export const NULL_ADDRESS = constants.NULL_ADDRESS;
|
||||||
export const NULL_BYTES = constants.NULL_BYTES;
|
export const NULL_BYTES = constants.NULL_BYTES;
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { schemas } from '@0x/json-schemas';
|
import { schemas } from '@0x/json-schemas';
|
||||||
import {
|
import {
|
||||||
ECSignature,
|
ECSignature,
|
||||||
|
ExchangeProxyMetaTransaction,
|
||||||
Order,
|
Order,
|
||||||
SignatureType,
|
SignatureType,
|
||||||
|
SignedExchangeProxyMetaTransaction,
|
||||||
SignedOrder,
|
SignedOrder,
|
||||||
SignedZeroExTransaction,
|
SignedZeroExTransaction,
|
||||||
ValidatorSignature,
|
ValidatorSignature,
|
||||||
ZeroExTransaction,
|
ZeroExTransaction,
|
||||||
} from '@0x/types';
|
} from '@0x/types';
|
||||||
import { providerUtils } from '@0x/utils';
|
import { hexUtils, providerUtils } from '@0x/utils';
|
||||||
import { Web3Wrapper } from '@0x/web3-wrapper';
|
import { Web3Wrapper } from '@0x/web3-wrapper';
|
||||||
import { SupportedProvider } from 'ethereum-types';
|
import { SupportedProvider } from 'ethereum-types';
|
||||||
import * as ethUtil from 'ethereumjs-util';
|
import * as ethUtil from 'ethereumjs-util';
|
||||||
@ -16,6 +18,7 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { assert } from './assert';
|
import { assert } from './assert';
|
||||||
import { eip712Utils } from './eip712_utils';
|
import { eip712Utils } from './eip712_utils';
|
||||||
|
import { getExchangeProxyMetaTransactionHash } from './hash_utils';
|
||||||
import { orderHashUtils } from './order_hash_utils';
|
import { orderHashUtils } from './order_hash_utils';
|
||||||
import { transactionHashUtils } from './transaction_hash_utils';
|
import { transactionHashUtils } from './transaction_hash_utils';
|
||||||
import { TypedDataError } from './types';
|
import { TypedDataError } from './types';
|
||||||
@ -187,6 +190,96 @@ export const signatureUtils = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Signs an Exchange Proxy meta-transaction and returns a SignedExchangeProxyMetaTransaction.
|
||||||
|
* First `eth_signTypedData` is requested then a fallback to `eth_sign` if not
|
||||||
|
* available on the supplied provider.
|
||||||
|
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||||
|
* @param transaction The ExchangeProxyMetaTransaction to sign.
|
||||||
|
* @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
|
||||||
|
* must be available via the supplied Provider.
|
||||||
|
* @return A SignedExchangeProxyMetaTransaction containing the order and
|
||||||
|
* elliptic curve signature with Signature Type.
|
||||||
|
*/
|
||||||
|
async ecSignExchangeProxyMetaTransactionAsync(
|
||||||
|
supportedProvider: SupportedProvider,
|
||||||
|
transaction: ExchangeProxyMetaTransaction,
|
||||||
|
signerAddress: string,
|
||||||
|
): Promise<SignedExchangeProxyMetaTransaction> {
|
||||||
|
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||||
|
try {
|
||||||
|
const signedTransaction = await signatureUtils.ecSignTypedDataExchangeProxyMetaTransactionAsync(
|
||||||
|
supportedProvider,
|
||||||
|
transaction,
|
||||||
|
signerAddress,
|
||||||
|
);
|
||||||
|
return signedTransaction;
|
||||||
|
} catch (err) {
|
||||||
|
// HACK: We are unable to handle specific errors thrown since provider is not an object
|
||||||
|
// under our control. It could be Metamask Web3, Ethers, or any general RPC provider.
|
||||||
|
// We check for a user denying the signature request in a way that supports Metamask and
|
||||||
|
// Coinbase Wallet. Unfortunately for signers with a different error message,
|
||||||
|
// they will receive two signature requests.
|
||||||
|
if (err.message.includes('User denied message signature')) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
const transactionHash = getExchangeProxyMetaTransactionHash(transaction);
|
||||||
|
const signatureHex = await signatureUtils.ecSignHashAsync(
|
||||||
|
supportedProvider,
|
||||||
|
transactionHash,
|
||||||
|
signerAddress,
|
||||||
|
);
|
||||||
|
const signedTransaction = {
|
||||||
|
...transaction,
|
||||||
|
signature: signatureHex,
|
||||||
|
};
|
||||||
|
return signedTransaction;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Signs an Exchange Proxy meta-transaction using `eth_signTypedData` and
|
||||||
|
* returns a SignedZeroExTransaction.
|
||||||
|
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||||
|
* @param transaction The Exchange Proxy transaction to sign.
|
||||||
|
* @param signerAddress The hex encoded Ethereum address you wish
|
||||||
|
* to sign it with. This address must be available via the supplied Provider.
|
||||||
|
* @return A SignedExchangeProxyMetaTransaction containing the
|
||||||
|
* ExchangeProxyMetaTransaction and elliptic curve signature with Signature Type.
|
||||||
|
*/
|
||||||
|
async ecSignTypedDataExchangeProxyMetaTransactionAsync(
|
||||||
|
supportedProvider: SupportedProvider,
|
||||||
|
transaction: ExchangeProxyMetaTransaction,
|
||||||
|
signerAddress: string,
|
||||||
|
): Promise<SignedExchangeProxyMetaTransaction> {
|
||||||
|
const provider = providerUtils.standardizeOrThrow(supportedProvider);
|
||||||
|
assert.isETHAddressHex('signerAddress', signerAddress);
|
||||||
|
assert.doesConformToSchema('transaction', transaction, schemas.zeroExTransactionSchema, [schemas.hexSchema]);
|
||||||
|
const web3Wrapper = new Web3Wrapper(provider);
|
||||||
|
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
|
||||||
|
const normalizedSignerAddress = signerAddress.toLowerCase();
|
||||||
|
const typedData = eip712Utils.createExchangeProxyMetaTransactionTypedData(transaction);
|
||||||
|
try {
|
||||||
|
const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
|
||||||
|
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
|
||||||
|
const signatureHex = hexUtils.concat(
|
||||||
|
ecSignatureRSV.v,
|
||||||
|
ecSignatureRSV.r,
|
||||||
|
ecSignatureRSV.s,
|
||||||
|
SignatureType.EIP712,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
...transaction,
|
||||||
|
signature: signatureHex,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
// Detect if Metamask to transition users to the MetamaskSubprovider
|
||||||
|
if ((provider as any).isMetaMask) {
|
||||||
|
throw new Error(TypedDataError.InvalidMetamaskSigner);
|
||||||
|
} else {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type.
|
* Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type.
|
||||||
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
* @param supportedProvider Web3 provider to use for all JSON RPC requests
|
||||||
@ -245,12 +338,7 @@ export const signatureUtils = {
|
|||||||
* @return Hex encoded string of signature (v,r,s) with Signature Type
|
* @return Hex encoded string of signature (v,r,s) with Signature Type
|
||||||
*/
|
*/
|
||||||
convertECSignatureToSignatureHex(ecSignature: ECSignature): string {
|
convertECSignatureToSignatureHex(ecSignature: ECSignature): string {
|
||||||
const signatureBuffer = Buffer.concat([
|
const signatureHex = hexUtils.concat(ecSignature.v, ecSignature.r, ecSignature.s);
|
||||||
ethUtil.toBuffer(ecSignature.v),
|
|
||||||
ethUtil.toBuffer(ecSignature.r),
|
|
||||||
ethUtil.toBuffer(ecSignature.s),
|
|
||||||
]);
|
|
||||||
const signatureHex = `0x${signatureBuffer.toString('hex')}`;
|
|
||||||
const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign);
|
const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign);
|
||||||
return signatureWithType;
|
return signatureWithType;
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "3.2.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add `ExchangeProxyMetaTransaction` and `SignedExchangeProxyMetaTransaction`",
|
||||||
|
"pr": 2610
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"timestamp": 1592969527,
|
"timestamp": 1592969527,
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
|
@ -54,6 +54,30 @@ export interface SignedZeroExTransaction extends ZeroExTransaction {
|
|||||||
signature: string;
|
signature: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange Proxy meta transaction struct.
|
||||||
|
*/
|
||||||
|
export interface ExchangeProxyMetaTransaction {
|
||||||
|
signer: string;
|
||||||
|
sender: string;
|
||||||
|
minGasPrice: BigNumber;
|
||||||
|
maxGasPrice: BigNumber;
|
||||||
|
expirationTimeSeconds: BigNumber;
|
||||||
|
salt: BigNumber;
|
||||||
|
callData: string;
|
||||||
|
value: BigNumber;
|
||||||
|
feeToken: string;
|
||||||
|
feeAmount: BigNumber;
|
||||||
|
domain: EIP712DomainWithDefaultSchema;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `ExchangeProxyMetaTransaction` with `signature` field.
|
||||||
|
*/
|
||||||
|
export interface SignedExchangeProxyMetaTransaction extends ExchangeProxyMetaTransaction {
|
||||||
|
signature: string;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Elliptic Curve signature
|
* Elliptic Curve signature
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +29,10 @@
|
|||||||
{
|
{
|
||||||
"note": "Update `ZeroExRevertErrors`",
|
"note": "Update `ZeroExRevertErrors`",
|
||||||
"pr": 2597
|
"pr": 2597
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Add more revert errors to `ZeroExRevertErrors`",
|
||||||
|
"pr": 2610
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"timestamp": 1592969527
|
"timestamp": 1592969527
|
||||||
|
@ -52,4 +52,6 @@ export const ZeroExRevertErrors = {
|
|||||||
Spender: require('./revert_errors/zero-ex/spender_revert_errors'),
|
Spender: require('./revert_errors/zero-ex/spender_revert_errors'),
|
||||||
TransformERC20: require('./revert_errors/zero-ex/transform_erc20_revert_errors'),
|
TransformERC20: require('./revert_errors/zero-ex/transform_erc20_revert_errors'),
|
||||||
Wallet: require('./revert_errors/zero-ex/wallet_revert_errors'),
|
Wallet: require('./revert_errors/zero-ex/wallet_revert_errors'),
|
||||||
|
MetaTransactions: require('./revert_errors/zero-ex/meta_transaction_revert_errors'),
|
||||||
|
SignatureValidator: require('./revert_errors/zero-ex/signature_validator_revert_errors'),
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
import { RevertError } from '../../revert_error';
|
||||||
|
import { Numberish } from '../../types';
|
||||||
|
|
||||||
|
// tslint:disable:max-classes-per-file
|
||||||
|
export class InvalidMetaTransactionsArrayLengthsError extends RevertError {
|
||||||
|
constructor(mtxCount?: Numberish, signatureCount?: Numberish) {
|
||||||
|
super(
|
||||||
|
'InvalidMetaTransactionsArrayLengthsError',
|
||||||
|
'InvalidMetaTransactionsArrayLengthsError(uint256 mtxCount, uint256 signatureCount)',
|
||||||
|
{
|
||||||
|
mtxCount,
|
||||||
|
signatureCount,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionAlreadyExecutedError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, executedBlockNumber?: Numberish) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionAlreadyExecutedError',
|
||||||
|
'MetaTransactionAlreadyExecutedError(bytes32 mtxHash, uint256 executedBlockNumber)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
executedBlockNumber,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionUnsupportedFunctionError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, selector?: string) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionUnsupportedFunctionError',
|
||||||
|
'MetaTransactionUnsupportedFunctionError(bytes32 mtxHash, bytes4 selector)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
selector,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionWrongSenderError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, sender?: string, expectedSender?: string) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionWrongSenderError',
|
||||||
|
'MetaTransactionWrongSenderError(bytes32 mtxHash, address sender, address expectedSender)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
sender,
|
||||||
|
expectedSender,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionExpiredError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, time?: Numberish, expirationTime?: Numberish) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionExpiredError',
|
||||||
|
'MetaTransactionExpiredError(bytes32 mtxHash, uint256 time, uint256 expirationTime)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
time,
|
||||||
|
expirationTime,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionGasPriceError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, gasPrice?: Numberish, minGasPrice?: Numberish, maxGasPrice?: Numberish) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionGasPriceError',
|
||||||
|
'MetaTransactionGasPriceError(bytes32 mtxHash, uint256 gasPrice, uint256 minGasPrice, uint256 maxGasPrice)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
gasPrice,
|
||||||
|
minGasPrice,
|
||||||
|
maxGasPrice,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionInsufficientEthError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, ethBalance?: Numberish, ethRequired?: Numberish) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionInsufficientEthError',
|
||||||
|
'MetaTransactionInsufficientEthError(bytes32 mtxHash, uint256 ethBalance, uint256 ethRequired)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
ethBalance,
|
||||||
|
ethRequired,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionInvalidSignatureError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, signature?: string, errData?: string) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionInvalidSignatureError',
|
||||||
|
'MetaTransactionInvalidSignatureError(bytes32 mtxHash, bytes signature, bytes errData)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
signature,
|
||||||
|
errData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MetaTransactionCallFailedError extends RevertError {
|
||||||
|
constructor(mtxHash?: string, callData?: string, returnData?: string) {
|
||||||
|
super(
|
||||||
|
'MetaTransactionCallFailedError',
|
||||||
|
'MetaTransactionCallFailedError(bytes32 mtxHash, bytes callData, bytes returnData)',
|
||||||
|
{
|
||||||
|
mtxHash,
|
||||||
|
callData,
|
||||||
|
returnData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = [
|
||||||
|
InvalidMetaTransactionsArrayLengthsError,
|
||||||
|
MetaTransactionAlreadyExecutedError,
|
||||||
|
MetaTransactionUnsupportedFunctionError,
|
||||||
|
MetaTransactionWrongSenderError,
|
||||||
|
MetaTransactionExpiredError,
|
||||||
|
MetaTransactionGasPriceError,
|
||||||
|
MetaTransactionInsufficientEthError,
|
||||||
|
MetaTransactionInvalidSignatureError,
|
||||||
|
MetaTransactionCallFailedError,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Register the types we've defined.
|
||||||
|
for (const type of types) {
|
||||||
|
RevertError.registerType(type);
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
import { RevertError } from '../../revert_error';
|
||||||
|
|
||||||
|
// tslint:disable:max-classes-per-file
|
||||||
|
|
||||||
|
export enum SignatureValidationErrorCodes {
|
||||||
|
AlwaysInvalid = 0,
|
||||||
|
InvalidLength = 1,
|
||||||
|
Unsupported = 2,
|
||||||
|
Illegal = 3,
|
||||||
|
WrongSigner = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SignatureValidationError extends RevertError {
|
||||||
|
constructor(code?: SignatureValidationErrorCodes, hash?: string, signerAddress?: string, signature?: string) {
|
||||||
|
super(
|
||||||
|
'SignatureValidationError',
|
||||||
|
'SignatureValidationError(uint8 code, bytes32 hash, address signerAddress, bytes signature)',
|
||||||
|
{
|
||||||
|
code,
|
||||||
|
hash,
|
||||||
|
signerAddress,
|
||||||
|
signature,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const types = [SignatureValidationError];
|
||||||
|
|
||||||
|
// Register the types we've defined.
|
||||||
|
for (const type of types) {
|
||||||
|
RevertError.registerType(type);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user