外部插件
Oracle Plugin
Last updated January 31, 2026
Oracle Plugin 将 Core Asset 连接到外部 Oracle 账户以实现自定义验证逻辑。基于时间、价格、所有权或任何您实现的自定义规则来拒绝转账、销毁或更新操作。
您将学到
- 创建用于验证的 Oracle 账户
- 配置生命周期检查(拒绝转账、更新、销毁)
- 使用基于 PDA 的 Oracle 寻址
- 部署基于时间和条件的限制
概要
Oracle Plugin 根据外部 Oracle 账户验证生命周期事件。Oracle 账户存储验证结果,Core 程序读取这些结果来批准或拒绝操作。
- 可以拒绝创建、转账、销毁和更新事件
- Oracle 账户在 Asset 外部(由您控制)
- 动态性:更新 Oracle 账户以改变行为
- 支持 PDA 派生,实现按 Asset 或按所有者的 Oracle
不在范围内
AppData 存储(参见 AppData Plugin)、内置冻结行为(参见 Freeze Delegate)以及链下 Oracle。
快速开始
跳转至: Oracle 账户结构 · 使用 Oracle 创建 · 添加到 Asset · 默认 Oracle
- 部署带有验证规则的 Oracle 账户
- 将 Oracle Plugin 适配器添加到 Asset/Collection
- 配置生命周期检查(要验证哪些事件)
- 更新 Oracle 账户以动态改变验证行为
什么是 Oracle Plugin?
Oracle Plugin 是由 Authority 在 MPL Core Asset 或 Collection 外部创建的链上账户。如果 Asset 或 Collection 启用了 Oracle Adapter 并分配了 Oracle Account,则 Oracle Account 将被 MPL Core 程序加载,用于对生命周期事件进行验证。 Oracle Plugin 存储与 4 个生命周期事件(create、transfer、burn 和 update)相关的数据,可以配置为执行 Reject 验证。 更新和更改 Oracle Account 的能力提供了强大的交互式生命周期体验。
适用于
| MPL Core Asset | ✅ |
| MPL Core Collection | ✅ |
允许的验证
以下验证结果可以从 Oracle Account 返回到 Oracle Plugin。
| Can Approve | ❌ |
| Can Reject | ✅ |
| Can Pass | ❌ |
链上 Oracle 账户结构
Oracle Account 应具有以下链上账户结构。
Oracle Account 的链上账户结构
#[account]
pub struct Validation {
pub validation: OracleValidation,
}
impl Validation {
pub fn size() -> usize {
8 // anchor discriminator
+ 5 // validation
}
}
pub enum OracleValidation {
Uninitialized,
V1 {
create: ExternalValidationResult,
transfer: ExternalValidationResult,
burn: ExternalValidationResult,
update: ExternalValidationResult,
},
}
pub enum ExternalValidationResult {
Approved,
Rejected,
Pass,
}
Oracle 账户偏移量
由于账户所需的鉴别器大小不同,账户结构在不同账户框架(Anchor、Shank 等)之间会略有不同:
- 如果
OracleValidation结构体位于 Oracle 账户数据部分的开头,则为ValidationResultsOffset选择NoOffset。 - 如果 Oracle 账户仅包含
OracleValidation结构体但由 Anchor 程序管理,请为ValidationResultsOffset选择Anchor,以便结构体可以在 Anchor 账户鉴别器之后定位。 - 如果
OracleValidation结构体位于 Oracle 账户的其他偏移位置,请使用Custom偏移量。
resultsOffset / result_offset
const resultsOffset: ValidationResultsOffset =
| { type: 'NoOffset' }
| { type: 'Anchor' }
| { type: 'Custom'; offset: bigint };
更新 Oracle 账户
因为 Oracle Account 由创建者/开发者创建和维护,OracleValidation 结构体可以随时更新,使生命周期具有动态性。
Oracle Adapter
Oracle Adapter 接受以下参数和数据。
链上结构体
pub struct Oracle {
/// The address of the oracle, or if using the `pda` option,
/// a program ID from which to derive a PDA.
pub base_address: Pubkey,
/// Optional account specification (PDA derived from `base_address` or other
/// available account specifications). Note that even when this
/// configuration is used there is still only one
/// Oracle account specified by the adapter.
pub base_address_config: Option<ExtraAccount>,
/// Validation results offset in the Oracle account.
/// Default is `ValidationResultsOffset::NoOffset`
pub results_offset: ValidationResultsOffset,
}
声明 Oracle Plugin 的 PDA
Oracle Plugin Adapter 的默认行为是为适配器提供一个静态 base_address,然后适配器可以从中读取并提供验证结果。 如果您希望使 Oracle Plugin Adapter 更具动态性,您可以将 program_id 作为 base_address 传入,然后传入一个 ExtraAccount,可用于派生一个或多个指向 Oracle Account 地址的 PDA。这允许 Oracle Adapter 访问来自多个派生 Oracle Account 的数据。请注意,使用 ExtraAccount 时还有其他高级的非 PDA 规范可用。
ExtraAccount 选项列表
对于 Collection 中所有 Asset 都相同的额外账户示例是 PreconfiguredCollection PDA,它使用 Collection 的 Pubkey 来派生 Oracle 账户。更动态的额外账户示例是 PreconfiguredOwner PDA,它使用所有者的 pubkey 来派生 Oracle 账户。
pub enum ExtraAccount {
/// Program-based PDA with seeds \["mpl-core"\]
PreconfiguredProgram {
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// Collection-based PDA with seeds \["mpl-core", collection_pubkey\]
PreconfiguredCollection {
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// Owner-based PDA with seeds \["mpl-core", owner_pubkey\]
PreconfiguredOwner {
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// Recipient-based PDA with seeds \["mpl-core", recipient_pubkey\]
/// If the lifecycle event has no recipient the derivation will fail.
PreconfiguredRecipient {
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// Asset-based PDA with seeds \["mpl-core", asset_pubkey\]
PreconfiguredAsset {
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// PDA based on user-specified seeds.
CustomPda {
/// Seeds used to derive the PDA.
seeds: Vec<Seed>,
/// Program ID if not the base address/program ID for the external plugin.
custom_program_id: Option<Pubkey>,
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
/// Directly-specified address.
Address {
/// Address.
address: Pubkey,
/// Account is a signer
is_signer: bool,
/// Account is writable.
is_writable: bool,
},
}
创建和添加 Oracle Plugin
使用 Oracle Plugin 创建 Asset
使用 Oracle Plugin 创建 MPL Core Asset
import { generateSigner, publicKey } from '@metaplex-foundation/umi'
import {
create,
CheckResult
} from '@metaplex-foundation/mpl-core'
const collectionSigner = generateSigner(umi)
const oracleAccount = publicKey('11111111111111111111111111111111')
const asset = await create(umi, {
... CreateAssetArgs,
plugins: [
{
type: 'Oracle',
resultsOffset: {
type: 'Anchor',
},
baseAddress: oracleAccount,
lifecycleChecks: {
update: [CheckResult.CAN_REJECT],
},
baseAddressConfig: undefined,
},
],
});.sendAndConfirm(umi)
将 Oracle Plugin 添加到 Asset
将 Oracle Plugin 添加到 Collection
import { publicKey } from '@metaplex-foundation/umi'
import { addPlugin, CheckResult } from '@metaplex-foundation/mpl-core'
const asset = publicKey('11111111111111111111111111111111')
const oracleAccount = publicKey('22222222222222222222222222222222')
addPlugin(umi, {
asset,
plugin: {
type: 'Oracle',
resultsOffset: {
type: 'Anchor',
},
lifecycleChecks: {
create: [CheckResult.CAN_REJECT],
},
baseAddress: oracleAccount,
},
})
使用 Oracle Plugin 创建 Collection
使用 Oracle Plugin 创建 Collection
import { generateSigner, publicKey } from '@metaplex-foundation/umi'
import {
create,
CheckResult
} from '@metaplex-foundation/mpl-core'
const collectionSigner = generateSigner(umi)
const oracleAccount = publicKey('11111111111111111111111111111111')
const collection = await createCollection(umi, {
... CreateCollectionArgs,
plugins: [
{
type: 'Oracle',
resultsOffset: {
type: 'Anchor',
},
baseAddress: oracleAccount,
lifecycleChecks: {
update: [CheckResult.CAN_REJECT],
},
baseAddressConfig: undefined,
},
],
});.sendAndConfirm(umi)
将 Oracle Plugin 添加到 Collection
将 Oracle Plugin 添加到 Collection
import { publicKey } from '@metaplex-foundation/umi'
import { addCollectionPlugin, CheckResult } from '@metaplex-foundation/mpl-core'
const collection = publicKey('11111111111111111111111111111111')
const oracleAccount = publicKey('22222222222222222222222222222222')
await addCollectionPlugin(umi, {
collection: collection,
plugin: {
type: 'Oracle',
resultsOffset: {
type: 'Anchor',
},
lifecycleChecks: {
update: [CheckResult.CAN_REJECT],
},
baseAddress: oracleAccount,
},
}).sendAndConfirm(umi)
Metaplex 部署的默认 Oracle
在某些罕见情况下,如 灵魂绑定 NFT,可能需要始终拒绝或批准生命周期事件的 Oracle。为此,以下 Oracle 已部署,任何人都可以使用:
- Transfer Oracle:始终拒绝转账。
AwPRxL5f6GDVajyE1bBcfSWdQT58nWMoS36A1uFtpCZY - Update Oracle:始终拒绝更新。
6cKyMV4toCVCEtvh6Sh5RQ1fevynvBDByaQP4ufz1Zj6 - Create Oracle:始终拒绝创建。
2GhRFi9RhqmtEFWCwrAHC61Lti3jEKuCKPcZTfuujaGr
使用示例/想法
示例 1
Asset 在 UTC 中午到午夜期间不可转账。
- 在您选择的程序中创建链上 Oracle Plugin。
- 将 Oracle Plugin Adapter 添加到 Asset 或 Collection,指定您希望进行拒绝验证的生命周期事件。
- 编写一个定时任务,在中午和午夜写入和更新您的 Oracle Plugin,将验证位从 true/false/true 翻转。
示例 2
只有当地板价高于 $10 且 Asset 具有 "red hat" 属性时才能更新。
- 在您选择的程序中创建链上 Oracle Plugin。
- 将 Oracle Plugin Adapter 添加到 Asset,指定您希望进行拒绝验证的生命周期事件。
- 开发者编写 Anchor 程序,可以写入派生相同 PRECONFIGURED_ASSET 账户的 Oracle Account。
- 开发者编写 web2 脚本,监视市场价格,并根据已知的具有 'Red Hat' 特征的 Asset 哈希列表,更新和写入相关的 Oracle Account。
常见错误
Oracle validation failed
Oracle 账户返回了拒绝。检查您的 Oracle 账户状态和验证逻辑。
Oracle account not found
Oracle 账户地址无效或不存在。验证基地址和任何 PDA 派生。
Invalid results offset
ValidationResultsOffset 与您的 Oracle 账户结构不匹配。Anchor 程序使用 Anchor,原始账户使用 NoOffset。
注意事项
- Oracle 账户是外部的。由您部署和维护
- 验证是只读的:Core 读取 Oracle,不写入
- 使用定时任务或事件监听器动态更新 Oracle 状态
- PDA 派生允许按 Asset、按所有者或按 Collection 的 Oracle
常见问题
Oracle Plugin 可以批准转账吗,还是只能拒绝?
Oracle Plugin 只能拒绝或通过。它们无法强制批准被其他 Plugin 拒绝的转账。
如何创建基于时间的转账限制?
部署一个 Oracle 账户,并使用定时任务在特定时间更新验证结果。参见上面的示例 1。
一个 Oracle 可以用于多个 Asset 吗?
可以。使用静态基地址用于单个 Oracle,或者使用 PDA 派生配合 PreconfiguredCollection 实现 Collection 范围的验证。
Oracle 和 Freeze Delegate 有什么区别?
Freeze Delegate 是内置的二元状态(冻结/解冻)。Oracle 允许自定义逻辑——基于时间、价格或任何您实现的条件。
使用 Oracle 需要编写 Solana 程序吗?
是的。Oracle 账户必须是具有正确结构的 Solana 账户。您可以使用 Anchor 或原生 Rust。参见 Oracle Plugin 示例 指南。
术语表
| 术语 | 定义 |
|---|---|
| Oracle Account | 存储验证结果的外部 Solana 账户 |
| Oracle Adapter | 附加到 Asset 的 Plugin 组件,引用 Oracle |
| ValidationResultsOffset | 在 Oracle 账户中定位验证数据的字节偏移量 |
| ExtraAccount | 用于动态 Oracle 寻址的 PDA 派生配置 |
| Lifecycle Check | 指定 Oracle 验证哪些事件的配置 |
相关页面
- External Plugin 概述 - 了解 External Plugin
- AppData Plugin - 数据存储而非验证
- Oracle Plugin 示例 - 完整实现指南
- Freeze Delegate - 内置冻结替代方案
