Skip to content

Commit

Permalink
Merge pull request #137 from holaplex/main
Browse files Browse the repository at this point in the history
Release 10/18 - Batched mints signing
  • Loading branch information
imabdulbasit authored Oct 18, 2023
2 parents 74d18bd + e6e5ce7 commit 0e1479d
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 15 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
2 changes: 2 additions & 0 deletions api/src/events/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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 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
152 changes: 149 additions & 3 deletions api/src/events/solana.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::time::Instant;
use std::{collections::HashMap, time::Instant};

use hex::FromHex;
use hub_core::{
Expand All @@ -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, SolanaMintTransaction, SolanaNftEventKey, SolanaNftEvents,
SolanaPendingTransaction, TreasuryEventKey, TreasuryEvents,
};

#[derive(Debug, Clone, Copy)]
Expand Down Expand Up @@ -154,11 +155,156 @@ 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<_>>>();

match futs_result {
Ok(results) => {
let mut hashmap = HashMap::new();
for messages in results {
for msg in messages {
let bytes = <[u8; 64]>::from_hex(msg.signature.full_sig)?;
let signature = bs58::encode(bytes).into_string();

hashmap
.entry(msg.content)
.or_insert(Vec::new())
.push(signature);
}
}

for tx in payload.mint_transactions {
let key = key.clone();

let SolanaMintTransaction {
mint_id,
signer_signature,
serialized_message,
} = tx;

let hex_message = hex::encode(serialized_message.clone());

let mut signatures = hashmap
.get(&hex_message)
.ok_or(ProcessorError::MissingSignedMessage)?
.clone();

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

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

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

Err(e) => {
error!("Error signing mint batch: {:?}", e);

let txn = SolanaTransactionResult {
serialized_message: None,
signed_message_signatures: vec![],
status: TransactionStatus::Failed.into(),
};

for tx in payload.mint_transactions {
let evt = Event::SolanaMintOpenDropSigned(txn.clone());
let key = key.clone();
self.producer()
.send(
Some(&TreasuryEvents { event: Some(evt) }),
Some(&TreasuryEventKey {
id: tx.mint_id,
user_id: key.user_id,
project_id: key.project_id,
}),
)
.await?;
}
},
}

Ok(())
}
}

#[async_trait]
Expand Down
13 changes: 8 additions & 5 deletions fireblocks/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl Client {
.retry(
&ExponentialBuilder::default()
.with_jitter()
.with_min_delay(Duration::from_millis(250))
.with_min_delay(Duration::from_millis(30))
.with_max_times(10),
)
.await?;
Expand Down 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 0e1479d

Please sign in to comment.