Skip to main content
Our OApp and OFT contract standards inherit the OpenZeppelin Ownable standard by default, which gives deployed contracts flexible, secure administration. But these contracts expose two distinct control roles, the Owner and the Delegate, and decisions about transferring or renouncing them must be made carefully. This page explains what each role controls, why they usually share one address, and how to hand both to a multisig without bricking your configuration.

Contract Ownership Pattern

When you deploy a LayerZero contract, the trailing constructor argument seeds both roles at once:
The single _delegate value is passed to two base constructors:
  • OFT(..., _delegate) registers _delegate as the contract’s Delegate inside the LayerZero Endpoint.
  • Ownable(_delegate) sets _delegate as the contract Owner.
So at deploy time the Owner and the Delegate are the same address. This matches the constructor in our canonical OFT example, examples/oft/contracts/MyOFT.sol (OFT already inherits Ownable, so MyOFT only needs to list OFT and pass Ownable(_delegate) in its constructor).
Some quickstarts name this argument _owner instead of _delegate (see the OFT quickstart and OApp overview). It is the same mechanic with a different label.
The Delegate is stored and enforced by the Endpoint, in its per-OApp delegates mapping, not on your contract. Calling setDelegate() on your OApp writes that value on the Endpoint. See setDelegate in the Endpoint V2 API.

Understanding Owner vs Delegate

The two roles control different layers of your application: The Owner is the only role that can change the Delegate; the Delegate cannot change the Owner. For the full per-role permission table, see Security and roles and the Delegate glossary entry.
RBAC variant: Some OApps replace Ownable with role-based access control, for example the Stablecoin OFT, where DEFAULT_ADMIN_ROLE replaces the single owner and setDelegate() is disabled. The guidance on this page applies to standard Ownable OApp and OFT contracts; see the RBAC reference for that model.

Why They Should Match

Keep the Owner and the Delegate set to the same address unless you have a specific reason to split them:
  • Our wiring tooling (lz:oapp:wire) calls both owner-gated functions (setPeer, setEnforcedOptions) and delegate-gated Endpoint functions (setConfig) in a single run. If the two roles are different addresses, the run reverts on whichever calls the signer is not authorized for: owner-gated calls revert through OpenZeppelin Ownable (OwnableUnauthorizedAccount), and delegate-gated Endpoint calls revert with LZ_Unauthorized from the Endpoint.
  • Fewer privileged addresses means a smaller attack surface and simpler multisig management.
If you do split them, understand that the Delegate independently controls all Endpoint and security configuration, with no signature required from the Owner.

Transferring Control Safely

One ordering rule matters a great deal when you move control to a new address:
Set the Delegate before transferring ownership. Only the current Owner can call setDelegate(). Once you call transferOwnership(), the old key can no longer call owner-gated setters such as setDelegate() or setEnforcedOptions().
If you transfer ownership to your multisig first but leave the old EOA as the Delegate, later Endpoint calls (for example setSendLibrary() during wiring) revert, because the multisig is not the Delegate yet. For that reason our own tooling moves the Delegate first, then the Owner. A correct manual sequence:
With devtools you do not script this by hand: set the delegate and owner fields in layerzero.config.ts, then use the wiring flow and npx hardhat lz:ownable:transfer-ownership. See Adding a delegate and Adding an owner.
Set an explicit Delegate and pin your config. An OApp with no Delegate set (or one whose contract never exposed setDelegate()) cannot configure its own Endpoint settings and is stuck on mutable protocol defaults, which means relying on us to configure DVNs and libraries on its behalf. Set a Delegate and pin your DVN, library, and confirmation config explicitly.

Use a Multisig for Both Roles

Whatever address holds these roles can reconfigure your application, so our recommendation is direct:
Use a multisig for both the contract Owner and the Endpoint Delegate (they can be the same multisig). An EOA owner is low-hanging fruit for attackers. Transfer both roles, not just the Owner.
  • Retain control with a secure multisig. Do not renounce ownership of critical contracts. Transfer both roles to a multisig and choose a quorum that no single party can satisfy alone.
  • Owner and Delegate are equally sensitive. The split is functional (application vs protocol), not a difference in blast radius: each role can ultimately reconfigure peers or security and disrupt your messaging. Secure both equally.
  • Stay flexible. Keeping control lets you adjust peers, delegates, DVN configuration, and enforced options as your crosschain deployment evolves.
  • Document and audit. Record who holds each role, and review your multisig signers and quorum regularly.
Renouncing is a one-way door. Do not renounce ownership unless you intend permanent immutability. Renouncing is irreversible: you can never call owner-gated functions again, so your peers and enforced options are frozen and you can no longer change the Delegate. It is a legitimate way to make an OApp’s peers immutable, but note that it does not touch the Delegate itself: the Delegate keeps its setConfig and message-recovery powers (clear, skip, nilify, burn) until you neutralize it separately.

Transfer to a Safe Multisig

Safe (formerly Gnosis Safe) is the common choice for the Owner and Delegate multisig. Rather than transferring by hand, add a safeConfig block (with safeUrl and safeAddress) to the relevant network in your hardhat.config.ts, then push the ownership and wiring transactions through the Safe for approval with the lz:oapp:wire --safe flag. The full setup is documented in Wiring via Safe multisig. To call transferOwnership(), setDelegate(), or any owner function interactively against your own deployment, use the contracts playground.

Non-EVM Caveats

Owner and Delegate are distinct roles on Solana too, but the address you register is VM-specific and easy to get wrong. With a Squads multisig, the owner and delegate must be the Squads Vault address, not the Multisig Account address; our tooling rejects the Multisig Account address for these roles. You pass the Multisig Account address only through the --multisig-key helper flag, and the tooling derives the Vault (at index 0) from it. Setting the wrong account here is hard to undo, so follow the exact steps in Transferring OFT ownership on Solana.

Summary

  • The Owner controls application policy (setPeer, setEnforcedOptions); the Delegate controls Endpoint and security config (setConfig, libraries, message recovery).
  • They start as the same address at deploy and should usually stay that way; a mismatch makes wiring revert on whichever role’s calls the signer is not authorized for.
  • Set the Delegate before you transfer ownership, because only the Owner can call setDelegate().
  • Use a multisig for both roles. Do not use an EOA for production ownership.
  • Do not renounce ownership unless you intend permanent, unrecoverable immutability.