Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
120 changes: 54 additions & 66 deletions src/clients/SpokePoolClient/SpokePoolClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
Address,
toAddressType,
} from "../../utils";
import { FundsDepositedRaw, FilledRelayRaw } from "./types";
import { duplicateEvent, sortEventsAscendingInPlace } from "../../utils/EventUtils";
import { CHAIN_IDs, ZERO_ADDRESS } from "../../constants";
import {
Expand All @@ -39,7 +40,6 @@ import {
SortableEvent,
SpeedUpWithBlock,
TokensBridged,
RelayExecutionEventInfo,
} from "../../interfaces";
import { BaseAbstractClient, UpdateFailureReason } from "../BaseAbstractClient";
import { AcrossConfigStoreClient } from "../AcrossConfigStoreClient";
Expand Down Expand Up @@ -520,26 +520,28 @@ export abstract class SpokePoolClient extends BaseAbstractClient {

// Performs the indexing of a deposit-like spoke pool event.
const queryDepositEvents = async (eventName: string) => {
const depositEvents = (queryResults[eventsToQuery.indexOf(eventName)] ?? []).map((_event) => {
const event = _event as Omit<
DepositWithBlock,
"depositor" | "recipient" | "inputToken" | "outputToken" | "exclusiveRelayer"
> & {
depositor: string;
recipient: string;
inputToken: string;
outputToken: string;
exclusiveRelayer: string;
};
return {
...event,
depositor: toAddressType(event.depositor, this.chainId),
recipient: toAddressType(event.recipient, event.destinationChainId),
inputToken: toAddressType(event.inputToken, this.chainId),
outputToken: toAddressType(event.outputToken, event.destinationChainId),
exclusiveRelayer: toAddressType(event.exclusiveRelayer, event.destinationChainId),
} as DepositWithBlock;
});
const depositEvents = (queryResults[eventsToQuery.indexOf(eventName)] ?? [])
.map((event) => {
if (!FundsDepositedRaw.is(event)) {
this.log("warn", `Skipping malformed ${eventName} event.`, { event });
return;
}

const deposit: Omit<DepositWithBlock, "quoteBlockNumber" | "fromLiteChain" | "toLiteChain"> = {
...event,
originChainId: this.chainId,
depositor: toAddressType(event.depositor, this.chainId),
recipient: toAddressType(event.recipient, event.destinationChainId),
inputToken: toAddressType(event.inputToken, this.chainId),
outputToken: toAddressType(event.outputToken, event.destinationChainId),
exclusiveRelayer: toAddressType(event.exclusiveRelayer, event.destinationChainId),
messageHash: getMessageHash(event.message),
};

return deposit;
})
.filter(isDefined);

if (depositEvents.length > 0) {
this.log(
"debug",
Expand All @@ -557,19 +559,13 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
const quoteBlockNumber = quoteBlockNumbers[Number(event.quoteTimestamp)];

// Derive and append the common properties that are not part of the onchain event.
const deposit = {
const deposit: DepositWithBlock = {
...event,
messageHash: getMessageHash(event.message),
quoteBlockNumber,
originChainId: this.chainId,
// The following properties are placeholders to be updated immediately.
fromLiteChain: true,
toLiteChain: true,
fromLiteChain: this.isOriginLiteChain(event),
toLiteChain: this.isDestinationLiteChain(event),
};

deposit.fromLiteChain = this.isOriginLiteChain(deposit);
deposit.toLiteChain = this.isDestinationLiteChain(deposit);

if (deposit.outputToken.isZeroAddress()) {
deposit.outputToken = this.getDestinationTokenForDeposit(deposit);
}
Expand Down Expand Up @@ -694,39 +690,31 @@ export abstract class SpokePoolClient extends BaseAbstractClient {

// Performs indexing of filled relay-like events.
const queryFilledRelayEvents = (eventName: string) => {
const fillEvents = (queryResults[eventsToQuery.indexOf(eventName)] ?? []).map((_event) => {
const event = _event as Omit<
FillWithBlock,
| "depositor"
| "recipient"
| "inputToken"
| "outputToken"
| "exclusiveRelayer"
| "relayer"
| "relayExecutionInfo"
> & {
depositor: string;
recipient: string;
inputToken: string;
outputToken: string;
exclusiveRelayer: string;
relayer: string;
relayExecutionInfo: Omit<RelayExecutionEventInfo, "updatedRecipient"> & { updatedRecipient: string };
};
return {
...event,
depositor: toAddressType(event.depositor, event.originChainId),
recipient: toAddressType(event.recipient, this.chainId),
inputToken: toAddressType(event.inputToken, event.originChainId),
outputToken: toAddressType(event.outputToken, this.chainId),
exclusiveRelayer: toAddressType(event.exclusiveRelayer, this.chainId),
relayer: toAddressType(event.relayer, this.chainId),
relayExecutionInfo: {
...event.relayExecutionInfo,
updatedRecipient: toAddressType(event.relayExecutionInfo.updatedRecipient, this.chainId),
},
} as FillWithBlock;
});
const fillEvents = (queryResults[eventsToQuery.indexOf(eventName)] ?? [])
.map((event) => {
if (!FilledRelayRaw.is(event)) {
this.log("warn", `Skipping malformed ${eventName} event.`, { event });
return;
}

const fill: FillWithBlock = {
...event,
destinationChainId: this.chainId,
depositor: toAddressType(event.depositor, event.originChainId),
recipient: toAddressType(event.recipient, this.chainId),
inputToken: toAddressType(event.inputToken, event.originChainId),
outputToken: toAddressType(event.outputToken, this.chainId),
exclusiveRelayer: toAddressType(event.exclusiveRelayer, this.chainId),
relayer: toAddressType(event.relayer, this.chainId),
relayExecutionInfo: {
...event.relayExecutionInfo,
updatedRecipient: toAddressType(event.relayExecutionInfo.updatedRecipient, this.chainId),
},
};

return fill;
})
.filter(isDefined);

if (fillEvents.length > 0) {
this.log("debug", `Using ${fillEvents.length} newly queried ${eventName} events for chain ${this.chainId}`, {
Expand Down Expand Up @@ -904,17 +892,17 @@ export abstract class SpokePoolClient extends BaseAbstractClient {
* @returns True if the deposit originates from a lite chain, false otherwise. If the hub pool client is not defined,
* this method will return false.
*/
protected isOriginLiteChain(deposit: DepositWithBlock): boolean {
protected isOriginLiteChain(deposit: Pick<DepositWithBlock, "originChainId" | "quoteTimestamp">): boolean {
return this.configStoreClient?.isChainLiteChainAtTimestamp(deposit.originChainId, deposit.quoteTimestamp) ?? false;
}

/**
* Determines whether the deposit destination chain is a lite chain.
* @param deposit The deposit to evaluate.
* @returns True if the deposit is destined to a lite chain, false otherwise. If the hub pool client is not defined,
* @returns True if the deposit is destined to a lite chain, false otherwise. If the ConfigStoreClient is not defined,
* this method will return false.
*/
protected isDestinationLiteChain(deposit: DepositWithBlock): boolean {
protected isDestinationLiteChain(deposit: Pick<DepositWithBlock, "destinationChainId" | "quoteTimestamp">): boolean {
return (
this.configStoreClient?.isChainLiteChainAtTimestamp(deposit.destinationChainId, deposit.quoteTimestamp) ?? false
);
Expand Down
36 changes: 36 additions & 0 deletions src/clients/SpokePoolClient/types.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,38 @@
import { assign, number, omit, type, string } from "superstruct";
import { BigNumberValidator } from "../../utils";

export const EVM_SPOKE_POOL_CLIENT_TYPE = "EVM";
export const SVM_SPOKE_POOL_CLIENT_TYPE = "SVM";

const RelayDataRaw = type({
originChainId: number(),
depositId: BigNumberValidator,
depositor: string(),
recipient: string(),
inputToken: string(),
outputToken: string(),
inputAmount: BigNumberValidator,
outputAmount: BigNumberValidator,
quoteTimestamp: number(),
fillDeadline: number(),
message: string(),
exclusivityDeadline: number(),
exclusiveRelayer: string(),
});

export const FundsDepositedRaw = assign(omit(RelayDataRaw, ["originChainId"]), type({ destinationChainId: number() }));

export const FilledRelayRaw = assign(
omit(RelayDataRaw, ["message", "quoteTimestamp"]),
type({
messageHash: string(),
relayer: string(),
repaymentChainId: number(),
relayExecutionInfo: type({
updatedRecipient: string(),
updatedOutputAmount: BigNumberValidator,
updatedMessageHash: string(),
fillType: number(),
}),
})
);
2 changes: 1 addition & 1 deletion src/utils/ValidatorUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BigNumber } from "../utils";

const AddressValidator = define<string>("AddressValidator", (v) => ethersUtils.isAddress(String(v)));
const HexValidator = define<string>("HexValidator", (v) => ethersUtils.isHexString(String(v)));
const BigNumberValidator = define<BigNumber>("BigNumberValidator", (v) => BigNumber.isBigNumber(v));
export const BigNumberValidator = define<BigNumber>("BigNumberValidator", (v) => BigNumber.isBigNumber(v));

const V3DepositSchema = object({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: Refactor to share some definitions with V3DepositSchema. It's unfortunately not composable because it defines all possible fields, but it has the advantage of being more granular in its validation. I would propose to refactor such that it is defined as a product of smaller, composable types.

depositId: BigNumberValidator,
Expand Down
Loading