@0x/contracts-zero-ex
: Address review feedback.
This commit is contained in:
parent
bdc1dbf08a
commit
e8106f04b5
@ -36,7 +36,7 @@ interface IMetaTransactions {
|
|||||||
// Maximum gas price.
|
// Maximum gas price.
|
||||||
uint256 maxGasPrice;
|
uint256 maxGasPrice;
|
||||||
// MTX is invalid after this time.
|
// MTX is invalid after this time.
|
||||||
uint256 expirationTime;
|
uint256 expirationTimeSeconds;
|
||||||
// Nonce to make this MTX unique.
|
// Nonce to make this MTX unique.
|
||||||
uint256 salt;
|
uint256 salt;
|
||||||
// Encoded call data to a function on the exchange proxy.
|
// Encoded call data to a function on the exchange proxy.
|
||||||
@ -65,33 +65,33 @@ interface IMetaTransactions {
|
|||||||
/// @dev Execute a single meta-transaction.
|
/// @dev Execute a single meta-transaction.
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
/// @param signature The signature by `mtx.signer`.
|
/// @param signature The signature by `mtx.signer`.
|
||||||
/// @return returnData The ABI-encoded result of the underlying call.
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
function executeMetaTransaction(
|
function executeMetaTransaction(
|
||||||
MetaTransactionData calldata mtx,
|
MetaTransactionData calldata mtx,
|
||||||
bytes calldata signature
|
bytes calldata signature
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
returns (bytes memory returnData);
|
returns (bytes memory returnResult);
|
||||||
|
|
||||||
/// @dev Execute multiple meta-transactions.
|
/// @dev Execute multiple meta-transactions.
|
||||||
/// @param mtxs The meta-transactions.
|
/// @param mtxs The meta-transactions.
|
||||||
/// @param signatures The signature by each respective `mtx.signer`.
|
/// @param signatures The signature by each respective `mtx.signer`.
|
||||||
/// @return returnDatas The ABI-encoded results of the underlying calls.
|
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||||
function executeMetaTransactions(
|
function batchExecuteMetaTransactions(
|
||||||
MetaTransactionData[] calldata mtxs,
|
MetaTransactionData[] calldata mtxs,
|
||||||
bytes[] calldata signatures
|
bytes[] calldata signatures
|
||||||
)
|
)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
returns (bytes[] memory returnDatas);
|
returns (bytes[] memory returnResults);
|
||||||
|
|
||||||
/// @dev Execute a meta-transaction via `sender`. Privileged variant.
|
/// @dev Execute a meta-transaction via `sender`. Privileged variant.
|
||||||
/// Only callable from within.
|
/// Only callable from within.
|
||||||
/// @param sender Who is executing the meta-transaction..
|
/// @param sender Who is executing the meta-transaction..
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
/// @param signature The signature by `mtx.signer`.
|
/// @param signature The signature by `mtx.signer`.
|
||||||
/// @return returnData The ABI-encoded result of the underlying call.
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
function _executeMetaTransaction(
|
function _executeMetaTransaction(
|
||||||
address sender,
|
address sender,
|
||||||
MetaTransactionData calldata mtx,
|
MetaTransactionData calldata mtx,
|
||||||
@ -99,7 +99,7 @@ interface IMetaTransactions {
|
|||||||
)
|
)
|
||||||
external
|
external
|
||||||
payable
|
payable
|
||||||
returns (bytes memory returnData);
|
returns (bytes memory returnResult);
|
||||||
|
|
||||||
/// @dev Get the block at which a meta-transaction has been executed.
|
/// @dev Get the block at which a meta-transaction has been executed.
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
|
@ -73,7 +73,7 @@ contract MetaTransactions is
|
|||||||
"address sender,"
|
"address sender,"
|
||||||
"uint256 minGasPrice,"
|
"uint256 minGasPrice,"
|
||||||
"uint256 maxGasPrice,"
|
"uint256 maxGasPrice,"
|
||||||
"uint256 expirationTime,"
|
"uint256 expirationTimeSeconds,"
|
||||||
"uint256 salt,"
|
"uint256 salt,"
|
||||||
"bytes callData,"
|
"bytes callData,"
|
||||||
"uint256 value,"
|
"uint256 value,"
|
||||||
@ -98,7 +98,7 @@ contract MetaTransactions is
|
|||||||
returns (bytes4 success)
|
returns (bytes4 success)
|
||||||
{
|
{
|
||||||
_registerFeatureFunction(this.executeMetaTransaction.selector);
|
_registerFeatureFunction(this.executeMetaTransaction.selector);
|
||||||
_registerFeatureFunction(this.executeMetaTransactions.selector);
|
_registerFeatureFunction(this.batchExecuteMetaTransactions.selector);
|
||||||
_registerFeatureFunction(this._executeMetaTransaction.selector);
|
_registerFeatureFunction(this._executeMetaTransaction.selector);
|
||||||
_registerFeatureFunction(this.getMetaTransactionExecutedBlock.selector);
|
_registerFeatureFunction(this.getMetaTransactionExecutedBlock.selector);
|
||||||
_registerFeatureFunction(this.getMetaTransactionHashExecutedBlock.selector);
|
_registerFeatureFunction(this.getMetaTransactionHashExecutedBlock.selector);
|
||||||
@ -109,7 +109,7 @@ contract MetaTransactions is
|
|||||||
/// @dev Execute a single meta-transaction.
|
/// @dev Execute a single meta-transaction.
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
/// @param signature The signature by `mtx.signer`.
|
/// @param signature The signature by `mtx.signer`.
|
||||||
/// @return returnData The ABI-encoded result of the underlying call.
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
function executeMetaTransaction(
|
function executeMetaTransaction(
|
||||||
MetaTransactionData memory mtx,
|
MetaTransactionData memory mtx,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
@ -117,7 +117,7 @@ contract MetaTransactions is
|
|||||||
public
|
public
|
||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
returns (bytes memory returnData)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
return _executeMetaTransactionPrivate(
|
return _executeMetaTransactionPrivate(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
@ -129,15 +129,15 @@ contract MetaTransactions is
|
|||||||
/// @dev Execute multiple meta-transactions.
|
/// @dev Execute multiple meta-transactions.
|
||||||
/// @param mtxs The meta-transactions.
|
/// @param mtxs The meta-transactions.
|
||||||
/// @param signatures The signature by each respective `mtx.signer`.
|
/// @param signatures The signature by each respective `mtx.signer`.
|
||||||
/// @return returnDatas The ABI-encoded results of the underlying calls.
|
/// @return returnResults The ABI-encoded results of the underlying calls.
|
||||||
function executeMetaTransactions(
|
function batchExecuteMetaTransactions(
|
||||||
MetaTransactionData[] memory mtxs,
|
MetaTransactionData[] memory mtxs,
|
||||||
bytes[] memory signatures
|
bytes[] memory signatures
|
||||||
)
|
)
|
||||||
public
|
public
|
||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
returns (bytes[] memory returnDatas)
|
returns (bytes[] memory returnResults)
|
||||||
{
|
{
|
||||||
if (mtxs.length != signatures.length) {
|
if (mtxs.length != signatures.length) {
|
||||||
LibMetaTransactionsRichErrors.InvalidMetaTransactionsArrayLengthsError(
|
LibMetaTransactionsRichErrors.InvalidMetaTransactionsArrayLengthsError(
|
||||||
@ -145,9 +145,9 @@ contract MetaTransactions is
|
|||||||
signatures.length
|
signatures.length
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
returnDatas = new bytes[](mtxs.length);
|
returnResults = new bytes[](mtxs.length);
|
||||||
for (uint256 i = 0; i < mtxs.length; ++i) {
|
for (uint256 i = 0; i < mtxs.length; ++i) {
|
||||||
returnDatas[i] = _executeMetaTransactionPrivate(
|
returnResults[i] = _executeMetaTransactionPrivate(
|
||||||
msg.sender,
|
msg.sender,
|
||||||
mtxs[i],
|
mtxs[i],
|
||||||
signatures[i]
|
signatures[i]
|
||||||
@ -160,7 +160,7 @@ contract MetaTransactions is
|
|||||||
/// @param sender Who is executing the meta-transaction..
|
/// @param sender Who is executing the meta-transaction..
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
/// @param signature The signature by `mtx.signer`.
|
/// @param signature The signature by `mtx.signer`.
|
||||||
/// @return returnData The ABI-encoded result of the underlying call.
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
function _executeMetaTransaction(
|
function _executeMetaTransaction(
|
||||||
address sender,
|
address sender,
|
||||||
MetaTransactionData memory mtx,
|
MetaTransactionData memory mtx,
|
||||||
@ -170,7 +170,7 @@ contract MetaTransactions is
|
|||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
onlySelf
|
onlySelf
|
||||||
returns (bytes memory returnData)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
return _executeMetaTransactionPrivate(sender, mtx, signature);
|
return _executeMetaTransactionPrivate(sender, mtx, signature);
|
||||||
}
|
}
|
||||||
@ -214,7 +214,7 @@ contract MetaTransactions is
|
|||||||
mtx.sender,
|
mtx.sender,
|
||||||
mtx.minGasPrice,
|
mtx.minGasPrice,
|
||||||
mtx.maxGasPrice,
|
mtx.maxGasPrice,
|
||||||
mtx.expirationTime,
|
mtx.expirationTimeSeconds,
|
||||||
mtx.salt,
|
mtx.salt,
|
||||||
keccak256(mtx.callData),
|
keccak256(mtx.callData),
|
||||||
mtx.value,
|
mtx.value,
|
||||||
@ -227,14 +227,14 @@ contract MetaTransactions is
|
|||||||
/// @param sender Who is executing the meta-transaction..
|
/// @param sender Who is executing the meta-transaction..
|
||||||
/// @param mtx The meta-transaction.
|
/// @param mtx The meta-transaction.
|
||||||
/// @param signature The signature by `mtx.signer`.
|
/// @param signature The signature by `mtx.signer`.
|
||||||
/// @return returnData The ABI-encoded result of the underlying call.
|
/// @return returnResult The ABI-encoded result of the underlying call.
|
||||||
function _executeMetaTransactionPrivate(
|
function _executeMetaTransactionPrivate(
|
||||||
address sender,
|
address sender,
|
||||||
MetaTransactionData memory mtx,
|
MetaTransactionData memory mtx,
|
||||||
bytes memory signature
|
bytes memory signature
|
||||||
)
|
)
|
||||||
private
|
private
|
||||||
returns (bytes memory returnData)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
ExecuteState memory state;
|
ExecuteState memory state;
|
||||||
state.sender = sender;
|
state.sender = sender;
|
||||||
@ -252,7 +252,7 @@ contract MetaTransactions is
|
|||||||
// Execute the call based on the selector.
|
// Execute the call based on the selector.
|
||||||
state.selector = mtx.callData.readBytes4(0);
|
state.selector = mtx.callData.readBytes4(0);
|
||||||
if (state.selector == ITransformERC20.transformERC20.selector) {
|
if (state.selector == ITransformERC20.transformERC20.selector) {
|
||||||
returnData = _executeTransformERC20Call(state);
|
returnResult = _executeTransformERC20Call(state);
|
||||||
} else {
|
} else {
|
||||||
LibMetaTransactionsRichErrors
|
LibMetaTransactionsRichErrors
|
||||||
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
.MetaTransactionUnsupportedFunctionError(state.hash, state.selector)
|
||||||
@ -290,12 +290,12 @@ contract MetaTransactions is
|
|||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
// Must not be expired.
|
// Must not be expired.
|
||||||
if (state.mtx.expirationTime <= block.timestamp) {
|
if (state.mtx.expirationTimeSeconds <= block.timestamp) {
|
||||||
LibMetaTransactionsRichErrors
|
LibMetaTransactionsRichErrors
|
||||||
.MetaTransactionExpiredError(
|
.MetaTransactionExpiredError(
|
||||||
state.hash,
|
state.hash,
|
||||||
block.timestamp,
|
block.timestamp,
|
||||||
state.mtx.expirationTime
|
state.mtx.expirationTimeSeconds
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
// Must have a valid gas price.
|
// Must have a valid gas price.
|
||||||
@ -349,12 +349,36 @@ contract MetaTransactions is
|
|||||||
/// the taker address.
|
/// the taker address.
|
||||||
function _executeTransformERC20Call(ExecuteState memory state)
|
function _executeTransformERC20Call(ExecuteState memory state)
|
||||||
private
|
private
|
||||||
returns (bytes memory returnData)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
// HACK(dorothy-zbornak): `abi.decode()` with the individual args
|
// HACK(dorothy-zbornak): `abi.decode()` with the individual args
|
||||||
// will cause a stack overflow. But we can prefix the call data with an
|
// 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.
|
// offset to transform it into the encoding for the equivalent single struct arg,
|
||||||
// Decoding a single struct consumes far less stack space.
|
// 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;
|
TransformERC20Args memory args;
|
||||||
{
|
{
|
||||||
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
bytes memory encodedStructArgs = new bytes(state.mtx.callData.length - 4 + 32);
|
||||||
@ -397,15 +421,15 @@ contract MetaTransactions is
|
|||||||
/// Warning: Do not let unadulerated `callData` into this function.
|
/// Warning: Do not let unadulerated `callData` into this function.
|
||||||
function _callSelf(bytes32 hash, bytes memory callData, uint256 value)
|
function _callSelf(bytes32 hash, bytes memory callData, uint256 value)
|
||||||
private
|
private
|
||||||
returns (bytes memory returnData)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
bool success;
|
bool success;
|
||||||
(success, returnData) = address(this).call{value: value}(callData);
|
(success, returnResult) = address(this).call{value: value}(callData);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(
|
LibMetaTransactionsRichErrors.MetaTransactionCallFailedError(
|
||||||
hash,
|
hash,
|
||||||
callData,
|
callData,
|
||||||
returnData
|
returnResult
|
||||||
).rrevert();
|
).rrevert();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ contract SimpleFunctionRegistry is
|
|||||||
ISimpleFunctionRegistry,
|
ISimpleFunctionRegistry,
|
||||||
FixinCommon
|
FixinCommon
|
||||||
{
|
{
|
||||||
// solhint-disable
|
/// @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);
|
||||||
|
@ -49,20 +49,20 @@ 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ contract FullMigration {
|
|||||||
return address(_initialMigration);
|
return address(_initialMigration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @dev Deploy the `ZeroEx` contract with the full feature set,
|
/// @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
|
/// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
|
||||||
@ -84,7 +84,7 @@ contract FullMigration {
|
|||||||
/// @param features Features to add to the proxy.
|
/// @param features Features to add to the proxy.
|
||||||
/// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
|
/// @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,
|
ZeroEx zeroEx,
|
||||||
Features memory features,
|
Features memory features,
|
||||||
@ -93,10 +93,10 @@ contract FullMigration {
|
|||||||
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.
|
||||||
_initialMigration.deploy(
|
_initialMigration.initializeZeroEx(
|
||||||
address(uint160(address(this))),
|
address(uint160(address(this))),
|
||||||
zeroEx,
|
zeroEx,
|
||||||
InitialMigration.BootstrapFeatures({
|
InitialMigration.BootstrapFeatures({
|
||||||
@ -117,7 +117,7 @@ contract FullMigration {
|
|||||||
return zeroEx;
|
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
|
||||||
|
@ -35,29 +35,29 @@ 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
|
/// @param zeroEx The instance of the ZeroEx contract. ZeroEx should
|
||||||
/// been constructed with this contract as the bootstrapper.
|
/// 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 configured ZeroEx contract. Same as the `zeroEx` parameter.
|
/// @return _zeroEx The configured ZeroEx contract. Same as the `zeroEx` parameter.
|
||||||
function deploy(
|
function initializeZeroEx(
|
||||||
address payable owner,
|
address payable owner,
|
||||||
ZeroEx zeroEx,
|
ZeroEx zeroEx,
|
||||||
BootstrapFeatures memory features
|
BootstrapFeatures memory features
|
||||||
@ -66,8 +66,8 @@ contract InitialMigration {
|
|||||||
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");
|
||||||
|
|
||||||
// Bootstrap the initial feature set.
|
// Bootstrap the initial feature set.
|
||||||
IBootstrap(address(zeroEx)).bootstrap(
|
IBootstrap(address(zeroEx)).bootstrap(
|
||||||
|
@ -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,AffiliateFeeTransformerSignatureValidator,MetaTransactions",
|
"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|FixinEIP712|FlashWallet|FullMigration|IAllowanceTarget|IBootstrap|IERC20Transformer|IExchange|IFeature|IFlashWallet|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|TestFillQuoteTransformerExchange|TestFillQuoteTransformerHost|TestFullMigration|TestInitialMigration|TestMetaTransactionsTransformERC20Feature|TestMigrator|TestMintTokenERC20Transformer|TestMintableERC20Token|TestSimpleFunctionRegistryFeatureImpl1|TestSimpleFunctionRegistryFeatureImpl2|TestTokenSpender|TestTokenSpenderERC20Token|TestTransformERC20|TestTransformerBase|TestTransformerDeployerTransformer|TestTransformerHost|TestWeth|TestWethTransformerHost|TestZeroExFeature|TokenSpender|TransformERC20|Transformer|TransformerDeployer|WethTransformer|ZeroEx).json"
|
"abis": "./test/generated-artifacts/@(AffiliateFeeTransformer|AllowanceTarget|Bootstrap|FillQuoteTransformer|FixinCommon|FixinEIP712|FixinGasToken|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",
|
||||||
|
@ -73,7 +73,7 @@ export async function initialMigrateAsync(
|
|||||||
migrator.address,
|
migrator.address,
|
||||||
);
|
);
|
||||||
const _features = await deployBootstrapFeaturesAsync(provider, txDefaults, features);
|
const _features = await deployBootstrapFeaturesAsync(provider, txDefaults, features);
|
||||||
await migrator.deploy(owner, zeroEx.address, _features).awaitTransactionSuccessAsync();
|
await migrator.initializeZeroEx(owner, zeroEx.address, _features).awaitTransactionSuccessAsync();
|
||||||
return zeroEx;
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +170,6 @@ export async function fullMigrateAsync(
|
|||||||
transformerDeployer: txDefaults.from as string,
|
transformerDeployer: txDefaults.from as string,
|
||||||
...opts,
|
...opts,
|
||||||
};
|
};
|
||||||
await migrator.deploy(owner, zeroEx.address, _features, _opts).awaitTransactionSuccessAsync();
|
await migrator.initializeZeroEx(owner, zeroEx.address, _features, _opts).awaitTransactionSuccessAsync();
|
||||||
return zeroEx;
|
return zeroEx;
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,8 @@ 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 FixinGasToken from '../test/generated-artifacts/FixinGasToken.json';
|
|
||||||
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.json';
|
import * as FixinEIP712 from '../test/generated-artifacts/FixinEIP712.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';
|
||||||
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
import * as IAllowanceTarget from '../test/generated-artifacts/IAllowanceTarget.json';
|
||||||
@ -114,8 +114,8 @@ export const artifacts = {
|
|||||||
TokenSpender: TokenSpender as ContractArtifact,
|
TokenSpender: TokenSpender as ContractArtifact,
|
||||||
TransformERC20: TransformERC20 as ContractArtifact,
|
TransformERC20: TransformERC20 as ContractArtifact,
|
||||||
FixinCommon: FixinCommon as ContractArtifact,
|
FixinCommon: FixinCommon as ContractArtifact,
|
||||||
FixinGasToken: FixinGasToken as ContractArtifact,
|
|
||||||
FixinEIP712: FixinEIP712 as ContractArtifact,
|
FixinEIP712: FixinEIP712 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,
|
||||||
|
@ -74,7 +74,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
sender,
|
sender,
|
||||||
minGasPrice: getRandomInteger('2', '1e9'),
|
minGasPrice: getRandomInteger('2', '1e9'),
|
||||||
maxGasPrice: getRandomInteger('1e9', '100e9'),
|
maxGasPrice: getRandomInteger('1e9', '100e9'),
|
||||||
expirationTime: new BigNumber(Math.floor(_.now() / 1000) + 360),
|
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000) + 360),
|
||||||
salt: new BigNumber(hexUtils.random()),
|
salt: new BigNumber(hexUtils.random()),
|
||||||
callData: hexUtils.random(4),
|
callData: hexUtils.random(4),
|
||||||
value: getRandomInteger(1, '1e18'),
|
value: getRandomInteger(1, '1e18'),
|
||||||
@ -362,7 +362,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
|
|
||||||
it('fails if expired', async () => {
|
it('fails if expired', async () => {
|
||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
expirationTime: new BigNumber(Math.floor(_.now() / 1000 - 60)),
|
expirationTimeSeconds: new BigNumber(Math.floor(_.now() / 1000 - 60)),
|
||||||
});
|
});
|
||||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||||
const signature = await signMetaTransactionAsync(mtx);
|
const signature = await signMetaTransactionAsync(mtx);
|
||||||
@ -375,7 +375,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionExpiredError(
|
new ZeroExRevertErrors.MetaTransactions.MetaTransactionExpiredError(
|
||||||
mtxHash,
|
mtxHash,
|
||||||
undefined,
|
undefined,
|
||||||
mtx.expirationTime,
|
mtx.expirationTimeSeconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -425,7 +425,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('executeMetaTransactions()', () => {
|
describe('batchExecuteMetaTransactions()', () => {
|
||||||
it('can execute multiple transactions', async () => {
|
it('can execute multiple transactions', async () => {
|
||||||
const mtxs = _.times(2, i => {
|
const mtxs = _.times(2, i => {
|
||||||
const args = getRandomTransformERC20Args();
|
const args = getRandomTransformERC20Args();
|
||||||
@ -447,9 +447,39 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
||||||
value: BigNumber.sum(...mtxs.map(mtx => mtx.value)),
|
value: BigNumber.sum(...mtxs.map(mtx => mtx.value)),
|
||||||
};
|
};
|
||||||
const rawResults = await feature.executeMetaTransactions(mtxs, signatures).callAsync(callOpts);
|
const rawResults = await feature.batchExecuteMetaTransactions(mtxs, signatures).callAsync(callOpts);
|
||||||
expect(rawResults).to.eql(mtxs.map(() => RAW_SUCCESS_RESULT));
|
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 mtx => signMetaTransactionAsync(mtx)));
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: BigNumber.max(...mtxs.map(mtx => mtx.minGasPrice)),
|
||||||
|
value: BigNumber.sum(...mtxs.map(mtx => mtx.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()', () => {
|
describe('getMetaTransactionExecutedBlock()', () => {
|
||||||
|
@ -66,13 +66,12 @@ blockchainTests.resets('SignatureValidator feature', env => {
|
|||||||
const hash = hexUtils.random();
|
const hash = hexUtils.random();
|
||||||
const signer = _.sampleSize(signers, 1)[0];
|
const signer = _.sampleSize(signers, 1)[0];
|
||||||
const signature = hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 1);
|
const signature = hexUtils.slice(await signatureUtils.ecSignHashAsync(env.provider, hash, signer), 1);
|
||||||
const notSigner = randomAddress();
|
const tx = feature.validateHashSignature(hash, signer, signature).callAsync();
|
||||||
const tx = feature.validateHashSignature(hash, notSigner, signature).callAsync();
|
|
||||||
return expect(tx).to.revertWith(
|
return expect(tx).to.revertWith(
|
||||||
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
new ZeroExRevertErrors.SignatureValidator.SignatureValidationError(
|
||||||
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.InvalidLength,
|
ZeroExRevertErrors.SignatureValidator.SignatureValidationErrorCodes.InvalidLength,
|
||||||
hash,
|
hash,
|
||||||
notSigner,
|
signer,
|
||||||
signature,
|
signature,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -44,7 +44,9 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
await migrator.getBootstrapper().callAsync(),
|
await migrator.getBootstrapper().callAsync(),
|
||||||
);
|
);
|
||||||
features = await deployFullFeaturesAsync(env.provider, env.txDefaults, zeroEx.address);
|
features = await deployFullFeaturesAsync(env.provider, env.txDefaults, zeroEx.address);
|
||||||
await migrator.deploy(owner, zeroEx.address, features, { transformerDeployer }).awaitTransactionSuccessAsync();
|
await migrator
|
||||||
|
.initializeZeroEx(owner, zeroEx.address, features, { transformerDeployer })
|
||||||
|
.awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ZeroEx has the correct owner', async () => {
|
it('ZeroEx has the correct owner', async () => {
|
||||||
@ -58,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, zeroEx.address, 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');
|
||||||
});
|
});
|
||||||
@ -89,7 +91,7 @@ blockchainTests.resets('Full migration', env => {
|
|||||||
contractType: IMetaTransactionsContract,
|
contractType: IMetaTransactionsContract,
|
||||||
fns: [
|
fns: [
|
||||||
'executeMetaTransaction',
|
'executeMetaTransaction',
|
||||||
'executeMetaTransactions',
|
'batchExecuteMetaTransactions',
|
||||||
'_executeMetaTransaction',
|
'_executeMetaTransaction',
|
||||||
'getMetaTransactionExecutedBlock',
|
'getMetaTransactionExecutedBlock',
|
||||||
'getMetaTransactionHashExecutedBlock',
|
'getMetaTransactionHashExecutedBlock',
|
||||||
|
@ -42,7 +42,7 @@ blockchainTests.resets('Initial migration', env => {
|
|||||||
artifacts,
|
artifacts,
|
||||||
migrator.address,
|
migrator.address,
|
||||||
);
|
);
|
||||||
await migrator.deploy(owner, zeroEx.address, features).awaitTransactionSuccessAsync();
|
await migrator.initializeZeroEx(owner, zeroEx.address, features).awaitTransactionSuccessAsync();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Self-destructs after deployment', async () => {
|
it('Self-destructs after deployment', async () => {
|
||||||
@ -50,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, zeroEx.address, 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');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,8 +8,8 @@ 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_gas_token';
|
|
||||||
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
export * from '../test/generated-wrappers/fixin_e_i_p712';
|
||||||
|
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';
|
||||||
export * from '../test/generated-wrappers/i_allowance_target';
|
export * from '../test/generated-wrappers/i_allowance_target';
|
||||||
|
@ -28,8 +28,8 @@
|
|||||||
"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/FixinGasToken.json",
|
|
||||||
"test/generated-artifacts/FixinEIP712.json",
|
"test/generated-artifacts/FixinEIP712.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",
|
||||||
"test/generated-artifacts/IAllowanceTarget.json",
|
"test/generated-artifacts/IAllowanceTarget.json",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user