Skip to content

Commit

Permalink
Merge pull request #48 from holaplex/abdul/switch-collection
Browse files Browse the repository at this point in the history
Assemble Switch collection transaction
  • Loading branch information
imabdulbasit authored Aug 28, 2023
2 parents 3168686 + e0f9f64 commit ffeeec9
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 39 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install rustfmt
- name: Install nightly
uses: actions-rs/toolchain@v1
with:
toolchain: 1.69.0
toolchain: nightly-2022-12-11
override: true
components: rustfmt, clippy
- name: cargo fmt
Expand Down
14 changes: 13 additions & 1 deletion consumer/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use holaplex_hub_nfts_solana_core::proto::{
use holaplex_hub_nfts_solana_entity::{collection_mints, collections, update_revisions};
use hub_core::prelude::*;
use solana_program::pubkey::Pubkey;

#[derive(Clone)]
pub struct MasterEditionAddresses {
pub metadata: Pubkey,
Expand Down Expand Up @@ -62,6 +61,12 @@ pub struct UpdateCollectionMintAddresses {
pub update_authority: Pubkey,
}

#[derive(Clone)]
pub struct SwitchCollectionAddresses {
pub payer: Pubkey,
pub new_collection_authority: Pubkey,
}

#[derive(Clone)]
pub struct TransferAssetAddresses {
pub owner: Pubkey,
Expand Down Expand Up @@ -121,6 +126,13 @@ pub trait CollectionBackend {
&self,
revision: &update_revisions::Model,
) -> Result<TransactionResponse<UpdateCollectionMintAddresses>>;

fn switch(
&self,
mint: &collection_mints::Model,
collection: &collections::Model,
new_collection: &collections::Model,
) -> Result<TransactionResponse<SwitchCollectionAddresses>>;
}

pub trait MintBackend<T, R> {
Expand Down
52 changes: 51 additions & 1 deletion consumer/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ use holaplex_hub_nfts_solana_core::{
MintMetaplexMetadataTransaction, SolanaCompletedMintTransaction,
SolanaCompletedTransferTransaction, SolanaCompletedUpdateTransaction,
SolanaFailedTransaction, SolanaNftEventKey, SolanaNftEvents, SolanaPendingTransaction,
SolanaTransactionFailureReason, TransferMetaplexAssetTransaction, UpdateSolanaMintPayload,
SolanaTransactionFailureReason, SwitchCollectionPayload, TransferMetaplexAssetTransaction,
UpdateSolanaMintPayload,
},
sea_orm::{ActiveModelTrait, DatabaseConnection, DbErr, EntityTrait, Set},
Collection, CollectionMint, CompressionLeaf, Services,
Expand Down Expand Up @@ -115,6 +116,7 @@ pub enum EventKind {
RetryMintToCollection,
UpdateCollectionMint,
RetryUpdateCollectionMint,
SwitchMintCollection,
}

impl EventKind {
Expand All @@ -133,6 +135,7 @@ impl EventKind {
Self::RetryMintToCollection => "mint to collection retry",
Self::UpdateCollectionMint => "collection mint update",
Self::RetryUpdateCollectionMint => "collection mint update retry",
Self::SwitchMintCollection => "switch mint collection",
}
}

Expand All @@ -159,6 +162,9 @@ impl EventKind {
EventKind::RetryUpdateCollectionMint => {
SolanaNftEvent::RetryUpdateMintSigningRequested(tx)
},
EventKind::SwitchMintCollection => {
SolanaNftEvent::SwitchMintCollectionSigningRequested(tx)
},
}
}

Expand Down Expand Up @@ -310,6 +316,11 @@ impl EventKind {
signature,
})
},
Self::SwitchMintCollection => {
SolanaNftEvent::SwitchMintCollectionSubmitted(SolanaCompletedUpdateTransaction {
signature,
})
},
})
}

Expand All @@ -328,6 +339,7 @@ impl EventKind {
Self::RetryMintToCollection => SolanaNftEvent::RetryMintToCollectionFailed(tx),
Self::UpdateCollectionMint => SolanaNftEvent::UpdateCollectionMintFailed(tx),
Self::RetryUpdateCollectionMint => SolanaNftEvent::RetryUpdateMintFailed(tx),
Self::SwitchMintCollection => SolanaNftEvent::SwitchMintCollectionFailed(tx),
}
}
}
Expand Down Expand Up @@ -484,6 +496,15 @@ impl Processor {
)
.await
},
Some(NftEvent::SolanaSwitchMintCollectionRequested(payload)) => {
self.process_nft(
EventKind::SwitchMintCollection,
&key,
self.switch_mint_collection(&UncompressedRef(self.solana()), payload),
)
.await
},

_ => Ok(()),
}
},
Expand Down Expand Up @@ -540,6 +561,10 @@ impl Processor {
self.process_treasury(EventKind::RetryUpdateCollectionMint, key, res)
.await
},
Some(TreasuryEvent::SolanaSwitchMintCollectionSigned(res)) => {
self.process_treasury(EventKind::SwitchMintCollection, key, res)
.await
},
_ => Ok(()),
}
},
Expand Down Expand Up @@ -921,6 +946,31 @@ impl Processor {
Ok(tx.into())
}

async fn switch_mint_collection<B: CollectionBackend>(
&self,
backend: &B,
payload: SwitchCollectionPayload,
) -> ProcessResult<SolanaPendingTransaction> {
let conn = self.db.get();

let (mint, collection) =
CollectionMint::find_by_id_with_collection(conn, payload.mint_id.parse()?)
.await?
.ok_or(ProcessorErrorKind::RecordNotFound)?;

let collection = collection.ok_or(ProcessorErrorKind::RecordNotFound)?;

let new_collection = Collection::find_by_id(conn, payload.collection_id.parse()?)
.await?
.ok_or(ProcessorErrorKind::RecordNotFound)?;

let tx = backend
.switch(&mint, &collection, &new_collection)
.map_err(ProcessorErrorKind::Solana)?;

Ok(tx.into())
}

async fn retry_mint_drop<
B: MintBackend<MintMetaplexEditionTransaction, MintEditionAddresses>,
>(
Expand Down
21 changes: 9 additions & 12 deletions consumer/src/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,18 +218,15 @@ impl Processor {
let (metadata_pubkey, _) = find_metadata_account(&mint);

let (master_edition, _) = find_master_edition_account(&mint);
let collection_model = Collection::create(
conn,
collections::ActiveModel {
master_edition: Set(master_edition.to_string()),
update_authority: Set(update_authority.to_string()),
associated_token_account: Set(ata.to_string()),
owner: Set(owner.to_string()),
mint: Set(mint.to_string()),
metadata: Set(metadata_pubkey.to_string()),
..Default::default()
},
)
let collection_model = Collection::create(conn, collections::ActiveModel {
master_edition: Set(master_edition.to_string()),
update_authority: Set(update_authority.to_string()),
associated_token_account: Set(ata.to_string()),
owner: Set(owner.to_string()),
mint: Set(mint.to_string()),
metadata: Set(metadata_pubkey.to_string()),
..Default::default()
})
.await?;

producer
Expand Down
102 changes: 99 additions & 3 deletions consumer/src/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ use mpl_bubblegum::state::metaplex_adapter::{
Collection, Creator as BubblegumCreator, TokenProgramVersion,
};
use mpl_token_metadata::{
instruction::{mint_new_edition_from_master_edition_via_token, update_metadata_accounts_v2},
instruction::{
mint_new_edition_from_master_edition_via_token, set_and_verify_sized_collection_item,
unverify_sized_collection_item, update_metadata_accounts_v2,
},
state::{Creator, DataV2, EDITION, PREFIX},
};
use solana_client::rpc_client::RpcClient as SolanaRpcClient;
Expand Down Expand Up @@ -42,8 +45,9 @@ use crate::{
asset_api::RpcClient,
backend::{
CollectionBackend, MasterEditionAddresses, MintBackend, MintCompressedMintV1Addresses,
MintEditionAddresses, MintMetaplexAddresses, TransactionResponse, TransferAssetAddresses,
TransferBackend, TransferCompressedMintV1Addresses, UpdateCollectionMintAddresses,
MintEditionAddresses, MintMetaplexAddresses, SwitchCollectionAddresses,
TransactionResponse, TransferAssetAddresses, TransferBackend,
TransferCompressedMintV1Addresses, UpdateCollectionMintAddresses,
UpdateMasterEditionAddresses,
},
};
Expand Down Expand Up @@ -583,6 +587,98 @@ impl<'a> CollectionBackend for UncompressedRef<'a> {
},
})
}

fn switch(
&self,
mint: &collection_mints::Model,
collection: &collections::Model,
new_collection: &collections::Model,
) -> Result<TransactionResponse<SwitchCollectionAddresses>> {
let rpc = &self.0.rpc_client;
let payer = self.0.treasury_wallet_address;

let mint_pubkey = Pubkey::from_str(&mint.mint)?;
let program_pubkey = mpl_token_metadata::id();
let metadata_seeds = &[
PREFIX.as_bytes(),
program_pubkey.as_ref(),
mint_pubkey.as_ref(),
];
let (metadata, _) = Pubkey::find_program_address(metadata_seeds, &program_pubkey);

let collection_authority = collection.owner.parse()?;
let collection_mint = Pubkey::from_str(&collection.mint)?;
let collection_metadata_seeds = &[
PREFIX.as_bytes(),
program_pubkey.as_ref(),
collection_mint.as_ref(),
];
let (collection_metadata, _) =
Pubkey::find_program_address(collection_metadata_seeds, &program_pubkey);

let collection_master_edition = collection.master_edition.parse()?;

let unverify_ins = unverify_sized_collection_item(
program_pubkey,
metadata,
collection_authority,
payer,
collection_mint,
collection_metadata,
collection_master_edition,
None,
);

let new_collection_mint = Pubkey::from_str(&new_collection.mint)?;

let new_collection_metadata_seeds = &[
PREFIX.as_bytes(),
program_pubkey.as_ref(),
new_collection_mint.as_ref(),
];
let (new_collection_metadata, _) =
Pubkey::find_program_address(new_collection_metadata_seeds, &program_pubkey);
println!("new_collection_metadata: {:?}", new_collection_metadata);

let new_collection_authority = Pubkey::from_str(&new_collection.owner)?;
let new_collection_update_authority = Pubkey::from_str(&new_collection.update_authority)?;

let verify_ins = set_and_verify_sized_collection_item(
program_pubkey,
metadata,
new_collection_authority,
payer,
new_collection_update_authority,
new_collection.mint.parse()?,
new_collection_metadata,
new_collection.master_edition.parse()?,
None,
);

let instructions = vec![unverify_ins, verify_ins];

let blockhash = rpc.get_latest_blockhash()?;

let message = solana_program::message::Message::new_with_blockhash(
&instructions,
Some(&payer),
&blockhash,
);

let serialized_message = message.serialize();

Ok(TransactionResponse {
serialized_message,
signatures_or_signers_public_keys: vec![
payer.to_string(),
collection_authority.to_string(),
],
addresses: SwitchCollectionAddresses {
payer,
new_collection_authority,
},
})
}
}

impl<'a> MintBackend<MintMetaplexEditionTransaction, MintEditionAddresses> for EditionRef<'a> {
Expand Down
12 changes: 6 additions & 6 deletions core/proto.lock
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[[schemas]]
subject = "nfts"
version = 25
sha512 = "90dadff6bc75b59bb79d9ed2a65d582923f9ce66b5915c020306571bb446d68ff6648543386838510a60081d7cbb14f43fa2ae22c4c8ecd85874bee4323dd26a"
version = 27
sha512 = "ec52dffa482eb3beb72b1ae1745f3b8cb1b6d321d20fdc30078f4362c924b0f75ebe3dc53c54e5de92ea55163b64f09fe4e2b749c1bc5bc64d92f8356835eb90"

[[schemas]]
subject = "solana_nfts"
version = 9
sha512 = "312a84e8ae8b9222c7ec2b307d036dae0bd8dac4363e813c2fcffd5d7fba8741bd802953b1ec0a96baf57a7ce852debb724fcccf3b0bd8a27a9e4cc60344a56f"
version = 10
sha512 = "1bcb166ab5dfdf4841d60caa07a4098dcec03d7e3b0e63adb090ed2f5fe990c1e13826867e8df7521ec631027d5a931a08865fd2cf2daa905807c4b7dca40213"

[[schemas]]
subject = "treasury"
version = 21
sha512 = "734cff313b8b4854b9a4c03cfd6f95f07b1fd86f8678393ab466443d9da4d6e7c9fc400bdbcc718d83e6c7711857941d4b6dc0ea5d1d926f05a7859a65a15509"
version = 22
sha512 = "bde788b07f818aa52e684dcbd91e1f1e3db82f242f616ec2a42ab6d412df33a1461677c229f2f9bae345938c2f325e6332a95caef2c7e01a47531af53e39bf03"
6 changes: 3 additions & 3 deletions core/proto.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
endpoint = "https://schemas.holaplex.tools"

[schemas]
nfts = 25
treasury = 21
solana_nfts = 9
nfts = 27
treasury = 22
solana_nfts = 10
19 changes: 8 additions & 11 deletions indexer/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,14 @@ impl GeyserGrpcConnector {
slots.insert("client".to_owned(), SubscribeRequestFilterSlots {});

let mut transactions = HashMap::new();
transactions.insert(
"client".to_string(),
SubscribeRequestFilterTransactions {
vote: Some(false),
failed: Some(false),
signature: None,
account_include: vec![spl_token::ID.to_string(), mpl_bubblegum::ID.to_string()],
account_exclude: Vec::new(),
account_required: Vec::new(),
},
);
transactions.insert("client".to_string(), SubscribeRequestFilterTransactions {
vote: Some(false),
failed: Some(false),
signature: None,
account_include: vec![spl_token::ID.to_string(), mpl_bubblegum::ID.to_string()],
account_exclude: Vec::new(),
account_required: Vec::new(),
});

SubscribeRequest {
accounts: HashMap::new(),
Expand Down

0 comments on commit ffeeec9

Please sign in to comment.