Skip to content

Commit

Permalink
Adds a store v2 init implementatioin that allows a uri to point to an…
Browse files Browse the repository at this point in the history
… off chain settings resource (metaplex-foundation#1094)

* Adds a store v2 init implementatioin that allows a uri to point to an off chain settings resource

* adds more tests
  • Loading branch information
austbot authored Dec 2, 2021
1 parent e1e0901 commit 59ab126
Show file tree
Hide file tree
Showing 9 changed files with 380 additions and 18 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
needs:
- test-token-vault
- test-token-metadata
- test-metaplex
steps:
- run: echo "Done"

Expand Down Expand Up @@ -107,4 +108,39 @@ jobs:
cargo +${{ env.RUST_TOOLCHAIN }} test -- --nocapture --test-threads 1
cargo +${{ env.RUST_TOOLCHAIN }} test-bpf --version
cargo +${{ env.RUST_TOOLCHAIN }} test-bpf -- --nocapture --test-threads 1
test-metaplex:
runs-on: ubuntu-latest
env:
cache_id: test-metaplex

steps:
# Setup Deps
- uses: actions/checkout@v2
- uses: ./.github/actions/install-linux-build-deps
- uses: ./.github/actions/install-solana
with:
solana_version: ${{ env.SOLANA_VERSION }}
- uses: ./.github/actions/install-rust
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}

# Restore Cache from previous build/test
- uses: actions/cache@v2
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
./rust/target
key: ${{ env.cache_id }}-${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-${{ env.RUSTC_HASH }}

# Run test
- name: test-metaplex
id: run_test
working-directory: ./rust/metaplex/program
run: |
cargo +${{ env.RUST_TOOLCHAIN }} test -- --nocapture --test-threads 1
cargo +${{ env.RUST_TOOLCHAIN }} test-bpf --version
cargo +${{ env.RUST_TOOLCHAIN }} test-bpf -- --nocapture --test-threads 1
2 changes: 2 additions & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions rust/metaplex/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ metaplex-token-metadata = { path = "../../token-metadata/program", features = [
thiserror = "1.0"
borsh = "0.9.1"

[dev-dependencies]
solana-sdk = "1.7.11"
solana-program-test = "1.7.11"

[lib]
crate-type = ["cdylib", "lib"]
55 changes: 55 additions & 0 deletions rust/metaplex/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ use {
pub struct SetStoreArgs {
pub public: bool,
}

#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub struct SetStoreV2Args {
pub public: bool,
pub settings_uri: Option<String>,
}
#[derive(BorshSerialize, BorshDeserialize, Clone)]
pub struct SetWhitelistedCreatorArgs {
pub activated: bool,
Expand Down Expand Up @@ -665,6 +671,21 @@ pub enum MetaplexInstruction {
/// 7. `[]` Rent sysvar
/// 8. `[]` Clock sysvar
SetAuctionCache,

/// Given a signer wallet, create a store with pda ['metaplex', wallet] (if it does not exist) and/or update it
/// (if it already exists). Stores can be set to open (anybody can publish) or closed (publish only via whitelist).
///
/// 0. `[writable]` The store key, seed of ['metaplex', admin wallet]
/// 1. `[writable]` The store config key, seed of ['metaplex', store key]
/// 2. `[signer]` The admin wallet
/// 3. `[signer]` Payer
/// 4. `[]` Token program
/// 5. `[]` Token vault program
/// 6. `[]` Token metadata program
/// 7. `[]` Auction program
/// 8. `[]` System
/// 8. `[]` Rent sysvar
SetStoreV2(SetStoreV2Args),
}

/// Creates an DeprecatedInitAuctionManager instruction
Expand Down Expand Up @@ -1099,6 +1120,40 @@ pub fn create_set_store_instruction(
}
}

/// Creates an SetStore instruction
pub fn create_set_store_v2_instruction(
program_id: Pubkey,
store: Pubkey,
config: Pubkey,
admin: Pubkey,
payer: Pubkey,
public: bool,
settings_uri: Option<String>,
) -> Instruction {
let accounts = vec![
AccountMeta::new(store, false),
AccountMeta::new(config, false),
AccountMeta::new_readonly(admin, true),
AccountMeta::new_readonly(payer, true),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(metaplex_token_vault::id(), false),
AccountMeta::new_readonly(metaplex_token_metadata::id(), false),
AccountMeta::new_readonly(metaplex_auction::id(), false),
AccountMeta::new_readonly(solana_program::system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
Instruction {
program_id,
accounts,
data: MetaplexInstruction::SetStoreV2(SetStoreV2Args {
public,
settings_uri,
})
.try_to_vec()
.unwrap(),
}
}

#[allow(clippy::too_many_arguments)]
pub fn create_deprecated_populate_participation_printing_account_instruction(
program_id: Pubkey,
Expand Down
2 changes: 1 addition & 1 deletion rust/metaplex/program/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ pub mod utils;
// Export current sdk types for downstream users building with a different sdk version
pub use solana_program;

solana_program::declare_id!("p1exdMJcjVao65QdewkaZRUnU6VPSXhus9n2GzWfh98");
solana_program::declare_id!("p1exdMJcjVao65QdewkaZRUnU6VPSXhus9n2GzWfh98");
6 changes: 6 additions & 0 deletions rust/metaplex/program/src/processor.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::processor::set_store::process_set_store_v2;

use {
crate::instruction::MetaplexInstruction,
borsh::BorshDeserialize,
Expand Down Expand Up @@ -94,6 +96,10 @@ pub fn process_instruction<'a>(
msg!("Instruction: Set Store");
process_set_store(program_id, accounts, args.public)
}
MetaplexInstruction::SetStoreV2(args) => {
msg!("Instruction: Set Store V2");
process_set_store_v2(program_id, accounts, args.public, args.settings_uri)
}
MetaplexInstruction::SetWhitelistedCreator(args) => {
msg!("Instruction: Set Whitelisted Creator");
process_set_whitelisted_creator(program_id, accounts, args.activated)
Expand Down
135 changes: 119 additions & 16 deletions rust/metaplex/program/src/processor/set_store.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::state::StoreConfig;

use {
crate::{
error::MetaplexError,
state::{Key, Store, MAX_STORE_SIZE, PREFIX},
state::{Key, Store, CONFIG, MAX_STORE_CONFIG_V1_SIZE, MAX_STORE_SIZE, PREFIX},
utils::{
assert_derivation, assert_owned_by, assert_signer, create_or_allocate_account_raw,
},
Expand All @@ -14,23 +16,19 @@ use {
},
};

pub fn process_set_store<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
pub fn set_store_logic<'a>(
public: bool,
program_id: &Pubkey,
auction_program_info: &'a AccountInfo<'a>,
token_vault_program_info: &'a AccountInfo<'a>,
rent_info: &'a AccountInfo<'a>,
system_info: &'a AccountInfo<'a>,
token_metadata_program_info: &'a AccountInfo<'a>,
token_program_info: &'a AccountInfo<'a>,
store_info: &'a AccountInfo<'a>,
admin_wallet_info: &'a AccountInfo<'a>,
payer_info: &'a AccountInfo<'a>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let store_info = next_account_info(account_info_iter)?;
let admin_wallet_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?;
let token_program_info = next_account_info(account_info_iter)?;
let token_vault_program_info = next_account_info(account_info_iter)?;
let token_metadata_program_info = next_account_info(account_info_iter)?;
let auction_program_info = next_account_info(account_info_iter)?;
let system_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;

assert_signer(payer_info)?;
assert_signer(admin_wallet_info)?;
if !store_info.data_is_empty() {
Expand Down Expand Up @@ -88,3 +86,108 @@ pub fn process_set_store<'a>(
store.serialize(&mut *store_info.data.borrow_mut())?;
Ok(())
}

pub fn process_set_store<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
public: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let store_info = next_account_info(account_info_iter)?;
let admin_wallet_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?;
let token_program_info = next_account_info(account_info_iter)?;
let token_vault_program_info = next_account_info(account_info_iter)?;
let token_metadata_program_info = next_account_info(account_info_iter)?;
let auction_program_info = next_account_info(account_info_iter)?;
let system_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;

set_store_logic(
public,
program_id,
auction_program_info,
token_vault_program_info,
rent_info,
system_info,
token_metadata_program_info,
token_program_info,
store_info,
admin_wallet_info,
payer_info,
)
}

pub fn process_set_store_v2<'a>(
program_id: &'a Pubkey,
accounts: &'a [AccountInfo<'a>],
public: bool,
settings_uri: Option<String>,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let store_info = next_account_info(account_info_iter)?;
let store_config_info = next_account_info(account_info_iter)?;
let admin_wallet_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?;
let token_program_info = next_account_info(account_info_iter)?;
let token_vault_program_info = next_account_info(account_info_iter)?;
let token_metadata_program_info = next_account_info(account_info_iter)?;
let auction_program_info = next_account_info(account_info_iter)?;
let system_info = next_account_info(account_info_iter)?;
let rent_info = next_account_info(account_info_iter)?;

let res = set_store_logic(
public,
program_id,
auction_program_info,
token_vault_program_info,
rent_info,
system_info,
token_metadata_program_info,
token_program_info,
store_info,
admin_wallet_info,
payer_info,
);
if res.is_err() {
return res;
}
if !store_config_info.data_is_empty() {
assert_owned_by(store_config_info, program_id)?;
}
let store_config_bump = assert_derivation(
program_id,
store_config_info,
&[
PREFIX.as_bytes(),
program_id.as_ref(),
CONFIG.as_bytes(),
store_info.key.as_ref(),
],
)?;

if store_config_info.data_is_empty() {
create_or_allocate_account_raw(
*program_id,
store_config_info,
rent_info,
system_info,
payer_info,
MAX_STORE_CONFIG_V1_SIZE,
&[
PREFIX.as_bytes(),
program_id.as_ref(),
CONFIG.as_bytes(),
store_info.key.as_ref(),
&[store_config_bump],
],
)?;
}
let mut config = StoreConfig::from_account_info(store_config_info)?;
config.key = Key::StoreConfigV1;
config.settings_uri = settings_uri;
config.serialize(&mut *store_config_info.data.borrow_mut())?;
Ok(())
}
29 changes: 28 additions & 1 deletion rust/metaplex/program/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub const PREFIX: &str = "metaplex";
pub const TOTALS: &str = "totals";
pub const INDEX: &str = "index";
pub const CACHE: &str = "cache";
pub const CONFIG: &str = "config";
pub const BASE_TRACKER_SIZE: usize = 1 + 1 + 1 + 4;

pub const MAX_INDEXED_ELEMENTS: usize = 100;
Expand Down Expand Up @@ -49,7 +50,15 @@ pub const MAX_AUCTION_MANAGER_V2_SIZE: usize = 1 + //key
1 + //status
8 + // winning configs validated
200; // padding
pub const MAX_STORE_SIZE: usize = 2 + 32 + 32 + 32 + 32 + 100;
pub const MAX_STORE_SIZE: usize = 2 + // Store Version Key
32 + // Auction Program Key
32 + // Token Vault Program Key
32 + // Token Metadata Program Key
32 + // Token Program Key
100; // Padding;
pub const MAX_STORE_CONFIG_V1_SIZE: usize = 2 + // StoreConfig Version Key
200 + // Settings Uri Len
100; // Padding;
pub const MAX_WHITELISTED_CREATOR_SIZE: usize = 2 + 32 + 10;
pub const MAX_PAYOUT_TICKET_SIZE: usize = 1 + 32 + 8;
pub const MAX_BID_REDEMPTION_TICKET_SIZE: usize = 3;
Expand Down Expand Up @@ -88,6 +97,7 @@ pub enum Key {
AuctionWinnerTokenTypeTrackerV1,
StoreIndexerV1,
AuctionCacheV1,
StoreConfigV1,
}

pub struct CommonWinningIndexChecks<'a> {
Expand Down Expand Up @@ -732,6 +742,23 @@ impl Store {
Ok(store)
}
}
#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize)]
pub struct StoreConfig {
pub key: Key,
pub settings_uri: Option<String>,
}
impl StoreConfig {
pub fn from_account_info(a: &AccountInfo) -> Result<StoreConfig, ProgramError> {
let store: StoreConfig = try_from_slice_checked(
&a.data.borrow_mut(),
Key::StoreConfigV1,
MAX_STORE_CONFIG_V1_SIZE,
)?;

Ok(store)
}
}

#[repr(C)]
#[derive(Clone, BorshSerialize, BorshDeserialize, Copy)]
Expand Down
Loading

0 comments on commit 59ab126

Please sign in to comment.