功能
Execute和Asset签名
Last updated September 1, 2026
MPL Core Execute指令为MPL Core Asset引入了Asset Signer的概念。 这些Asset Signer代表Asset本身充当签名者,这使MPL Core Asset能够:
- 转移Solana和SPL代币
- 成为其他账户的authority
- 执行已分配给
assetSignerPda的其他需要交易/指令/CPI签名的操作和验证 MPL Core Asset能够签署并向区块链提交交易/CPI。这有效地为Core Asset提供了自己的钱包,形式为assetSigner。
销毁前先提取 Asset Signer 余额
销毁 Core Asset 会使 execute 指令失败。程序无法再加载该 Asset,因此不能以 assetSignerPda 签名。该 PDA 中剩余的 SOL、代币或其他资产将滞留且无法找回。
Asset Signer PDA
Asset现在可以访问assetSignerPda账户/地址,这允许MPL Core程序上的execute指令传递发送给它的额外指令,以使用assetSignerPda签署CPI指令。 这允许assetSignerPda账户代表当前资产所有者有效地拥有和执行账户指令。 您可以将assetSignerPda视为附加到Core Asset的钱包。
为Asset Signer PDA注资,而不是Asset地址
assetSignerPda与Asset账户是不同的地址。只有assetSignerPda可以代表Asset持有SOL和代币。绝不能将Asset账户本身用作钱包。
findAssetSignerPda()
const assetId = publickey('11111111111111111111111111111111')
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
Execute指令
概述
execute指令允许用户传入Core Asset以及一些通过指令,当它到达链上MPL Core程序的execute指令时,AssetSigner将签署这些指令。 execute指令及其参数的概述。
const executeIx = await execute(umi, {
{
// 通过`fetchAsset()`签署交易的asset
asset: AssetV1,
// 通过`fetchCollection()`的collection
collection?: CollectionV1,
// TransactionBuilder | Instruction[]
instructions: ExecuteInput,
// 交易/指令所需的额外签名者
signers?: Signer[]
}
})
验证
assetSignerPda验证
MPL Core Execute指令将验证当前Asset所有者也已签署交易。这确保只有当前Asset所有者可以使用execute指令与assetSignerPda执行交易。
控制Execute操作
execute功能可以使用Freeze Execute插件进行控制。此插件允许您冻结资产上的execute操作,防止在解冻之前处理任何execute指令。 Freeze Execute插件特别适用于:
- 担保NFT:在需要时防止提取底层资产
- 无托管协议:在协议操作期间临时锁定execute功能
- 安全措施:为可以执行复杂操作的资产添加额外的保护层 当Freeze Execute插件处于活动状态并设置为
frozen: true时,任何使用execute指令的尝试都将被阻止,直到插件更新为frozen: false。
销毁带有 Asset Signer 余额的 Asset
销毁 Core Asset 会永久禁用 execute,因此留在 assetSignerPda 中的 SOL、代币或嵌套 Core Asset 无法再被转移。
execute 指令需要由 MPL Core 程序拥有的有效 Core Asset 账户。您销毁 Asset 后,该账户不再是有效 Asset。assetSignerPda 地址仍然存在并且仍可持有资金——只是再也没有指令能花掉它们。
在销毁之前用 execute 把 PDA 中的一切转出:
- 使用
findAssetSignerPda或mplx core asset execute info推导 PDA - 转移 PDA 拥有的 SOL、SPL 代币和任何 Core Asset
- 确认 PDA 为空
- 销毁 Asset
示例
从Asset Signer转移SOL
在以下示例中,我们将发送到assetSignerPda的SOL转移到我们选择的目的地。
import {
execute,
findAssetSignerPda,
fetchAsset,
fetchCollection,
} from '@metaplex-foundation/mpl-core'
import { transferSol } from '@metaplex-foundation/mpl-toolbox'
import { publickey, createNoopSigner, sol } from '@metaplex-foundation/umi'
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(umi, assetId)
// 可选 - 如果Asset是收藏品的一部分,获取收藏品对象
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// Asset signer账户中有1 SOL余额
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// 我们希望将SOL转移到的目标账户
const destination = publickey('2222222222222222222222222222222222')
// 标准`transferSol()` transactionBuilder
const transferSolIx = transferSol(umi, {
// 创建noopSigner,因为assetSigner稍后将在CPI期间签名
source: createNoopSigner(publicKey(assetSigner)),
// 目标地址
destination,
// 您希望转移的金额
amount: sol(0.5),
})
// 调用`execute`指令并发送到链上
const res = await execute(umi, {
// 使用此asset执行指令
asset,
// 如果Asset是收藏品的一部分,通过`fetchCollection()`传入收藏品对象
collection,
// 要执行的transactionBuilder/instruction[]
instructions: transferSolIx,
}).sendAndConfirm(umi)
console.log({ res })
从Asset Signer转移SPL代币
在以下示例中,我们将assetSignerPda账户中的部分SPL代币余额转移到目的地。 此示例基于有关基础钱包地址的派生代币账户的最佳实践。如果代币不在基于assetSignerPda地址正确派生的代币账户中,则需要调整此示例。
import {
execute,
findAssetSignerPda,
fetchAsset,
fetchCollection,
} from '@metaplex-foundation/mpl-core'
import {
transferTokens,
findAssociatedTokenPda,
} from '@metaplex-foundation/mpl-toolbox'
import { publickey } from '@metaplex-foundation/umi'
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(umi, assetId)
// 可选 - 如果Asset是收藏品的一部分,获取收藏品对象
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
const splTokenMint = publickey('2222222222222222222222222222222222')
// Asset signer有代币余额
const assetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// 我们希望将SOL转移到的目标钱包
const destinationWallet = publickey('3333333333333333333333333333333')
// 标准`transferTokens()` transactionBuilder
const transferTokensIx = transferTokens(umi, {
// Source是`assetSignerPda`派生的代币账户
source: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: assetSignerPda,
}),
// Destination是`destinationWallet`派生的代币账户
destination: findAssociatedTokenPda(umi, {
mint: splTokenMint,
owner: destinationWallet,
}),
// 以lamport为单位的发送金额
amount: 5000,
})
// 调用`execute`指令并发送到链上
const res = await execute(umi, {
// 使用此asset执行指令
asset,
// 如果Asset是收藏品的一部分,通过`fetchCollection()`传入收藏品对象
collection,
// 要执行的transactionBuilder/instruction[]
instructions: transferTokensIx,
}).sendAndConfirm(umi)
console.log({ res })
将Asset所有权转移到另一个Asset
在以下示例中,我们将一个Core Asset拥有的另一个Core Asset转移到另一个Asset。
import {
execute,
fetchAsset,
fetchCollection,
findAssetSignerPda,
transfer,
} from '@metaplex-foundation/mpl-core'
import { publickey } from '@metaplex-foundation/umi'
// 我们希望转移的Asset
const assetId = publickey('11111111111111111111111111111111')
const asset = await fetchAsset(assetId)
// 可选 - 如果Asset是收藏品的一部分,获取收藏品对象
const collection =
asset.updateAuthority.type == 'Collection' && asset.updateAuthority.address
? await fetchCollection(umi, asset.updateAuthority.address)
: undefined
// 拥有我们要转移的Asset的Asset ID
const sourceAssetId = publickey('2222222222222222222222222222222222')
// 源Asset对象
const sourceAsset = fetchAsset(umi, sourceAssetId)
// Asset signer账户中有1 SOL余额
const sourceAssetSignerPda = findAssetSignerPda(umi, { asset: assetId })
// 我们希望将SOL转移到的目标账户
const destinationAssetId = publickey('33333333333333333333333333333333')
// 我们希望将Asset转移到的目标Asset signer
const destinationAssetSignerPda = findAssetSignerPda(umi, {
asset: destinationAssetId,
})
const transferAssetIx = transfer(umi, {
// 通过`fetchAsset()`的Asset对象
asset,
// 可选 - 通过`fetchCollection()`的Collection对象
collection,
// Asset的新所有者
newOwner: destinationAssetSignerPda,
}).sendAndConfirm(umi)
const res = await execute(umi, {
// 使用此asset执行指令
asset,
// 如果Asset是收藏品的一部分,通过`fetchCollection()`传入收藏品对象
collection,
// 要执行的transactionBuilder/instruction[]
instructions: transferAssetIx,
}).sendAndConfirm(umi)
console.log({ res })
注意事项
- 只有当前 Asset 所有者可以调用
execute(所有者必须签署外层交易) - Freeze Execute 插件 可以在解冻之前阻止
execute - 销毁 Asset 对 Asset Signer 资金不可逆:请先清空 PDA
assetSignerPda由 Asset 地址确定性推导,转移 Asset 不会改变它execute指令会收取由传入指令的payer账户支付的协议费用。payer通常是Asset所有者,但也可以是assetSignerPda本身。这与Solana交易手续费是两回事,交易手续费无法由PDA支付。当前金额请参阅协议费用页面。该费用会转入Asset账户,随后由Metaplex费用收集器清扫。- Asset账户中超过免租金最低余额的每一个lamport都会被视为协议费用并被收取。请将SOL和代币保存在
assetSignerPda中,切勿保存在Asset账户中。 assetSignerPda是系统所有的账户。Solana运行时会拒绝任何使其剩余非零余额低于0字节账户免租金最低余额的交易,因此转出必须要么清空账户,要么至少保留该最低余额。如果PDA同时是execute的payer,协议费用会在同一指令中从PDA扣除,因此计算剩余余额时请把该费用算进去。请在目标集群上查询getMinimumBalanceForRentExemption(0)获取当前最低余额,而不要写死数值。
FAQ
销毁 Asset 后,Asset Signer PDA 中的资金会怎样?
会滞留且无法找回。execute 需要有效的 Core Asset,因此销毁后 PDA 仍可持有 SOL、代币和嵌套 Asset,但没有任何签名能把它们转出。
销毁后可以找回滞留的 Asset Signer 资金吗?
不可以。没有指令能在缺少原始 Asset 账户的情况下以 assetSignerPda 签名。
转移 Asset 会滞留 Asset Signer 钱包吗?
不会。转移只会更改谁可以调用 execute。PDA 仍绑定到 Asset 地址。禁用 execute 的是销毁,而不是转移。
