From 259c56f68609a42fced2a88657c66610f598ac61 Mon Sep 17 00:00:00 2001 From: imabdulbasit Date: Mon, 16 Oct 2023 15:58:51 +0500 Subject: [PATCH 1/3] mutation for batched random queued mints --- api/proto.lock | 16 +-- api/proto.toml | 14 +-- api/src/mutations/mint.rs | 201 +++++++++++++++++++++++++++++++++++++- 3 files changed, 215 insertions(+), 16 deletions(-) diff --git a/api/proto.lock b/api/proto.lock index 2fbdf20..4c4d14f 100644 --- a/api/proto.lock +++ b/api/proto.lock @@ -1,27 +1,27 @@ [[schemas]] subject = "customer" -version = 2 +version = 1 sha512 = "d75800df0d4744c6b0f4d9a9952d3bfd0bb6b24a8babd19104cc11b54a525f85551b3c7375d69aeabbcf629cd826aa0bc6b0c0467add20716c504f5e856ce1c5" [[schemas]] subject = "nfts" -version = 30 -sha512 = "bee70bd6f0f18a8049f93bceb9c4b500b49352f9d19d55d5a411da92cbd786c88bec47f73e1ef6946ceefc7de8e558f704bf8187be9d9f4e49bd102baec29327" +version = 1 +sha512 = "449574f8551ab8c17824af9e08b1658ad1b26ac80340230ddf02e7a1e0979d8a47025913a6598799cf83dd1a9cda87697ee87a13f404ebb52c95ea0084205767" [[schemas]] subject = "organization" -version = 5 +version = 1 sha512 = "9fb28ac73d9712292297394a5fa53a7dae9deba6847353582987ba749859301c23c05fd49d2ce84a1640f8864c5c04d59fa38907700b280000e5c4afc96654bf" [[schemas]] subject = "polygon_nfts" -version = 6 +version = 1 sha512 = "c5ddf43d2958ec690ee2261d0ff9808b67ce810d2fc4b6077f96f561929a920f03509fc8bd7adbda219250eb019f5f7be8a3f51c554f665ea1881f7a973ef2a6" [[schemas]] subject = "solana_nfts" -version = 11 -sha512 = "967fefde938a0f6ce05194e4fca15673e681caac54d8aeec114c5d38418632b9696dbaf5362345a15114e5abb49de55d0af8b9edcc0f2c91f9ef1ccc4ff55d68" +version = 1 +sha512 = "c1b1f3af0097d52622e7b8628fd6085fa4cced9e91883fff309de370f4ff55f8a58c388e5eee69d1755b113e39c772f113ff6220d8a7dc435b79bc51e121473e" [[schemas]] subject = "timestamp" @@ -30,5 +30,5 @@ sha512 = "d167e0a143c813073eef8597f0b237e5a8eaf32abbf709724e8071b2dd73ce0438b82f [[schemas]] subject = "treasury" -version = 23 +version = 1 sha512 = "0e4d77999767d5971122e720c1cee7a57c3e47ce69f58a582f1762d8e65e031ea3bd9024cfc21bd7da5db6e38a71657151c58cdfa21d9ff643fb2fc657105cf5" diff --git a/api/proto.toml b/api/proto.toml index 44a45d7..c05a453 100644 --- a/api/proto.toml +++ b/api/proto.toml @@ -1,11 +1,11 @@ [registry] -endpoint = "https://schemas.holaplex.tools" +endpoint = "http://localhost:8081" [schemas] -organization = 5 -nfts = 30 -customer = 2 -treasury = 23 -solana_nfts = 11 -polygon_nfts = 6 +organization = 1 +nfts = 1 +customer = 1 +treasury = 1 +solana_nfts = 1 +polygon_nfts = 1 timestamp = 1 \ No newline at end of file diff --git a/api/src/mutations/mint.rs b/api/src/mutations/mint.rs index 7af2012..579deee 100644 --- a/api/src/mutations/mint.rs +++ b/api/src/mutations/mint.rs @@ -39,7 +39,8 @@ use crate::{ objects::{CollectionMint, Creator, MetadataJsonInput}, proto::{ self, nft_events::Event as NftEvent, CreationStatus as NftCreationStatus, MetaplexMetadata, - MintCollectionCreation, MintCreation, NftEventKey, NftEvents, RetryUpdateSolanaMintPayload, + MintCollectionCreation, MintCreation, MintOpenDropTransaction, NftEventKey, NftEvents, + RetryUpdateSolanaMintPayload, SolanaMintOpenDropBatchedPayload, }, Actions, AppContext, OrganizationId, UserID, }; @@ -1291,6 +1292,190 @@ impl Mutation { collection_mint: mint.into(), }) } + + async fn mint_random_queued_to_drop_batched( + &self, + ctx: &Context<'_>, + input: MintRandomQueuedBatchedInput, + ) -> Result { + let AppContext { + db, + user_id, + organization_id, + balance, + .. + } = ctx.data::()?; + let credits = ctx.data::>()?; + let conn = db.get(); + let nfts_producer = ctx.data::>()?; + + let UserID(id) = user_id; + let OrganizationId(org) = organization_id; + + let user_id = id.ok_or(Error::new("X-USER-ID header not found"))?; + let org_id = org.ok_or(Error::new("X-ORGANIZATION-ID header not found"))?; + let balance = balance + .0 + .ok_or(Error::new("X-CREDIT-BALANCE header not found"))?; + + let batch_size = input.recipients.len(); + + if batch_size == 0 { + return Err(Error::new("No recipients provided")); + } + + if batch_size > 250 { + return Err(Error::new("Batch size cannot be greater than 250")); + } + + let drop = drops::Entity::find_by_id(input.drop) + .one(conn) + .await? + .ok_or(Error::new("drop not found"))?; + + let mints = CollectionMints::find() + .filter(collection_mints::Column::CollectionId.eq(drop.collection_id)) + .filter(collection_mints::Column::CreationStatus.eq(CreationStatus::Queued)) + .order_by(SimpleExpr::FunctionCall(Func::random()), Order::Asc) + .limit(Some(batch_size.try_into()?)) + .all(conn) + .await?; + + if mints.len() != batch_size { + return Err(Error::new("Not enough mints found for the drop")); + } + + let collection = collections::Entity::find_by_id(drop.collection_id) + .one(conn) + .await? + .ok_or(Error::new("collection not found"))?; + + let project_id = collection.project_id; + let blockchain = collection.blockchain; + + if blockchain != BlockchainEnum::Solana { + return Err(Error::new("Only Solana is supported at this time")); + } + + let owner_address = fetch_owner(conn, project_id, blockchain).await?; + + let action = if input.compressed { + Actions::MintCompressed + } else { + Actions::Mint + }; + + let event_key = NftEventKey { + id: collection.id.to_string(), + user_id: user_id.to_string(), + project_id: project_id.to_string(), + }; + + let mut transactions = Vec::new(); + + for (mint, recipient) in mints.clone().into_iter().zip(input.recipients.into_iter()) { + let metadata_json = metadata_jsons::Entity::find_by_id(mint.id) + .one(conn) + .await? + .ok_or(Error::new("metadata json not found"))?; + + let metadata_uri = metadata_json + .uri + .ok_or(Error::new("No metadata json uri found"))?; + + let creators = mint_creators::Entity::find_by_collection_mint_id(mint.id) + .all(conn) + .await?; + + let TransactionId(deduction_id) = credits + .submit_pending_deduction( + org_id, + user_id, + action, + collection.blockchain.into(), + balance, + ) + .await?; + + let tx = conn.begin().await?; + + let mut collection_am = collections::ActiveModel::from(collection.clone()); + collection_am.total_mints = Set(collection.total_mints.add(1)); + collection_am.update(&tx).await?; + + let mut mint_am: collection_mints::ActiveModel = mint.into(); + + mint_am.creation_status = Set(CreationStatus::Pending); + mint_am.credits_deduction_id = Set(Some(deduction_id)); + mint_am.compressed = Set(Some(input.compressed)); + mint_am.owner = Set(Some(recipient.clone())); + mint_am.seller_fee_basis_points = Set(collection.seller_fee_basis_points); + + let mint = mint_am.update(&tx).await?; + + let mint_history_am = mint_histories::ActiveModel { + mint_id: Set(mint.id), + wallet: Set(recipient.clone()), + collection_id: Set(collection.id), + tx_signature: Set(None), + status: Set(CreationStatus::Pending), + created_at: Set(Utc::now().into()), + ..Default::default() + }; + + mint_history_am.insert(&tx).await?; + + tx.commit().await?; + + nfts_producer + .send( + Some(&NftEvents { + event: Some(NftEvent::DropMinted(MintCreation { + drop_id: drop.id.to_string(), + status: NftCreationStatus::InProgress as i32, + })), + }), + Some(&NftEventKey { + id: mint.id.to_string(), + project_id: drop.project_id.to_string(), + user_id: user_id.to_string(), + }), + ) + .await?; + + transactions.push(MintOpenDropTransaction { + recipient_address: recipient, + metadata: Some(MetaplexMetadata { + owner_address: owner_address.clone(), + name: metadata_json.name, + symbol: metadata_json.symbol, + metadata_uri, + seller_fee_basis_points: mint.seller_fee_basis_points.into(), + creators: creators.into_iter().map(Into::into).collect(), + }), + mint_id: mint.id.to_string(), + }); + } + + nfts_producer + .send( + Some(&NftEvents { + event: Some(NftEvent::SolanaMintOpenDropBatched( + SolanaMintOpenDropBatchedPayload { + collection_id: collection.id.to_string(), + compressed: input.compressed, + mint_open_drop_transactions: transactions, + }, + )), + }), + Some(&event_key), + ) + .await?; + + Ok(MintRandomQueuedBatchedPayload { + collection_mints: mints.into_iter().map(Into::into).collect(), + }) + } } fn validate_compress(blockchain: BlockchainEnum, compressed: bool) -> Result<(), Error> { @@ -1475,3 +1660,17 @@ pub struct MintRandomQueuedInput { recipient: String, compressed: bool, } + +/// Represents input data for `mint_random_queued_batched` mutation +#[derive(Debug, Clone, InputObject)] +pub struct MintRandomQueuedBatchedInput { + drop: Uuid, + recipients: Vec, + compressed: bool, +} + +/// Represents payload data for `mint_random_queued_batched` mutation +#[derive(Debug, Clone, SimpleObject)] +pub struct MintRandomQueuedBatchedPayload { + collection_mints: Vec, +} From a94699266265d2bd7f6ccfbb5f331e7dade3c23d Mon Sep 17 00:00:00 2001 From: imabdulbasit Date: Tue, 17 Oct 2023 09:52:48 +0500 Subject: [PATCH 2/3] update schemas --- api/proto.lock | 14 +++++++------- api/proto.toml | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/proto.lock b/api/proto.lock index 4c4d14f..22051c0 100644 --- a/api/proto.lock +++ b/api/proto.lock @@ -1,27 +1,27 @@ [[schemas]] subject = "customer" -version = 1 +version = 2 sha512 = "d75800df0d4744c6b0f4d9a9952d3bfd0bb6b24a8babd19104cc11b54a525f85551b3c7375d69aeabbcf629cd826aa0bc6b0c0467add20716c504f5e856ce1c5" [[schemas]] subject = "nfts" -version = 1 +version = 31 sha512 = "449574f8551ab8c17824af9e08b1658ad1b26ac80340230ddf02e7a1e0979d8a47025913a6598799cf83dd1a9cda87697ee87a13f404ebb52c95ea0084205767" [[schemas]] subject = "organization" -version = 1 +version = 5 sha512 = "9fb28ac73d9712292297394a5fa53a7dae9deba6847353582987ba749859301c23c05fd49d2ce84a1640f8864c5c04d59fa38907700b280000e5c4afc96654bf" [[schemas]] subject = "polygon_nfts" -version = 1 +version = 6 sha512 = "c5ddf43d2958ec690ee2261d0ff9808b67ce810d2fc4b6077f96f561929a920f03509fc8bd7adbda219250eb019f5f7be8a3f51c554f665ea1881f7a973ef2a6" [[schemas]] subject = "solana_nfts" -version = 1 -sha512 = "c1b1f3af0097d52622e7b8628fd6085fa4cced9e91883fff309de370f4ff55f8a58c388e5eee69d1755b113e39c772f113ff6220d8a7dc435b79bc51e121473e" +version = 12 +sha512 = "4f85496c50a82cb40faa097cf6d0cb23275b3b90cb561d01388f3e5a71282a8b8e1eea617b7d712b0e415d65af483209fac2db1591456fa814a1f41a1c457433" [[schemas]] subject = "timestamp" @@ -30,5 +30,5 @@ sha512 = "d167e0a143c813073eef8597f0b237e5a8eaf32abbf709724e8071b2dd73ce0438b82f [[schemas]] subject = "treasury" -version = 1 +version = 23 sha512 = "0e4d77999767d5971122e720c1cee7a57c3e47ce69f58a582f1762d8e65e031ea3bd9024cfc21bd7da5db6e38a71657151c58cdfa21d9ff643fb2fc657105cf5" diff --git a/api/proto.toml b/api/proto.toml index c05a453..5bb1a54 100644 --- a/api/proto.toml +++ b/api/proto.toml @@ -1,11 +1,11 @@ [registry] -endpoint = "http://localhost:8081" +endpoint = "https://schemas.holaplex.tools" [schemas] -organization = 1 -nfts = 1 -customer = 1 -treasury = 1 -solana_nfts = 1 -polygon_nfts = 1 +organization = 5 +nfts = 31 +customer = 2 +treasury = 23 +solana_nfts = 12 +polygon_nfts = 6 timestamp = 1 \ No newline at end of file From 6c05730c28b35da5e2f10c9e6a30cd29ec6ee569 Mon Sep 17 00:00:00 2001 From: imabdulbasit Date: Tue, 17 Oct 2023 17:03:40 +0500 Subject: [PATCH 3/3] load creators and metadata json in batch --- api/src/mutations/mint.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/api/src/mutations/mint.rs b/api/src/mutations/mint.rs index 579deee..da3de74 100644 --- a/api/src/mutations/mint.rs +++ b/api/src/mutations/mint.rs @@ -1333,7 +1333,15 @@ impl Mutation { .await? .ok_or(Error::new("drop not found"))?; - let mints = CollectionMints::find() + let result = CollectionMints::find() + .select_also(metadata_jsons::Entity) + .join( + JoinType::InnerJoin, + metadata_jsons::Entity::belongs_to(CollectionMints) + .from(metadata_jsons::Column::Id) + .to(collection_mints::Column::Id) + .into(), + ) .filter(collection_mints::Column::CollectionId.eq(drop.collection_id)) .filter(collection_mints::Column::CreationStatus.eq(CreationStatus::Queued)) .order_by(SimpleExpr::FunctionCall(Func::random()), Order::Asc) @@ -1341,6 +1349,10 @@ impl Mutation { .all(conn) .await?; + let (mints, _): (Vec<_>, Vec<_>) = result.iter().cloned().unzip(); + + let creators = mints.load_many(mint_creators::Entity, conn).await?; + if mints.len() != batch_size { return Err(Error::new("Not enough mints found for the drop")); } @@ -1373,20 +1385,16 @@ impl Mutation { let mut transactions = Vec::new(); - for (mint, recipient) in mints.clone().into_iter().zip(input.recipients.into_iter()) { - let metadata_json = metadata_jsons::Entity::find_by_id(mint.id) - .one(conn) - .await? - .ok_or(Error::new("metadata json not found"))?; - + for (((mint, metadata_json), creators), recipient) in result + .into_iter() + .zip(creators.into_iter()) + .zip(input.recipients.into_iter()) + { + let metadata_json = metadata_json.ok_or(Error::new("No metadata json found"))?; let metadata_uri = metadata_json .uri .ok_or(Error::new("No metadata json uri found"))?; - let creators = mint_creators::Entity::find_by_collection_mint_id(mint.id) - .all(conn) - .await?; - let TransactionId(deduction_id) = credits .submit_pending_deduction( org_id, @@ -1399,10 +1407,6 @@ impl Mutation { let tx = conn.begin().await?; - let mut collection_am = collections::ActiveModel::from(collection.clone()); - collection_am.total_mints = Set(collection.total_mints.add(1)); - collection_am.update(&tx).await?; - let mut mint_am: collection_mints::ActiveModel = mint.into(); mint_am.creation_status = Set(CreationStatus::Pending);