on master help sign in
Refactor transaction funding & awaiting logic
58241ecFang
src/bridge/lib/txn.js+5−3
@@ -161,13 +161,15 @@ const sendSignedTransaction = (web3, stx) => {
161 })
161 })
162}
162}
163
163
164−// returns a Promise
164+// returns a Promise<bool>, where the bool indicates tx success/failure
165const waitForTransactionConfirm = (web3, txHash) => {
165const waitForTransactionConfirm = (web3, txHash) => {
166 return new Promise((resolve, reject) => {
166 return new Promise((resolve, reject) => {
167 const checkForConfirm = async () => {
167 const checkForConfirm = async () => {
168 console.log('checking for confirm', txHash);
168 console.log('checking for confirm', txHash);
169− let confirmed = isTransactionConfirmed(web3, txHash);
169+ const receipt = await web3.eth.getTransactionReceipt(txHash);
170+ console.log('tried, got', receipt);
171+ let confirmed = (receipt !== null);
170− if (confirmed) resolve();
172+ if (confirmed) resolve(receipt.status === '0x1');
171 else setTimeout(checkForConfirm, 13000);
173 else setTimeout(checkForConfirm, 13000);
172 }
174 }
173 checkForConfirm();
175 checkForConfirm();
src/bridge/views/InviteTransactions.js+71−21
@@ -214,19 +214,27 @@ class InviteVerify extends React.Component {
214
214
215 if (cost > balance) {
215 if (cost > balance) {
216
216
217+ try {
218+
217− const fundsRemaining = await tank.remainingTransactions(point);
219+ const fundsRemaining = await tank.remainingTransactions(point);
218− if (fundsRemaining < signedTxs.length) {
220+ if (fundsRemaining < signedTxs.length) {
219− throw new Error(`TODO present user with "please fund $(address) with at least $(cost), then click to retry"`);
221+ throw new Error('request invalid');
220− }
222+ }
223+
224+ const res = await tank.fundTransactions(signedTxs);
225+ if (!res.success) {
226+ throw new Error('request rejected');
227+ } else {
228+ await waitForTransactionConfirm(web3, res.txHash);
229+ let newBalance = await web3.eth.getBalance(address);
230+ console.log('funds have confirmed', balance >= cost, balance, newBalance);
231+ }
232+
233+ } catch (e) {
234+
235+ console.log('funding failed', e);
236+ await this.waitForBalance(web3, address, cost);
221
237
222− const res = await tank.fundTransactions(signedTxs);
223− if (!res.success) {
224− //TODO show pls fund msg
225− console.log('tank request failed');
226− } else {
227− await waitForTransactionConfirm(web3, res.txHash);
228− let newBalance = await web3.eth.getBalance(address);
229− console.log('funds have confirmed', balance >= cost, balance, newBalance);
230 }
238 }
231
239
232 } else {
240 } else {
@@ -234,28 +242,70 @@ class InviteVerify extends React.Component {
234 }
242 }
235 }
243 }
236
244
245+ // resolves when address has at least minBalance
246+ //
247+ async waitForBalance(web3, address, minBalance) {
248+ console.log('awaiting balance', address, minBalance);
249+ return new Promise((resolve, reject) => {
250+ let oldBalance = null;
251+ const checkForBalance = async () => {
252+ const balance = await web3.eth.getBalance(address);
253+ if (balance >= minBalance) {
254+ resolve();
255+ } else {
256+ if (balance !== oldBalance) {
257+ this.askForFunding(address, minBalance, balance);
258+ }
259+ setTimeout(checkForBalance, 13000);
260+ }
261+ };
262+ checkForBalance();
263+ });
264+ }
265+
237 async sendAndAwaitConfirm(web3, signedTxs) {
266 async sendAndAwaitConfirm(web3, signedTxs) {
238− let promises = signedTxs.map(tx => {
267+ await Promise.all(signedTxs.map(tx => {
239 console.log('sending...');
268 console.log('sending...');
240 return new Promise((resolve, reject) => {
269 return new Promise((resolve, reject) => {
241 web3.eth.sendSignedTransaction(tx).then(res => {
270 web3.eth.sendSignedTransaction(tx).then(res => {
242− //TODO figure out how to handle the doesn't-resolve-immediately case
271+ console.log('sent, now waiting for confirm!', res.transactionHash);
243− //res.transactionHash;
272+ waitForTransactionConfirm(web3, res.transactionHash)
244− resolve();
273+ .then(resolve);
245 }).catch(async err => {
274 }).catch(async err => {
246 // if there's an error, check if it's because the transaction was
275 // if there's an error, check if it's because the transaction was
247 // already confirmed prior to sending.
276 // already confirmed prior to sending.
248 // this is really only the case in local dev environments.
277 // this is really only the case in local dev environments.
249 const txHash = web3.utils.keccak256(tx);
278 const txHash = web3.utils.keccak256(tx);
279+ console.log(err.message);
280+ if (err.message.slice(0,54) ===
281+ "Returned error: the tx doesn't have the correct nonce.") {
282+ console.log('nonce error, awaiting confirm', err.message.slice(55));
283+ //TODO max wait time before assume failure?
284+ let res = await waitForTransactionConfirm(web3, txHash);
285+ if (res) resolve();
286+ else reject(new Error('Unexpected tx failure'));
287+ } else {
250− const confirmed = await isTransactionConfirmed(web3, txHash);
288+ const confirmed = await isTransactionConfirmed(web3, txHash);
289+ console.log('error, but maybe confirmed:', confirmed);
251− if (confirmed) resolve();
290+ if (confirmed) resolve();
252− else reject(err);
291+ else reject(err);
292+ }
253 });
293 });
254 });
294 });
255− });
295+ }));
256 }
296 }
257
297
258− //TODO waitForBalance(address, minBalance)
298+ awaitForResult(desired, retryTime, func) {
299+ return new Promise((resolve, reject) => {
300+ const retry = async () => {
301+ const result = await func();
302+ console.log('result vs desired', result, desired);
303+ if (result === desired) resolve();
304+ else setTimeout(retry, retryTime);
305+ };
306+ retry();
307+ });
308+ }
259
309
260 render() {
310 render() {
261
311