Skip to content

Commit

Permalink
refactor(relayer): Assume only v3 deposits (#1240)
Browse files Browse the repository at this point in the history
Since the v3 launch is now > 1 day old, no operational relayers should
be seeing new v2 events. This commit starts the process of stripping out
v2 support.
  • Loading branch information
pxrl authored Feb 28, 2024
1 parent 87f31c8 commit a0228f8
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 59 deletions.
87 changes: 40 additions & 47 deletions src/relayer/Relayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ export class Relayer {
const unfilledDeposits = await getUnfilledDeposits(spokePoolClients, hubPoolClient, this.config.maxRelayerLookBack);

const maxVersion = configStoreClient.configStoreVersion;
return sdkUtils.filterAsync(unfilledDeposits, async ({ deposit, version, invalidFills, unfilledAmount }) => {
const { quoteTimestamp, depositId, depositor, recipient, originChainId, destinationChainId } = deposit;
return sdkUtils.filterAsync(unfilledDeposits, async ({ deposit, version, invalidFills }) => {
const { depositId, depositor, recipient, originChainId, destinationChainId, inputToken, outputToken } = deposit;
const destinationChain = getNetworkName(destinationChainId);

// If we don't have the latest code to support this deposit, skip it.
Expand All @@ -84,7 +84,7 @@ export class Relayer {
}

// Skip deposits with quoteTimestamp in the future (impossible to know HubPool utilization => LP fee cannot be computed).
if (quoteTimestamp > hubPoolClient.currentTime) {
if (deposit.quoteTimestamp > hubPoolClient.currentTime) {
return false;
}

Expand All @@ -100,7 +100,6 @@ export class Relayer {

// Skip any L1 tokens that are not specified in the config.
// If relayerTokens is an empty list, we'll assume that all tokens are supported.
const inputToken = sdkUtils.getDepositInputToken(deposit);
const l1Token = hubPoolClient.getL1TokenInfoForL2Token(inputToken, originChainId);
if (relayerTokens.length > 0 && !relayerTokens.includes(l1Token.address)) {
this.logger.debug({
Expand All @@ -112,41 +111,34 @@ export class Relayer {
return false;
}

// Filters specific to v3.
if (sdkUtils.isV3Deposit(deposit)) {
// It would be preferable to use host time since it's more reliably up-to-date, but this creates issues in test.
const currentTime = this.clients.spokePoolClients[destinationChainId].getCurrentTime();
if (deposit.fillDeadline <= currentTime) {
return false;
}
// It would be preferable to use host time since it's more reliably up-to-date, but this creates issues in test.
const currentTime = this.clients.spokePoolClients[destinationChainId].getCurrentTime();
if (deposit.fillDeadline <= currentTime) {
return false;
}

if (deposit.exclusivityDeadline > currentTime && getAddress(deposit.exclusiveRelayer) !== this.relayerAddress) {
return false;
}
if (deposit.exclusivityDeadline > currentTime && getAddress(deposit.exclusiveRelayer) !== this.relayerAddress) {
return false;
}

// Filter out deposits that require in-protocol swaps.
// Resolve L1 token and perform additional checks
// @todo: This is only relevant if inputToken and outputToken are equivalent.
const outputToken = sdkUtils.getDepositOutputToken(deposit);
if (!hubPoolClient.areTokensEquivalent(inputToken, originChainId, outputToken, destinationChainId)) {
this.logger.warn({
at: "Relayer::getUnfilledDeposits",
message: "Skipping deposit including in-protocol token swap.",
deposit,
});
return false;
}
if (!hubPoolClient.areTokensEquivalent(inputToken, originChainId, outputToken, destinationChainId)) {
this.logger.warn({
at: "Relayer::getUnfilledDeposits",
message: "Skipping deposit including in-protocol token swap.",
deposit,
});
return false;
}

const destSpokePool = this.clients.spokePoolClients[destinationChainId].spokePool;
const fillStatus = await sdkUtils.relayFillStatus(destSpokePool, deposit, "latest", destinationChainId);
if (fillStatus === FillStatus.Filled) {
this.logger.debug({
at: "Relayer::getUnfilledDeposits",
message: "Skipping deposit that was already filled.",
deposit,
});
return false;
}
const destSpokePool = this.clients.spokePoolClients[destinationChainId].spokePool;
const fillStatus = await sdkUtils.relayFillStatus(destSpokePool, deposit, "latest", destinationChainId);
if (fillStatus === FillStatus.Filled) {
this.logger.debug({
at: "Relayer::getUnfilledDeposits",
message: "Skipping deposit that was already filled.",
deposit,
});
return false;
}

// Skip deposit with message if sending fills with messages is not supported.
Expand Down Expand Up @@ -177,15 +169,16 @@ export class Relayer {
// The relayer should *not* be filling deposits that the HubPool doesn't have liquidity for otherwise the relayer's
// refund will be stuck for potentially 7 days. Note: Filter for supported tokens first, since the relayer only
// queries for limits on supported tokens.
if (acrossApiClient.updatedLimits && unfilledAmount.gt(acrossApiClient.getLimit(l1Token.address))) {
const { inputAmount } = deposit;
if (acrossApiClient.updatedLimits && inputAmount.gt(acrossApiClient.getLimit(l1Token.address))) {
this.logger.warn({
at: "Relayer::getUnfilledDeposits",
message: "😱 Skipping deposit with greater unfilled amount than API suggested limit",
limit: acrossApiClient.getLimit(l1Token.address),
l1Token: l1Token.address,
depositId,
inputAmount: sdkUtils.getDepositInputAmount(deposit),
unfilledAmount: unfilledAmount.toString(),
inputToken,
inputAmount,
originChainId,
transactionHash: deposit.transactionHash,
});
Expand Down Expand Up @@ -231,14 +224,14 @@ export class Relayer {
const unfilledDeposits = await this._getUnfilledDeposits();

// Sum the total unfilled deposit amount per origin chain and set a MDC for that chain.
const unfilledDepositAmountsPerChain: { [chainId: number]: BigNumber } = unfilledDeposits.reduce((agg, curr) => {
const unfilledAmountUsd = profitClient.getFillAmountInUsd(curr.deposit, curr.unfilledAmount);
if (!agg[curr.deposit.originChainId]) {
agg[curr.deposit.originChainId] = bnZero;
}
agg[curr.deposit.originChainId] = agg[curr.deposit.originChainId].add(unfilledAmountUsd);
return agg;
}, {});
const unfilledDepositAmountsPerChain: { [chainId: number]: BigNumber } = unfilledDeposits.reduce(
(agg, { deposit }) => {
const unfilledAmountUsd = profitClient.getFillAmountInUsd(deposit, deposit.outputAmount);
agg[deposit.originChainId] = (agg[deposit.originChainId] ?? bnZero).add(unfilledAmountUsd);
return agg;
},
{}
);

// Sort thresholds in ascending order.
const minimumDepositConfirmationThresholds = Object.keys(config.minDepositConfirmations)
Expand Down
27 changes: 15 additions & 12 deletions src/utils/FillUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import assert from "assert";
import { utils as sdkUtils } from "@across-protocol/sdk-v2";
import { HubPoolClient } from "../clients";
import {
DepositWithBlock,
Fill,
FillsToRefund,
FillWithBlock,
SpokePoolClientsByChain,
V2DepositWithBlock,
V2FillWithBlock,
V3DepositWithBlock,
V3FillWithBlock,
} from "../interfaces";
import { getBlockForTimestamp, getRedisCache, queryHistoricalDepositForFill } from "../utils";
Expand Down Expand Up @@ -256,7 +257,7 @@ export function getFillsInRange(
// due to the additional members and the use of DepositWithBlock instead of Deposit.
// @todo Better alignment with the upstream UnfilledDeposit type.
export type RelayerUnfilledDeposit = {
deposit: DepositWithBlock;
deposit: V3DepositWithBlock;
version: number;
unfilledAmount: BigNumber;
fillCount: number;
Expand Down Expand Up @@ -290,30 +291,32 @@ export async function getUnfilledDeposits(
}

// Iterate over each chainId and check for unfilled deposits.
for (const originClient of Object.values(spokePoolClients)) {
const { chainId: originChainId } = originClient;
const chainIdx = chainIds.indexOf(originChainId);
assert(chainIdx !== -1, `Invalid chain index for chainId ${originChainId} (${chainIdx})`);
const earliestBlockNumber = earliestBlockNumbers[chainIdx];
chainIds.forEach((originChainId, idx) => {
const originClient = spokePoolClients[originChainId];
const earliestBlockNumber = earliestBlockNumbers[idx];

for (const destinationChain of chainIds.filter((chainId) => chainId !== originChainId)) {
// Find all unfilled deposits for the current loops originChain -> destinationChain. Note that this also
// validates that the deposit is filled "correctly" for the given deposit information. This includes validation
// of the all deposit -> relay props, the realizedLpFeePct and the origin->destination token mapping.
const destinationClient = spokePoolClients[destinationChain];
const depositsForDestinationChain: DepositWithBlock[] =
originClient.getDepositsForDestinationChain(destinationChain);
const deposits = originClient
.getDepositsForDestinationChain(destinationChain)
.filter(sdkUtils.isV3Deposit<V3DepositWithBlock, V2DepositWithBlock>);

const unfilledDepositsForDestinationChain = depositsForDestinationChain
const unfilledDepositsForDestinationChain = deposits
.filter((deposit) => deposit.blockNumber >= earliestBlockNumber)
.map((deposit) => {
const version = hubPoolClient.configStoreClient.getConfigStoreVersionForTimestamp(deposit.quoteTimestamp);
return { ...destinationClient.getValidUnfilledAmountForDeposit(deposit), deposit, version };
});

// Remove any deposits that have no unfilled amount and append the remaining deposits to unfilledDeposits array.
unfilledDeposits.push(...unfilledDepositsForDestinationChain.filter((deposit) => deposit.unfilledAmount.gt(0)));
unfilledDeposits.push(
...unfilledDepositsForDestinationChain.filter((deposit) => deposit.unfilledAmount.gt(bnZero))
);
}
}
});

return unfilledDeposits;
}

0 comments on commit a0228f8

Please sign in to comment.