Skip to main content

Relayers

Private operations in Orbinum — unshield and private_transfer — are submitted as unsigned Substrate extrinsics (ensure_none). There is no signing account, so there is no public address that can be correlated with the private operation.

The fee for including the transaction is not a standard gas payment. It is embedded directly inside the ZK proof as a public input, deducted from the user's note value. The block author — always an Aura validator — includes the unsigned extrinsic and receives the embedded fee, attributed automatically by pallet-shielded-pool.

This means every Aura validator is an implicit relayer. There is no separate relay process, no relay service URL, and no special node configuration required to handle private transactions.


What the relay model solves

No public signer on private extrinsics

unshield and private_transfer use ensure_none — there is no account to deduct gas from. The embedded fee in the ZK proof compensates the validator who authors the block.

Public signing would leak identity

If the user submitted from a public account, that account could be correlated with the private operation. Unsigned extrinsics avoid this entirely — the authorization is the ZK proof itself.


Validator registration

To receive fee attribution, a validator must register an EVM H160 address in pallet-relayer. This is done through the register_relayer(evm_address) extrinsic, which is restricted to active Aura validators by T::IsValidator::contains(&who).

Substrate AccountId (sr25519)

The validator's consensus identity. Used by Aura for block authorship.

  • Receives and accumulates fees in PendingRelayerFees
  • Signs the register_relayer extrinsic
  • Identified on-chain as the block author
EVM H160 (ECDSA)

An EVM address controlled by the validator, registered for fee claiming.

  • Receives tokens via claim_relay_fees_to_evm
  • Linked to the AccountId via register_relayer
  • Not used for signing relay transactions

Both identities are linked on-chain when the validator calls register_relayer(evm_address). This writes two indexes:

  • RelayerRegistry[H160] → AccountId — used to attribute fees when a relayed call is executed
  • RelayerByAccount[AccountId] → H160 — used to transfer claimed fees to the EVM account
Validators only

The register_relayer extrinsic is rejected with NotValidator if the caller is not an active Aura validator. There is no whitelist-based or open relay registry.


Where the fee comes from

The relay fee is not a standard gas payment. It is a value committed inside the ZK proof itself:

// Unshield circuit constraint:
note_value === amount + fee

// Transfer circuit constraint:
input_sum === output_sum + fee

The circuit enforces that fee is deducted from the user's note. Changing the fee value after proof generation causes verification to fail. The fee is a public signal — visible to anyone — but immutable once proved.

After on-chain verification, pallet-shielded-pool identifies the current block author's AccountId and increments PendingRelayerFees[AccountId][asset_id]. The fee remains physically inside the shielded pool until the validator claims it.


System component overview


Key components

ComponentLocationRole
pallet-relayerframe/relayer/Registry, fee accounting, RelayerInterface
pallet-shielded-poolframe/shielded-pool/Executes the private op, attributes fee to block author
pallet-zk-verifierframe/zk-verifier/Verifies the Groth16 proof on-chain
ShieldedPool precompileprecompiles/shielded-pool/EVM path: bridges user-signed EVM tx into Substrate

Section contents