@0xsequence/relayer 0.40.5 → 0.41.0
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.
- package/dist/0xsequence-relayer.cjs.dev.js +172 -57
- package/dist/0xsequence-relayer.cjs.prod.js +172 -57
- package/dist/0xsequence-relayer.esm.js +172 -57
- package/dist/declarations/src/index.d.ts +2 -2
- package/dist/declarations/src/local-relayer.d.ts +2 -2
- package/dist/declarations/src/provider-relayer.d.ts +2 -2
- package/dist/declarations/src/rpc-relayer/index.d.ts +3 -3
- package/package.json +5 -5
- package/src/index.ts +6 -2
- package/src/local-relayer.ts +11 -3
- package/src/provider-relayer.ts +115 -49
- package/src/rpc-relayer/index.ts +105 -28
|
@@ -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(
|
|
243
|
-
|
|
244
|
-
}
|
|
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
|
|
249
|
-
|
|
250
|
+
const retry = async function retry(f, errorMessage) {
|
|
251
|
+
let fails = 0;
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
while (!timedOut) {
|
|
254
|
+
try {
|
|
255
|
+
return await f();
|
|
256
|
+
} catch (error) {
|
|
257
|
+
fails++;
|
|
255
258
|
|
|
256
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
272
|
+
throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`);
|
|
273
|
+
};
|
|
269
274
|
|
|
270
|
-
|
|
271
|
-
|
|
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 (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
641
|
-
if (typeof
|
|
642
|
-
utils.logger.info('computing id',
|
|
643
|
-
|
|
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 ${
|
|
647
|
-
let
|
|
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 (
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
821
|
-
var
|
|
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(
|
|
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
|
|
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
|
|
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(
|
|
243
|
-
|
|
244
|
-
}
|
|
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
|
|
249
|
-
|
|
250
|
+
const retry = async function retry(f, errorMessage) {
|
|
251
|
+
let fails = 0;
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
while (!timedOut) {
|
|
254
|
+
try {
|
|
255
|
+
return await f();
|
|
256
|
+
} catch (error) {
|
|
257
|
+
fails++;
|
|
255
258
|
|
|
256
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
272
|
+
throw new Error(`timed out after ${fails} failed attempts${errorMessage ? `: ${errorMessage}` : ''}`);
|
|
273
|
+
};
|
|
269
274
|
|
|
270
|
-
|
|
271
|
-
|
|
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 (
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
641
|
-
if (typeof
|
|
642
|
-
utils.logger.info('computing id',
|
|
643
|
-
|
|
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 ${
|
|
647
|
-
let
|
|
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 (
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
821
|
-
var
|
|
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(
|
|
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
|
|
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
|
|
957
|
+
return _this2.provider.waitForTransaction(txReceipt.transactionHash, confirmations);
|
|
843
958
|
}
|
|
844
959
|
};
|
|
845
960
|
}
|