@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.
@@ -21,7 +21,14 @@ import * as proto from './relayer.gen'
21
21
 
22
22
  export { proto }
23
23
 
24
- const FAILED_STATUSES = [proto.ETHTxnStatus.FAILED, proto.ETHTxnStatus.PARTIALLY_FAILED, proto.ETHTxnStatus.DROPPED]
24
+ const FINAL_STATUSES = [
25
+ proto.ETHTxnStatus.DROPPED,
26
+ proto.ETHTxnStatus.SUCCEEDED,
27
+ proto.ETHTxnStatus.PARTIALLY_FAILED,
28
+ proto.ETHTxnStatus.FAILED
29
+ ]
30
+
31
+ const FAILED_STATUSES = [proto.ETHTxnStatus.DROPPED, proto.ETHTxnStatus.PARTIALLY_FAILED, proto.ETHTxnStatus.FAILED]
25
32
 
26
33
  export interface RpcRelayerOptions extends BaseRelayerOptions {
27
34
  url: string
@@ -39,32 +46,51 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
39
46
  this.service = new proto.Relayer(options.url, fetchPonyfill().fetch)
40
47
  }
41
48
 
42
- async waitReceipt(metaTxnHash: string | SignedTransactions, wait: number = 1000): Promise<proto.GetMetaTxnReceiptReturn> {
43
- if (typeof metaTxnHash !== 'string') {
44
- logger.info('computing id', metaTxnHash.config, metaTxnHash.context, metaTxnHash.chainId, ...metaTxnHash.transactions)
45
- return this.waitReceipt(
46
- computeMetaTxnHash(addressOf(metaTxnHash.config, metaTxnHash.context), metaTxnHash.chainId, ...metaTxnHash.transactions)
47
- )
49
+ async waitReceipt(
50
+ metaTxnId: string | SignedTransactions,
51
+ delay: number = 1000,
52
+ maxFails: number = 5,
53
+ isCancelled?: () => boolean
54
+ ): Promise<proto.GetMetaTxnReceiptReturn> {
55
+ if (typeof metaTxnId !== 'string') {
56
+ logger.info('computing id', metaTxnId.config, metaTxnId.context, metaTxnId.chainId, ...metaTxnId.transactions)
57
+
58
+ metaTxnId = computeMetaTxnHash(addressOf(metaTxnId.config, metaTxnId.context), metaTxnId.chainId, ...metaTxnId.transactions)
48
59
  }
49
60
 
50
- logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnHash}`)
51
- let result = await this.service.getMetaTxnReceipt({ metaTxID: metaTxnHash })
52
-
53
- // TODO: remove check for 'UNKNOWN' status when 'QUEUED' status is supported
54
- // TODO: fix backend to not return literal 'null' txnReceipt
55
- while (
56
- !result.receipt ||
57
- !result.receipt.txnReceipt ||
58
- result.receipt.txnReceipt === 'null' ||
59
- result.receipt.status === 'UNKNOWN' ||
60
- result.receipt.status === 'QUEUED' ||
61
- result.receipt.status === 'SENT'
62
- ) {
63
- await new Promise(r => setTimeout(r, wait))
64
- result = await this.service.getMetaTxnReceipt({ metaTxID: metaTxnHash })
61
+ logger.info(`[rpc-relayer/waitReceipt] waiting for ${metaTxnId}`)
62
+
63
+ let fails = 0
64
+
65
+ while (isCancelled === undefined || !isCancelled()) {
66
+ try {
67
+ const { receipt } = await this.service.getMetaTxnReceipt({ metaTxID: metaTxnId })
68
+
69
+ if (!receipt) {
70
+ throw new Error('missing expected receipt')
71
+ }
72
+
73
+ if (!receipt.txnReceipt) {
74
+ throw new Error('missing expected transaction receipt')
75
+ }
76
+
77
+ if (FINAL_STATUSES.includes(receipt.status as proto.ETHTxnStatus)) {
78
+ return { receipt }
79
+ }
80
+ } catch (e) {
81
+ fails++
82
+
83
+ if (fails === maxFails) {
84
+ throw e
85
+ }
86
+ }
87
+
88
+ if (isCancelled === undefined || !isCancelled()) {
89
+ await new Promise(resolve => setTimeout(resolve, delay))
90
+ }
65
91
  }
66
92
 
67
- return result
93
+ throw new Error(`Cancelled waiting for transaction receipt ${metaTxnId}`)
68
94
  }
69
95
 
70
96
  async simulate(wallet: string, ...transactions: Transaction[]): Promise<SimulateResult[]> {
@@ -180,7 +206,11 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
180
206
  return nonce
181
207
  }
182
208
 
183
- async relay(signedTxs: SignedTransactions, quote?: FeeQuote): Promise<TransactionResponse> {
209
+ async relay(
210
+ signedTxs: SignedTransactions,
211
+ quote?: FeeQuote,
212
+ waitForReceipt: boolean = true
213
+ ): Promise<TransactionResponse<RelayerTxReceipt>> {
184
214
  logger.info(
185
215
  `[rpc-relayer/relay] relaying signed meta-transactions ${JSON.stringify(signedTxs)} with quote ${JSON.stringify(quote)}`
186
216
  )
@@ -213,11 +243,58 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
213
243
 
214
244
  logger.info(`[rpc-relayer/relay] got relay result ${JSON.stringify(metaTxn)}`)
215
245
 
216
- return this.wait(metaTxn.txnHash)
246
+ if (waitForReceipt) {
247
+ return this.wait(metaTxn.txnHash)
248
+ } else {
249
+ const response = {
250
+ hash: metaTxn.txnHash,
251
+ confirmations: 0,
252
+ from: walletAddress,
253
+ wait: (_confirmations?: number): Promise<ethers.providers.TransactionReceipt> => Promise.reject(new Error('impossible'))
254
+ }
255
+
256
+ const wait = async (confirmations?: number): Promise<ethers.providers.TransactionReceipt> => {
257
+ if (!this.provider) {
258
+ throw new Error('cannot wait for receipt, relayer has no provider set')
259
+ }
260
+
261
+ const waitResponse = await this.wait(metaTxn.txnHash)
262
+ const transactionHash = waitResponse.receipt?.transactionHash
263
+
264
+ if (!transactionHash) {
265
+ throw new Error('cannot wait for receipt, unknown native transaction hash')
266
+ }
267
+
268
+ Object.assign(response, waitResponse)
269
+
270
+ return this.provider.waitForTransaction(transactionHash, confirmations)
271
+ }
272
+
273
+ response.wait = wait
274
+
275
+ return response as TransactionResponse
276
+ }
217
277
  }
218
278
 
219
- async wait(metaTxnHash: string | SignedTransactions, wait: number = 1000): Promise<TransactionResponse<RelayerTxReceipt>> {
220
- const { receipt } = await this.waitReceipt(metaTxnHash, wait)
279
+ async wait(
280
+ metaTxnId: string | SignedTransactions,
281
+ timeout?: number,
282
+ delay: number = 1000,
283
+ maxFails: number = 5
284
+ ): Promise<TransactionResponse<RelayerTxReceipt>> {
285
+ let timedOut = false
286
+
287
+ const { receipt } = await (timeout !== undefined
288
+ ? Promise.race([
289
+ this.waitReceipt(metaTxnId, delay, maxFails, () => timedOut),
290
+ new Promise<proto.GetMetaTxnReceiptReturn>((_, reject) =>
291
+ setTimeout(() => {
292
+ timedOut = true
293
+ reject(`Timeout waiting for transaction receipt ${metaTxnId}`)
294
+ }, timeout)
295
+ )
296
+ ])
297
+ : this.waitReceipt(metaTxnId, delay, maxFails))
221
298
 
222
299
  if (!receipt.txnReceipt || FAILED_STATUSES.includes(receipt.status as proto.ETHTxnStatus)) {
223
300
  throw new MetaTransactionResponseException(receipt)
@@ -229,7 +306,7 @@ export class RpcRelayer extends BaseRelayer implements Relayer {
229
306
  blockHash: txReceipt.blockHash,
230
307
  blockNumber: ethers.BigNumber.from(txReceipt.blockNumber).toNumber(),
231
308
  confirmations: 1,
232
- from: typeof metaTxnHash === 'string' ? undefined : addressOf(metaTxnHash.config, metaTxnHash.context),
309
+ from: typeof metaTxnId === 'string' ? undefined : addressOf(metaTxnId.config, metaTxnId.context),
233
310
  hash: txReceipt.transactionHash,
234
311
  raw: receipt.txnReceipt,
235
312
  receipt: txReceipt, // extended type which is Sequence-specific. Contains the decoded metaTxReceipt