外部プラグイン

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

  1. バリデーションルールを持つOracle Accountをデプロイ
  2. Oracle Plugin AdapterをAsset/Collectionに追加
  3. ライフサイクルチェックを設定(どのイベントをバリデーションするか)
  4. Oracle Accountを更新してバリデーション動作を動的に変更

Oracle Pluginとは?

Oracle Pluginは、MPL Core AssetまたはCollectionとは別に、Authorityによって外部で作成されるオンチェーンアカウントです。AssetまたはCollectionでOracle Adapterが有効になり、Oracle Accountが割り当てられている場合、Oracle AccountはMPL Coreプログラムによってライフサイクルイベントに対するバリデーションのために読み込まれます。 Oracle Pluginはcreatetransferburnupdateの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の後に構造体を配置できるようにValidationResultsOffsetAnchorを選択します。
  • 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 AdapterOracleを参照するAssetにアタッチされたPluginコンポーネント
ValidationResultsOffsetOracle Account内のバリデーションデータを見つけるためのバイトオフセット
ExtraAccount動的なOracleアドレッシングのためのPDA導出設定
Lifecycle CheckOracleがバリデーションするイベントを指定する設定

関連ページ