> ## Documentation Index
> Fetch the complete documentation index at: https://docs.layerzero.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices for Contract Ownership

> Learn the Owner and Delegate roles on LayerZero OApp and OFT contracts, why they usually share one address, and how to transfer both to a multisig safely.

Our OApp and OFT contract standards inherit the [OpenZeppelin `Ownable` standard](https://docs.openzeppelin.com/contracts/5.x/access-control) 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:

```solidity theme={null}
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { OFT } from "@layerzerolabs/oft-evm/contracts/OFT.sol";

contract MyOFT is OFT {
    constructor(
        string memory _name,
        string memory _symbol,
        address _lzEndpoint,
        address _delegate
    )
        OFT(_name, _symbol, _lzEndpoint, _delegate) // registers _delegate as the Endpoint delegate
        Ownable(_delegate)                          // sets _delegate as the contract owner
    {}
}
```

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`](https://github.com/LayerZero-Labs/devtools/blob/main/examples/oft/contracts/MyOFT.sol) (`OFT` already inherits `Ownable`, so `MyOFT` only needs to list `OFT` and pass `Ownable(_delegate)` in its constructor).

<Note>
  Some quickstarts name this argument `_owner` instead of `_delegate` (see the [OFT quickstart](/v2/developers/evm/oft/quickstart) and [OApp overview](/v2/developers/evm/oapp/overview)). It is the same mechanic with a different label.
</Note>

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](/v2/developers/evm/technical-reference/api#setdelegate).

## Understanding Owner vs Delegate

The two roles control different layers of your application:

| Role         | Lives in                                         | Controls                     | Key functions                                                                                                                                                |
| ------------ | ------------------------------------------------ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Owner**    | Your OApp contract (OpenZeppelin `Ownable`)      | Application-level policy     | `setPeer()`, `setEnforcedOptions()`, `setDelegate()`                                                                                                         |
| **Delegate** | The LayerZero Endpoint (set via `setDelegate()`) | Protocol and security config | `setConfig()` (DVNs, Executor, confirmations), `setSendLibrary()`, `setReceiveLibrary()`, plus message recovery `skip()` / `nilify()` / `burn()` / `clear()` |

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](/v2/concepts/technical-reference/oapp-reference#security-and-roles) and the [Delegate glossary entry](/v2/concepts/glossary#delegate).

<Note>
  **RBAC variant:** Some OApps replace `Ownable` with role-based access control, for example the [Stablecoin OFT](/v2/developers/evm/stablecoin-oft/rbac-reference), 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](/v2/developers/evm/stablecoin-oft/rbac-reference) for that model.
</Note>

### 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:

<Warning>
  **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()`.
</Warning>

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:

```typescript theme={null}
const safeMultisig = "0xYourSafeAddress";

// 1. Point the Delegate at your multisig FIRST, while the deployer is still Owner.
await (await oft.setDelegate(safeMultisig)).wait();

// 2. Then transfer ownership. After this, the old key loses owner-gated access.
await (await oft.transferOwnership(safeMultisig)).wait();
```

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](/v2/get-started/create-lz-oapp/configuring-pathways#adding-delegate) and [Adding an owner](/v2/get-started/create-lz-oapp/configuring-pathways#adding-owner).

<Tip>
  **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.
</Tip>

## Use a Multisig for Both Roles

Whatever address holds these roles can reconfigure your application, so our recommendation is direct:

<Warning>
  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.
</Warning>

* **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.

<Warning>
  **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.
</Warning>

## Transfer to a Safe Multisig

[Safe](https://safe.global/) (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](/v2/get-started/create-lz-oapp/configuring-pathways#wiring-via-safe-multisig).

To call `transferOwnership()`, `setDelegate()`, or any owner function interactively against your own deployment, use the [contracts playground](/v2/developers/evm/contracts-playground).

## Non-EVM Caveats

<Note>
  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](https://squads.so/) 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](/v2/developers/solana/technical-reference/solana-guidance#transferring-oft-ownership-on-solana).
</Note>

## 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.
