From 8a340c8ff3f9865e8901f0afccf4c936f7d1d257 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Tue, 26 Jun 2018 18:37:21 -0400 Subject: [PATCH] Fixed sendTransaction for JsonRpcSigner. --- providers/json-rpc-provider.js | 25 +++++++++++-- providers/provider.js | 47 +++++++++++++----------- src.ts/providers/json-rpc-provider.ts | 27 ++++++++++++-- src.ts/providers/provider.ts | 51 +++++++++++++++------------ 4 files changed, 102 insertions(+), 48 deletions(-) diff --git a/providers/json-rpc-provider.js b/providers/json-rpc-provider.js index 6da90c0f..9fa64ca9 100644 --- a/providers/json-rpc-provider.js +++ b/providers/json-rpc-provider.js @@ -118,7 +118,7 @@ var JsonRpcSigner = /** @class */ (function (_super) { }; JsonRpcSigner.prototype.sendTransaction = function (transaction) { var _this = this; - var tx = hexlifyTransaction(transaction); + var tx = properties_1.shallowCopy(transaction); if (tx.from == null) { tx.from = this.getAddress().then(function (address) { if (!address) { @@ -128,7 +128,28 @@ var JsonRpcSigner = /** @class */ (function (_super) { }); } return properties_1.resolveProperties(tx).then(function (tx) { - return _this.provider.send('eth_sendTransaction', [tx]); + tx = hexlifyTransaction(tx); + return _this.provider.send('eth_sendTransaction', [tx]).then(function (hash) { + // @TODO: Make a pollProperty method in utils + return new Promise(function (resolve, reject) { + var count = 0; + var check = function () { + _this.provider.getTransaction(hash).then(function (tx) { + if (tx == null) { + if (count++ > 500) { + // @TODO: Better error + reject(new Error('could not find transaction')); + return; + } + setTimeout(check, 200); + return; + } + resolve(_this.provider._wrapTransaction(tx.raw, hash)); + }); + }; + setTimeout(check, 50); + }); + }); }); }; JsonRpcSigner.prototype.signMessage = function (message) { diff --git a/providers/provider.js b/providers/provider.js index 9cf0a203..27af4852 100644 --- a/providers/provider.js +++ b/providers/provider.js @@ -244,6 +244,7 @@ function checkTransactionResponse(transaction) { if (transaction.to == null && transaction.creates == null) { transaction.creates = address_1.getContractAddress(transaction); } + // @TODO: use transaction.serialize? Have to add support for including v, r, and s... if (!transaction.raw) { // Very loose providers (e.g. TestRPC) don't provide a signature or raw if (transaction.v && transaction.r && transaction.s) { @@ -797,31 +798,35 @@ var Provider = /** @class */ (function () { var signedTransaction = _a.signedTransaction; var params = { signedTransaction: bytes_1.hexlify(signedTransaction) }; return _this.perform('sendTransaction', params).then(function (hash) { - if (bytes_1.hexDataLength(hash) !== 32) { - throw new Error('invalid response - sendTransaction'); - } - // A signed transaction always has a from (and we add wait below) - var tx = transaction_1.parse(signedTransaction); - // Check the hash we expect is the same as the hash the server reported - if (tx.hash !== hash) { - errors.throwError('Transaction hash mismatch from Proivder.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash }); - } - _this._emitted['t:' + tx.hash.toLowerCase()] = 'pending'; - tx.wait = function (timeout) { - return _this.waitForTransaction(hash, timeout).then(function (receipt) { - if (receipt.status === 0) { - errors.throwError('transaction failed', errors.CALL_EXCEPTION, { - transaction: tx - }); - } - return receipt; - }); - }; - return tx; + return _this._wrapTransaction(signedTransaction, hash); }); }); }); }; + Provider.prototype._wrapTransaction = function (signedTransaction, hash) { + var _this = this; + if (bytes_1.hexDataLength(hash) !== 32) { + throw new Error('invalid response - sendTransaction'); + } + // A signed transaction always has a from (and we add wait below) + var tx = transaction_1.parse(signedTransaction); + // Check the hash we expect is the same as the hash the server reported + if (tx.hash !== hash) { + errors.throwError('Transaction hash mismatch from Proivder.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash }); + } + this._emitted['t:' + tx.hash.toLowerCase()] = 'pending'; + tx.wait = function (timeout) { + return _this.waitForTransaction(hash, timeout).then(function (receipt) { + if (receipt.status === 0) { + errors.throwError('transaction failed', errors.CALL_EXCEPTION, { + transaction: tx + }); + } + return receipt; + }); + }; + return tx; + }; Provider.prototype.call = function (transaction) { var _this = this; var tx = properties_1.shallowCopy(transaction); diff --git a/src.ts/providers/json-rpc-provider.ts b/src.ts/providers/json-rpc-provider.ts index 7602b20c..f33d5dfb 100644 --- a/src.ts/providers/json-rpc-provider.ts +++ b/src.ts/providers/json-rpc-provider.ts @@ -9,7 +9,7 @@ import { Signer } from '../wallet/wallet'; import { getAddress } from '../utils/address'; import { BigNumber } from '../utils/bignumber'; import { Arrayish, hexlify, hexStripZeros } from '../utils/bytes'; -import { defineReadOnly, resolveProperties } from '../utils/properties'; +import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties'; import { toUtf8Bytes } from '../utils/utf8'; import { ConnectionInfo, fetchJson } from '../utils/web'; @@ -109,7 +109,7 @@ export class JsonRpcSigner extends Signer { } sendTransaction(transaction: TransactionRequest): Promise { - let tx = hexlifyTransaction(transaction); + let tx = shallowCopy(transaction); if (tx.from == null) { tx.from = this.getAddress().then((address) => { @@ -119,7 +119,28 @@ export class JsonRpcSigner extends Signer { } return resolveProperties(tx).then((tx) => { - return this.provider.send('eth_sendTransaction', [ tx ]); + tx = hexlifyTransaction(tx); + return this.provider.send('eth_sendTransaction', [ tx ]).then((hash) => { + // @TODO: Make a pollProperty method in utils + return new Promise((resolve: (result: TransactionResponse) => void, reject) => { + let count = 0; + let check = () => { + this.provider.getTransaction(hash).then((tx: TransactionResponse) => { + if (tx == null) { + if (count++ > 500) { + // @TODO: Better error + reject(new Error('could not find transaction')); + return; + } + setTimeout(check, 200); + return; + } + resolve(this.provider._wrapTransaction(tx.raw, hash)); + }); + } + setTimeout(check, 50); + }); + }); }); } diff --git a/src.ts/providers/provider.ts b/src.ts/providers/provider.ts index e1deadb5..ca0bd95f 100644 --- a/src.ts/providers/provider.ts +++ b/src.ts/providers/provider.ts @@ -62,6 +62,9 @@ export interface TransactionResponse extends Transaction { // Not optional (as it is in Transaction) from: string; + // The raw transaction + raw?: string, + // This function waits until the transaction has been mined wait: (timeout?: number) => Promise }; @@ -350,6 +353,7 @@ export function checkTransactionResponse(transaction: any): TransactionResponse transaction.creates = getContractAddress(transaction); } + // @TODO: use transaction.serialize? Have to add support for including v, r, and s... if (!transaction.raw) { // Very loose providers (e.g. TestRPC) don't provide a signature or raw if (transaction.v && transaction.r && transaction.s) { @@ -960,33 +964,36 @@ export class Provider { return resolveProperties({ signedTransaction: signedTransaction }).then(({ signedTransaction }) => { var params = { signedTransaction: hexlify(signedTransaction) }; return this.perform('sendTransaction', params).then((hash) => { - if (hexDataLength(hash) !== 32) { throw new Error('invalid response - sendTransaction'); } - - // A signed transaction always has a from (and we add wait below) - var tx = parseTransaction(signedTransaction); - - // Check the hash we expect is the same as the hash the server reported - if (tx.hash !== hash) { - errors.throwError('Transaction hash mismatch from Proivder.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash }); - } - this._emitted['t:' + tx.hash.toLowerCase()] = 'pending'; - tx.wait = (timeout?: number) => { - return this.waitForTransaction(hash, timeout).then((receipt) => { - if (receipt.status === 0) { - errors.throwError('transaction failed', errors.CALL_EXCEPTION, { - transaction: tx - }); - } - return receipt; - }); - }; - - return tx; + return this._wrapTransaction(signedTransaction, hash); }); }); }); } + _wrapTransaction(signedTransaction: string, hash?: string): TransactionResponse { + if (hexDataLength(hash) !== 32) { throw new Error('invalid response - sendTransaction'); } + + // A signed transaction always has a from (and we add wait below) + var tx = parseTransaction(signedTransaction); + + // Check the hash we expect is the same as the hash the server reported + if (tx.hash !== hash) { + errors.throwError('Transaction hash mismatch from Proivder.sendTransaction.', errors.UNKNOWN_ERROR, { expectedHash: tx.hash, returnedHash: hash }); + } + this._emitted['t:' + tx.hash.toLowerCase()] = 'pending'; + tx.wait = (timeout?: number) => { + return this.waitForTransaction(hash, timeout).then((receipt) => { + if (receipt.status === 0) { + errors.throwError('transaction failed', errors.CALL_EXCEPTION, { + transaction: tx + }); + } + return receipt; + }); + }; + return tx; + } + call(transaction: TransactionRequest): Promise { let tx: TransactionRequest = shallowCopy(transaction);