@across-protocol/sdk 4.3.58 → 4.3.59
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/cjs/clients/BundleDataClient/BundleDataClient.d.ts +2 -15
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js +45 -332
- package/dist/cjs/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
- package/dist/cjs/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js +5 -0
- package/dist/cjs/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js +4 -3
- package/dist/cjs/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/cjs/clients/HubPoolClient.d.ts +3 -9
- package/dist/cjs/clients/HubPoolClient.js +44 -68
- package/dist/cjs/clients/HubPoolClient.js.map +1 -1
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js +4 -3
- package/dist/cjs/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/cjs/clients/mocks/MockHubPoolClient.js +1 -1
- package/dist/cjs/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/cjs/providers/retryProvider.js +6 -4
- package/dist/cjs/providers/retryProvider.js.map +1 -1
- package/dist/cjs/providers/utils.js +1 -1
- package/dist/cjs/providers/utils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/BundleDataClient.d.ts +2 -15
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js +47 -373
- package/dist/esm/clients/BundleDataClient/BundleDataClient.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js +6 -4
- package/dist/esm/clients/BundleDataClient/utils/DataworkerUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js +6 -1
- package/dist/esm/clients/BundleDataClient/utils/FillUtils.js.map +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js +3 -2
- package/dist/esm/clients/BundleDataClient/utils/PoolRebalanceUtils.js.map +1 -1
- package/dist/esm/clients/HubPoolClient.d.ts +3 -9
- package/dist/esm/clients/HubPoolClient.js +48 -74
- package/dist/esm/clients/HubPoolClient.js.map +1 -1
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js +4 -3
- package/dist/esm/clients/SpokePoolClient/SpokePoolClient.js.map +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/esm/clients/mocks/MockHubPoolClient.js +1 -1
- package/dist/esm/clients/mocks/MockHubPoolClient.js.map +1 -1
- package/dist/esm/providers/retryProvider.js +6 -4
- package/dist/esm/providers/retryProvider.js.map +1 -1
- package/dist/esm/providers/utils.js +2 -2
- package/dist/esm/providers/utils.js.map +1 -1
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts +2 -15
- package/dist/types/clients/BundleDataClient/BundleDataClient.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/DataworkerUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/FillUtils.d.ts.map +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts +1 -1
- package/dist/types/clients/BundleDataClient/utils/PoolRebalanceUtils.d.ts.map +1 -1
- package/dist/types/clients/HubPoolClient.d.ts +3 -9
- package/dist/types/clients/HubPoolClient.d.ts.map +1 -1
- package/dist/types/clients/SpokePoolClient/SpokePoolClient.d.ts.map +1 -1
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts +2 -2
- package/dist/types/clients/mocks/MockHubPoolClient.d.ts.map +1 -1
- package/dist/types/providers/retryProvider.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/clients/BundleDataClient/BundleDataClient.ts +12 -358
- package/src/clients/BundleDataClient/utils/DataworkerUtils.ts +13 -20
- package/src/clients/BundleDataClient/utils/FillUtils.ts +7 -1
- package/src/clients/BundleDataClient/utils/PoolRebalanceUtils.ts +8 -2
- package/src/clients/HubPoolClient.ts +49 -80
- package/src/clients/SpokePoolClient/SpokePoolClient.ts +15 -11
- package/src/clients/mocks/MockHubPoolClient.ts +3 -3
- package/src/providers/retryProvider.ts +6 -4
- package/src/providers/utils.ts +2 -2
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import assert from "assert";
|
|
2
2
|
import _ from "lodash";
|
|
3
3
|
import {
|
|
4
|
-
ProposedRootBundle,
|
|
5
4
|
SlowFillRequestWithBlock,
|
|
6
5
|
SpokePoolClientsByChain,
|
|
7
6
|
FillType,
|
|
@@ -14,7 +13,6 @@ import {
|
|
|
14
13
|
BundleSlowFills,
|
|
15
14
|
ExpiredDepositsToRefundV3,
|
|
16
15
|
Clients,
|
|
17
|
-
CombinedRefunds,
|
|
18
16
|
FillWithBlock,
|
|
19
17
|
Deposit,
|
|
20
18
|
DepositWithBlock,
|
|
@@ -29,10 +27,8 @@ import {
|
|
|
29
27
|
assign,
|
|
30
28
|
fixedPointAdjustment,
|
|
31
29
|
isDefined,
|
|
32
|
-
toBN,
|
|
33
30
|
forEachAsync,
|
|
34
31
|
getBlockRangeForChain,
|
|
35
|
-
getImpliedBundleBlockRanges,
|
|
36
32
|
getRelayEventKey,
|
|
37
33
|
isSlowFill,
|
|
38
34
|
mapAsync,
|
|
@@ -42,7 +38,6 @@ import {
|
|
|
42
38
|
duplicateEvent,
|
|
43
39
|
invalidOutputToken,
|
|
44
40
|
Address,
|
|
45
|
-
getNetworkName,
|
|
46
41
|
toBytes32,
|
|
47
42
|
convertRelayDataParamsToBytes32,
|
|
48
43
|
convertFillParamsToBytes32,
|
|
@@ -50,11 +45,8 @@ import {
|
|
|
50
45
|
import winston from "winston";
|
|
51
46
|
import {
|
|
52
47
|
BundleDataSS,
|
|
53
|
-
getEndBlockBuffers,
|
|
54
48
|
getRefundInformationFromFill,
|
|
55
|
-
|
|
56
|
-
getWidestPossibleExpectedBlockRange,
|
|
57
|
-
isChainDisabled,
|
|
49
|
+
isChainDisabledAtBlock,
|
|
58
50
|
prettyPrintV3SpokePoolEvents,
|
|
59
51
|
V3DepositWithBlock,
|
|
60
52
|
V3FillWithBlock,
|
|
@@ -370,346 +362,6 @@ export class BundleDataClient {
|
|
|
370
362
|
return bundleData;
|
|
371
363
|
}
|
|
372
364
|
|
|
373
|
-
// @dev This function should probably be moved to the InventoryClient since it bypasses loadData completely now.
|
|
374
|
-
async getPendingRefundsFromValidBundles(): Promise<CombinedRefunds[]> {
|
|
375
|
-
const refunds = [];
|
|
376
|
-
if (!this.clients.hubPoolClient.isUpdated) {
|
|
377
|
-
throw new Error("BundleDataClient::getPendingRefundsFromValidBundles HubPoolClient not updated.");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const bundle = this.clients.hubPoolClient.getLatestFullyExecutedRootBundle(
|
|
381
|
-
this.clients.hubPoolClient.latestHeightSearched
|
|
382
|
-
);
|
|
383
|
-
if (bundle !== undefined) {
|
|
384
|
-
refunds.push(await this.getPendingRefundsFromBundle(bundle));
|
|
385
|
-
} // No more valid bundles in history!
|
|
386
|
-
return refunds;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// @dev This function should probably be moved to the InventoryClient since it bypasses loadData completely now.
|
|
390
|
-
// Return refunds from input bundle.
|
|
391
|
-
async getPendingRefundsFromBundle(bundle: ProposedRootBundle): Promise<CombinedRefunds> {
|
|
392
|
-
const nextBundleMainnetStartBlock = this.clients.hubPoolClient.getNextBundleStartBlockNumber(
|
|
393
|
-
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
394
|
-
this.clients.hubPoolClient.latestHeightSearched,
|
|
395
|
-
this.clients.hubPoolClient.chainId
|
|
396
|
-
);
|
|
397
|
-
const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(nextBundleMainnetStartBlock);
|
|
398
|
-
|
|
399
|
-
// Reconstruct latest bundle block range.
|
|
400
|
-
const bundleEvaluationBlockRanges = getImpliedBundleBlockRanges(
|
|
401
|
-
this.clients.hubPoolClient,
|
|
402
|
-
this.clients.configStoreClient,
|
|
403
|
-
bundle
|
|
404
|
-
);
|
|
405
|
-
let combinedRefunds: CombinedRefunds;
|
|
406
|
-
// Here we don't call loadData because our fallback is to approximate refunds if we don't have arweave data, rather
|
|
407
|
-
// than use the much slower loadData to compute all refunds. We don't need to consider slow fills or deposit
|
|
408
|
-
// expiries here so we can skip some steps. We also don't need to compute LP fees as they should be small enough
|
|
409
|
-
// so as not to affect this approximate refund count.
|
|
410
|
-
const arweaveData = await this.loadArweaveData(bundleEvaluationBlockRanges);
|
|
411
|
-
if (!isDefined(arweaveData)) {
|
|
412
|
-
combinedRefunds = await this.getApproximateRefundsForBlockRange(chainIds, bundleEvaluationBlockRanges);
|
|
413
|
-
} else {
|
|
414
|
-
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
415
|
-
combinedRefunds = getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3);
|
|
416
|
-
// If we don't have a spoke pool client for a chain, then we won't be able to deduct refunds correctly for this
|
|
417
|
-
// chain. For most of the pending bundle's liveness period, these past refunds are already executed so this is
|
|
418
|
-
// a reasonable assumption. This empty refund chain also matches what the alternative
|
|
419
|
-
// `getApproximateRefundsForBlockRange` would return.
|
|
420
|
-
Object.keys(combinedRefunds).forEach((chainId) => {
|
|
421
|
-
if (!this.spokePoolClientManager.getClient(Number(chainId))) {
|
|
422
|
-
delete combinedRefunds[Number(chainId)];
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// The latest proposed bundle's refund leaves might have already been partially or entirely executed.
|
|
428
|
-
// We have to deduct the executed amounts from the total refund amounts.
|
|
429
|
-
return this.deductExecutedRefunds(combinedRefunds, bundle);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
// @dev This helper function should probably be moved to the InventoryClient
|
|
433
|
-
async getApproximateRefundsForBlockRange(chainIds: number[], blockRanges: number[][]): Promise<CombinedRefunds> {
|
|
434
|
-
const refundsForChain: CombinedRefunds = {};
|
|
435
|
-
const bundleEndBlockForMainnet = blockRanges[0][1];
|
|
436
|
-
for (const chainId of chainIds) {
|
|
437
|
-
const spokePoolClient = this.spokePoolClientManager.getClient(chainId);
|
|
438
|
-
if (!isDefined(spokePoolClient)) {
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
const chainIndex = chainIds.indexOf(chainId);
|
|
442
|
-
// @dev This function does not account for pre-fill refunds as it is optimized for speed. The way to detect
|
|
443
|
-
// pre-fill refunds is to load all deposits that are unmatched by fills in the spoke pool client's memory
|
|
444
|
-
// and then query the FillStatus on-chain, but that might slow this function down too much. For now, we
|
|
445
|
-
// will live with this expected inaccuracy as it should be small. The pre-fill would have to precede the deposit
|
|
446
|
-
// by more than the caller's event lookback window which is expected to be unlikely.
|
|
447
|
-
const fillsToCount = spokePoolClient.getFills().filter((fill) => {
|
|
448
|
-
if (
|
|
449
|
-
fill.blockNumber < blockRanges[chainIndex][0] ||
|
|
450
|
-
fill.blockNumber > blockRanges[chainIndex][1] ||
|
|
451
|
-
isZeroValueFillOrSlowFillRequest(fill) ||
|
|
452
|
-
invalidOutputToken(fill)
|
|
453
|
-
) {
|
|
454
|
-
return false;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
const originSpokePoolClient = this.spokePoolClientManager.getClient(fill.originChainId);
|
|
458
|
-
// If origin spoke pool client isn't defined, we can't validate it.
|
|
459
|
-
if (!isDefined(originSpokePoolClient)) {
|
|
460
|
-
return false;
|
|
461
|
-
}
|
|
462
|
-
const matchingDeposit = originSpokePoolClient.getDeposit(fill.depositId);
|
|
463
|
-
const hasMatchingDeposit =
|
|
464
|
-
matchingDeposit !== undefined && getRelayEventKey(fill) === getRelayEventKey(matchingDeposit);
|
|
465
|
-
return hasMatchingDeposit;
|
|
466
|
-
});
|
|
467
|
-
await forEachAsync(fillsToCount, async (_fill) => {
|
|
468
|
-
const originChain = getNetworkName(_fill.originChainId);
|
|
469
|
-
const originSpokePoolClient = this.spokePoolClientManager.getClient(_fill.originChainId);
|
|
470
|
-
assert(isDefined(originSpokePoolClient), `No SpokePoolClient for chain ${originChain}`);
|
|
471
|
-
const matchingDeposit = originSpokePoolClient.getDeposit(_fill.depositId);
|
|
472
|
-
assert(
|
|
473
|
-
isDefined(matchingDeposit),
|
|
474
|
-
`No ${originChain} deposit found for ${getNetworkName(_fill.destinationChainId)} fill ${_fill.depositId}`
|
|
475
|
-
);
|
|
476
|
-
|
|
477
|
-
const spokeClient = this.spokePoolClientManager.getClient(_fill.destinationChainId);
|
|
478
|
-
assert(
|
|
479
|
-
isDefined(spokeClient),
|
|
480
|
-
`SpokePoolClient for ${getNetworkName(_fill.destinationChainId)} not found for fill.`
|
|
481
|
-
);
|
|
482
|
-
|
|
483
|
-
let provider;
|
|
484
|
-
if (isEVMSpokePoolClient(spokeClient)) {
|
|
485
|
-
provider = spokeClient.spokePool.provider;
|
|
486
|
-
} else if (isSVMSpokePoolClient(spokeClient)) {
|
|
487
|
-
provider = spokeClient.svmEventsClient.getRpc();
|
|
488
|
-
}
|
|
489
|
-
const fill = await verifyFillRepayment(
|
|
490
|
-
_fill,
|
|
491
|
-
provider!,
|
|
492
|
-
matchingDeposit,
|
|
493
|
-
this.clients.hubPoolClient,
|
|
494
|
-
bundleEndBlockForMainnet
|
|
495
|
-
);
|
|
496
|
-
if (!isDefined(fill)) {
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
const { chainToSendRefundTo, repaymentToken } = getRefundInformationFromFill(
|
|
500
|
-
{
|
|
501
|
-
...fill,
|
|
502
|
-
fromLiteChain: matchingDeposit.fromLiteChain,
|
|
503
|
-
},
|
|
504
|
-
this.clients.hubPoolClient,
|
|
505
|
-
bundleEndBlockForMainnet
|
|
506
|
-
);
|
|
507
|
-
// Assume that lp fees are 0 for the sake of speed. In the future we could batch compute
|
|
508
|
-
// these or make hardcoded assumptions based on the origin-repayment chain direction. This might result
|
|
509
|
-
// in slight over estimations of refunds, but its not clear whether underestimating or overestimating is
|
|
510
|
-
// worst from the relayer's perspective.
|
|
511
|
-
const { relayer, inputAmount: refundAmount } = fill;
|
|
512
|
-
refundsForChain[chainToSendRefundTo] ??= {};
|
|
513
|
-
refundsForChain[chainToSendRefundTo][repaymentToken.toBytes32()] ??= {};
|
|
514
|
-
const existingRefundAmount =
|
|
515
|
-
refundsForChain[chainToSendRefundTo][repaymentToken.toBytes32()][relayer.toBytes32()] ?? bnZero;
|
|
516
|
-
refundsForChain[chainToSendRefundTo][repaymentToken.toBytes32()][relayer.toBytes32()] =
|
|
517
|
-
existingRefundAmount.add(refundAmount);
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
return refundsForChain;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
getUpcomingDepositAmount(chainId: number, l2Token: Address, latestBlockToSearch: number): BigNumber {
|
|
524
|
-
const spokePoolClient = this.spokePoolClientManager.getClient(chainId);
|
|
525
|
-
if (!isDefined(spokePoolClient)) {
|
|
526
|
-
return toBN(0);
|
|
527
|
-
}
|
|
528
|
-
return spokePoolClient
|
|
529
|
-
.getDeposits()
|
|
530
|
-
.filter((deposit) => deposit.blockNumber > latestBlockToSearch && deposit.inputToken.eq(l2Token))
|
|
531
|
-
.reduce((acc, deposit) => {
|
|
532
|
-
return acc.add(deposit.inputAmount);
|
|
533
|
-
}, toBN(0));
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// @dev This function should probably be moved to the InventoryClient since it bypasses loadData completely now.
|
|
537
|
-
// Return refunds from the next valid bundle. This will contain any refunds that have been sent but are not included
|
|
538
|
-
// in a valid bundle with all of its leaves executed. This contains refunds from:
|
|
539
|
-
// - Bundles that passed liveness but have not had all of their pool rebalance leaves executed.
|
|
540
|
-
// - Bundles that are pending liveness
|
|
541
|
-
// - Fills sent after the pending, but not validated, bundle
|
|
542
|
-
async getNextBundleRefunds(): Promise<CombinedRefunds[]> {
|
|
543
|
-
const hubPoolClient = this.clients.hubPoolClient;
|
|
544
|
-
const nextBundleMainnetStartBlock = hubPoolClient.getNextBundleStartBlockNumber(
|
|
545
|
-
this.chainIdListForBundleEvaluationBlockNumbers,
|
|
546
|
-
hubPoolClient.latestHeightSearched,
|
|
547
|
-
hubPoolClient.chainId
|
|
548
|
-
);
|
|
549
|
-
const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(nextBundleMainnetStartBlock);
|
|
550
|
-
const combinedRefunds: CombinedRefunds[] = [];
|
|
551
|
-
|
|
552
|
-
// @dev: If spoke pool client is undefined for a chain, then the end block will be null or undefined, which
|
|
553
|
-
// should be handled gracefully and effectively cause this function to ignore refunds for the chain.
|
|
554
|
-
let widestBundleBlockRanges = await getWidestPossibleExpectedBlockRange(
|
|
555
|
-
chainIds,
|
|
556
|
-
this.spokePoolClientManager.getSpokePoolClients(),
|
|
557
|
-
getEndBlockBuffers(chainIds, this.blockRangeEndBlockBuffer),
|
|
558
|
-
this.clients,
|
|
559
|
-
this.clients.hubPoolClient.latestHeightSearched,
|
|
560
|
-
this.clients.configStoreClient.getEnabledChains(this.clients.hubPoolClient.latestHeightSearched)
|
|
561
|
-
);
|
|
562
|
-
// Return block ranges for blocks after _pendingBlockRanges and up to widestBlockRanges.
|
|
563
|
-
// If a chain is disabled or doesn't have a spoke pool client, return a range of 0
|
|
564
|
-
function getBlockRangeDelta(_pendingBlockRanges: number[][]): number[][] {
|
|
565
|
-
return widestBundleBlockRanges.map((blockRange, index) => {
|
|
566
|
-
// If pending block range doesn't have an entry for the widest range, which is possible when a new chain
|
|
567
|
-
// is added to the CHAIN_ID_INDICES list, then simply set the initial block range to the widest block range.
|
|
568
|
-
// This will produce a block range delta of 0 where the returned range for this chain is [widest[1], widest[1]].
|
|
569
|
-
const initialBlockRange = _pendingBlockRanges[index] ?? blockRange;
|
|
570
|
-
// If chain is disabled, return disabled range
|
|
571
|
-
if (initialBlockRange[0] === initialBlockRange[1]) {
|
|
572
|
-
return initialBlockRange;
|
|
573
|
-
}
|
|
574
|
-
// If pending bundle end block exceeds widest end block or if widest end block is undefined
|
|
575
|
-
// (which is possible if the spoke pool client for the chain is not defined), return an empty range since there are no
|
|
576
|
-
// "new" events to consider for this chain.
|
|
577
|
-
if (!isDefined(blockRange[1]) || initialBlockRange[1] >= blockRange[1]) {
|
|
578
|
-
return [initialBlockRange[1], initialBlockRange[1]];
|
|
579
|
-
}
|
|
580
|
-
// If initialBlockRange][0] > widestBlockRange[0], then we'll ignore any blocks
|
|
581
|
-
// between initialBlockRange[0] and widestBlockRange[0] (inclusive) for simplicity reasons. In practice
|
|
582
|
-
// this should not happen.
|
|
583
|
-
return [initialBlockRange[1] + 1, blockRange[1]];
|
|
584
|
-
});
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// If there is a pending bundle that has not been fully executed, then it should have arweave
|
|
588
|
-
// data so we can load it from there.
|
|
589
|
-
if (hubPoolClient.hasPendingProposal()) {
|
|
590
|
-
const pendingBundleBlockRanges = getImpliedBundleBlockRanges(
|
|
591
|
-
hubPoolClient,
|
|
592
|
-
this.clients.configStoreClient,
|
|
593
|
-
hubPoolClient.getLatestProposedRootBundle()
|
|
594
|
-
);
|
|
595
|
-
// Similar to getAppoximateRefundsForBlockRange, we'll skip the full bundle reconstruction if the arweave
|
|
596
|
-
// data is undefined and use the much faster approximation method which doesn't consider LP fees which is
|
|
597
|
-
// ok for this use case.
|
|
598
|
-
const arweaveData = await this.loadArweaveData(pendingBundleBlockRanges);
|
|
599
|
-
if (!isDefined(arweaveData)) {
|
|
600
|
-
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, pendingBundleBlockRanges));
|
|
601
|
-
} else {
|
|
602
|
-
const { bundleFillsV3, expiredDepositsToRefundV3 } = arweaveData;
|
|
603
|
-
combinedRefunds.push(getRefundsFromBundle(bundleFillsV3, expiredDepositsToRefundV3));
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
// Shorten the widestBundleBlockRanges now to not double count the pending bundle blocks.
|
|
607
|
-
widestBundleBlockRanges = getBlockRangeDelta(pendingBundleBlockRanges);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
// Next, load all refunds sent after the last bundle proposal. This can be expensive so we'll skip the full
|
|
611
|
-
// bundle reconstruction and make some simplifying assumptions:
|
|
612
|
-
// - Only look up fills sent after the pending bundle's end blocks
|
|
613
|
-
// - Skip LP fee computations and just assume the relayer is being refunded the full deposit.inputAmount
|
|
614
|
-
const start = performance.now();
|
|
615
|
-
combinedRefunds.push(await this.getApproximateRefundsForBlockRange(chainIds, widestBundleBlockRanges));
|
|
616
|
-
this.logger.debug({
|
|
617
|
-
at: "BundleDataClient#getNextBundleRefunds",
|
|
618
|
-
message: `Loading approximate refunds for next bundle in ${Math.round(performance.now() - start) / 1000}s.`,
|
|
619
|
-
blockRanges: JSON.stringify(widestBundleBlockRanges),
|
|
620
|
-
});
|
|
621
|
-
return combinedRefunds;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// @dev This helper function should probably be moved to the InventoryClient
|
|
625
|
-
getExecutedRefunds(
|
|
626
|
-
spokePoolClient: SpokePoolClient,
|
|
627
|
-
relayerRefundRoot: string
|
|
628
|
-
): {
|
|
629
|
-
[tokenAddress: string]: {
|
|
630
|
-
[relayer: string]: BigNumber;
|
|
631
|
-
};
|
|
632
|
-
} {
|
|
633
|
-
if (!isDefined(spokePoolClient)) {
|
|
634
|
-
return {};
|
|
635
|
-
}
|
|
636
|
-
// @dev Search from right to left since there can be multiple root bundles with the same relayer refund root.
|
|
637
|
-
// The caller should take caution if they're trying to use this function to find matching refunds for older
|
|
638
|
-
// root bundles as opposed to more recent ones.
|
|
639
|
-
const bundle = _.findLast(
|
|
640
|
-
spokePoolClient.getRootBundleRelays(),
|
|
641
|
-
(bundle) => bundle.relayerRefundRoot === relayerRefundRoot
|
|
642
|
-
);
|
|
643
|
-
if (bundle === undefined) {
|
|
644
|
-
return {};
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const executedRefundLeaves = spokePoolClient
|
|
648
|
-
.getRelayerRefundExecutions()
|
|
649
|
-
.filter((leaf) => leaf.rootBundleId === bundle.rootBundleId);
|
|
650
|
-
const executedRefunds: { [tokenAddress: string]: { [relayer: string]: BigNumber } } = {};
|
|
651
|
-
for (const refundLeaf of executedRefundLeaves) {
|
|
652
|
-
const tokenAddress = refundLeaf.l2TokenAddress.toBytes32();
|
|
653
|
-
const executedTokenRefunds = (executedRefunds[tokenAddress] ??= {});
|
|
654
|
-
|
|
655
|
-
for (let i = 0; i < refundLeaf.refundAddresses.length; i++) {
|
|
656
|
-
const relayer = refundLeaf.refundAddresses[i].toBytes32();
|
|
657
|
-
const refundAmount = refundLeaf.refundAmounts[i];
|
|
658
|
-
|
|
659
|
-
executedTokenRefunds[relayer] ??= bnZero;
|
|
660
|
-
executedTokenRefunds[relayer] = executedTokenRefunds[relayer].add(refundAmount);
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
return executedRefunds;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// @dev This helper function should probably be moved to the InventoryClient
|
|
667
|
-
private deductExecutedRefunds(
|
|
668
|
-
allRefunds: CombinedRefunds,
|
|
669
|
-
bundleContainingRefunds: ProposedRootBundle
|
|
670
|
-
): CombinedRefunds {
|
|
671
|
-
for (const chainIdStr of Object.keys(allRefunds)) {
|
|
672
|
-
const chainId = Number(chainIdStr);
|
|
673
|
-
const spokePoolClient = this.spokePoolClientManager.getClient(chainId);
|
|
674
|
-
if (!isDefined(spokePoolClient)) {
|
|
675
|
-
continue;
|
|
676
|
-
}
|
|
677
|
-
const executedRefunds = this.getExecutedRefunds(spokePoolClient, bundleContainingRefunds.relayerRefundRoot);
|
|
678
|
-
|
|
679
|
-
for (const tokenAddress of Object.keys(allRefunds[chainId])) {
|
|
680
|
-
const refunds = allRefunds[chainId][tokenAddress];
|
|
681
|
-
if (executedRefunds[tokenAddress] === undefined || refunds === undefined) {
|
|
682
|
-
continue;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
for (const relayer of Object.keys(refunds)) {
|
|
686
|
-
const executedAmount = executedRefunds[tokenAddress][relayer];
|
|
687
|
-
if (executedAmount === undefined) {
|
|
688
|
-
continue;
|
|
689
|
-
}
|
|
690
|
-
// Since there should only be a single executed relayer refund leaf for each relayer-token-chain combination,
|
|
691
|
-
// we can deduct this refund and mark it as executed if the executed amount is > 0.
|
|
692
|
-
refunds[relayer] = bnZero;
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
return allRefunds;
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
getRefundsFor(bundleRefunds: CombinedRefunds, relayer: Address, chainId: number, token: Address): BigNumber {
|
|
700
|
-
if (!bundleRefunds[chainId] || !bundleRefunds[chainId][token.toBytes32()]) {
|
|
701
|
-
return BigNumber.from(0);
|
|
702
|
-
}
|
|
703
|
-
const allRefunds = bundleRefunds[chainId][token.toBytes32()];
|
|
704
|
-
return allRefunds && allRefunds[relayer.toBytes32()] ? allRefunds[relayer.toBytes32()] : BigNumber.from(0);
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
getTotalRefund(refunds: CombinedRefunds[], relayer: Address, chainId: number, refundToken: Address): BigNumber {
|
|
708
|
-
return refunds.reduce((totalRefund, refunds) => {
|
|
709
|
-
return totalRefund.add(this.getRefundsFor(refunds, relayer, chainId, refundToken));
|
|
710
|
-
}, bnZero);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
365
|
private async loadArweaveData(blockRangesForChains: number[][]): Promise<LoadDataReturnValue> {
|
|
714
366
|
const arweaveKey = this.getArweaveBundleDataClientKey(blockRangesForChains);
|
|
715
367
|
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
@@ -761,8 +413,8 @@ export class BundleDataClient {
|
|
|
761
413
|
throw new Error("HubPoolClient not updated");
|
|
762
414
|
}
|
|
763
415
|
|
|
764
|
-
const
|
|
765
|
-
const
|
|
416
|
+
const [bundleStartBlockForMainnet, bundleEndBlockForMainnet] = blockRangesForChains[0];
|
|
417
|
+
const chainIds = this.clients.configStoreClient.getChainIdIndicesForBlock(bundleStartBlockForMainnet);
|
|
766
418
|
|
|
767
419
|
if (blockRangesForChains.length > chainIds.length) {
|
|
768
420
|
throw new Error(
|
|
@@ -786,11 +438,6 @@ export class BundleDataClient {
|
|
|
786
438
|
// (2) the fill deadline has passed. We'll need to decrement running balances for these deposits on the
|
|
787
439
|
// destination chain where the slow fill would have been executed.
|
|
788
440
|
|
|
789
|
-
const _isChainDisabled = (chainId: number): boolean => {
|
|
790
|
-
const blockRangeForChain = getBlockRangeForChain(blockRangesForChains, chainId, chainIds);
|
|
791
|
-
return isChainDisabled(blockRangeForChain);
|
|
792
|
-
};
|
|
793
|
-
|
|
794
441
|
const _canCreateSlowFillLeaf = (deposit: DepositWithBlock): boolean => {
|
|
795
442
|
return (
|
|
796
443
|
// Cannot slow fill when input and output tokens are not equivalent.
|
|
@@ -826,7 +473,11 @@ export class BundleDataClient {
|
|
|
826
473
|
// Infer chain ID's to load from number of block ranges passed in.
|
|
827
474
|
const allChainIds = blockRangesForChains
|
|
828
475
|
.map((_blockRange, index) => chainIds[index])
|
|
829
|
-
.filter(
|
|
476
|
+
.filter(
|
|
477
|
+
(chainId) =>
|
|
478
|
+
!isChainDisabledAtBlock(chainId, bundleStartBlockForMainnet, this.clients.configStoreClient) &&
|
|
479
|
+
spokePoolClients[chainId] !== undefined
|
|
480
|
+
);
|
|
830
481
|
allChainIds.forEach((chainId) => {
|
|
831
482
|
const spokePoolClient = spokePoolClients[chainId];
|
|
832
483
|
if (!spokePoolClient.isUpdated) {
|
|
@@ -1676,7 +1327,10 @@ export class BundleDataClient {
|
|
|
1676
1327
|
(
|
|
1677
1328
|
await mapAsync(chainIds, async (chainId, index) => {
|
|
1678
1329
|
const blockRangeForChain = blockRangesForChains[index];
|
|
1679
|
-
if (
|
|
1330
|
+
if (
|
|
1331
|
+
!isDefined(blockRangeForChain) ||
|
|
1332
|
+
isChainDisabledAtBlock(chainId, blockRangesForChains[0][0], this.clients.configStoreClient)
|
|
1333
|
+
) {
|
|
1680
1334
|
return;
|
|
1681
1335
|
}
|
|
1682
1336
|
const [_startBlockForChain, _endBlockForChain] = blockRangeForChain;
|
|
@@ -399,21 +399,15 @@ export function _getMarginalRunningBalances(
|
|
|
399
399
|
([l2TokenAddress, { realizedLpFees: totalRealizedLpFee, totalRefundAmount }]) => {
|
|
400
400
|
// If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
|
|
401
401
|
// there are no relevant L1 running balances.
|
|
402
|
-
if (
|
|
403
|
-
!clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
|
|
404
|
-
toAddressType(l2TokenAddress, repaymentChainId),
|
|
405
|
-
repaymentChainId,
|
|
406
|
-
mainnetBundleEndBlock
|
|
407
|
-
)
|
|
408
|
-
) {
|
|
409
|
-
chainWithRefundsOnly.add(repaymentChainId);
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
402
|
const l1Token = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
413
403
|
toAddressType(l2TokenAddress, repaymentChainId),
|
|
414
404
|
repaymentChainId,
|
|
415
405
|
mainnetBundleEndBlock
|
|
416
406
|
);
|
|
407
|
+
if (!l1Token) {
|
|
408
|
+
chainWithRefundsOnly.add(repaymentChainId);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
417
411
|
const l1TokenAddr = l1Token.toNative();
|
|
418
412
|
assert(l1Token.isEVM(), `Expected an EVM address: ${l1TokenAddr}`);
|
|
419
413
|
|
|
@@ -439,6 +433,9 @@ export function _getMarginalRunningBalances(
|
|
|
439
433
|
destinationChainId,
|
|
440
434
|
mainnetBundleEndBlock
|
|
441
435
|
);
|
|
436
|
+
|
|
437
|
+
assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
|
|
438
|
+
|
|
442
439
|
const lpFee = deposit.lpFeePct.mul(deposit.inputAmount).div(fixedPointAdjustment);
|
|
443
440
|
updateRunningBalance(
|
|
444
441
|
runningBalances,
|
|
@@ -468,6 +465,8 @@ export function _getMarginalRunningBalances(
|
|
|
468
465
|
destinationChainId,
|
|
469
466
|
mainnetBundleEndBlock
|
|
470
467
|
);
|
|
468
|
+
assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
|
|
469
|
+
|
|
471
470
|
const lpFee = deposit.lpFeePct.mul(deposit.inputAmount).div(fixedPointAdjustment);
|
|
472
471
|
updateRunningBalance(
|
|
473
472
|
runningBalances,
|
|
@@ -523,21 +522,15 @@ export function _getMarginalRunningBalances(
|
|
|
523
522
|
deposits.forEach((deposit) => {
|
|
524
523
|
// If the repayment token and repayment chain ID do not map to a PoolRebalanceRoute graph, then
|
|
525
524
|
// there are no relevant L1 running balances.
|
|
526
|
-
if (
|
|
527
|
-
!clients.hubPoolClient.l2TokenHasPoolRebalanceRoute(
|
|
528
|
-
deposit.inputToken,
|
|
529
|
-
deposit.originChainId,
|
|
530
|
-
mainnetBundleEndBlock
|
|
531
|
-
)
|
|
532
|
-
) {
|
|
533
|
-
chainWithRefundsOnly.add(deposit.originChainId);
|
|
534
|
-
return;
|
|
535
|
-
}
|
|
536
525
|
const l1TokenCounterpart = clients.hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
537
526
|
toAddressType(inputToken, originChainId),
|
|
538
527
|
originChainId,
|
|
539
528
|
mainnetBundleEndBlock
|
|
540
529
|
);
|
|
530
|
+
if (!l1TokenCounterpart) {
|
|
531
|
+
chainWithRefundsOnly.add(deposit.originChainId);
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
541
534
|
updateRunningBalance(runningBalances, originChainId, l1TokenCounterpart.toEvmAddress(), deposit.inputAmount);
|
|
542
535
|
});
|
|
543
536
|
});
|
|
@@ -37,18 +37,21 @@ export function getRefundInformationFromFill(
|
|
|
37
37
|
|
|
38
38
|
// Now figure out the equivalent L2 token for the repayment token. If the inputToken doesn't have a
|
|
39
39
|
// PoolRebalanceRoute, then the repayment chain would have been the originChainId after the getRepaymentChainId()
|
|
40
|
-
// call and we would have returned already, so the following call should always
|
|
40
|
+
// call and we would have returned already, so the following call should always return a valid L1 token.
|
|
41
41
|
const l1TokenCounterpart = hubPoolClient.getL1TokenForL2TokenAtBlock(
|
|
42
42
|
relayData.inputToken,
|
|
43
43
|
relayData.originChainId,
|
|
44
44
|
bundleEndBlockForMainnet
|
|
45
45
|
);
|
|
46
46
|
|
|
47
|
+
assert(isDefined(l1TokenCounterpart), "getRefundInformationFromFill: l1TokenCounterpart is undefined");
|
|
48
|
+
|
|
47
49
|
const repaymentToken = hubPoolClient.getL2TokenForL1TokenAtBlock(
|
|
48
50
|
l1TokenCounterpart,
|
|
49
51
|
chainToSendRefundTo,
|
|
50
52
|
bundleEndBlockForMainnet
|
|
51
53
|
);
|
|
54
|
+
assert(isDefined(repaymentToken), "getRefundInformationFromFill: repaymentToken is undefined");
|
|
52
55
|
|
|
53
56
|
return {
|
|
54
57
|
chainToSendRefundTo,
|
|
@@ -183,6 +186,9 @@ function _repaymentChainTokenIsValid(
|
|
|
183
186
|
relayData.originChainId,
|
|
184
187
|
bundleEndBlockForMainnet
|
|
185
188
|
);
|
|
189
|
+
if (!l1TokenCounterpart) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
186
192
|
if (
|
|
187
193
|
!hubPoolClient.l2TokenEnabledForL1TokenAtBlock(
|
|
188
194
|
l1TokenCounterpart,
|
|
@@ -98,8 +98,12 @@ export async function getWidestPossibleExpectedBlockRange(
|
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
export function
|
|
102
|
-
|
|
101
|
+
export function isChainDisabledAtBlock(
|
|
102
|
+
chainId: number,
|
|
103
|
+
mainnetBlock: number,
|
|
104
|
+
configStoreClient: AcrossConfigStoreClient
|
|
105
|
+
): boolean {
|
|
106
|
+
return configStoreClient.getDisabledChainsForBlock(mainnetBlock).includes(chainId);
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
// Note: this function computes the intended transfer amount before considering the transfer threshold.
|
|
@@ -202,6 +206,8 @@ export function updateRunningBalanceForDeposit(
|
|
|
202
206
|
deposit.originChainId,
|
|
203
207
|
mainnetBundleEndBlock
|
|
204
208
|
);
|
|
209
|
+
assert(isDefined(l1TokenCounterpart), "updateRunningBalanceForDeposit: l1TokenCounterpart is undefined");
|
|
210
|
+
|
|
205
211
|
updateRunningBalance(runningBalances, deposit.originChainId, l1TokenCounterpart.toEvmAddress(), updateAmount);
|
|
206
212
|
}
|
|
207
213
|
|