功能
可编程 NFT (pNFTs)
如概述页面所述,可编程 NFT (pNFTs) 是一种新的资产标准,允许创作者在特定操作上定义自定义规则,并更精细地委托给第三方权限。
不再绕过 Token Metadata
由于 Token Metadata 程序构建在 SPL Token 程序之上,任何所有者或 spl-token 委托都可以直接与 SPL Token 程序交互,并在转移和销毁等关键操作上绕过 Token Metadata 程序。虽然这在程序之间创建了良好的可组合性模式,但这也意味着 Token Metadata 程序无法代表创作者强制执行任何规则。
为什么这可能是问题的一个很好的例子是 Token Metadata 无法强制执行二次销售版税。尽管版税百分比存储在 Metadata 账户上,但执行转移的用户或程序可以决定是否要遵守它。我们在下面的部分中详细讨论了这一点以及 pNFTs 如何解决这个问题。
可编程 NFT 的引入是为了以灵活的方式解决这个问题,允许创作者自定义其资产的授权层。
可编程 NFT 的工作方式如下:
- pNFT 的 Token 账户在 SPL Token 程序上始终是冻结的,无论 pNFT 是否被委托。这确保没有人可以通过直接与 SPL Token 程序交互来绕过 Token Metadata 程序。
- 每当在 pNFT 的 Token 账户上执行操作时,Token Metadata 程序会解冻账户、执行操作,然后再次冻结账户。所有这些都在同一指令中原子性地发生。这样,所有可以在 SPL Token 程序上进行的操作对 pNFTs 仍然可用,但它们始终通过 Token Metadata 程序执行。
- 当在 pNFT 上设置 Token 委托 时,信息存储在 Token Record 账户中。由于 pNFTs 在 SPL Token 程序上始终是冻结的,Token Record 账户负责跟踪 pNFT 是否真正被锁定。
- 因为每个影响 pNFT 的操作都必须通过 Token Metadata 程序,我们创建了一个瓶颈,允许我们为这些操作强制执行授权规则。这些规则在由 Token Auth Rules 程序管理的 Rule Set 账户中定义。
本质上,这赋予了 pNFTs 以下能力:
- 拥有更精细的委托。
- 在任何操作上强制执行规则。
让我们更详细地深入了解这两种能力。
更精细的委托
由于所有 pNFTs 操作都必须通过 Token Metadata 程序,它可以在 spl-token 委托之上创建一个新的委托系统。一个更精细的系统,允许 pNFT 所有者选择他们想要委托给第三方的操作。
这个新委托系统的信息存储在一个特殊的 Token Record PDA 上,该 PDA 从 pNFT 的 Mint 和 Token 账户派生。当新的委托权限被分配给 pNFT 时,Token Metadata 程序会在 Token 账户和 Token Record 账户上同步该信息。
我们在委托权限页面的"Token 委托"部分中更详细地讨论这些委托。
附加账户
pNFTs 在大多数操作中需要附加账户,包括 tokenRecord、authorizationRules、authorizationRulesProgram。
Token Record
tokenRecord 账户负责保存关于代币及其状态的详细信息,例如 delegates、它的 lock 状态。
有几种方法可以访问 tokenRecord 账户,一种是通过 fetchDigitalAssetWithAssociatedToken,它返回所有需要的账户,包括 metadata、token account 和 token record。 另一种方法是使用 mint ID 和 token account 地址通过 findTokenRecordPda 函数生成 token record PDA 地址。
带 Token 的资产
您可以使用 fetchDigitalAssetWithAssociatedToken 函数获取所有需要的账户,它返回 pNFT metadata 账户、token 账户和 token record 账户等数据。
1import { publicKey } from '@metaplex-foundation/umi';
2import { fetchDigitalAssetWithAssociatedToken } from '@metaplex-foundation/mpl-token-metadata';
3
4const mint = publicKey('11111111111111111111111111111111');
5const owner = publicKey('22222222222222222222222222222222');
6
7const assetWithToken = await fetchDigitalAssetWithAssociatedToken(
8 umi,
9 mint,
10 owner
11);
12
13console.log('Metadata:', assetWithToken.metadata.name);
14console.log('Token Record:', assetWithToken.tokenRecord);
1import { address } from '@solana/addresses';
2import { fetchDigitalAssetWithAssociatedToken } from '@metaplex-foundation/mpl-token-metadata-kit';
3
4const mint = address('11111111111111111111111111111111');
5const owner = address('22222222222222222222222222222222');
6
7const assetWithToken = await fetchDigitalAssetWithAssociatedToken(
8 rpc,
9 mint,
10 owner
11);
12
13console.log('Metadata:', assetWithToken.metadata.name);
14console.log('Token Record:', assetWithToken.tokenRecord);
Token Record PDA
使用 mintId 和存储 pNFT 资产的钱包的 tokenAccount 生成 tokenRecord 账户的 PDA 地址。
1import { publicKey } from '@metaplex-foundation/umi';
2import { findTokenRecordPda } from '@metaplex-foundation/mpl-token-metadata';
3
4const mint = publicKey('11111111111111111111111111111111');
5const token = publicKey('22222222222222222222222222222222');
6
7const tokenRecordPda = findTokenRecordPda(umi, {
8 mint,
9 token,
10});
11
12console.log('Token Record PDA:', tokenRecordPda);
1import { address } from '@solana/addresses';
2import { findTokenRecordPda } from '@metaplex-foundation/mpl-token-metadata-kit';
3
4const mint = address('11111111111111111111111111111111');
5const token = address('22222222222222222222222222222222');
6
7const [tokenRecordPda] = await findTokenRecordPda({
8 mint,
9 token,
10});
11
12console.log('Token Record PDA:', tokenRecordPda);
RuleSet
如果您有可用的 metadata 账户数据,您可以检查 metadata 账户上的 programmableConfig 字段来获取规则集。
1import { unwrapOptionRecursively } from '@metaplex-foundation/umi';
2
3// Assuming assetWithToken was fetched using fetchDigitalAssetWithAssociatedToken
4
5const ruleSet = unwrapOptionRecursively(
6 assetWithToken.metadata.programmableConfig
7)?.ruleSet;
8
9console.log('Rule Set:', ruleSet);
1// No additional imports needed - programmableConfig is directly on metadata
2
3// Assuming assetWithToken was fetched using fetchDigitalAssetWithAssociatedToken
4
5// In Kit, options are represented as { __option: 'Some', value: ... } or { __option: 'None' }
6const programmableConfig = assetWithToken.metadata.programmableConfig;
7const ruleSet = programmableConfig.__option === 'Some'
8 ? programmableConfig.value.ruleSet
9 : null;
10
11console.log('Rule Set:', ruleSet);
Authorization Rules 程序
如果您的 pNFT 资产上设置了 ruleSet,您需要传入 Authorization Rules Program ID 以便验证 ruleSet。
1import { getMplTokenAuthRulesProgramId } from '@metaplex-foundation/mpl-token-auth-rules';
2
3const authorizationRulesProgram = getMplTokenAuthRulesProgramId(umi);
4
5console.log('Auth Rules Program:', authorizationRulesProgram);
1import { address } from '@solana/addresses';
2
3// Token Auth Rules Program ID
4const authorizationRulesProgram = address('auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg');
5
6console.log('Auth Rules Program:', authorizationRulesProgram);
Authorization Data
如果您的 pNFT 资产上有需要额外数据进行验证的 ruleSet,您可以在指令参数中将其作为 authorizationData: { payload: ... } 传入。
在任何操作上强制执行规则
可编程 NFT 最重要的功能之一是它们能够在任何影响它们的操作上强制执行一组规则。整个授权层由另一个名为 Token Auth Rules 的 Metaplex 程序提供。虽然该程序用于使 pNFTs 可编程,但它是一个通用程序,可用于为任何用例创建和验证授权规则。
对于 pNFTs,支持以下操作:
| 操作 | 描述 |
|---|---|
Transfer:Owner | 由 pNFT 所有者发起的转移 |
Transfer:SaleDelegate | 由销售委托发起的转移 |
Transfer:TransferDelegate | 由转移或锁定转移委托发起的转移 |
Transfer:MigrationDelegate | 由迁移委托(pNFT 迁移期间使用的旧委托)发起的转移 |
Transfer:WalletToWallet | 钱包之间的转移(目前未使用) |
Delegate:Sale | 批准销售委托 |
Delegate:Transfer | 批准转移委托 |
Delegate:LockedTransfer | 批准锁定转移委托 |
Delegate:Utility | 批准实用委托 |
Delegate:Staking | 批准质押委托 |
创作者可以为任何这些操作分配自定义规则。当执行该操作时,Token Metadata 程序将在允许操作执行之前确保规则有效。可用规则由 Token Auth Rules 程序直接记录,但值得注意的是有两种类型的规则:
- 原始规则:这些规则明确告诉我们操作是否被允许。例如:
PubkeyMatch规则仅当给定字段的公钥与给定公钥匹配时才会通过;ProgramOwnedList仅当拥有给定字段账户的程序是给定程序列表的一部分时才会通过;Pass规则始终通过;等等。 - 复合规则:这些规则将多个规则聚合在一起以创建更复杂的授权逻辑。例如:
All规则仅当它包含的所有规则都通过时才会通过;Any规则仅当它包含的至少一个规则通过时才会通过;Not规则仅当它包含的规则不通过时才会通过;等等。
一旦我们为操作定义了所有规则,我们就可以将它们存储在 Token Auth Rules 程序的 Rule Set 账户中。每当我们需要更改此 Rule Set 时,新的 Rule Set Revision 会被附加到 Rule Set 账户。这确保任何当前锁定在特定修订版中的 pNFT 可以在移动到最新修订版之前解锁。
用例:版税强制执行
现在我们对 pNFTs 有了更好的理解,让我们看一个可以用 PNFTs 解决的具体用例:版税强制执行。
如上所述,没有 pNFTs,任何人都可以通过直接与 SPL Token 程序交互来绕过存储在 Metadata 账户上的版税百分比。这意味着创作者必须依赖与其资产交互的用户和程序的善意。
然而,使用 pNFTs,创作者可以设计一个 Rule Set,确保不强制执行版税的程序被禁止在其资产上执行转移。他们可以使用规则组合来创建允许列表或拒绝列表,具体取决于他们的需求。
此外,由于 Rule Sets 可以在多个 pNFTs 之间共享和重用,创作者可以创建和共享社区 Rule Sets,以确保任何停止支持版税的程序立即被禁止与使用此类社区 Rule Set 的任何 pNFTs 交互。这为程序支持版税创造了强大的激励,否则它们将被禁止与大量资产交互。
