@0x:contracts-exchange Added unit tests for batchExecuteTransactions
				
					
				
			This commit is contained in:
		
				
					committed by
					
						
						Amir Bandeali
					
				
			
			
				
	
			
			
			
						parent
						
							1724ecd4c3
						
					
				
				
					commit
					907771f084
				
			@@ -57,4 +57,4 @@ contract MixinTransferSimulator is
 | 
			
		||||
        }
 | 
			
		||||
        revert("TRANSFERS_SUCCESSFUL");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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))
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user