@0xsequence/relayer 0.40.6 → 0.41.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -237,50 +237,95 @@ class ProviderRelayer extends BaseRelayer {
237
237
  return transactions.encodeNonce(space, nonce);
238
238
  }
239
239
 
240
- async wait(metaTxnId, timeout) {
240
+ async wait(metaTxnId, timeout, delay = this.waitPollRate, maxFails = 5) {
241
+ var _this2 = this;
242
+
241
243
  if (typeof metaTxnId !== 'string') {
242
- utils.logger.info("computing id", metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
243
- return this.wait(transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions), timeout);
244
- } // Transactions can only get executed on nonce change
245
- // get all nonce changes and look for metaTxnIds in between logs
244
+ utils.logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
245
+ metaTxnId = transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions);
246
+ }
246
247
 
248
+ let timedOut = false;
247
249
 
248
- const timeoutTime = new Date().getTime() + timeout;
249
- let lastBlock = this.fromBlockLog;
250
+ const retry = async function retry(f, errorMessage) {
251
+ let fails = 0;
250
252
 
251
- if (lastBlock < 0) {
252
- const block = await this.provider.getBlockNumber();
253
- lastBlock = block + lastBlock;
254
- }
253
+ while (!timedOut) {
254
+ try {
255
+ return await f();
256
+ } catch (error) {
257
+ fails++;
255
258
 
256
- const normalMetaTxnId = metaTxnId.replace('0x', '');
259
+ if (maxFails !== undefined && fails >= maxFails) {
260
+ utils.logger.error(`giving up after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`, error);
261
+ throw error;
262
+ } else {
263
+ utils.logger.warn(`attempt #${fails} failed${errorMessage ? `: ${errorMessage}` : ''}`, error);
264
+ }
265
+ }
257
266
 
258
- while (new Date().getTime() < timeoutTime) {
259
- const block = await this.provider.getBlockNumber();
260
- const logs = await this.provider.getLogs({
261
- fromBlock: Math.max(0, lastBlock - this.deltaBlocksLog),
262
- toBlock: block,
263
- // Nonce change event topic
264
- topics: ['0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881']
265
- });
266
- lastBlock = block; // Get receipts of all transactions
267
+ if (delay > 0) {
268
+ await new Promise(resolve => setTimeout(resolve, delay));
269
+ }
270
+ }
267
271
 
268
- const txs = await Promise.all(logs.map(l => this.provider.getTransactionReceipt(l.transactionHash))); // Find a transaction with a TxExecuted log
272
+ throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`);
273
+ };
269
274
 
270
- const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 && // TxFailed event topic
271
- l.topics[0] === "0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7" && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId))); // If found return that
275
+ const waitReceipt = async function waitReceipt() {
276
+ // Transactions can only get executed on nonce change
277
+ // get all nonce changes and look for metaTxnIds in between logs
278
+ let lastBlock = _this2.fromBlockLog;
272
279
 
273
- if (found) {
274
- return _extends({
275
- receipt: found
276
- }, await this.provider.getTransaction(found.transactionHash));
277
- } // Otherwise wait and try again
280
+ if (lastBlock < 0) {
281
+ const block = await retry(() => _this2.provider.getBlockNumber(), 'unable to get latest block number');
282
+ lastBlock = block + lastBlock;
283
+ }
284
+
285
+ if (typeof metaTxnId !== 'string') {
286
+ throw new Error('impossible');
287
+ }
278
288
 
289
+ const normalMetaTxnId = metaTxnId.replace('0x', '');
279
290
 
280
- await new Promise(r => setTimeout(r, this.waitPollRate));
281
- }
291
+ while (!timedOut) {
292
+ const block = await retry(() => _this2.provider.getBlockNumber(), 'unable to get latest block number');
293
+ const logs = await retry(() => _this2.provider.getLogs({
294
+ fromBlock: Math.max(0, lastBlock - _this2.deltaBlocksLog),
295
+ toBlock: block,
296
+ // Nonce change event topic
297
+ topics: ['0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881']
298
+ }), `unable to get NonceChange logs for blocks ${Math.max(0, lastBlock - _this2.deltaBlocksLog)} to ${block}`);
299
+ lastBlock = block; // Get receipts of all transactions
300
+
301
+ const txs = await Promise.all(logs.map(l => retry(() => _this2.provider.getTransactionReceipt(l.transactionHash), `unable to get receipt for transaction ${l.transactionHash}`))); // Find a transaction with a TxExecuted log
302
+
303
+ const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 && // TxFailed event topic
304
+ l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId))); // If found return that
305
+
306
+ if (found) {
307
+ return _extends({
308
+ receipt: found
309
+ }, await retry(() => _this2.provider.getTransaction(found.transactionHash), `unable to get transaction ${found.transactionHash}`));
310
+ } // Otherwise wait and try again
282
311
 
283
- throw new Error(`Timeout waiting for transaction receipt ${metaTxnId}`);
312
+
313
+ if (!timedOut) {
314
+ await new Promise(r => setTimeout(r, delay));
315
+ }
316
+ }
317
+
318
+ throw new Error(`Timeout waiting for transaction receipt ${metaTxnId}`);
319
+ };
320
+
321
+ if (timeout !== undefined) {
322
+ return Promise.race([waitReceipt(), new Promise((_, reject) => setTimeout(() => {
323
+ timedOut = true;
324
+ reject(`Timeout waiting for transaction receipt ${metaTxnId}`);
325
+ }, timeout))]);
326
+ } else {
327
+ return waitReceipt();
328
+ }
284
329
  }
285
330
 
286
331
  }
@@ -329,7 +374,7 @@ class LocalRelayer extends ProviderRelayer {
329
374
  this.txnOptions = transactionRequest;
330
375
  }
331
376
 
332
- async relay(signedTxs, quote) {
377
+ async relay(signedTxs, quote, waitForReceipt = true) {
333
378
  if (quote !== undefined) {
334
379
  utils.logger.warn(`LocalRelayer doesn't accept fee quotes`);
335
380
  }
@@ -348,10 +393,18 @@ class LocalRelayer extends ProviderRelayer {
348
393
  // const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum.add(tx.gasLimit), ethers.BigNumber.from(0))
349
394
  // txRequest.gasLimit = gasLimit
350
395
 
351
- return this.signer.sendTransaction(_extends({
396
+ const responsePromise = this.signer.sendTransaction(_extends({
352
397
  to,
353
398
  data
354
399
  }, this.txnOptions));
400
+
401
+ if (waitForReceipt) {
402
+ const response = await responsePromise;
403
+ response.receipt = await response.wait();
404
+ return response;
405
+ } else {
406
+ return responsePromise;
407
+ }
355
408
  }
356
409
 
357
410
  }
@@ -626,7 +679,8 @@ var relayer_gen = /*#__PURE__*/Object.freeze({
626
679
  Relayer: Relayer
627
680
  });
628
681
 
629
- const FAILED_STATUSES = [ETHTxnStatus.FAILED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.DROPPED];
682
+ const FINAL_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.SUCCEEDED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
683
+ const FAILED_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
630
684
  function isRpcRelayerOptions(obj) {
631
685
  return obj.url !== undefined && typeof obj.url === 'string';
632
686
  }
@@ -637,26 +691,50 @@ class RpcRelayer extends BaseRelayer {
637
691
  this.service = new Relayer(options.url, fetchPonyfill__default["default"]().fetch);
638
692
  }
639
693
 
640
- async waitReceipt(metaTxnHash, wait = 1000) {
641
- if (typeof metaTxnHash !== 'string') {
642
- utils.logger.info('computing id', metaTxnHash.config, metaTxnHash.context, metaTxnHash.chainId, ...metaTxnHash.transactions);
643
- return this.waitReceipt(transactions.computeMetaTxnHash(config.addressOf(metaTxnHash.config, metaTxnHash.context), metaTxnHash.chainId, ...metaTxnHash.transactions));
694
+ async waitReceipt(metaTxnId, delay = 1000, maxFails = 5, isCancelled) {
695
+ if (typeof metaTxnId !== 'string') {
696
+ utils.logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
697
+ metaTxnId = transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions);
644
698
  }
645
699
 
646
- utils.logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnHash}`);
647
- let result = await this.service.getMetaTxnReceipt({
648
- metaTxID: metaTxnHash
649
- }); // TODO: remove check for 'UNKNOWN' status when 'QUEUED' status is supported
650
- // TODO: fix backend to not return literal 'null' txnReceipt
700
+ utils.logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnId}`);
701
+ let fails = 0;
651
702
 
652
- while (!result.receipt || !result.receipt.txnReceipt || result.receipt.txnReceipt === 'null' || result.receipt.status === 'UNKNOWN' || result.receipt.status === 'QUEUED' || result.receipt.status === 'SENT') {
653
- await new Promise(r => setTimeout(r, wait));
654
- result = await this.service.getMetaTxnReceipt({
655
- metaTxID: metaTxnHash
656
- });
703
+ while (isCancelled === undefined || !isCancelled()) {
704
+ try {
705
+ const {
706
+ receipt
707
+ } = await this.service.getMetaTxnReceipt({
708
+ metaTxID: metaTxnId
709
+ });
710
+
711
+ if (!receipt) {
712
+ throw new Error('missing expected receipt');
713
+ }
714
+
715
+ if (!receipt.txnReceipt) {
716
+ throw new Error('missing expected transaction receipt');
717
+ }
718
+
719
+ if (FINAL_STATUSES.includes(receipt.status)) {
720
+ return {
721
+ receipt
722
+ };
723
+ }
724
+ } catch (e) {
725
+ fails++;
726
+
727
+ if (fails === maxFails) {
728
+ throw e;
729
+ }
730
+ }
731
+
732
+ if (isCancelled === undefined || !isCancelled()) {
733
+ await new Promise(resolve => setTimeout(resolve, delay));
734
+ }
657
735
  }
658
736
 
659
- return result;
737
+ throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`);
660
738
  }
661
739
 
662
740
  async simulate(wallet, ...transactions$1) {
@@ -781,7 +859,9 @@ class RpcRelayer extends BaseRelayer {
781
859
  return nonce;
782
860
  }
783
861
 
784
- async relay(signedTxs, quote) {
862
+ async relay(signedTxs, quote, waitForReceipt = true) {
863
+ var _this = this;
864
+
785
865
  utils.logger.info(`[rpc-relayer/relay] relaying signed meta-transactions ${JSON.stringify(signedTxs)} with quote ${JSON.stringify(quote)}`);
786
866
  let typecheckedQuote;
787
867
 
@@ -814,15 +894,50 @@ class RpcRelayer extends BaseRelayer {
814
894
  quote: typecheckedQuote
815
895
  });
816
896
  utils.logger.info(`[rpc-relayer/relay] got relay result ${JSON.stringify(metaTxn)}`);
817
- return this.wait(metaTxn.txnHash);
897
+
898
+ if (waitForReceipt) {
899
+ return this.wait(metaTxn.txnHash);
900
+ } else {
901
+ const response = {
902
+ hash: metaTxn.txnHash,
903
+ confirmations: 0,
904
+ from: walletAddress,
905
+ wait: _confirmations => Promise.reject(new Error('impossible'))
906
+ };
907
+
908
+ const wait = async function wait(confirmations) {
909
+ var _waitResponse$receipt;
910
+
911
+ if (!_this.provider) {
912
+ throw new Error('cannot wait for receipt, relayer has no provider set');
913
+ }
914
+
915
+ const waitResponse = await _this.wait(metaTxn.txnHash);
916
+ const transactionHash = (_waitResponse$receipt = waitResponse.receipt) == null ? void 0 : _waitResponse$receipt.transactionHash;
917
+
918
+ if (!transactionHash) {
919
+ throw new Error('cannot wait for receipt, unknown native transaction hash');
920
+ }
921
+
922
+ Object.assign(response, waitResponse);
923
+ return _this.provider.waitForTransaction(transactionHash, confirmations);
924
+ };
925
+
926
+ response.wait = wait;
927
+ return response;
928
+ }
818
929
  }
819
930
 
820
- async wait(metaTxnHash, wait = 1000) {
821
- var _this = this;
931
+ async wait(metaTxnId, timeout, delay = 1000, maxFails = 5) {
932
+ var _this2 = this;
822
933
 
934
+ let timedOut = false;
823
935
  const {
824
936
  receipt
825
- } = await this.waitReceipt(metaTxnHash, wait);
937
+ } = await (timeout !== undefined ? Promise.race([this.waitReceipt(metaTxnId, delay, maxFails, () => timedOut), new Promise((_, reject) => setTimeout(() => {
938
+ timedOut = true;
939
+ reject(`Timeout waiting for transaction receipt ${metaTxnId}`);
940
+ }, timeout))]) : this.waitReceipt(metaTxnId, delay, maxFails));
826
941
 
827
942
  if (!receipt.txnReceipt || FAILED_STATUSES.includes(receipt.status)) {
828
943
  throw new MetaTransactionResponseException(receipt);
@@ -833,13 +948,13 @@ class RpcRelayer extends BaseRelayer {
833
948
  blockHash: txReceipt.blockHash,
834
949
  blockNumber: ethers.ethers.BigNumber.from(txReceipt.blockNumber).toNumber(),
835
950
  confirmations: 1,
836
- from: typeof metaTxnHash === 'string' ? undefined : config.addressOf(metaTxnHash.config, metaTxnHash.context),
951
+ from: typeof metaTxnId === 'string' ? undefined : config.addressOf(metaTxnId.config, metaTxnId.context),
837
952
  hash: txReceipt.transactionHash,
838
953
  raw: receipt.txnReceipt,
839
954
  receipt: txReceipt,
840
955
  // extended type which is Sequence-specific. Contains the decoded metaTxReceipt
841
956
  wait: async function (confirmations) {
842
- return _this.provider.waitForTransaction(txReceipt.transactionHash, confirmations);
957
+ return _this2.provider.waitForTransaction(txReceipt.transactionHash, confirmations);
843
958
  }
844
959
  };
845
960
  }
@@ -237,50 +237,95 @@ class ProviderRelayer extends BaseRelayer {
237
237
  return transactions.encodeNonce(space, nonce);
238
238
  }
239
239
 
240
- async wait(metaTxnId, timeout) {
240
+ async wait(metaTxnId, timeout, delay = this.waitPollRate, maxFails = 5) {
241
+ var _this2 = this;
242
+
241
243
  if (typeof metaTxnId !== 'string') {
242
- utils.logger.info("computing id", metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
243
- return this.wait(transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions), timeout);
244
- } // Transactions can only get executed on nonce change
245
- // get all nonce changes and look for metaTxnIds in between logs
244
+ utils.logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
245
+ metaTxnId = transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions);
246
+ }
246
247
 
248
+ let timedOut = false;
247
249
 
248
- const timeoutTime = new Date().getTime() + timeout;
249
- let lastBlock = this.fromBlockLog;
250
+ const retry = async function retry(f, errorMessage) {
251
+ let fails = 0;
250
252
 
251
- if (lastBlock < 0) {
252
- const block = await this.provider.getBlockNumber();
253
- lastBlock = block + lastBlock;
254
- }
253
+ while (!timedOut) {
254
+ try {
255
+ return await f();
256
+ } catch (error) {
257
+ fails++;
255
258
 
256
- const normalMetaTxnId = metaTxnId.replace('0x', '');
259
+ if (maxFails !== undefined && fails >= maxFails) {
260
+ utils.logger.error(`giving up after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`, error);
261
+ throw error;
262
+ } else {
263
+ utils.logger.warn(`attempt #${fails} failed${errorMessage ? `: ${errorMessage}` : ''}`, error);
264
+ }
265
+ }
257
266
 
258
- while (new Date().getTime() < timeoutTime) {
259
- const block = await this.provider.getBlockNumber();
260
- const logs = await this.provider.getLogs({
261
- fromBlock: Math.max(0, lastBlock - this.deltaBlocksLog),
262
- toBlock: block,
263
- // Nonce change event topic
264
- topics: ['0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881']
265
- });
266
- lastBlock = block; // Get receipts of all transactions
267
+ if (delay > 0) {
268
+ await new Promise(resolve => setTimeout(resolve, delay));
269
+ }
270
+ }
267
271
 
268
- const txs = await Promise.all(logs.map(l => this.provider.getTransactionReceipt(l.transactionHash))); // Find a transaction with a TxExecuted log
272
+ throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`);
273
+ };
269
274
 
270
- const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 && // TxFailed event topic
271
- l.topics[0] === "0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7" && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId))); // If found return that
275
+ const waitReceipt = async function waitReceipt() {
276
+ // Transactions can only get executed on nonce change
277
+ // get all nonce changes and look for metaTxnIds in between logs
278
+ let lastBlock = _this2.fromBlockLog;
272
279
 
273
- if (found) {
274
- return _extends({
275
- receipt: found
276
- }, await this.provider.getTransaction(found.transactionHash));
277
- } // Otherwise wait and try again
280
+ if (lastBlock < 0) {
281
+ const block = await retry(() => _this2.provider.getBlockNumber(), 'unable to get latest block number');
282
+ lastBlock = block + lastBlock;
283
+ }
284
+
285
+ if (typeof metaTxnId !== 'string') {
286
+ throw new Error('impossible');
287
+ }
278
288
 
289
+ const normalMetaTxnId = metaTxnId.replace('0x', '');
279
290
 
280
- await new Promise(r => setTimeout(r, this.waitPollRate));
281
- }
291
+ while (!timedOut) {
292
+ const block = await retry(() => _this2.provider.getBlockNumber(), 'unable to get latest block number');
293
+ const logs = await retry(() => _this2.provider.getLogs({
294
+ fromBlock: Math.max(0, lastBlock - _this2.deltaBlocksLog),
295
+ toBlock: block,
296
+ // Nonce change event topic
297
+ topics: ['0x1f180c27086c7a39ea2a7b25239d1ab92348f07ca7bb59d1438fcf527568f881']
298
+ }), `unable to get NonceChange logs for blocks ${Math.max(0, lastBlock - _this2.deltaBlocksLog)} to ${block}`);
299
+ lastBlock = block; // Get receipts of all transactions
300
+
301
+ const txs = await Promise.all(logs.map(l => retry(() => _this2.provider.getTransactionReceipt(l.transactionHash), `unable to get receipt for transaction ${l.transactionHash}`))); // Find a transaction with a TxExecuted log
302
+
303
+ const found = txs.find(tx => tx.logs.find(l => l.topics.length === 0 && l.data.replace('0x', '') === normalMetaTxnId || l.topics.length === 1 && // TxFailed event topic
304
+ l.topics[0] === '0x3dbd1590ea96dd3253a91f24e64e3a502e1225d602a5731357bc12643070ccd7' && l.data.length >= 64 && l.data.replace('0x', '').startsWith(normalMetaTxnId))); // If found return that
305
+
306
+ if (found) {
307
+ return _extends({
308
+ receipt: found
309
+ }, await retry(() => _this2.provider.getTransaction(found.transactionHash), `unable to get transaction ${found.transactionHash}`));
310
+ } // Otherwise wait and try again
282
311
 
283
- throw new Error(`Timeout waiting for transaction receipt ${metaTxnId}`);
312
+
313
+ if (!timedOut) {
314
+ await new Promise(r => setTimeout(r, delay));
315
+ }
316
+ }
317
+
318
+ throw new Error(`Timeout waiting for transaction receipt ${metaTxnId}`);
319
+ };
320
+
321
+ if (timeout !== undefined) {
322
+ return Promise.race([waitReceipt(), new Promise((_, reject) => setTimeout(() => {
323
+ timedOut = true;
324
+ reject(`Timeout waiting for transaction receipt ${metaTxnId}`);
325
+ }, timeout))]);
326
+ } else {
327
+ return waitReceipt();
328
+ }
284
329
  }
285
330
 
286
331
  }
@@ -329,7 +374,7 @@ class LocalRelayer extends ProviderRelayer {
329
374
  this.txnOptions = transactionRequest;
330
375
  }
331
376
 
332
- async relay(signedTxs, quote) {
377
+ async relay(signedTxs, quote, waitForReceipt = true) {
333
378
  if (quote !== undefined) {
334
379
  utils.logger.warn(`LocalRelayer doesn't accept fee quotes`);
335
380
  }
@@ -348,10 +393,18 @@ class LocalRelayer extends ProviderRelayer {
348
393
  // const gasLimit = signedTxs.transactions.reduce((sum, tx) => sum.add(tx.gasLimit), ethers.BigNumber.from(0))
349
394
  // txRequest.gasLimit = gasLimit
350
395
 
351
- return this.signer.sendTransaction(_extends({
396
+ const responsePromise = this.signer.sendTransaction(_extends({
352
397
  to,
353
398
  data
354
399
  }, this.txnOptions));
400
+
401
+ if (waitForReceipt) {
402
+ const response = await responsePromise;
403
+ response.receipt = await response.wait();
404
+ return response;
405
+ } else {
406
+ return responsePromise;
407
+ }
355
408
  }
356
409
 
357
410
  }
@@ -626,7 +679,8 @@ var relayer_gen = /*#__PURE__*/Object.freeze({
626
679
  Relayer: Relayer
627
680
  });
628
681
 
629
- const FAILED_STATUSES = [ETHTxnStatus.FAILED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.DROPPED];
682
+ const FINAL_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.SUCCEEDED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
683
+ const FAILED_STATUSES = [ETHTxnStatus.DROPPED, ETHTxnStatus.PARTIALLY_FAILED, ETHTxnStatus.FAILED];
630
684
  function isRpcRelayerOptions(obj) {
631
685
  return obj.url !== undefined && typeof obj.url === 'string';
632
686
  }
@@ -637,26 +691,50 @@ class RpcRelayer extends BaseRelayer {
637
691
  this.service = new Relayer(options.url, fetchPonyfill__default["default"]().fetch);
638
692
  }
639
693
 
640
- async waitReceipt(metaTxnHash, wait = 1000) {
641
- if (typeof metaTxnHash !== 'string') {
642
- utils.logger.info('computing id', metaTxnHash.config, metaTxnHash.context, metaTxnHash.chainId, ...metaTxnHash.transactions);
643
- return this.waitReceipt(transactions.computeMetaTxnHash(config.addressOf(metaTxnHash.config, metaTxnHash.context), metaTxnHash.chainId, ...metaTxnHash.transactions));
694
+ async waitReceipt(metaTxnId, delay = 1000, maxFails = 5, isCancelled) {
695
+ if (typeof metaTxnId !== 'string') {
696
+ utils.logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions);
697
+ metaTxnId = transactions.computeMetaTxnHash(config.addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions);
644
698
  }
645
699
 
646
- utils.logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnHash}`);
647
- let result = await this.service.getMetaTxnReceipt({
648
- metaTxID: metaTxnHash
649
- }); // TODO: remove check for 'UNKNOWN' status when 'QUEUED' status is supported
650
- // TODO: fix backend to not return literal 'null' txnReceipt
700
+ utils.logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnId}`);
701
+ let fails = 0;
651
702
 
652
- while (!result.receipt || !result.receipt.txnReceipt || result.receipt.txnReceipt === 'null' || result.receipt.status === 'UNKNOWN' || result.receipt.status === 'QUEUED' || result.receipt.status === 'SENT') {
653
- await new Promise(r => setTimeout(r, wait));
654
- result = await this.service.getMetaTxnReceipt({
655
- metaTxID: metaTxnHash
656
- });
703
+ while (isCancelled === undefined || !isCancelled()) {
704
+ try {
705
+ const {
706
+ receipt
707
+ } = await this.service.getMetaTxnReceipt({
708
+ metaTxID: metaTxnId
709
+ });
710
+
711
+ if (!receipt) {
712
+ throw new Error('missing expected receipt');
713
+ }
714
+
715
+ if (!receipt.txnReceipt) {
716
+ throw new Error('missing expected transaction receipt');
717
+ }
718
+
719
+ if (FINAL_STATUSES.includes(receipt.status)) {
720
+ return {
721
+ receipt
722
+ };
723
+ }
724
+ } catch (e) {
725
+ fails++;
726
+
727
+ if (fails === maxFails) {
728
+ throw e;
729
+ }
730
+ }
731
+
732
+ if (isCancelled === undefined || !isCancelled()) {
733
+ await new Promise(resolve => setTimeout(resolve, delay));
734
+ }
657
735
  }
658
736
 
659
- return result;
737
+ throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`);
660
738
  }
661
739
 
662
740
  async simulate(wallet, ...transactions$1) {
@@ -781,7 +859,9 @@ class RpcRelayer extends BaseRelayer {
781
859
  return nonce;
782
860
  }
783
861
 
784
- async relay(signedTxs, quote) {
862
+ async relay(signedTxs, quote, waitForReceipt = true) {
863
+ var _this = this;
864
+
785
865
  utils.logger.info(`[rpc-relayer/relay] relaying signed meta-transactions ${JSON.stringify(signedTxs)} with quote ${JSON.stringify(quote)}`);
786
866
  let typecheckedQuote;
787
867
 
@@ -814,15 +894,50 @@ class RpcRelayer extends BaseRelayer {
814
894
  quote: typecheckedQuote
815
895
  });
816
896
  utils.logger.info(`[rpc-relayer/relay] got relay result ${JSON.stringify(metaTxn)}`);
817
- return this.wait(metaTxn.txnHash);
897
+
898
+ if (waitForReceipt) {
899
+ return this.wait(metaTxn.txnHash);
900
+ } else {
901
+ const response = {
902
+ hash: metaTxn.txnHash,
903
+ confirmations: 0,
904
+ from: walletAddress,
905
+ wait: _confirmations => Promise.reject(new Error('impossible'))
906
+ };
907
+
908
+ const wait = async function wait(confirmations) {
909
+ var _waitResponse$receipt;
910
+
911
+ if (!_this.provider) {
912
+ throw new Error('cannot wait for receipt, relayer has no provider set');
913
+ }
914
+
915
+ const waitResponse = await _this.wait(metaTxn.txnHash);
916
+ const transactionHash = (_waitResponse$receipt = waitResponse.receipt) == null ? void 0 : _waitResponse$receipt.transactionHash;
917
+
918
+ if (!transactionHash) {
919
+ throw new Error('cannot wait for receipt, unknown native transaction hash');
920
+ }
921
+
922
+ Object.assign(response, waitResponse);
923
+ return _this.provider.waitForTransaction(transactionHash, confirmations);
924
+ };
925
+
926
+ response.wait = wait;
927
+ return response;
928
+ }
818
929
  }
819
930
 
820
- async wait(metaTxnHash, wait = 1000) {
821
- var _this = this;
931
+ async wait(metaTxnId, timeout, delay = 1000, maxFails = 5) {
932
+ var _this2 = this;
822
933
 
934
+ let timedOut = false;
823
935
  const {
824
936
  receipt
825
- } = await this.waitReceipt(metaTxnHash, wait);
937
+ } = await (timeout !== undefined ? Promise.race([this.waitReceipt(metaTxnId, delay, maxFails, () => timedOut), new Promise((_, reject) => setTimeout(() => {
938
+ timedOut = true;
939
+ reject(`Timeout waiting for transaction receipt ${metaTxnId}`);
940
+ }, timeout))]) : this.waitReceipt(metaTxnId, delay, maxFails));
826
941
 
827
942
  if (!receipt.txnReceipt || FAILED_STATUSES.includes(receipt.status)) {
828
943
  throw new MetaTransactionResponseException(receipt);
@@ -833,13 +948,13 @@ class RpcRelayer extends BaseRelayer {
833
948
  blockHash: txReceipt.blockHash,
834
949
  blockNumber: ethers.ethers.BigNumber.from(txReceipt.blockNumber).toNumber(),
835
950
  confirmations: 1,
836
- from: typeof metaTxnHash === 'string' ? undefined : config.addressOf(metaTxnHash.config, metaTxnHash.context),
951
+ from: typeof metaTxnId === 'string' ? undefined : config.addressOf(metaTxnId.config, metaTxnId.context),
837
952
  hash: txReceipt.transactionHash,
838
953
  raw: receipt.txnReceipt,
839
954
  receipt: txReceipt,
840
955
  // extended type which is Sequence-specific. Contains the decoded metaTxReceipt
841
956
  wait: async function (confirmations) {
842
- return _this.provider.waitForTransaction(txReceipt.transactionHash, confirmations);
957
+ return _this2.provider.waitForTransaction(txReceipt.transactionHash, confirmations);
843
958
  }
844
959
  };
845
960
  }