Skip to content

Commit

Permalink
Merge pull request #133 from holaplex/abdul/batch-mint
Browse files Browse the repository at this point in the history
Sign batched queued mints transaction
  • Loading branch information
imabdulbasit authored Oct 18, 2023
2 parents e240946 + 8963aca commit 17f65e0
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 13 deletions.
8 changes: 4 additions & 4 deletions api/proto.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ sha512 = "d75800df0d4744c6b0f4d9a9952d3bfd0bb6b24a8babd19104cc11b54a525f85551b3c

[[schemas]]
subject = "nfts"
version = 29
sha512 = "b3b2136bd6c7a136d317da84395661de5fc056e8270510575a3281d78884d99a0d89f444754ed02cb18ad26dcc7cd65300c1df73b9d74d2edc6bcc8d552465d0"
version = 31
sha512 = "449574f8551ab8c17824af9e08b1658ad1b26ac80340230ddf02e7a1e0979d8a47025913a6598799cf83dd1a9cda87697ee87a13f404ebb52c95ea0084205767"

[[schemas]]
subject = "organization"
Expand All @@ -20,8 +20,8 @@ sha512 = "c5ddf43d2958ec690ee2261d0ff9808b67ce810d2fc4b6077f96f561929a920f03509f

[[schemas]]
subject = "solana_nfts"
version = 11
sha512 = "967fefde938a0f6ce05194e4fca15673e681caac54d8aeec114c5d38418632b9696dbaf5362345a15114e5abb49de55d0af8b9edcc0f2c91f9ef1ccc4ff55d68"
version = 12
sha512 = "4f85496c50a82cb40faa097cf6d0cb23275b3b90cb561d01388f3e5a71282a8b8e1eea617b7d712b0e415d65af483209fac2db1591456fa814a1f41a1c457433"

[[schemas]]
subject = "timestamp"
Expand Down
4 changes: 2 additions & 2 deletions api/proto.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ endpoint = "https://schemas.holaplex.tools"

[schemas]
organization = 5
nfts = 29
nfts = 31
customer = 2
treasury = 23
solana_nfts = 11
solana_nfts = 12
polygon_nfts = 6
timestamp = 1
5 changes: 5 additions & 0 deletions api/src/events/processor.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use fireblocks::Fireblocks;
use hub_core::{
bs58,
prelude::*,
producer::{Producer, SendError},
thiserror, uuid,
Expand Down Expand Up @@ -49,6 +50,8 @@ pub enum ProcessorError {
MissingSafeTransferFromTxn,
#[error("Signed message not found in transaction response")]
MissingSignedMessage,
#[error("Invalid number of signer pubkeys")]
InvalidNumberOfSigners,

#[error("Invalid ECDSA pubkey recovery scalar")]
#[permanent]
Expand All @@ -68,6 +71,8 @@ pub enum ProcessorError {
DbError(#[from] DbErr),
#[error("Error sending message")]
SendError(#[from] SendError),
#[error("Base58 decode error")]
Bs58DecodeError(#[from] bs58::decode::Error),
}

pub type Result<T> = std::result::Result<T, ProcessorError>;
Expand Down
2 changes: 1 addition & 1 deletion api/src/events/signer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub(crate) async fn sign_message<G: Sign>(
let transaction = fireblocks
.client()
.create()
.raw_transaction(asset_id, vault_id, message, note)
.raw_transaction(asset_id, vault_id, vec![message], note)
.await
.map_err(ProcessorError::Fireblocks)?;

Expand Down
111 changes: 109 additions & 2 deletions api/src/events/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ use hub_core::{

use super::{
signer::{find_vault_id_by_wallet_address, sign_message, Sign},
Processor, Result,
Processor, ProcessorError, Result,
};
use crate::proto::{
solana_nft_events::Event as SolanaNftEvent,
treasury_events::{Event, SolanaTransactionResult, TransactionStatus},
SolanaNftEventKey, SolanaNftEvents, SolanaPendingTransaction, TreasuryEventKey, TreasuryEvents,
SolanaMintPendingTransactions, SolanaNftEventKey, SolanaNftEvents, SolanaPendingTransaction,
TreasuryEventKey, TreasuryEvents,
};

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -154,11 +155,117 @@ impl<'a> Solana<'a> {
self.send_and_notify(EventKind::RetryMintOpenDrop, key, payload)
.await?;
},
Some(SolanaNftEvent::MintOpenDropBatchedSigningRequested(payload)) => {
self.sign_mint_batch(key, payload).await?;
},
_ => (),
}

Ok(())
}

pub async fn sign_mint_batch(
&self,
key: SolanaNftEventKey,
payload: SolanaMintPendingTransactions,
) -> Result<()> {
let conn = self.0.db.get();
let fireblocks = &self.0.fireblocks;
let pubkeys = payload.signers_pubkeys.clone();

if pubkeys.len() != 2 {
return Err(ProcessorError::InvalidNumberOfSigners);
}

let note = &format!(
"Mint batch signing for collection {:?} by {:?} for project {:?}",
key.id, key.user_id, key.project_id,
);

let messages = &payload
.mint_transactions
.clone()
.into_iter()
.map(|m| m.serialized_message)
.collect::<Vec<_>>();

let tx = |vault: String| async move {
let asset_id = fireblocks.assets().id("SOL");

let transaction = fireblocks
.client()
.create()
.raw_transaction(asset_id, vault, messages.clone(), note.to_string())
.await
.map_err(ProcessorError::Fireblocks)?;

let details = fireblocks
.client()
.wait_on_transaction_completion(transaction.id)
.await
.map_err(ProcessorError::Fireblocks)?;

Result::<_>::Ok(details)
};

let mut futures = Vec::new();

for req_sig in pubkeys.clone() {
let vault_id = find_vault_id_by_wallet_address(conn, req_sig).await?;
futures.push(tx(vault_id));
}

let futs_result = future::join_all(futures)
.await
.into_iter()
.map(|r| r.map(|d| d.signed_messages))
.collect::<Result<Vec<_>>>()?;

let signatures = futs_result[0]
.clone()
.into_iter()
.zip(futs_result[1].clone().into_iter())
.zip(
payload
.mint_transactions
.iter()
.map(|m| m.signer_signature.clone()),
)
.collect::<Vec<_>>();

for ((sig1, sig2), sig3) in signatures {
let key = key.clone();
let mut signatures = Vec::new();
let content = bs58::decode(sig1.content).into_vec()?;

let sig1_bytes = <[u8; 64]>::from_hex(sig1.signature.full_sig)?;
signatures.push(bs58::encode(sig1_bytes).into_string());

let sig2_bytes = <[u8; 64]>::from_hex(sig2.signature.full_sig)?;
signatures.push(bs58::encode(sig2_bytes).into_string());

// Uncompressed mint message needs to be signed by mint key pair
if let Some(sig3) = sig3 {
signatures.insert(1, sig3);
}

let txn = SolanaTransactionResult {
serialized_message: Some(content),
signed_message_signatures: signatures,
status: TransactionStatus::Completed.into(),
};

let evt = Event::SolanaMintOpenDropSigned(txn);
self.producer()
.send(
Some(&TreasuryEvents { event: Some(evt) }),
Some(&key.into()),
)
.await?;
}

Ok(())
}
}

#[async_trait]
Expand Down
11 changes: 7 additions & 4 deletions fireblocks/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ impl CreateRequestBuilder {
&self,
asset_id: String,
vault_id: String,
message: Vec<u8>,
messages: Vec<Vec<u8>>,
note: String,
) -> Result<CreateTransactionResponse> {
let tx = CreateTransaction {
Expand All @@ -488,9 +488,12 @@ impl CreateRequestBuilder {
amount: "0".to_string(),
feelevel: None,
extra_parameters: Some(ExtraParameters::RawMessageData(RawMessageData {
messages: vec![UnsignedMessage {
content: hex::encode(&message),
}],
messages: messages
.into_iter()
.map(|m| UnsignedMessage {
content: hex::encode(m),
})
.collect(),
})),
note: Some(note),
};
Expand Down

0 comments on commit 17f65e0

Please sign in to comment.