外部プラグイン
Oracle Plugin
Last updated January 31, 2026
Oracle Pluginは、Core Assetを外部Oracle Accountに接続してカスタムバリデーションロジックを実現します。時間、価格、所有権、または実装する任意のカスタムルールに基づいて、転送、バーン、更新を拒否できます。
学習内容
- バリデーション用のOracle Accountの作成
- ライフサイクルチェックの設定(転送、更新、バーンの拒否)
- PDAベースのOracleアドレッシングの使用
- 時間ベースおよび条件ベースの制限のデプロイ
概要
Oracle Pluginは、外部Oracle Accountに対してライフサイクルイベントをバリデーションします。Oracle Accountはバリデーション結果を保存し、Coreプログラムがそれを読み取って操作を承認または拒否します。
- create、transfer、burn、updateイベントを拒否可能
- Oracle AccountはAssetの外部(あなたが管理)
- 動的:Oracle Accountを更新して動作を変更
- Asset単位またはオーナー単位のOracleのためのPDA導出をサポート
対象外
AppDataストレージ(AppData Pluginを参照)、組み込みのフリーズ動作(Freeze Delegateを参照)、およびオフチェーンOracleは対象外です。
クイックスタート
ジャンプ先: Oracle Accountの構造 · Oracleで作成 · Assetに追加 · デフォルトOracle
- バリデーションルールを持つOracle Accountをデプロイ
- Oracle Plugin AdapterをAsset/Collectionに追加
- ライフサイクルチェックを設定(どのイベントをバリデーションするか)
- Oracle Accountを更新してバリデーション動作を動的に変更
Oracle Pluginとは?
Oracle Pluginは、MPL Core AssetまたはCollectionとは別に、Authorityによって外部で作成されるオンチェーンアカウントです。AssetまたはCollectionでOracle Adapterが有効になり、Oracle Accountが割り当てられている場合、Oracle AccountはMPL Coreプログラムによってライフサイクルイベントに対するバリデーションのために読み込まれます。 Oracle Pluginはcreate、transfer、burn、updateの4つのライフサイクルイベントに関連するデータを保存し、Rejectバリデーションを実行するように設定できます。 Oracle Accountを更新・変更できることで、強力でインタラクティブなライフサイクル体験が可能になります。
対応状況
| MPL Core Asset | ✅ |
| MPL Core Collection | ✅ |
許可されるバリデーション
Oracle AccountからOracle Pluginに以下のバリデーション結果を返すことができます。
| Can Approve | ❌ |
| Can Reject | ✅ |
| Can Pass | ❌ |
オンチェーンOracle Accountの構造
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 Accountのオフセット
アカウント構造は、アカウントに必要なdiscriminatorサイズにより、アカウントフレームワーク(Anchor、Shankなど)間でわずかに異なります:
OracleValidation構造体がOracle Accountのデータセクションの先頭にある場合、ValidationResultsOffsetにはNoOffsetを選択します。- Oracle Accountが
OracleValidation構造体のみを含み、Anchorプログラムによって管理されている場合、Anchorアカウントdiscriminatorの後に構造体を配置できるようにValidationResultsOffsetにAnchorを選択します。 OracleValidation構造体がOracle Account内の他のオフセットにある場合、Customオフセットを使用します。
resultsOffset / result_offset
const resultsOffset: ValidationResultsOffset =
| { type: 'NoOffset' }
| { type: 'Anchor' }
| { type: 'Custom'; offset: bigint };
Oracle Accountの更新
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をより動的にしたい場合は、base_addressとしてprogram_idを渡し、次にOracle Accountアドレスを指す1つ以上のPDAを導出するために使用できるExtraAccountを渡すことができます。これにより、Oracle Adapterは複数の導出されたOracle Accountからデータにアクセスできます。ExtraAccountを使用する場合、他の高度な非PDA仕様も利用可能であることに注意してください。
ExtraAccountオプションのリスト
Collection内のすべてのAssetで同じ追加アカウントの例はPreconfiguredCollection PDAで、CollectionのPubkeyを使用してOracle Accountを導出します。より動的な追加アカウントの例はPreconfiguredOwner PDAで、オーナーのpubkeyを使用してOracle Accountを導出します。
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)
AssetにOracle Pluginを追加
CollectionにOracle Pluginを追加
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)
CollectionにOracle Pluginを追加
CollectionにOracle Pluginを追加
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
Soulbound NFTのようなまれなケースでは、常にライフサイクルイベントを拒否または承認するOracleがあると便利な場合があります。そのために、以下のOracleがデプロイされており、誰でも使用できます:
- Transfer Oracle: 常に転送を拒否します。
AwPRxL5f6GDVajyE1bBcfSWdQT58nWMoS36A1uFtpCZY - Update Oracle: 常に更新を拒否します。
6cKyMV4toCVCEtvh6Sh5RQ1fevynvBDByaQP4ufz1Zj6 - Create Oracle: 常に作成を拒否します。
2GhRFi9RhqmtEFWCwrAHC61Lti3jEKuCKPcZTfuujaGr
使用例/アイデア
例1
正午から深夜(UTC)の間、Assetを転送不可にする。
- 任意のプログラムでオンチェーンOracle Pluginを作成します。
- 拒否バリデーションを行いたいライフサイクルイベントを指定して、Oracle Plugin AdapterをAssetまたはCollectionに追加します。
- 正午と深夜にOracle Pluginに書き込み更新を行い、バリデーションビットをtrue/false/trueに切り替えるcronを作成します。
例2
フロア価格が$10以上で、Assetに「red hat」属性がある場合のみ更新可能。
- 任意のプログラムでオンチェーンOracle Pluginを作成します。
- 拒否バリデーションを行いたいライフサイクルイベントを指定して、Oracle Plugin AdapterをAssetに追加します。
- 開発者は同じPRECONFIGURED_ASSETアカウントを導出するOracle Accountに書き込むことができるAnchorプログラムを作成します。
- 開発者はマーケットプレイスの価格を監視し、'Red Hat'トレイトを持つAssetの既知のハッシュリストを使用して、関連するOracle Accountを更新・書き込みするweb2スクリプトを作成します。
一般的なエラー
Oracle validation failed
Oracle Accountが拒否を返しました。Oracle Accountの状態とバリデーションロジックを確認してください。
Oracle account not found
Oracle Accountアドレスが無効か存在しません。ベースアドレスとPDA導出を確認してください。
Invalid results offset
ValidationResultsOffsetがOracle Accountの構造と一致しません。AnchorプログラムにはAnchorを、生のアカウントにはNoOffsetを使用してください。
注意事項
- Oracle Accountは外部です。あなたがデプロイして維持します
- バリデーションは読み取り専用です:CoreはOracleを読み取りますが、書き込みはしません
- cronジョブやイベントリスナーを使用してOracle状態を動的に更新します
- PDA導出により、Asset単位、オーナー単位、またはCollection単位のOracleが可能になります
FAQ
Oracle Pluginは転送を承認できますか?それとも拒否のみ?
Oracle Pluginは拒否またはパスのみ可能です。他のPluginが拒否した転送を強制的に承認することはできません。
時間ベースの転送制限を作成するにはどうすればよいですか?
Oracle Accountをデプロイし、cronジョブを使用して特定の時間にバリデーション結果を更新します。上記の例1を参照してください。
1つのOracleを複数のAssetに使用できますか?
はい。単一のOracleには静的なベースアドレスを使用するか、Collection全体のバリデーションにはPreconfiguredCollectionを使用したPDA導出を使用します。
OracleとFreeze Delegateの違いは何ですか?
Freeze Delegateは組み込みでバイナリ(凍結/非凍結)です。Oracleはカスタムロジック(時間ベース、価格ベース、または実装する任意の条件)を可能にします。
OracleにはSolanaプログラムを書く必要がありますか?
はい。Oracle Accountは正しい構造を持つSolanaアカウントである必要があります。AnchorまたはネイティブRustを使用できます。Oracle Pluginの例ガイドを参照してください。
用語集
| 用語 | 定義 |
|---|---|
| Oracle Account | バリデーション結果を保存する外部Solanaアカウント |
| Oracle Adapter | Oracleを参照するAssetにアタッチされたPluginコンポーネント |
| ValidationResultsOffset | Oracle Account内のバリデーションデータを見つけるためのバイトオフセット |
| ExtraAccount | 動的なOracleアドレッシングのためのPDA導出設定 |
| Lifecycle Check | Oracleがバリデーションするイベントを指定する設定 |
関連ページ
- External Plugins概要 - External Pluginの理解
- AppData Plugin - バリデーションではなくデータストレージ
- Oracle Pluginの例 - 完全な実装ガイド
- Freeze Delegate - 組み込みのフリーズ代替機能
