diff --git a/packages/web3-core-method/src/index.js b/packages/web3-core-method/src/index.js index 8c296d87e2e..353ec31c839 100644 --- a/packages/web3-core-method/src/index.js +++ b/packages/web3-core-method/src/index.js @@ -241,7 +241,7 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { // fire "receipt" and confirmation events and resolve after - var checkConfirmation = function (err, blockHeader, sub, existingReceipt, isPolling) { + var checkConfirmation = function (existingReceipt, err, blockHeader, sub, isPolling) { if (!err) { // create fake unsubscribe if (!sub) { @@ -261,7 +261,6 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { }) // if CONFIRMATION listener exists check for confirmations, by setting canUnsubscribe = false .then(function(receipt) { - if (!receipt || !receipt.blockHash) { throw new Error('Receipt missing or blockHash null'); } @@ -274,7 +273,12 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { // check if confirmation listener exists if (defer.eventEmitter.listeners('confirmation').length > 0) { - defer.eventEmitter.emit('confirmation', confirmationCount, receipt); + // If there was an immediately retrieved receipt, it's already + // been confirmed by the direct call to checkConfirmation needed + // for parity instant-seal + if (existingReceipt === undefined || confirmationCount !== 0){ + defer.eventEmitter.emit('confirmation', confirmationCount, receipt); + } canUnsubscribe = false; confirmationCount++; @@ -396,15 +400,12 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { }; // start watching for confirmation depending on the support features of the provider - var startWatching = function() { + var startWatching = function(existingReceipt) { // if provider allows PUB/SUB if (_.isFunction(this.requestManager.provider.on)) { - _ethereumCall.subscribe('newBlockHeaders', checkConfirmation); + _ethereumCall.subscribe('newBlockHeaders', checkConfirmation.bind(null, existingReceipt)); } else { - // if provider is http and polling - intervalId = setInterval(function() { - checkConfirmation(null, null, null, null, true); - }, 1000); + intervalId = setInterval(checkConfirmation.bind(null, existingReceipt, null, null, null, true), 1000); } }.bind(this); @@ -414,13 +415,11 @@ Method.prototype._confirmTransaction = function (defer, result, payload) { .then(function(receipt) { if (receipt && receipt.blockHash) { if (defer.eventEmitter.listeners('confirmation').length > 0) { - // if the promise has not been resolved we must keep on watching for new Blocks, if a confrimation listener is present - setTimeout(function(){ - if (!promiseResolved) startWatching(); - }, 1000); + // We must keep on watching for new Blocks, if a confirmation listener is present + startWatching(receipt); } + checkConfirmation(receipt, null, null, null); - return checkConfirmation(null, null, null, receipt); } else if (!promiseResolved) { startWatching(); } diff --git a/test/contract.js b/test/contract.js index 6bc9c2aef24..4f6c9c37764 100644 --- a/test/contract.js +++ b/test/contract.js @@ -2142,6 +2142,102 @@ var runTests = function(contractFactory) { }); + it('should sendTransaction and receive multiple confirmations', function(done){ + var provider = new FakeIpcProvider(); + var signature = sha3('mySend(address,uint256)').slice(0, 10); + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_sendTransaction'); + assert.deepEqual(payload.params, [{ + data: signature +'000000000000000000000000'+ addressLowercase.replace('0x','') +'000000000000000000000000000000000000000000000000000000000000000a', + from: address2, + to: addressLowercase, + gasPrice: "0x1369ed97fb71" + }]); + }); + provider.injectResult('0x1234000000000000000000000000000000000000000000000000000000056789'); + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_getTransactionReceipt'); + assert.deepEqual(payload.params, ['0x1234000000000000000000000000000000000000000000000000000000056789']); + }); + + provider.injectResult({ + contractAddress: null, + cumulativeGasUsed: '0xa', + transactionIndex: '0x3', + transactionHash: '0x1234', + blockNumber: '0xa', + blockHash: '0x1234', + gasUsed: '0x0', + logs: [] + }); + + provider.injectValidation(function (payload) { + assert.equal(payload.method, 'eth_subscribe'); + assert.deepEqual(payload.params, ['newHeads']); + }); + provider.injectResult('0x1234567'); + + var contract = contractFactory(abi, address, provider); + + var count = 0; + contract.methods.mySend(address, 10).send({from: address2, gasPrice: '21345678654321'}) + .on('confirmation', function (confirmationNumber, receipt) { + count++; + if(count === 1) { + assert.deepEqual(receipt, { + contractAddress: null, + cumulativeGasUsed: 10, + transactionIndex: 3, + transactionHash: '0x1234', + blockNumber: 10, + blockHash: '0x1234', + gasUsed: 0, + events: {} + }); + + assert.equal(confirmationNumber, 0) + } + if(count === 2) { + assert.deepEqual(receipt, { + contractAddress: null, + cumulativeGasUsed: 10, + transactionIndex: 3, + transactionHash: '0x1234', + blockNumber: 10, + blockHash: '0x1234', + gasUsed: 0, + events: {} + }); + + assert.equal(confirmationNumber, 1) + done(); + }; + }); + + // fake newBlocks + provider.injectNotification({ + method: 'eth_subscription', + params: { + subscription: '0x1234567', + result: { + blockNumber: '0x10' + } + } + }); + + provider.injectNotification({ + method: 'eth_subscription', + params: { + subscription: '0x1234567', + result: { + blockNumber: '0x11' + } + } + }); + }); + it('should sendTransaction to contract function', function () { var provider = new FakeIpcProvider(); var signature = 'mySend(address,uint256)';