@0x/contracts-zero-ex
: Prevent EP ETH balance from reducing when executin mtxs (#365)
Co-authored-by: Lawrence Forman <me@merklejerk.com>
This commit is contained in:
parent
9ff77c1cd5
commit
4b3d98f43c
@ -1,4 +1,13 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "0.29.4",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Prevent EP ETH balance from reducing when executin mtxs",
|
||||||
|
"pr": 365
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "0.29.3",
|
"version": "0.29.3",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -78,7 +78,7 @@ contract MetaTransactionsFeature is
|
|||||||
/// @dev Name of this feature.
|
/// @dev Name of this feature.
|
||||||
string public constant override FEATURE_NAME = "MetaTransactions";
|
string public constant override FEATURE_NAME = "MetaTransactions";
|
||||||
/// @dev Version of this feature.
|
/// @dev Version of this feature.
|
||||||
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 0);
|
uint256 public immutable override FEATURE_VERSION = _encodeVersion(1, 2, 1);
|
||||||
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
/// @dev EIP712 typehash of the `MetaTransactionData` struct.
|
||||||
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
|
bytes32 public immutable MTX_EIP712_TYPEHASH = keccak256(
|
||||||
"MetaTransactionData("
|
"MetaTransactionData("
|
||||||
@ -105,6 +105,17 @@ contract MetaTransactionsFeature is
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @dev Ensures that the ETH balance of `this` does not go below the
|
||||||
|
/// initial ETH balance before the call (excluding ETH attached to the call).
|
||||||
|
modifier doesNotReduceEthBalance() {
|
||||||
|
uint256 initialBalance = address(this).balance - msg.value;
|
||||||
|
_;
|
||||||
|
require(
|
||||||
|
initialBalance <= address(this).balance,
|
||||||
|
"MetaTransactionsFeature/ETH_LEAK"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(address zeroExAddress)
|
constructor(address zeroExAddress)
|
||||||
public
|
public
|
||||||
FixinCommon()
|
FixinCommon()
|
||||||
@ -140,6 +151,7 @@ contract MetaTransactionsFeature is
|
|||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
nonReentrant(REENTRANCY_MTX)
|
nonReentrant(REENTRANCY_MTX)
|
||||||
|
doesNotReduceEthBalance
|
||||||
refundsAttachedEth
|
refundsAttachedEth
|
||||||
returns (bytes memory returnResult)
|
returns (bytes memory returnResult)
|
||||||
{
|
{
|
||||||
@ -164,6 +176,7 @@ contract MetaTransactionsFeature is
|
|||||||
payable
|
payable
|
||||||
override
|
override
|
||||||
nonReentrant(REENTRANCY_MTX)
|
nonReentrant(REENTRANCY_MTX)
|
||||||
|
doesNotReduceEthBalance
|
||||||
refundsAttachedEth
|
refundsAttachedEth
|
||||||
returns (bytes[] memory returnResults)
|
returns (bytes[] memory returnResults)
|
||||||
{
|
{
|
||||||
|
@ -46,6 +46,10 @@ contract TestMetaTransactionsTransformERC20Feature is
|
|||||||
payable
|
payable
|
||||||
returns (uint256 outputTokenAmount)
|
returns (uint256 outputTokenAmount)
|
||||||
{
|
{
|
||||||
|
if (msg.value == 555) {
|
||||||
|
tx.origin.transfer(1);
|
||||||
|
}
|
||||||
|
|
||||||
if (msg.value == 666) {
|
if (msg.value == 666) {
|
||||||
revert('FAIL');
|
revert('FAIL');
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
let nativeOrdersFeature: TestMetaTransactionsNativeOrdersFeatureContract;
|
let nativeOrdersFeature: TestMetaTransactionsNativeOrdersFeatureContract;
|
||||||
|
|
||||||
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
||||||
|
const TRANSFORM_ERC20_ONE_WEI_VALUE = new BigNumber(555);
|
||||||
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
|
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
|
||||||
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
|
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
|
||||||
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
|
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
|
||||||
@ -597,7 +598,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cannot reenter `executeMetaTransaction()`', async () => {
|
it('cannot reduce initial ETH balance', async () => {
|
||||||
const args = getRandomTransformERC20Args();
|
const args = getRandomTransformERC20Args();
|
||||||
const mtx = getRandomMetaTransaction({
|
const mtx = getRandomMetaTransaction({
|
||||||
callData: transformERC20Feature
|
callData: transformERC20Feature
|
||||||
@ -609,58 +610,23 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
args.transformations,
|
args.transformations,
|
||||||
)
|
)
|
||||||
.getABIEncodedTransactionData(),
|
.getABIEncodedTransactionData(),
|
||||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
value: TRANSFORM_ERC20_ONE_WEI_VALUE,
|
||||||
});
|
});
|
||||||
const mtxHash = mtx.getHash();
|
|
||||||
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
const callOpts = {
|
const callOpts = {
|
||||||
gasPrice: mtx.maxGasPrice,
|
gasPrice: mtx.maxGasPrice,
|
||||||
value: mtx.value,
|
value: mtx.value,
|
||||||
};
|
};
|
||||||
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
// Send pre-existing ETH to the EP.
|
||||||
return expect(tx).to.revertWith(
|
await env.web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
await env.web3Wrapper.sendTransactionAsync({
|
||||||
mtxHash,
|
from: owner,
|
||||||
undefined,
|
to: zeroEx.address,
|
||||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
value: new BigNumber(1),
|
||||||
feature.getSelector('executeMetaTransaction'),
|
}),
|
||||||
REENTRANCY_FLAG_MTX,
|
|
||||||
).encode(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
it('cannot reenter `batchExecuteMetaTransactions()`', async () => {
|
|
||||||
const args = getRandomTransformERC20Args();
|
|
||||||
const mtx = getRandomMetaTransaction({
|
|
||||||
callData: transformERC20Feature
|
|
||||||
.transformERC20(
|
|
||||||
args.inputToken,
|
|
||||||
args.outputToken,
|
|
||||||
args.inputTokenAmount,
|
|
||||||
args.minOutputTokenAmount,
|
|
||||||
args.transformations,
|
|
||||||
)
|
|
||||||
.getABIEncodedTransactionData(),
|
|
||||||
value: TRANSFORM_ERC20_BATCH_REENTER_VALUE,
|
|
||||||
});
|
|
||||||
const mtxHash = mtx.getHash();
|
|
||||||
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
|
||||||
const callOpts = {
|
|
||||||
gasPrice: mtx.maxGasPrice,
|
|
||||||
value: mtx.value,
|
|
||||||
};
|
|
||||||
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
const tx = feature.executeMetaTransaction(mtx, signature).awaitTransactionSuccessAsync(callOpts);
|
||||||
return expect(tx).to.revertWith(
|
return expect(tx).to.revertWith('MetaTransactionsFeature/ETH_LEAK');
|
||||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
|
||||||
mtxHash,
|
|
||||||
undefined,
|
|
||||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
|
||||||
feature.getSelector('batchExecuteMetaTransactions'),
|
|
||||||
REENTRANCY_FLAG_MTX,
|
|
||||||
).encode(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -817,6 +783,37 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cannot reduce initial ETH balance', async () => {
|
||||||
|
const args = getRandomTransformERC20Args();
|
||||||
|
const mtx = getRandomMetaTransaction({
|
||||||
|
callData: transformERC20Feature
|
||||||
|
.transformERC20(
|
||||||
|
args.inputToken,
|
||||||
|
args.outputToken,
|
||||||
|
args.inputTokenAmount,
|
||||||
|
args.minOutputTokenAmount,
|
||||||
|
args.transformations,
|
||||||
|
)
|
||||||
|
.getABIEncodedTransactionData(),
|
||||||
|
value: TRANSFORM_ERC20_ONE_WEI_VALUE,
|
||||||
|
});
|
||||||
|
const signature = await mtx.getSignatureWithProviderAsync(env.provider);
|
||||||
|
const callOpts = {
|
||||||
|
gasPrice: mtx.maxGasPrice,
|
||||||
|
value: mtx.value,
|
||||||
|
};
|
||||||
|
// Send pre-existing ETH to the EP.
|
||||||
|
await env.web3Wrapper.awaitTransactionSuccessAsync(
|
||||||
|
await env.web3Wrapper.sendTransactionAsync({
|
||||||
|
from: owner,
|
||||||
|
to: zeroEx.address,
|
||||||
|
value: new BigNumber(1),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||||
|
return expect(tx).to.revertWith('MetaTransactionsFeature/ETH_LEAK');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('getMetaTransactionExecutedBlock()', () => {
|
describe('getMetaTransactionExecutedBlock()', () => {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user