Overview
The Slow Clap module is responsible for handling incoming bridge functionality. Its core purpose is to enable multiple independent validators to reach a consensus on specific actions occurring on another network. To achieve this, the module relies on events emitted by EVM-compatible chains, which can be retrieved via an EVM JSON-RPC API.
As you can see from the documentation:
fromBlock- The starting block of the range to monitortoBlock- The ending block of the range to monitoraddress- The address of the Gatekeeper smart contract that emits bridging eventstopics- The hashed event signature and argument types
The Gatekeeper contract emits the following event types:
- Ghosted - Indicates that funds are being bridged into the Ghost network.
Arguments: receiver address (on Ghost network) and the amount being bridged - Materialized - Indicates that funds are being bridged out of the Ghost network.
Arguments: receiver address (on EVM chain) and the amount being bridged - Rotated - Signals a rotation of the collective governance key. Note: This functionality is not part of the Slow Clap module.
Block Ranges
As described above, we need to maintain the fromBlock and toBlock values. In an ideal world, it would be great to retrieve all events from the entire history, but real-world constraints make this impractical. For instance, some node operators behind RPC endpoints may disable eth_getLogs entirely or impose very strict rate limits.
That’s why Slow Clap continuously fetches the latest block numbers to ensure we’re operating within a valid range for retrieving events. fromBlock and toBlock are updated during each successful off-chain worker run, which occurs approximately every 6 seconds. The update rules are as follows:
fromBlockmust be less thantoBlockto request all events in the range. If this condition isn’t met, a new block is requested- The block number returned from the RPC response is stored as
toBlock, but with the network’s finality deviation subtracted toBlockcannot increase by more thanmax_block_distancein a single off-chain worker execution. If it would, it is capped attoBlock + max_block_distance
NOTE: due to the fact that not all EVM-based networks switched to PoS, so not all off them has safe, finalized, and etc. block statuses from JSON RPC API. Thus while we are aiming to get to uniformely working for any network we should play around the raw block numbers.
RPCs
Important note: Not all EVM-based networks have transitioned to Proof of Stake, so not all of them support block statuses like safe or finalized via the JSON-RPC API. Since our goal is to have a solution that works uniformly across any network, we must work directly with raw block numbers.
On the other hand, node operators can change their RPC endpoints at any time, which means there’s no guarantee about which validators are using which RPCs (or how many) making the system resistant to targeted attacks.
During each off-chain worker execution, the module retrieves all configured endpoints (default ones are used if no custom endpoints are set; otherwise, only custom endpoints are considered) and attempts to query each of them. The responses are then collected and processed according to the following rules:
- Store the median block number calculated from all successful responses
- “Clap” for the most frequently occurring set of events among all successful responses
- Handle significant deviation if we have an even number of successful responses and the spread between the two middle values exceeds
max_block_distance, special handling is applied - Resolve ties in event sets: If multiple distinct event sets occur with the same highest frequency, offchain-worker marked as failed and going to next one.
Example of strange block median: Given responses [420, 1337] with max_block_distance = 69 , the median would be 878.5. However, since 420 + 69 < 878.5 < 1337 - 69 , this indicates the two groups are too far apart to reliably merge.
Claps & Applauses
Each validator performs a Clap for the events they discover. Validators vote on each bridging transaction using their staked amount. The unique identifier for an EVM transaction is derived from the keccak256 hash of the following components:
- Receiver address
- Bridged amount
- Network ID
When more than 50% of the total staked amount and at least 2/3 + 1 of all validators agree on the exact same transaction, this consensus is called an Applause . At this point, the transaction is executed - funds are created from the void (minted) and the bridging imbalance is recorded.
Commissions are accumulated throughout the era and later distributed to all validators based on their points (i.e., the number of blocks they produced during the era).
If a transaction reaches Applause , any validator who did not clap for it will be slashed and disabled for the remainder of the era. The slash amount is calculated as a percentage equal to the missed amount relative to the total stake of all validators.
Block Commitments
We want to provide users who are bridging with as much transparency as possible. To achieve this, each validator performs a heartbeat every 10 minutes, during which they register their locally stored block number. This is done via an unsigned transaction containing a signed payload which is a small hack to prevent fake validator commitments. The payload includes:
- The block number for the specified network
- The Unix timestamp when the heartbeat occurred
Every 3 commitments, each validator emits a cross-check for block numbers. This means the network evaluates the median block number and identifies validators whose block height is delayed by more than 6 hours, based on the network’s avg_block_speed . It also checks the median value of the registered Unix timestamps to detect silent validators.
Validators that fail this check are slashed and disabled for the remainder of the current era. The slash amount is calculated using the formula:
slash_percentage = pow((delayed_validators - (total_validators * 9%)) * 4, 2)