Docs Navigationexpand_more

Public Relayer

Public Relayer: Permission Context

Format signed EIP-7710 delegations for the relayer permissionContext field.

What permissionContext is

Each entry in transactions[].permissionContext is a signed delegation object in the relayer's JSON format. The relayer uses this to redeem your user's permission on-chain.

This is the most common first-integration mistake: the relayer expects **structured JSON delegation objects**, not ABI-encoded hex from encodeDelegations().

Use toRelayerJson(), not encodeDelegations()

  • encodeDelegations() produces ABI hex for on-chain redemption contracts — do **not** pass this to the relayer.
  • Use a toRelayerJson() helper that converts bigint values to 0x-prefixed hex and Uint8Array values with bytesToHex.
  • See /examples/relayer-first-json-rpc-call.ts for a working implementation.

Required delegation fields

  • delegate — must equal targetAddress from relayer_getCapabilities for the chain. Mismatch causes silent redemption failure.
  • delegator — the smart account address (typically the user's EIP-7702 upgraded EOA).
  • authority — parent delegation hash, or 0x000…000 for a root delegation.
  • caveats — scope enforcers (amount limits, function selectors, expiry).
  • salt — fresh random 32-byte hex per delegation (never reuse).
  • signature — hex signature from smartAccount.signDelegation().

Minimal permissionContext shape

Single delegation in send payload

"permissionContext": [{
  "delegate": "<targetAddress from relayer_getCapabilities>",
  "delegator": "<smart account address>",
  "authority": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "caveats": [],
  "salt": "<fresh 32-byte hex>",
  "signature": "<hex>"
}]

When creating the delegation with @metamask/smart-accounts-kit, set to: targetAddress in createDelegation(). The leaf delegate field in the signed output must match that address.

Protect reusable delegations with delegationSecret

Delegations are designed to be reusable on-chain. After a transaction is mined, the signed delegation remains visible in chain data. Without protection, a third party could copy that delegation and submit a new relayer request that spends the same permission maliciously — especially when caveats are broad.

Pass an optional top-level delegationSecret on relayer_send7710Transaction and relayer_send7710TransactionMultichain send params (omit on estimate). It is **not** part of the delegation signature and never appears on-chain.

Send param (same secret on every submission)

"delegationSecret": "ChooseYourOwnSecretValueAndReuseItButDon'tRevealIt"
  • Pick one app-level secret (10–1024 characters), unrelated to delegation fields. Reuse the same value for every submission from your integration — not one secret per delegation.
  • On the first send with a given delegation + secret, the relayer stores both hashes. Later sends with the same delegation succeed only when the secret matches.
  • If the delegation is already bound and the secret does not match, the relayer rejects the request (4216 Delegation Secret Mismatch).
  • If you omit delegationSecret, unbound delegations are accepted; delegations already bound to a secret are rejected until you supply the matching secret.
  • Tightly scoped delegations (narrow caveats, short expiry) reduce replay risk, but delegationSecret is still recommended for open or long-lived permissions.

Related docs