/// /// import { EventEmitter } from "events"; import { ChainId, ChainName, ParsedVaa, SignedVaa } from "@certusone/wormhole-sdk"; import { ErrorMiddleware, Middleware, Next } from "./compose.middleware.js"; import { Context } from "./context.js"; import { Logger } from "winston"; import { UnrecoverableError } from "bullmq"; import { VaaId } from "./bundle-fetcher.helper.js"; import { RelayJob, Storage } from "./storage/storage.js"; import { Environment } from "./environment.js"; import { Registry } from "prom-client"; import { FetchVaaFn } from "./context.js"; export { UnrecoverableError }; export interface RelayerAppOpts { wormholeRpcs: string[]; concurrency: number; } export type FetchaVaasOpts = { ids: VaaId[]; delayBetweenRequestsInMs?: number; attempts?: number; }; export declare const defaultWormholeRpcs: { mainnet: string[]; testnet: string[]; devnet: string[]; }; export declare const defaultWormscanUrl: { mainnet: string; testnet: string; devnet: string; }; export declare const defaultOpts: (env: Environment) => { wormholeRpcs: string[]; concurrency: number; }; export interface SerializableVaaId { emitterChain: ChainId; emitterAddress: string; sequence: string; } export interface ParsedVaaWithBytes extends ParsedVaa { id: SerializableVaaId; bytes: SignedVaa; } export type FilterFN = (vaaBytes: ParsedVaaWithBytes) => Promise | boolean; export declare enum RelayerEvents { Received = "received", Added = "added", Skipped = "skipped", Completed = "completed", Failed = "failed" } export type ListenerFn = (vaa: ParsedVaaWithBytes, job?: RelayJob) => void; export declare class RelayerApp extends EventEmitter { env: Environment; storage?: Storage; filters: { emitterFilter?: { chainId?: ChainId; emitterAddress?: string; }; }[]; private pipeline?; private errorPipeline?; private chainRouters; private spyUrl?; private rootLogger?; private opts; private vaaFilters; private alreadyFilteredCache; private metrics; private registry; constructor(env?: Environment, opts?: Partial); get metricsRegistry(): Registry; /** * This function will run as soon as a VAA is received and will determine whether we want to process it or skip it. * This is useful if you're listening to a contract but you don't care about every one of the VAAs emitted by it (eg. The Token Bridge contract). * * WARNING: If your function throws, the VAA will be skipped (is this the right behavior?). If you want to process the VAA anyway, catch your errors and return true. * * @param newFilter pass in a function that will receive the raw bytes of the VAA and if it returns `true` or `Promise` the VAA will be processed, otherwise it will be skipped */ filter(newFilter: FilterFN): void; private shouldProcessVaa; on(eventName: RelayerEvents, listener: ListenerFn): this; emit(eventName: RelayerEvents, vaa: ParsedVaaWithBytes, job?: RelayJob, ...args: any): boolean; /** * Allows you to pass an object that specifies a combination of chains with address for which you want to run middleware. * * @example: * ``` * relayerApp.multiple({[CHAIN_ID_SOLANA]: "mysolanaAddress", [ CHAIN_ID_ETH ]: "0xMyEthAddress" }, middleware1, middleware2) * ``` * * This would run `middleware1` and `middleware2` for the address `mysolanaAddress` in Solana and for the address `0xMyEthAddress` in Ethereum. * @param chainsAndAddresses * @param middleware */ multiple(chainsAndAddresses: Partial<{ [k in ChainId]: string[] | string; }>, ...middleware: Middleware[]): void; /** * Pass in a set of middlewares that will run for each request * @example: * ``` * relayerApp.use(logging(logger)); * ``` * @param middleware */ use(...middleware: Middleware[] | ErrorMiddleware[]): void; fetchVaas(opts: FetchaVaasOpts): Promise; /** * Fetches a VAA from a wormhole compatible RPC. * You can specify how many times to retry in case it fails and how long to wait between retries * @param chain emitterChain * @param emitterAddress * @param sequence * @param retryTimeout backoff between retries * @param retries number of attempts */ readonly fetchVaa: FetchVaaFn; /** * processVaa allows you to put a VAA through the pipeline leveraging storage if needed. * @param vaa * @param opts You can use this to extend the context that will be passed to the middleware */ processVaa(vaa: Buffer, opts?: any): Promise; /** * Pushes a vaa through the pipeline. Unless you're the storage service you probably want to use `processVaa`. * @param vaa * @param opts */ private pushVaaThroughPipeline; /** * Gives you a Chain router so you can add middleware on an address. * @example: * ``` * relayerApp.chain(CHAIN_ID_ETH).address("0x0001234abcdef...", middleware1, middleware2); * ``` * * @param chainId */ chain(chainId: ChainId): ChainRouter; /** * A convenient shortcut to subscribe to tokenBridge messages. * @example: * ``` * relayerApp.tokenBridge(["ethereum", CHAIN_ID_SOLANA], middleware1, middleware2) * ``` * * Would run middleware1 and middleware2 for any tokenBridge vaa coming from ethereum or solana. * * @param chainsOrChain * @param handlers */ tokenBridge(chainsOrChain: ChainId[] | ChainName[] | ChainId | ChainName, ...handlers: Middleware[]): this; private spyFilters; /** * Pass in the URL where you have an instance of the spy listening. Usually localhost:7073 * * You can run the spy locally (for TESTNET) by doing: * ``` docker run \ --platform=linux/amd64 \ -p 7073:7073 \ --entrypoint /guardiand \ ghcr.io/wormhole-foundation/guardiand:latest \ spy --nodeKey /node.key --spyRPC "[::]:7073" --network /wormhole/testnet/2/1 --bootstrap 'dns4/t-guardian-01.testnet.xlabs.xyz/udp/8999/quic/p2p/12D3KooWCW3LGUtkCVkHZmVSZHzL3C4WRKWfqAiJPz1NR7dT9Bxh,/dns4/t-guardian-02.testnet.xlabs.xyz/udp/8999/quic/p2p/12D3KooWJXA6goBCiWM8ucjzc4jVUBSqL9Rri6UpjHbkMPErz5zK' * ``` * * You can run the spy locally (for MAINNET) by doing: * ``` docker run \ --platform=linux/amd64 \ -p 7073:7073 \ --entrypoint /guardiand \ ghcr.io/wormhole-foundation/guardiand:latest \ spy --nodeKey /node.key --spyRPC "[::]:7073" --network /wormhole/mainnet/2 --bootstrap '/dns4/wormhole-v2-mainnet-bootstrap.xlabs.xyz/udp/8999/quic/p2p/12D3KooWNQ9tVrcb64tw6bNs2CaNrUGPM7yRrKvBBheQ5yCyPHKC,/dns4/wormhole.mcf.rocks/udp/8999/quic/p2p/12D3KooWDZVv7BhZ8yFLkarNdaSWaB43D6UbQwExJ8nnGAEmfHcU,/dns4/wormhole-v2-mainnet-bootstrap.staking.fund/udp/8999/quic/p2p/12D3KooWG8obDX9DNi1KUwZNu9xkGwfKqTp2GFwuuHpWZ3nQruS1' * ``` * @param url */ spy(url: string): this; /** * Set a logger for the relayer app. Not to be confused with a logger for the middleware. This is for when the relayer app needs to log info/error. * * @param logger */ logger(logger: Logger): void; /** * Configure your storage by passing info redis connection info among other details. * If you are using RelayerApp, and you do not call this method, you will not be using storage. * Which means your VAAS will go straight through the pipeline instead of being added to a queue. * @param storage */ useStorage(storage: Storage): void; private generateChainRoutes; /** * Connect to the spy and start processing VAAs. */ listen(): Promise; private waitForReady; /** * Stop the worker from grabbing more jobs and wait until it finishes with the ones that it has. */ stop(): Promise | undefined; private onVaaFromQueue; } declare class ChainRouter { chainId: ChainId; _addressHandlers: Record>; constructor(chainId: ChainId); /** * Specify an address in native format (eg base58 for solana) and a set of middleware to run when we receive a VAA from that address * @param address * @param handlers */ address: (address: string, ...handlers: Middleware[]) => ChainRouter; spyFilters(): { emitterFilter: ContractFilter; }[]; process(ctx: ContextT, next: Next): Promise; } export type ContractFilter = { emitterAddress: string; chainId: ChainId; };