Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve(relayer): Batch query deposit fill status #1338

Merged
merged 27 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5150ff8
refactor(relayer): Factor out MDC computation
pxrl Mar 20, 2024
44bdb9a
refactor(relayer): Defer deposit MDCs check
pxrl Mar 20, 2024
c34ba19
Merge branch 'master' into pxrl/mdcRefactor
pxrl Mar 20, 2024
244127d
Merge branch 'pxrl/mdcRefactor' into pxrl/deferMDCCheck
pxrl Mar 20, 2024
bfef865
refactor(relayer): Relocate profitability & fill execution
pxrl Mar 20, 2024
489bc6f
simplify
pxrl Mar 20, 2024
55df690
Merge remote-tracking branch 'origin/pxrl/deferMDCCheck' into pxrl/re…
pxrl Mar 20, 2024
78eb55b
lint
pxrl Mar 20, 2024
4e618bb
Exit early
pxrl Mar 20, 2024
9e13803
fixes
pxrl Mar 20, 2024
1df1757
Merge remote-tracking branch 'origin/pxrl/deferMDCCheck' into pxrl/re…
pxrl Mar 20, 2024
40114d6
Merge remote-tracking branch 'origin/master' into pxrl/relayerRefacto…
pxrl Mar 21, 2024
0b3ccf8
Restore logging
pxrl Mar 21, 2024
89cf27b
Merge branch 'master' into pxrl/relayerRefactorEvaluate
pxrl Mar 21, 2024
5a246b9
Merge branch 'master' into pxrl/relayerRefactorEvaluate
pxrl Mar 21, 2024
3eb1877
improve(relayer): Batch query deposit fill status
pxrl Mar 21, 2024
4816ba4
Merge branch 'master' into pxrl/relayerRefactorEvaluate
pxrl Mar 21, 2024
fe7cbc2
Merge branch 'pxrl/relayerRefactorEvaluate' into pxrl/batchFillStatus
pxrl Mar 21, 2024
bf892e2
Improve test
pxrl Mar 21, 2024
05c7bdd
lint
pxrl Mar 21, 2024
8c7b76d
Tweak filter
pxrl Mar 21, 2024
da509bb
Restore comment
pxrl Mar 21, 2024
b72b550
Fix test
pxrl Mar 21, 2024
9d9225f
Remove redundant Object.values()
pxrl Mar 21, 2024
5d76888
Merge branch 'pxrl/relayerRefactorEvaluate' into pxrl/batchFillStatus
pxrl Mar 21, 2024
29e1bb8
Merge branch 'master' into pxrl/batchFillStatus
pxrl Mar 22, 2024
2a79c3a
Bump sdk & contracts
pxrl Mar 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
},
"dependencies": {
"@across-protocol/constants-v2": "1.0.14",
"@across-protocol/contracts-v2": "2.5.3",
"@across-protocol/sdk-v2": "0.22.16",
"@across-protocol/contracts-v2": "2.5.4",
"@across-protocol/sdk-v2": "0.22.19",
"@arbitrum/sdk": "^3.1.3",
"@consensys/linea-sdk": "^0.2.1",
"@defi-wonderland/smock": "^2.3.5",
Expand Down
13 changes: 1 addition & 12 deletions src/relayer/Relayer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { utils as sdkUtils } from "@across-protocol/sdk-v2";
import { utils as ethersUtils } from "ethers";
import { FillStatus, L1Token, V3Deposit, V3DepositWithBlock } from "../interfaces";
import { L1Token, V3Deposit, V3DepositWithBlock } from "../interfaces";
import {
BigNumber,
bnZero,
Expand Down Expand Up @@ -128,17 +128,6 @@ export class Relayer {
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.
if (!this.config.sendingMessageRelaysEnabled && !isMessageEmpty(resolveDepositMessage(deposit))) {
this.logger.warn({
Expand Down
37 changes: 22 additions & 15 deletions src/utils/FillUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HubPoolClient, SpokePoolClient } from "../clients";
import {
Fill,
FillsToRefund,
FillStatus,
FillWithBlock,
SpokePoolClientsByChain,
V2DepositWithBlock,
Expand Down Expand Up @@ -258,11 +259,12 @@ export function getFillsInRange(
// @todo Better alignment with the upstream UnfilledDeposit type.
export type RelayerUnfilledDeposit = {
deposit: V3DepositWithBlock;
fillStatus: number;
version: number;
invalidFills: Fill[];
};

// @description Returns an array of unfilled deposits over all spokePoolClients.
// @description Returns all unfilled deposits, indexed by destination chain.
// @param spokePoolClients Mapping of chainIds to SpokePoolClient objects.
// @param configStoreClient ConfigStoreClient instance.
// @param depositLookBack Deposit lookback (in seconds) since SpokePoolClient time as at last update.
Expand Down Expand Up @@ -291,34 +293,39 @@ export async function getUnfilledDeposits(
}

// Iterate over each chainId and check for unfilled deposits.
chainIds.forEach((destinationChainId) => {
await sdkUtils.mapAsync(chainIds, async (destinationChainId) => {
const destinationClient = spokePoolClients[destinationChainId];

unfilledDeposits[destinationChainId] = chainIds
// For each destination chain, query each _other_ SpokePool for deposits within the lookback.
const deposits = chainIds
.filter((chainId) => chainId !== destinationChainId)
.map((originChainId) => {
const originClient = spokePoolClients[originChainId];
const earliestBlockNumber = originFromBlocks[originChainId];
const { deploymentBlock, latestBlockSearched } = originClient;

// Basic sanity check...
assert(
earliestBlockNumber >= originClient.deploymentBlock && earliestBlockNumber <= originClient.latestBlockSearched
);
assert(earliestBlockNumber >= deploymentBlock && earliestBlockNumber <= latestBlockSearched);

// 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.
// Find all unfilled deposits for the current loops originChain -> destinationChain.
return originClient
.getDepositsForDestinationChain(destinationChainId)
.filter((deposit) => deposit.blockNumber >= earliestBlockNumber)
.filter(sdkUtils.isV3Deposit<V3DepositWithBlock, V2DepositWithBlock>) // @todo: Remove after v2 deprecated.
.map((deposit) => {
const version = hubPoolClient.configStoreClient.getConfigStoreVersionForTimestamp(deposit.quoteTimestamp);
return { ...destinationClient.getValidUnfilledAmountForDeposit(deposit), deposit, version };
})
.filter(({ unfilledAmount }) => unfilledAmount.gt(bnZero));
.filter(sdkUtils.isV3Deposit<V3DepositWithBlock, V2DepositWithBlock>); // @todo: Remove after v2 deprecated.
})
.flat();

// Resolve the latest fill status for each deposit and filter out any that are now filled.
const { spokePool } = destinationClient;
const fillStatus = await sdkUtils.fillStatusArray(spokePool, deposits);
unfilledDeposits[destinationChainId] = deposits
.map((deposit, idx) => ({ deposit, fillStatus: fillStatus[idx] }))
.filter(({ fillStatus }) => fillStatus !== FillStatus.Filled)
.map(({ deposit, fillStatus }) => {
const version = hubPoolClient.configStoreClient.getConfigStoreVersionForTimestamp(deposit.quoteTimestamp);
const { invalidFills } = destinationClient.getValidUnfilledAmountForDeposit(deposit);
return { deposit, version, fillStatus, invalidFills };
});
});

return unfilledDeposits;
Expand Down
36 changes: 35 additions & 1 deletion test/Relayer.BasicFill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { clients, constants, utils as sdkUtils } from "@across-protocol/sdk-v2";
import { AcrossApiClient, ConfigStoreClient, MultiCallerClient, TokenClient } from "../src/clients";
import { V2FillWithBlock, V3FillWithBlock } from "../src/interfaces";
import { CONFIG_STORE_VERSION } from "../src/common";
import { bnOne } from "../src/utils";
import { bnOne, getUnfilledDeposits } from "../src/utils";
import { Relayer } from "../src/relayer/Relayer";
import { RelayerConfig } from "../src/relayer/RelayerConfig"; // Tested
import {
Expand All @@ -28,6 +28,7 @@ import {
enableRoutesOnHubPool,
ethers,
expect,
fillV3Relay,
getLastBlockTime,
getV3RelayHash,
lastSpyLogIncludes,
Expand Down Expand Up @@ -235,6 +236,39 @@ describe("Relayer: Check for Unfilled Deposits and Fill", async function () {
expect(multiCallerClient.transactionCount()).to.equal(0); // no new transactions were enqueued.
});

it("Queries the latest onchain fill status for all deposits", async function () {
const deposit = await depositV3(
spokePool_1,
destinationChainId,
depositor,
inputToken,
inputAmount,
outputToken,
outputAmount
);
await updateAllClients();
let unfilledDeposits = await getUnfilledDeposits(spokePoolClients, hubPoolClient);
expect(Object.values(unfilledDeposits).flat().length).to.equal(1);

await relayerInstance.checkForUnfilledDepositsAndFill();
expect(lastSpyLogIncludes(spy, "Filling v3 deposit")).to.be.true;
expect(multiCallerClient.transactionCount()).to.equal(1); // One transaction, filling the one deposit.

// Verify that the deposit is still unfilled (relayer didn't execute it).
unfilledDeposits = await getUnfilledDeposits(spokePoolClients, hubPoolClient);
expect(Object.values(unfilledDeposits).flat().length).to.equal(1);

// Fill the deposit and immediately check for unfilled deposits (without SpokePoolClient update).
await fillV3Relay(spokePool_2, deposit, relayer);
unfilledDeposits = await getUnfilledDeposits(spokePoolClients, hubPoolClient);
expect(Object.values(unfilledDeposits).flat().length).to.equal(0);

// Verify that the relayer now sees that the deposit has been filled.
await relayerInstance.checkForUnfilledDepositsAndFill();
expect(lastSpyLogIncludes(spy, "0 unfilled deposits")).to.be.true;
expect(multiCallerClient.transactionCount()).to.equal(0);
});

it("Respects configured relayer routes", async function () {
relayerInstance = new Relayer(
relayer.address,
Expand Down
14 changes: 6 additions & 8 deletions test/Relayer.UnfilledDeposits.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as contracts from "@across-protocol/contracts-v2/dist/test-utils";
import { clients, utils as sdkUtils } from "@across-protocol/sdk-v2";
import { AcrossApiClient, ConfigStoreClient, MultiCallerClient, TokenClient } from "../src/clients";
import { FillStatus } from "../src/interfaces";
import {
CHAIN_ID_TEST_LIST,
amountToLp,
Expand Down Expand Up @@ -183,9 +184,8 @@ describe("Relayer: Unfilled Deposits", async function () {
[...deposits]
.sort((a, b) => (a.destinationChainId > b.destinationChainId ? 1 : -1))
.map((deposit) => ({
unfilledAmount: deposit.outputAmount,
deposit,
fillCount: 0,
fillStatus: FillStatus.Unfilled,
invalidFills: [],
version: configStoreClient.configStoreVersion,
}))
Expand Down Expand Up @@ -216,9 +216,8 @@ describe("Relayer: Unfilled Deposits", async function () {
.excludingEvery(["realizedLpFeePct", "quoteBlockNumber"])
.to.deep.equal([
{
unfilledAmount: deposit.outputAmount,
deposit: deposit,
fillCount: 0,
deposit,
fillStatus: FillStatus.Unfilled,
invalidFills: [invalidFill],
version: configStoreClient.configStoreVersion,
},
Expand Down Expand Up @@ -372,9 +371,8 @@ describe("Relayer: Unfilled Deposits", async function () {
.excludingEvery(["realizedLpFeePct", "quoteBlockNumber"])
.to.deep.equal([
{
unfilledAmount: deposit.outputAmount,
deposit: deposit,
fillCount: 0,
deposit,
fillStatus: FillStatus.Unfilled,
invalidFills: [invalidFill],
version: configStoreClient.configStoreVersion,
},
Expand Down
34 changes: 12 additions & 22 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,17 @@
"@uma/common" "^2.17.0"
hardhat "^2.9.3"

"@across-protocol/[email protected]":
"@across-protocol/[email protected]", "@across-protocol/constants-v2@^1.0.14":
version "1.0.14"
resolved "https://registry.yarnpkg.com/@across-protocol/constants-v2/-/constants-v2-1.0.14.tgz#2eb6624c306db3f184293d8abb023d2354abadce"
integrity sha512-7C8hyH/7aDh8AOd5DLQrtcey6Ip4Q6o8FmSiDrBnxcD/aVeH+L8cZhh/hRMLm8CA5Olx4usEarfEfZ2rN0AZfg==

"@across-protocol/constants-v2@^1.0.11":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@across-protocol/constants-v2/-/constants-v2-1.0.11.tgz#58d34b5cb50351d097f2ca43c5a30b5908faed7c"
integrity sha512-RpseYB2QxGyfyrfXtUeFxUSCUW1zqu442QFzsdD1LBZtymuzdHuL2MwtTdmRRnJSvzRTFTtlRh4bYDoExSb5zQ==

"@across-protocol/constants-v2@^1.0.12":
version "1.0.12"
resolved "https://registry.yarnpkg.com/@across-protocol/constants-v2/-/constants-v2-1.0.12.tgz#a85e8d39efa9c5294a368e229eab65357f00645e"
integrity sha512-UrrPOxV/+FjCHmlMnezviAMXDiGDzUNCwKC2ifQ0U8PGa+lNulb7KCGNIEIFAFLHNn+/CMFST5Vwf+aAqbumvQ==

"@across-protocol/[email protected]":
version "2.5.3"
resolved "https://registry.yarnpkg.com/@across-protocol/contracts-v2/-/contracts-v2-2.5.3.tgz#6e4d656e155a5b6ce0202dad5f3bc132e867f484"
integrity sha512-eIOX7K7NFS0KhVCZDU+ka+UPWq4UNf6Nht5YoutgrtMYC4tiubBAJN69auE+zClVniixKf0uz5klkDs988XhgA==
"@across-protocol/[email protected]":
version "2.5.4"
resolved "https://registry.yarnpkg.com/@across-protocol/contracts-v2/-/contracts-v2-2.5.4.tgz#7e1b6ff26d159abdad3a0ac51991f09df0f33f74"
integrity sha512-LdHN2XQIrzj3CyzVlYY69ppeLxohwjwzsAaRtZ7nbB/HsVwUuJJoCjoUAV+ePGjqKhxN2lojFFlni7uMnSUeRw==
dependencies:
"@across-protocol/constants-v2" "^1.0.11"
"@across-protocol/constants-v2" "^1.0.14"
"@defi-wonderland/smock" "^2.3.4"
"@eth-optimism/contracts" "^0.5.40"
"@ethersproject/abstract-provider" "5.7.0"
Expand All @@ -55,14 +45,14 @@
"@openzeppelin/contracts" "4.1.0"
"@uma/core" "^2.18.0"

"@across-protocol/[email protected].16":
version "0.22.16"
resolved "https://registry.yarnpkg.com/@across-protocol/sdk-v2/-/sdk-v2-0.22.16.tgz#5e898dfcd98f7805e8deea350aef1fcc591ac458"
integrity sha512-HgtcoF1m7SFETy5gThCGm1tPJdYpgCTYMDNtIl2c2CiN1LsJ9i29+VeEzLA13/rcEwDMZIQpDahOODc02e1yaQ==
"@across-protocol/[email protected].19":
version "0.22.19"
resolved "https://registry.yarnpkg.com/@across-protocol/sdk-v2/-/sdk-v2-0.22.19.tgz#65531294f3ccaacf3b2323f400e530a1e2ab6210"
integrity sha512-7b6eT9nKKnrBZVxzB+h3QsSNonsQbCfnbN3vSME/ZPjCmJGgWJj1O8LVwLn/6xNpOoLg13TD6aOkZQAjG+Oeyg==
dependencies:
"@across-protocol/across-token" "^1.0.0"
"@across-protocol/constants-v2" "^1.0.12"
"@across-protocol/contracts-v2" "2.5.3"
"@across-protocol/constants-v2" "^1.0.14"
"@across-protocol/contracts-v2" "2.5.4"
"@eth-optimism/sdk" "^3.2.2"
"@pinata/sdk" "^2.1.0"
"@types/mocha" "^10.0.1"
Expand Down
Loading