@0x/contracts-zero-ex
: Add reentrancy guard to mtx functions
`@0x/contracts-zero-ex`: Add refund mechanism to mtxs `@0x/contracts-zero-ex`: Pass sender to transfomers. `@0x/contracts-zero-ex`: Refund protocol fees to `refundReceiver` in FQT. `@0x/utils`: Add EP flavor of `IllegalReentrancyError` `@0x/order-utils`: Add `refundReceiver` to FQT transform data. `@0x/asset-swapper`: Add `refundReceiver` support to EP swap quote consumer.
This commit is contained in:
committed by
Lawrence Forman
parent
7b0a1c3630
commit
9cda9f69cd
@@ -36,6 +36,10 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
let allowanceTarget: string;
|
||||
|
||||
const MAX_FEE_AMOUNT = new BigNumber('1e18');
|
||||
const TRANSFORM_ERC20_FAILING_VALUE = new BigNumber(666);
|
||||
const TRANSFORM_ERC20_REENTER_VALUE = new BigNumber(777);
|
||||
const TRANSFORM_ERC20_BATCH_REENTER_VALUE = new BigNumber(888);
|
||||
const REENTRANCY_FLAG_MTX = 0x1;
|
||||
|
||||
before(async () => {
|
||||
[owner, sender, ...signers] = await env.getAccountAddressesAsync();
|
||||
@@ -263,7 +267,7 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
it('fails if the translated call fails', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
value: new BigNumber(666),
|
||||
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
@@ -469,6 +473,72 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||
});
|
||||
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.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
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 = 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.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('batchExecuteMetaTransactions'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('batchExecuteMetaTransactions()', () => {
|
||||
@@ -526,6 +596,102 @@ blockchainTests.resets('MetaTransactions feature', env => {
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionAlreadyExecutedError(mtxHash, block),
|
||||
);
|
||||
});
|
||||
|
||||
it('fails if a meta-transaction fails', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
value: new BigNumber(TRANSFORM_ERC20_FAILING_VALUE),
|
||||
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.batchExecuteMetaTransactions([mtx], [signature]).callAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new StringRevertError('FAIL').encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('cannot reenter `executeMetaTransaction()`', async () => {
|
||||
const args = getRandomTransformERC20Args();
|
||||
const mtx = getRandomMetaTransaction({
|
||||
callData: transformERC20Feature
|
||||
.transformERC20(
|
||||
args.inputToken,
|
||||
args.outputToken,
|
||||
args.inputTokenAmount,
|
||||
args.minOutputTokenAmount,
|
||||
args.transformations,
|
||||
)
|
||||
.getABIEncodedTransactionData(),
|
||||
value: TRANSFORM_ERC20_REENTER_VALUE,
|
||||
});
|
||||
const mtxHash = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
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 = getExchangeProxyMetaTransactionHash(mtx);
|
||||
const signature = await signMetaTransactionAsync(mtx);
|
||||
const callOpts = {
|
||||
gasPrice: mtx.maxGasPrice,
|
||||
value: mtx.value,
|
||||
};
|
||||
const tx = feature.batchExecuteMetaTransactions([mtx], [signature]).awaitTransactionSuccessAsync(callOpts);
|
||||
return expect(tx).to.revertWith(
|
||||
new ZeroExRevertErrors.MetaTransactions.MetaTransactionCallFailedError(
|
||||
mtxHash,
|
||||
undefined,
|
||||
new ZeroExRevertErrors.Common.IllegalReentrancyError(
|
||||
feature.getSelector('batchExecuteMetaTransactions'),
|
||||
REENTRANCY_FLAG_MTX,
|
||||
).encode(),
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMetaTransactionExecutedBlock()', () => {
|
||||
|
@@ -37,6 +37,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
const callDataSigner = ethjs.bufferToHex(ethjs.privateToAddress(ethjs.toBuffer(callDataSignerKey)));
|
||||
let owner: string;
|
||||
let taker: string;
|
||||
let sender: string;
|
||||
let transformerDeployer: string;
|
||||
let zeroEx: ZeroExContract;
|
||||
let feature: TransformERC20Contract;
|
||||
@@ -44,7 +45,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
let allowanceTarget: string;
|
||||
|
||||
before(async () => {
|
||||
[owner, taker, transformerDeployer] = await env.getAccountAddressesAsync();
|
||||
[owner, taker, sender, transformerDeployer] = await env.getAccountAddressesAsync();
|
||||
zeroEx = await fullMigrateAsync(
|
||||
owner,
|
||||
env.provider,
|
||||
@@ -59,12 +60,12 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
},
|
||||
{ transformerDeployer },
|
||||
);
|
||||
feature = new TransformERC20Contract(zeroEx.address, env.provider, env.txDefaults, abis);
|
||||
feature = new TransformERC20Contract(zeroEx.address, env.provider, { ...env.txDefaults, from: sender }, abis);
|
||||
wallet = new FlashWalletContract(await feature.getTransformWallet().callAsync(), env.provider, env.txDefaults);
|
||||
allowanceTarget = await new ITokenSpenderContract(zeroEx.address, env.provider, env.txDefaults)
|
||||
.getAllowanceTarget()
|
||||
.callAsync();
|
||||
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync();
|
||||
await feature.setQuoteSigner(callDataSigner).awaitTransactionSuccessAsync({ from: owner });
|
||||
});
|
||||
|
||||
const { MAX_UINT256, ZERO_AMOUNT } = constants;
|
||||
@@ -73,7 +74,7 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
it('createTransformWallet() replaces the current wallet', async () => {
|
||||
const newWalletAddress = await feature.createTransformWallet().callAsync({ from: owner });
|
||||
expect(newWalletAddress).to.not.eq(wallet.address);
|
||||
await feature.createTransformWallet().awaitTransactionSuccessAsync();
|
||||
await feature.createTransformWallet().awaitTransactionSuccessAsync({ from: owner });
|
||||
return expect(feature.getTransformWallet().callAsync()).to.eventually.eq(newWalletAddress);
|
||||
});
|
||||
|
||||
@@ -264,8 +265,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -320,8 +322,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
taker,
|
||||
sender,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -379,8 +382,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformation.data,
|
||||
@@ -496,8 +500,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
receipt.logs,
|
||||
[
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformations[0].data,
|
||||
@@ -505,8 +510,9 @@ blockchainTests.resets('TransformERC20 feature', env => {
|
||||
ethBalance: callValue,
|
||||
},
|
||||
{
|
||||
callDataHash: NULL_BYTES32,
|
||||
sender,
|
||||
taker,
|
||||
callDataHash: NULL_BYTES32,
|
||||
context: wallet.address,
|
||||
caller: zeroEx.address,
|
||||
data: transformations[1].data,
|
||||
|
Reference in New Issue
Block a user