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

Every active validator is automatically an EVM relayer. The EVM H160 address is derived from the validator's Aura session key by the node at startup — the validator does not generate or manage a separate EVM key. Registration in pallet-relayer is performed by sudo or governance on the validator's behalf, not by the validator themselves.

Substrate AccountId (sr25519)

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

  • Receives and accumulates fees in PendingRelayerFees
  • Identified on-chain as the block author
  • Used as the who argument when governance calls register_relayer
EVM H160 (derived from Aura key)

Derived automatically from the Aura session key at node startup. Registered on-chain by governance.

  • Receives tokens via claim_relay_fees_to_evm
  • Linked to the AccountId via register_relayer(who, evm_address)
  • Logged automatically by the node — the validator shares the log with the team

Both identities are linked on-chain via relayer.registerRelayer(who, evm_address), a call restricted to ManageOrigin (sudo or governance). 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
Governance-managed registry

register_relayer is a privileged call. It can only be submitted by sudo or the governance council — not by the validator account itself. Validators provide the logged EVM address to the Orbinum team, who submits the extrinsic on-chain.


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