Theme

Plugins

Freeze Execute

Overview

The Freeze Execute Plugin is an Owner Managed plugin that allows freezing the Execute lifecycle event on an Asset. When frozen, the asset cannot execute arbitrary instructions through its Asset Signer PDA, effectively blocking any execute operations until unfrozen.

Important: Since this is an Owner Managed plugin, the authority will not persist after the asset is transferred to a new owner. The new owner will need to re-add the authority if they want the previous authorities to be able to change the freeze status of the plugin.

The Freeze Execute Plugin is particularly useful for scenarios such as:

  • Backed NFTs: Lock NFTs that represent ownership of underlying assets (SOL, tokens) to prevent unauthorized withdrawals
  • Escrowless asset management: Freeze assets while they're involved in financial operations without transferring ownership
  • Staking protocols: Prevent asset execution during staking periods while maintaining ownership
  • Smart contract security: Add a layer of protection for assets that can execute complex operations
  • Governance controls: Implement freezing mechanisms for assets involved in governance or voting
  • Asset rental: Prevent execution while assets are being rented out
  • Collateral management: Lock assets used as collateral in DeFi protocols

Works With

MPL Core Assetβœ…
MPL Core Collectionβœ…

Arguments

ArgValue
frozenbool

Functions

Add Freeze Execute Plugin to an Asset

The addPlugin command adds the Freeze Execute Plugin to an Asset. This plugin allows the Asset's Execute functionality to be frozen, preventing execution of arbitrary instructions.

Adding a Freeze Execute Plugin to an MPL Core Asset

import { publicKey } from '@metaplex-foundation/umi'
import { addPlugin, mplCore } from '@metaplex-foundation/mpl-core'
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'

;(async () => {
  const umi = createUmi('https://api.devnet.solana.com').use(mplCore())

  const assetAddress = publicKey('11111111111111111111111111111111')

  await addPlugin(umi, {
    asset: assetAddress,
    plugin: { type: 'FreezeExecute', data: { frozen: false } },
  }).sendAndConfirm(umi)
})()

Creating an Asset with Freeze Execute Plugin

You can also add the Freeze Execute Plugin during asset creation:

Creating an Asset with Freeze Execute Plugin

import { generateSigner } from '@metaplex-foundation/umi'
import { create, mplCore } from '@metaplex-foundation/mpl-core'
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'

;(async () => {
  const umi = createUmi('https://api.devnet.solana.com').use(mplCore())
  const assetSigner = generateSigner(umi)
  const delegateAddress = generateSigner(umi)

  await create(umi, {
    asset: assetSigner,
    name: 'My Asset',
    uri: 'https://example.com/my-asset.json',
    plugins: [
      {
        type: 'FreezeExecute',
        data: { frozen: false },
        authority: { type: 'Address', address: delegateAddress.publicKey },
      },
    ],
  }).sendAndConfirm(umi)
})()

Creating a Collection with Freeze Execute Plugin

The Freeze Execute Plugin can also be applied to collections:

Creating a Collection with Freeze Execute Plugin

import { generateSigner } from '@metaplex-foundation/umi'
import { createCollection, mplCore } from '@metaplex-foundation/mpl-core'
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'

;(async () => {
  const umi = createUmi('https://api.devnet.solana.com').use(mplCore())
  const collectionSigner = generateSigner(umi)

  await createCollection(umi, {
    collection: collectionSigner,
    name: 'My Collection',
    uri: 'https://example.com/my-collection.json',
    plugins: [{ type: 'FreezeExecute', frozen: false }],
  }).sendAndConfirm(umi)
})()

Freezing Execute Operations

The updatePlugin command can be used to freeze the Asset's Execute functionality, preventing it from executing arbitrary instructions until unfrozen.

Freeze Execute Operations on an MPL Core Asset

import { createUmi, publicKey } from '@metaplex-foundation/umi'
import { updatePlugin, mplCore } from '@metaplex-foundation/mpl-core'

;(async () => {
  const umi = createUmi('https://api.devnet.solana.com').use(mplCore())
  const assetAddress = publicKey('11111111111111111111111111111111')

  await updatePlugin(umi, {
    asset: assetAddress,
    plugin: { type: 'FreezeExecute', data: { frozen: true } },
  }).sendAndConfirm(umi)
})()

Unfreezing Execute Operations

The updatePlugin command can also be used to unfreeze the Asset's Execute functionality, restoring its ability to execute arbitrary instructions.

Unfreeze Execute Operations on an MPL Core Asset

import { createUmi, publicKey } from '@metaplex-foundation/umi'
import { updatePlugin, mplCore } from '@metaplex-foundation/mpl-core'

;(async () => {
  const umi = createUmi('https://api.devnet.solana.com').use(mplCore())
  const assetAddress = publicKey('11111111111111111111111111111111')

  await updatePlugin(umi, {
    asset: assetAddress,
    plugin: { type: 'FreezeExecute', data: { frozen: false } },
  }).sendAndConfirm(umi)
})()

Plugin Authority

The Freeze Execute Plugin supports different authority types for controlling who can freeze/unfreeze execute operations:

  • Owner Authority (default): Only the asset owner can freeze/unfreeze
  • Delegate Authority: A specific address can be delegated to control freezing
  • Update Authority: The asset's update authority can control freezing, but only if explicitly delegated

Setting Plugin Authority

import { generateSigner } from "@metaplex-foundation/umi";
import { create, mplCore } from "@metaplex-foundation/mpl-core";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

(async () => {
  const umi = createUmi("https://api.devnet.solana.com").use(mplCore());

  const assetSigner = generateSigner(umi);
  const delegateAddress = generateSigner(umi);

  await create(umi, {
    asset: assetSigner,
    name: "My Asset",
    uri: "https://example.com/my-asset.json",
    plugins: [
      {
        type: "FreezeExecute",
        data: { frozen: false },
        authority: { type: "Address", address: delegateAddress.publicKey },
      },
    ],
  }).sendAndConfirm(umi);
})();

Important Notes

  • When the frozen field is set to true, any execute operations will be blocked
  • Default authority: The asset owner controls the plugin by default
  • Authority delegation: Only the current authority can freeze/unfreeze the execute functionality
  • Authority constraints: If authority is delegated to someone else, the original owner cannot unfreeze until authority is revoked
  • The plugin cannot be removed when frozen
  • Authority cannot be reassigned when frozen
  • The plugin works with the Execute instruction system

Example Use Case: Backed NFT

A common use case for the Freeze Execute Plugin is creating "backed NFTs" where the NFT represents ownership of underlying assets (like SOL or tokens) that can be withdrawn via execute instructions. The plugin allows you to temporarily freeze these execute operations.

Backed NFT Example

import {
  generateSigner,
  publicKey,
  sol,
  createNoopSigner,
  keypairIdentity,
} from "@metaplex-foundation/umi";
import {
  create,
  execute,
  findAssetSignerPda,
  updatePlugin,
  fetchAsset,
  mplCore,
} from "@metaplex-foundation/mpl-core";
import { transferSol } from "@metaplex-foundation/mpl-toolbox";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";

(async () => {
  const umi = createUmi("https://api.devnet.solana.com").use(mplCore());

  //use your wallet instead
  const wallet = generateSigner(umi);
  umi.use(keypairIdentity(wallet));

  // 1. Create asset with frozen execute functionality
  const assetSigner = generateSigner(umi);

  await create(umi, {
    asset: assetSigner,
    name: "Backed NFT",
    uri: "https://example.com/backed-nft.json",
    plugins: [{ type: "FreezeExecute", frozen: true }],
  }).sendAndConfirm(umi);

  // 2. Find the Asset Signer PDA
  const [assetSignerPda] = findAssetSignerPda(umi, {
    asset: assetSigner.publicKey,
  });

  // 3. Deposit SOL to "back" the NFT
  await transferSol(umi, {
    source: umi.identity,
    destination: publicKey(assetSignerPda),
    amount: sol(0.01), // 0.01 SOL backing
  }).sendAndConfirm(umi);

  // 4. Execute operations are blocked while frozen
  // This transaction will fail:
  try {
    await execute(umi, {
      asset: await fetchAsset(umi, assetSigner.publicKey),
      instructions: transferSol(umi, {
        source: createNoopSigner(publicKey(assetSignerPda)),
        destination: generateSigner(umi).publicKey,
        amount: sol(0.001),
      }),
    }).sendAndConfirm(umi, { send: { skipPreflight: true } });
  } catch (e) {
    console.log("execute failed as expected", e);
  }

  // 5. Unfreeze to allow withdrawals
  await updatePlugin(umi, {
    asset: assetSigner.publicKey,
    plugin: { type: "FreezeExecute", data: { frozen: false } },
  }).sendAndConfirm(umi);

  // 6. Now execute operations are allowed
  const recipient = generateSigner(umi);
  await execute(umi, {
    asset: await fetchAsset(umi, assetSigner.publicKey),
    instructions: transferSol(umi, {
      source: createNoopSigner(publicKey(assetSignerPda)),
      destination: recipient.publicKey,
      amount: sol(0.001),
    }),
  }).sendAndConfirm(umi);
})();
Previous
Freeze Delegate Plugin