# Shadow Delegation

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

{% embed url="<https://x.com/yugalabs/status/1914363571280159172?s=46&t=jXxAznseRvMWhHxSBWZ72A>" %}

### 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.\
\&#xNAN;**`ExclusiveDelegateResolver`**, developed by Yuga Labs, is responsible for interpreting that data and enforcing logic.

#### 2) Resolver Contracts ([ExclusiveDelegateResolver](https://github.com/yuga-labs/ExclusiveDelegateResolver))

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:

<figure><img src="/files/EkXUY4o76niCBrlYi286" alt="" width="375"><figcaption></figcaption></figure>

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:

```solidity
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:

```tsx
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;
  }
```

{% hint style="warning" %}
⚠️ **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.

💡 **Use `multicall()` to Minimize UX & Gas**\
Apps can batch the revocation and reassignment into a single transaction using the registry’s [`multicall()`](https://docs.delegate.xyz/upgrade-to-v2/batching) method to reduce friction and cost.

🛠️ **See It Live**\
Yuga Labs uses this pattern in production. See how their [Shadow Beacon contract](https://apescan.io/address/0x00000000000087c6dbadc090d39bc10316f20658#code) performs explicit revocation before reassignment (lines 933–991).
{% endhint %}

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

<table><thead><tr><th width="85.38671875">Bytes</th><th width="239.328125">Purpose</th><th>Notes</th></tr></thead><tbody><tr><td>0–23</td><td><code>rightsNamespace</code></td><td>Used to scope delegations (e.g., by chain or feature)</td></tr><tr><td>24–28</td><td>Reserved padding (<code>000000</code>)</td><td>Required by resolver formatting</td></tr><tr><td>29–33</td><td><code>uint40 expiry</code></td><td>Unix timestamp after which delegation is invalid</td></tr></tbody></table>

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

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

Example usage:

```solidity
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:

```solidity
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

* Delegate Registry v2: <https://github.com/delegatexyz/delegate-registry-v2>
* ApeChain Shadow Delegations: <https://docs.apechain.com/start-building/NFT-Shadows>
* Yuga’s Exclusive Resolver: <https://github.com/yuga-labs/ExclusiveDelegateResolver>

***

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.delegate.xyz/advanced-use-cases/shadow-delegation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
