#![cfg(feature = "test-sbf")]
pub mod setup;
use mpl_core::{
errors::MplCoreError,
instructions::{
AddCollectionExternalPluginAdapterV1Builder, AddExternalPluginAdapterV1Builder,
},
types::{
AppData, AppDataInitInfo, ExternalCheckResult, ExternalPluginAdapter,
ExternalPluginAdapterInitInfo, ExternalPluginAdapterSchema, HookableLifecycleEvent,
LifecycleHook, LifecycleHookInitInfo, Oracle, OracleInitInfo, PluginAuthority,
UpdateAuthority, ValidationResultsOffset,
},
};
pub use setup::*;
use solana_program::pubkey;
use solana_program_test::tokio;
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transaction::Transaction};
#[tokio::test]
#[ignore]
async fn test_add_lifecycle_hook() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::LifecycleHook(
LifecycleHookInitInfo {
hooked_program: pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 1 },
)],
extra_accounts: None,
data_authority: Some(PluginAuthority::UpdateAuthority),
schema: None,
},
))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![ExternalPluginAdapter::LifecycleHook(LifecycleHook {
hooked_program: pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
extra_accounts: None,
data_authority: Some(PluginAuthority::UpdateAuthority),
schema: ExternalPluginAdapterSchema::Binary,
})],
},
)
.await;
}
#[tokio::test]
#[ignore]
async fn test_cannot_add_lifecycle_hook_with_duplicate_lifecycle_checks() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::LifecycleHook(
LifecycleHookInitInfo {
hooked_program: pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![
(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 1 },
),
(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 1 },
),
],
extra_accounts: None,
data_authority: Some(PluginAuthority::UpdateAuthority),
schema: None,
},
))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err();
assert_custom_instruction_error!(0, error, MplCoreError::DuplicateLifecycleChecks);
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
}
#[tokio::test]
async fn test_temporarily_cannot_add_lifecycle_hook() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::LifecycleHook(
LifecycleHookInitInfo {
hooked_program: pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 1 },
)],
extra_accounts: None,
data_authority: Some(PluginAuthority::UpdateAuthority),
schema: None,
},
))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err();
assert_custom_instruction_error!(0, error, MplCoreError::NotAvailable);
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
}
#[tokio::test]
async fn test_temporarily_cannot_add_lifecycle_hook_on_collection() {
let mut context = program_test().start_with_context().await;
let collection = Keypair::new();
create_collection(
&mut context,
CreateCollectionHelperArgs {
collection: &collection,
update_authority: None,
payer: None,
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let update_authority = context.payer.pubkey();
assert_collection(
&mut context,
AssertCollectionHelperArgs {
collection: collection.pubkey(),
update_authority,
name: None,
uri: None,
num_minted: 0,
current_size: 0,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddCollectionExternalPluginAdapterV1Builder::new()
.collection(collection.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::LifecycleHook(
LifecycleHookInitInfo {
hooked_program: pubkey!("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 1 },
)],
extra_accounts: None,
data_authority: Some(PluginAuthority::UpdateAuthority),
schema: None,
},
))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err();
assert_custom_instruction_error!(0, error, MplCoreError::NotAvailable);
}
#[tokio::test]
async fn test_add_oracle() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::Oracle(OracleInitInfo {
base_address: Pubkey::default(),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 4 },
)],
base_address_config: None,
results_offset: None,
}))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![ExternalPluginAdapter::Oracle(Oracle {
base_address: Pubkey::default(),
base_address_config: None,
results_offset: ValidationResultsOffset::NoOffset,
})],
},
)
.await;
}
#[tokio::test]
async fn test_cannot_add_oracle_with_duplicate_lifecycle_checks() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::Oracle(OracleInitInfo {
base_address: Pubkey::default(),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![
(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 4 },
),
(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 4 },
),
],
base_address_config: None,
results_offset: None,
}))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err();
assert_custom_instruction_error!(0, error, MplCoreError::DuplicateLifecycleChecks);
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
}
#[tokio::test]
async fn test_add_app_data() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::AppData(AppDataInitInfo {
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
data_authority: PluginAuthority::UpdateAuthority,
schema: None,
}))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[add_external_plugin_adapter_ix],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
context.banks_client.process_transaction(tx).await.unwrap();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![ExternalPluginAdapter::AppData(AppData {
data_authority: PluginAuthority::UpdateAuthority,
schema: ExternalPluginAdapterSchema::Binary,
})],
},
)
.await;
}
#[tokio::test]
async fn test_cannot_add_duplicate_external_plugin_adapter() {
let mut context = program_test().start_with_context().await;
let asset = Keypair::new();
create_asset(
&mut context,
CreateAssetHelperArgs {
owner: None,
payer: None,
asset: &asset,
data_state: None,
name: None,
uri: None,
authority: None,
update_authority: None,
collection: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await
.unwrap();
let owner = context.payer.pubkey();
let update_authority = context.payer.pubkey();
assert_asset(
&mut context,
AssertAssetHelperArgs {
asset: asset.pubkey(),
owner,
update_authority: Some(UpdateAuthority::Address(update_authority)),
name: None,
uri: None,
plugins: vec![],
external_plugin_adapters: vec![],
},
)
.await;
let add_external_plugin_adapter_ix0 = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::Oracle(OracleInitInfo {
base_address: Pubkey::default(),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 4 },
)],
base_address_config: None,
results_offset: None,
}))
.instruction();
let add_external_plugin_adapter_ix1 = AddExternalPluginAdapterV1Builder::new()
.asset(asset.pubkey())
.payer(context.payer.pubkey())
.init_info(ExternalPluginAdapterInitInfo::Oracle(OracleInitInfo {
base_address: Pubkey::default(),
init_plugin_authority: Some(PluginAuthority::UpdateAuthority),
lifecycle_checks: vec![(
HookableLifecycleEvent::Transfer,
ExternalCheckResult { flags: 4 },
)],
base_address_config: None,
results_offset: None,
}))
.instruction();
let tx = Transaction::new_signed_with_payer(
&[
add_external_plugin_adapter_ix0,
add_external_plugin_adapter_ix1,
],
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction(tx)
.await
.unwrap_err();
assert_custom_instruction_error!(1, error, MplCoreError::ExternalPluginAdapterAlreadyExists);
}