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
Arg | Value |
---|---|
frozen | bool |
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 totrue
, 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);
})();