pallet-relayer Reference
The on-chain surface behind Relay Setup & Fees. For the operator walkthrough, start there.
Extrinsics
| Index | Extrinsic | Origin | Purpose |
|---|---|---|---|
0 | set_min_relay_fee(fee) | ManageOrigin (sudo / gov) | Adjusts the minimum fee required in ZK proofs. Capped by MaxMinRelayFee |
1 | set_allowed_selectors(selectors) | ManageOrigin | Publishes the selector list for off-chain relay services |
2 | register_relayer(evm_address, signature) | Validator (signed) | Binds H160 → AccountId. Requires active-set membership and a signature proving you hold the key |
3 | unregister_relayer() | Validator (signed) | Removes your own binding. Takes no arguments |
register_relayer checks in this order — the first failure is the error you see:
- caller is an active validator →
NotValidator - the address is usable →
InvalidEvmAddress - the signature proves ownership →
BadEvmSignature - the H160 is not already bound →
AlreadyRegistered - your account has no binding yet →
AccountAlreadyRegistered
A validator that unregisters stops receiving fees resolved through the registry, but still receives them as block author when no relayer resolves. To stop entirely, stop authoring.
set_allowed_selectors is advisoryThe list is published for off-chain relay services to read via relay_config().
It is not enforced by the runtime and does not restrict what can be relayed.
Events
relayer
| Event | Meaning |
|---|---|
RelayFeeAccumulated { relayer, asset_id, amount } | Fired on every relayed operation — the one to watch for fee telemetry |
RelayFeesConsumed { relayer, asset_id, amount } | Fees deducted from the pending balance (the claim step) |
RelayerRegistered { evm_address, account } | Registration completed |
RelayerUnregistered { evm_address, account } | Binding removed |
MinRelayFeeUpdated { new_fee } | Governance changed the minimum — wallets should adjust |
AllowedSelectorsUpdated { count } | Selector list republished |
shieldedPool
Fee attribution is decided in the shielded pool, so these three live there —
not in relayer. In Polkadot-JS look under shieldedPool.*:
| Event | Meaning |
|---|---|
RelayFeeDiverted { requested, credited, asset_id, amount } | A relayer address was named but did not resolve; the block author was credited instead |
SelfRelayedFee { author, asset_id, amount } | The resolved relayer was the block author |
ValidatorFeesClaimed { validator, asset_id, amount, commitment, leaf_index } | Claimed fees arrived as a private note |
SelfRelayedFee is not an accusation. With N authors in rotation it occurs about
1/N of the time on its own; only a rate sustained well above that is meaningful,
and adjudication is off-chain.
Errors
| Error | Cause |
|---|---|
NotValidator | Registering from an account that is not in the active set |
InvalidEvmAddress | The address cannot be used as a relay address |
BadEvmSignature | The ownership proof does not verify |
AlreadyRegistered | That H160 already belongs to another account |
AccountAlreadyRegistered | This account already has an address — unregister first. Hit when rotating keys |
NotRegistered | unregister_relayer from an account with no registration |
InsufficientPendingFees | Claiming more than the pending balance |
TooManySelectors | More than 16 selectors passed |
MinRelayFeeTooHigh | Fee above MaxMinRelayFee |
NotValidator exists in two palletsrelayer.NotValidator means "you are not in the active set, so you cannot
register". validatorSet.NotValidator means "this account is not a validator" on
a removal call. Same name, different pallet.
Storage
| Item | Shape |
|---|---|
RelayerRegistry | H160 → AccountId |
RelayerByAccount | AccountId → H160 (reverse index) |
PendingRelayerFees | (AccountId, asset_id) → u128, in planck |
MinRelayFee | u128 |
AllowedSelectors | bounded list, max 16 |
Leaving the validator set clears your binding but not your fees.
PendingRelayerFees is untouched by removal and stays claimable.
RPC methods
relayer_isRelayer(evm_address) → bool
relayer_pendingFees(account, asset_id) → u128 ← unclaimed balance
relayer_registeredEvmAddress(account) → Option<H160>
@orbinum/protocol wraps these as client.relayerStatus.