@0x:contracts-exchange Added unit tests for batchExecuteTransactions

This commit is contained in:
Alex Towle
2019-08-15 14:37:47 -07:00
committed by Amir Bandeali
parent 1724ecd4c3
commit 907771f084
3 changed files with 241 additions and 3 deletions

View File

@@ -57,4 +57,4 @@ contract MixinTransferSimulator is
}
revert("TRANSFERS_SUCCESSFUL");
}
}
}

View File

@@ -50,7 +50,7 @@ contract TestTransactions is
bool success = shouldSucceedCall;
bytes memory returnData = fallbackReturnData;
assembly {
if iszero(success) {
if or(iszero(success), gt(calldatasize, 0x0)) {
revert(add(0x20, returnData), mload(returnData))
}
return(add(0x20, returnData), mload(returnData))

View File

@@ -56,8 +56,244 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
};
});
describe('batchExecuteTransaction', () => {
it('should revert if the only call to executeTransaction fails', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).minus(10), // Set the expiration time to before the current timestamp
domain,
};
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.Expired,
transactionHash,
);
const tx = transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction],
[randomSignature()],
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the second call to executeTransaction fails', async () => {
// Set the contract to accept signatures.
await expect(transactionsContract.setShouldBeValid.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract to fail on calls to the fallback function. Note: This call is unnecessary but is kept for readability.
await expect(transactionsContract.setShouldCallSucceed.sendTransactionAsync(true)).to.be.fulfilled('');
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = {
...EMPTY_ZERO_EX_TRANSACTION,
data: '0x', // This call should succeed
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
domain,
};
const transaction2 = {
...EMPTY_ZERO_EX_TRANSACTION,
data: '0x32', // This call should fail because the calldata is invalid
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
domain,
};
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction2);
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHash,
constants.NULL_BYTES,
);
const tx = transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction1, transaction2],
[randomSignature(), randomSignature()],
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the first call to executeTransaction fails', async () => {
// Set the contract to accept signatures.
await expect(transactionsContract.setShouldBeValid.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract to fail on calls to the fallback function. Note: This call is unnecessary but is kept for readability.
await expect(transactionsContract.setShouldCallSucceed.sendTransactionAsync(true)).to.be.fulfilled('');
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = {
...EMPTY_ZERO_EX_TRANSACTION,
data: '0x32', // This call should fail because the calldata is invalid
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
domain,
};
const transaction2 = {
...EMPTY_ZERO_EX_TRANSACTION,
data: '0x', // This call should succeed
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
domain,
};
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction1);
const expectedError = new ExchangeRevertErrors.TransactionExecutionError(
transactionHash,
constants.NULL_BYTES,
);
const tx = transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction1, transaction2],
[randomSignature(), randomSignature()],
);
return expect(tx).to.revertWith(expectedError);
});
it('should revert if the same transaction is executed twice in a batch', async () => {
// Set the contract to accept signatures.
await expect(transactionsContract.setShouldBeValid.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract to fail on calls to the fallback function. Note: This call is unnecessary but is kept for readability.
await expect(transactionsContract.setShouldCallSucceed.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract fallbackReturnData to the recognizable string of bytes 0xDEADBEEF
await expect(transactionsContract.setFallbackReturnData.sendTransactionAsync('0xdeadbeef')).to.be.fulfilled(
'',
);
// Set up the necessary data for the transactions and tests
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
signerAddress: accounts[1], // This is different than the account that will be used to send.
domain,
};
const transaction2 = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
signerAddress: accounts[1], // This is different than the account that will be used to send.
domain,
};
const transactionHash2 = transactionHashUtils.getTransactionHashHex(transaction2);
// Verify that the transaction reverts
const expectedError = new ExchangeRevertErrors.TransactionError(
ExchangeRevertErrors.TransactionErrorCode.AlreadyExecuted,
transactionHash2,
);
const tx = transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction1, transaction2],
[randomSignature(), randomSignature()],
{
from: accounts[0],
},
);
return expect(tx).to.revertWith(expectedError);
});
it('should succeed if the only call to executeTransaction succeeds', async () => {
// Set the contract to accept signatures.
await expect(transactionsContract.setShouldBeValid.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract to fail on calls to the fallback function. Note: This call is unnecessary but is kept for readability.
await expect(transactionsContract.setShouldCallSucceed.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract fallbackReturnData to the recognizable string of bytes 0xDEADBEEF
await expect(transactionsContract.setFallbackReturnData.sendTransactionAsync('0xdeadbeef')).to.be.fulfilled(
'',
);
// Set up the necessary data for the transactions and tests
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
signerAddress: accounts[1], // This is different than the account that will be used to send.
domain,
};
const transactionHash = transactionHashUtils.getTransactionHashHex(transaction);
// Verify that the returndata of the transaction is 0xDEADBEEF
const result = await transactionsContract.batchExecuteTransactions.callAsync(
[transaction],
[randomSignature()],
{
from: accounts[0],
},
);
expect(result.length).to.be.eq(1);
expect(result[0] === '0xdeadbeef').to.be.true();
// Verify that the logs returned from the call are correct.
const receipt = await logDecoder.getTxWithDecodedLogsAsync(
await transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction],
[randomSignature()],
{
from: accounts[0],
},
),
);
const logs = receipt.logs as Array<LogWithDecodedArgs<TestTransactionsTransactionExecutionEventArgs>>;
expect(logs.length).to.be.eq(1);
expect(logs[0].event === 'TransactionExecution').to.be.true();
expect(logs[0].args.transactionHash).to.eq(transactionHash);
});
it('should succeed if the both calls to executeTransaction succeed', async () => {
// Set the contract to accept signatures.
await expect(transactionsContract.setShouldBeValid.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract to fail on calls to the fallback function. Note: This call is unnecessary but is kept for readability.
await expect(transactionsContract.setShouldCallSucceed.sendTransactionAsync(true)).to.be.fulfilled('');
// Set the contract fallbackReturnData to the recognizable string of bytes 0xDEADBEEF
await expect(transactionsContract.setFallbackReturnData.sendTransactionAsync('0xdeadbeef')).to.be.fulfilled(
'',
);
// Set up the necessary data for the transactions and tests
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction1 = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
signerAddress: accounts[0], // This is different than the account that will be used to send.
domain,
};
const transaction2 = {
...EMPTY_ZERO_EX_TRANSACTION,
expirationTimeSeconds: new BigNumber(currentTimestamp).plus(10),
signerAddress: accounts[1], // This is different than the account that will be used to send.
domain,
};
const transactionHash1 = transactionHashUtils.getTransactionHashHex(transaction1);
const transactionHash2 = transactionHashUtils.getTransactionHashHex(transaction2);
// Verify that the returndata of the transaction is 0xDEADBEEF
const result = await transactionsContract.batchExecuteTransactions.callAsync(
[transaction1, transaction2],
[randomSignature(), randomSignature()],
{
from: accounts[0],
},
);
expect(result.length).to.be.eq(2);
expect(result[0] === '0xdeadbeef').to.be.true();
expect(result[1] === '0xdeadbeef').to.be.true();
// Verify that the logs returned from the call are correct.
const receipt = await logDecoder.getTxWithDecodedLogsAsync(
await transactionsContract.batchExecuteTransactions.sendTransactionAsync(
[transaction1, transaction2],
[randomSignature(), randomSignature()],
{
from: accounts[0],
},
),
);
const logs = receipt.logs as Array<LogWithDecodedArgs<TestTransactionsTransactionExecutionEventArgs>>;
expect(logs.length).to.be.eq(2);
logs.map(log => expect(log.event === 'TransactionExecution').to.be.true());
expect(logs[0].args.transactionHash).to.eq(transactionHash1);
expect(logs[1].args.transactionHash).to.eq(transactionHash2);
});
});
describe('executeTransaction', () => {
it('should revert if the current time is past the expiration time of the metatransaction', async () => {
it('should revert if the current time is past the expiration time', async () => {
const currentTimestamp = await getLatestBlockTimestampAsync();
const transaction = {
...EMPTY_ZERO_EX_TRANSACTION,
@@ -250,6 +486,7 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
}),
);
const logs = receipt.logs as Array<LogWithDecodedArgs<TestTransactionsTransactionExecutionEventArgs>>;
expect(logs.length).to.be.eq(1);
expect(logs[0].event === 'TransactionExecution').to.be.true();
expect(logs[0].args.transactionHash).to.eq(transactionHash);
});
@@ -275,3 +512,4 @@ blockchainTests.resets('Transaction Unit Tests', ({ provider, web3Wrapper, txDef
});
});
});
// tslint:disable-line:max-file-line-count