The Generalised Relayer is built to act as a relayer for Arbitrary Message Bridges (AMBs) using the Generalised Incentives scheme.
The goal for the Generalised Relayer is 2 fold:
- Acts as a reference implementation of a relayer that understands Generalised Incentives.
- Lower the barrier to entry resulting in greater competition which will improve relaying speed, robustness, and resistance to censorship.
Currently, the Relayer supports the following AMBs:
- Wormhole
It also supports a 'Mock' AMB implementation that operates with signed messages (see the Generalised Incentives repository for more information).
Aside from the npm packages specified within package.json
, the Generalised Relayer relies on the following dependencies:
- Redis
ℹ️ There is no need to manually install/run any dependencies when running the Relayer with Docker.
The Relayer configuration is split into 2 distinct files.
⚠️ The Relayer will not run without the following configuration files.
Most of the Relayer configuration is specified within a .yaml
file located at the project's root directory. The configuration file must be named using the config.{$NODE_ENV}.yaml
format according to the environment variable NODE_ENV
of the runtime (e.g. on a production machine where NODE_ENV=production
, the configuration file must be named config.production.yaml
).
The
NODE_ENV
variable should ideally be set on the shell configuration file (i.e..bashrc
or equivalent), but may also be set by prepending it to the launch command, e.g.NODE_ENV=production docker compose up
. For more information see the Node documentation.
The .yaml
configuration file is divided into the following sections:
global
: Defines the global relayer configuration.- The
privateKey
of the account that will submit the relay transactions on all chains must be defined at this point. - Default configuration for the
getter
andsubmitter
can also be specified at this point.
- The
ambs
: The AMBs configuration.- Every AMB configuration must have at least the address of the Generalised Incentives contract that implements the AMB (
incentivesAddress
) at this point, or otherwise within the chain-specific AMB configuration (underchains -> $ambName -> incentivesAddress
).
- Every AMB configuration must have at least the address of the Generalised Incentives contract that implements the AMB (
chains
: Defines the configuration for each of the chains to be supported by the relayer.- This includes the
chainId
and therpc
to be used for the chain. - Each chain may override the global
getter
andsubmitter
configurations (those defined under theglobal
configuration), andamb
configurations.
- This includes the
ℹ️ For a full reference of the configuration file, see
config.example.yaml
.
Ports and docker specific configuration is set on a .env
file within the project's root directory. This includes the COMPOSE_PROFILES
environment variable which defines the docker services to enable (e.g. to enable the docker-compose.yaml
services tagged with the wormhole
profile set COMPOSE_PROFILES=wormhole
).
ℹ️ See
.env.example
for the required environment variables.
The simplest way to run the relayer is via docker compose
(refer to the Docker documentation for installation instructions). Run the Relayer with:
docker compose up [-d]
The -d
option detaches the process to the background.
Install the required dependencies with:
pnpm install
- NOTE: The
devDependencies
are required to build the project. If running on a production machine whereNODE_ENV=production
, usepnpm install --prod=false
Initiate a Redis database with:
docker container run -p 6379:6379 redis
- This command sets
6379
as the port for Redis communication. Make sure this port is correctly set on the.env
configuration file.
Build and start the Relayer with:
pnpm start
For further insight into the requirements for running the Relayer see the docker-compose.yaml
file.
The Relayer is devided into 4 main services: Getter
, Evaluator
, Collector
, and Submitter
. These services work together to get the GeneralisedIncentives message bounties, evaluate their value, collect the message proofs, and submit them on the destination chain. The services are run in parallel and communicate using Redis. Wherever it makes sense, chains are allocated seperate workers to ensure a chain fault doesn't propagate and impact the performance on other chains.
The Getter service is responsible for fetching on-chain bounties and messages. It works by searching for relevant EVM events triggered by the GeneralisedIncentives contract:
BountyPlaced
: Signals that a message has been sent and contains the associated relaying incentives.MessageDelivered
: Signals that a message has been relayed from the source chain to the destination chain (event published on the destination chain).BountyClaimed
: Signals that a message has been relayed from the destination chain to the source chain, and the bounty has been distributed.BountyIncreased
: Signals that the associated relaying incentive has been updated.
The incentive information gathered with these events is sent to the common Redis database for later use by the other services.
The Evaluator takes in messages along with their incentive parameters to estimate if it is worth relaying them. It exposes a method which is called on the submittor for evaluations.
⚠️ The Evaluator is not implemented yet. Currently, simple evaluation logic is written within the Submitter.
The Collector service collects the information to relay the cross-chain messages directly from the various AMB's, as for example the AMB's proofs. Every proof collected is sent to the Submitter via Redis to request the relay of the packet.
The Submitter service gets packets that need relaying from Redis. For every packet received, the submitter:
- Gets the associated bounty information (i.e. the relaying incentive) from Redis.
- Simulates the transaction to get a gas estimate.
- Evaluates whether the relaying bounty covers the gas cost of the packet submission.
- Submits the packet if the evaluation is successful using the processPacket method of the IncentivizedMessageEscrow contract.
- Confirms that the submission transaction is mined.
To make the Submitter as resilitent as possible to RPC failures/connection errors, each evaluation, submission and confirmation step is tried up to maxTries
times with a retryInterval
delay between tries (these default to 3
and 2000
ms, but can be modified on the Relayer config).
The Submitter additionally limits the maximum number of transactions within the 'submission' pipeline (i.e. transactions that have been started to be processed and are not completed), and will not accept any further relay orders once reached. If a submitted transactions fails to commit within the number of specified tries and timeout, the Submitter will attempt to cancel the transaction.
⚠️ If the Submitter fails to cancel a transaction, the Submitter pipeline will stall and no further orders will be processed until the stuck transaction is resolved.
The Relayer has the ability to automatically set the relay transactions gas pricing.
- The
maxFeePerGas
configuration sets the transactionmaxFeePerGas
property. This defines the maximum fee to be paid per gas for a transaction (including both the base fee and the miner fee). If not set, nomaxFeePerGas
is set on the transaction. - The
maxPriorityFeeAdjustmentFactor
determines the amount by which to modify the queried recommendedmaxPriorityFee
from the rpc. If not set, nomaxPriorityFee
is set on the transaction. - The
maxAllowedPriorityFeePerGas
sets the maximum value thatmaxPriorityFee
may be set to (after applying themaxPriorityFeeAdjustmentFactor
).
- The
gasPriceAdjustmentFactor
determines the amount by which to modify the queried recommendedgasPrice
from the rpc. If not set, nogasPrice
is set on the transaction. - The
maxAllowedGasPrice
sets the maximum value thatgasPrice
may be set to (after applying thegasPriceAdjustmentFactor
).
⚠️ If the above gas configuration is not specified, the transactions will be submitted using theethers
/rpc defaults.
If a transaction does not mine in time (maxTries * (confirmationTimeout + retryInterval)
approximately), the Relayer will attempt to reprice the transaction by resubmitting the transaction with higher gas price values. The gas prices are adjusted according to the priorityAdjustmentFactor
configuration. If not set, it defaults to 1.1
(i.e +10%).
The Relayer keeps an estimate of the Relayer account gas balance for each chain. A warning is emitted to the logs if the gas balance falls below a configurable threshold lowBalanceWarning
(in Wei).
The distinct services of the Relayer communicate with each other using a Redis database. To abstract the Redis implementation away, a helper library, store.lib.ts
, is provided.
The Relayer makes available a getAMBs
endpoint with which an external service may query the AMB messages corresponding to a transaction hash.
TODO
In order to add support for a new AMB, a new service folder under collector/
named after the AMB must be added, within which the service file, also named after the AMB, must define the service that collects the AMB proofs. The collector service must send the collected AMB data to Redis using the submitProof
helper of the store/store.lib.ts
library.
⚠️ The Collector service must not block the main event loop in any manner (e.g. make use of worker threads). See the Mock collector implementation for further reference.
The AMB configuration must be added to the .yaml
configuration file under the AMB name. This configuration will be automatically passed to the service once it is instantiated by the main Collector controller (see here).
It is recommended to update the docker-compose.yaml
file with any image/service that is required by the AMB to have a completely standalone implementation when running the Relayer with Docker.
ℹ️ It is also recommended to add a new Docker Compose profile for any image/service added to the
docker-compose.yaml
file so that it can be disabled at the user's will.
ℹ️ Update
config.example.yaml
with the new AMB details for future reference.
The mock implementation is proof-of-authentication (PoA) scheme which works well for developing and testing. To use it, deploy a Mock Generalised Incentive implementation using a known key. Then set the key in the Mock AMB config and run the Relayer.
The Relayer uses ethers
types for the contracts that it interacts with (e.g. the Generalised Incentives contract). These types are generated with the typechain
package using the contract abis (under the abis/
folder) upon installation of the npm
packages. If the contract abis change the types must be regenerated (see the postinstall
script on package.json
).