Delegate
  • Overview
  • Delegating a wallet
  • Comparison with Others
  • FAQ
  • Audits
  • Integrate In Your Project
    • Smart Contract Examples
    • Token Ownership Claiming
  • Upgrade to V2
    • V2 is a Separate Contract
    • V1 → V2 migration
    • Batching
    • Subdelegations
  • Advanced Use Cases
    • Shadow Delegation
  • Delegate Market
    • Overview
    • FAQ
  • Gaming
    • Delegate for Gaming
  • Technical Documentation
    • Delegate Registry
      • Contract Addresses
      • IDelegateRegistry.sol
    • Javascript SDK
      • Installation / Importing
      • Setup
      • Check Delegations
        • checkDelegateForAll
        • checkDelegateForContract
        • checkDelegateForERC721
        • checkDelegateForERC1155
        • checkDelegateForERC20
      • Fetch Delegations
      • Delegate/Revoke
        • delegateAll
        • delegateContract
        • delegateERC721
        • delegateERC1155
        • delegateERC20
    • REST API
      • v2
      • v1
  • Resources
    • Media Kit
    • Github
    • Twitter
    • Live Stats
  • V1 Registry (Legacy)
    • Technical Documentation
Powered by GitBook
On this page
  • In Production: How Yuga Uses Shadow Delegation
  • Infinite Use Cases
  • Core Concepts
  • How to Implement It
  • Resources
  • Summary
  1. Advanced Use Cases

Shadow Delegation

Implement non-custodial NFT delegation using Delegate Registry v2 and the shadow delegation pattern, enforced via Yuga Labs’ ExclusiveDelegateResolver.

PreviousSubdelegationsNextOverview

Last updated 14 days ago

In Production: How Yuga Uses Shadow Delegation

Yuga Labs uses shadow delegation to let holders of Ape NFTs on Ethereum delegate access to a wallet on ApeChain. This allows users to verify ownership and participate in ApeChain experiences without bridging or moving their Apes.

By combining Delegate Registry v2 and ExclusiveDelegateResolver, Yuga enables onchain delegation that’s:

  • Non-custodial: the NFT stays in the original wallet

  • Cross-chain: access is granted to a different wallet on another chain

  • Exclusive: only one delegate per token is valid at a time

Infinite Use Cases

Delegate Registry v2 provides the primitives for onchain NFT access control, forming the base layer upon which shadow delegation builds customizable, scoped, and enforceable delegation logic.

Use cases include:

  • Assign agents to act on behalf of wallets across chains

  • Enable wearables, emotes, or other metadata-bound behaviors

  • Grant permissions in identity systems, social graphs, or gaming avatars

  • Build composable avatars and modular inventories

  • Enforce execution rights on specific chains or applications


Core Concepts

1) Delegate Registry v2

Delegate Registry v2 is an onchain registry that allows users to grant permission to other addresses to act on their behalf. It serves as a data layer, returning a boolean for whether a delegation exists.

Delegations can be scoped:

  • to specific tokens via checkDelegateForERC721

  • to particular contracts checkDelegateForContract

  • across all tokens and contracts checkDelegateForAll

The registry does not enforce rules like exclusivity, expiration, or precedence. It simply stores data. ExclusiveDelegateResolver, developed by Yuga Labs, is responsible for interpreting that data and enforcing logic.

Resolver contracts apply custom logic to interpret delegation records by answering:

"Who currently has the right to act on this token, under these rules?"

Yuga Labs’ ExclusiveDelegateResolver is used in production (e.g. Otherside, ApeChain) to enforce exclusive, scoped, onchain delegation, letting one wallet act on behalf of another without moving the NFT. It scans delegations scoped to a rightsNamespace and applies specificity rules (token > contract > all-assets) to return the active delegate.

3) The Shadow Delegation Pattern

Shadow delegation is a design pattern that combines Delegate Registry v2 with a resolver like ExclusiveDelegateResolver to enable non-custodial, exclusive delegation, scoped by use case and enforced onchain.

In this pattern:

  • Only one delegation per token (or namespace) should be valid at a time

  • Previous delegates must be explicitly revoked before a new one is assigned

  • Asset ownership remains with the original holder; no escrow or transfers

This pattern works by combining scoped delegation rights with onchain resolution logic, illustrated below.

How to Implement It

Shadow delegation follows a simple 3-step flow:

  1. Delegate access to a token using delegateERC721()

  2. Resolve the active delegate using ExclusiveDelegateResolver

  3. Enforce the delegation with an onchain check (e.g. require(msg.sender == delegate))

Step 1: Delegate an NFT via Delegate Registry v2

The owner calls delegateERC721() on the registry to assign a delegate for a specific NFT:

delegateERC721(
  address delegate,
  address contract_,
  uint256 tokenId,
  bytes32 rights,
  bool value
);
  • delegate: The address receiving the delegation.

  • contract_: The NFT contract address.

  • tokenId: The specific token ID to delegate.

  • rights: A bytes32 value representing delegation scope.

  • value: Set to true to enable, or false to revoke.

In the shadow delegation pattern, you must scope delegation to a unique chain or context by computing a rights value. Use the following utility:

import { encodeAbiParameters, keccak256 } from 'viem';

function computeShadowRights(shadowChainId?: number): string {
    if (!isShadowDelegation) return '';
    if (!shadowChainId) return '0x000000000000000000000000000000000000000000000000000000ffffffffff';

    const encodedData = encodeAbiParameters([{ type: 'uint' }], [BigInt(shadowChainId)]);
    const hash = keccak256(encodedData);
    const namespace = hash.slice(0, 50);
    const padding = '000000';
    const maxRights = (BigInt(2) ** BigInt(40) - BigInt(1)).toString(16).padStart(10, '0');
    const rights = namespace + padding + maxRights;
		
    return rights;
  }

⚠️ Revoke Before Reassigning If you're delegating the same token (and rights) to a new address, you must first revoke the previous delegate using delegateERC721(..., false). Otherwise, the storage slot is overwritten, and the resolver may return an outdated result.

How Rights are Constructed and Resolved

The rights field is a 32-byte (bytes32) value that encodes the scope and expiry of a delegation. This is how the resolver knows what context the delegation applies to, how specific it is, and whether it’s still valid.

Bytes
Purpose
Notes

0–23

rightsNamespace

Used to scope delegations (e.g., by chain or feature)

24–28

Reserved padding (000000)

Required by resolver formatting

29–33

uint40 expiry

Unix timestamp after which delegation is invalid

Resolver Behavior:

  • Chain-specific scoping: Namespaces are typically derived from keccak256(chainId) so you can delegate the same token to different wallets on different chains. These are resolved independently.

  • Global fallback delegation: If the first 24 bytes are zero, i.e., 0x000...000, the delegation is considered global, valid across all chains or apps. These are ranked lowest in specificity and only take effect if no scoped delegation exists.

  • Automatic expiry: The final 5 bytes of rights encode an expiry timestamp (uint40). When the current block time exceeds this value, the delegation is considered invalid. If you don't want an expiry, use the max value: 0xffffffffff.

This layered resolution system allows you to combine scoped, exclusive, expiring, and cross-chain delegation behavior.

Step 2: Use the ExclusiveDelegateResolver

Once delegation is recorded, you can resolve it using ExclusiveDelegateResolver, which applies namespace-based resolution logic (token > contract > all-assets) and scopes based on the first 24 bytes of the rights field. This means a token-level delegation takes priority over a contract-level one under the same namespace.

Resolver interface:

function exclusiveOwnerByRights(
  address nftContract,
  uint256 tokenId,
  bytes24 rightsNamespace
) external view returns (address);

Example usage:

bytes24 rightsNamespace = bytes24(keccak256(abi.encode(shadowChainId)));

address delegate = IExclusiveDelegateResolver(resolverAddress).exclusiveOwnerByRights(
  nftContractAddress,
  tokenId,
  rightsNamespace
);

This reinforces the resolver concept from Core Concept #2. The registry stores raw data and the resolver interprets it according to a rule set (in this case, exclusivity via specificity).

Step 3: Enforce Delegation in Your App or Smart Contract

Whenever your application or NFT/game logic needs to check control, replace ownerOf(tokenId) or raw ownership checks with delegation resolution:

require(msg.sender == delegate, "Not authorized delegate");

This enforces non-custodial, exclusive access via the resolver logic, honoring only the delegate for the scoped rights.


Resources


Summary

Shadow delegation with Delegate Registry v2 and ExclusiveDelegateResolver enables:

  • Fully onchain delegation logic

  • Exclusive, non-custodial access

  • Use case-specific scoping via rightsNamespace

Developers can build on this pattern by customizing how delegations are scoped and managed, without needing to deploy their own resolver contract.

2) Resolver Contracts ()

💡 Use multicall() to Minimize UX & Gas Apps can batch the revocation and reassignment into a single transaction using the registry’s method to reduce friction and cost.

🛠️ See It Live Yuga Labs uses this pattern in production. See how their performs explicit revocation before reassignment (lines 933–991).

Delegate Registry v2:

ApeChain Shadow Delegations:

Yuga’s Exclusive Resolver:

ExclusiveDelegateResolver
multicall()
Shadow Beacon contract
https://github.com/delegatexyz/delegate-registry-v2
https://docs.apechain.com/start-building/NFT-Shadows
https://github.com/yuga-labs/ExclusiveDelegateResolver