use super::*;
use crate::collections::data::*;
use crate::derive::derive_metadata_pda;
use crate::spinner::create_alt_spinner;
use crate::theindexio::THE_INDEX_MAINNET;
use borsh::BorshDeserialize;
pub async fn get_collection_items(
collection_mint: String,
method: GetCollectionItemsMethods,
api_key: Option<String>,
) -> AnyResult<()> {
match method {
GetCollectionItemsMethods::TheIndexIO => {
if let Some(key) = api_key {
get_collection_items_by_the_index_io(collection_mint, key).await?
} else {
return Err(anyhow!(
"This method requires an index key for TheIndex.io."
));
}
}
}
Ok(())
}
pub async fn get_collection_items_by_the_index_io(
collection_mint: String,
api_key: String,
) -> AnyResult<()> {
let jrpc = JRPCRequest::new("getNFTsByCollection", vec![collection_mint.clone()]);
let url = format!("{THE_INDEX_MAINNET}/{api_key}");
let client = reqwest::Client::new();
let spinner = create_alt_spinner("Fetching data from TheIndex.io. . .");
let response = client.post(url).json(&jrpc).send().await?;
spinner.finish();
let res: RpcResponse = response.json().await?;
let mut mints: Vec<String> = res
.result
.iter()
.map(|nft| nft.metadata.mint.clone())
.collect();
let file_name = format!("{collection_mint}_collection_items.json");
let f = File::create(&file_name).unwrap();
mints.sort_unstable();
serde_json::to_writer_pretty(f, &mints).unwrap();
println!("Data written to {file_name}");
Ok(())
}
pub async fn check_collection_items(
async_client: AsyncRpcClient,
collection_mint: String,
mint_list_path: String,
debug: bool,
) -> AnyResult<()> {
let f = File::open(mint_list_path)?;
let mut mint_list: Vec<String> = serde_json::from_reader(f)?;
let mint_list_length = mint_list.len();
let mut collections: HashMap<String, Vec<String>> = HashMap::new();
let mut handles = Vec::new();
let mut errors = Vec::new();
let client = Arc::new(async_client);
let spinner = create_alt_spinner("Sending network requests and awaiting results...");
for mint in mint_list.drain(0..cmp::min(mint_list.len(), PARALLEL_LIMIT)) {
let client = client.clone();
handles.push(tokio::spawn(async move {
get_mint_collection(&client, mint.to_string()).await
}));
}
while !handles.is_empty() {
match select_all(handles).await {
(Ok(res), _index, remaining) => {
handles = remaining;
if let Ok((mint, collection_opt)) = res {
match collection_opt {
Some(collection) => {
collections
.entry(collection.key.to_string())
.or_default()
.push(mint.to_string());
}
None => {
collections
.entry("none".to_string())
.or_default()
.push(mint.to_string());
}
}
} else {
errors.push(res.err().unwrap());
}
}
(Err(err), _index, remaining) => {
errors.push(err.into());
handles = remaining;
}
}
if !mint_list.is_empty() {
if (PARALLEL_LIMIT - handles.len()) > (PARALLEL_LIMIT / 2) {
for mint in mint_list.drain(0..cmp::min(mint_list.len(), PARALLEL_LIMIT)) {
let client = client.clone();
handles.push(tokio::spawn(async move {
get_mint_collection(&client, mint.to_string()).await
}));
}
}
}
}
spinner.finish();
let mint_items = collections.get(&collection_mint).ok_or_else(|| {
anyhow!("No mints found for this parent. Run with --debug to see more details.")
})?;
let keys: Vec<&String> = collections.keys().collect();
if !keys.contains(&&collection_mint) || keys.len() > 1 {
if debug {
println!("Writing debug file...");
let out = File::create(format!("{collection_mint}-debug-collections.json"))?;
serde_json::to_writer_pretty(out, &collections)?;
}
return Err(anyhow!(
"Not all mints from the list belong to this parent.{}",
if debug {
""
} else {
" Run with --debug to see more details."
}
));
}
if mint_items.len() != mint_list_length || keys.is_empty() {
if debug {
let out = File::create(format!("{collection_mint}-debug-collections.json"))?;
serde_json::to_writer_pretty(out, &collections)?;
}
return Err(anyhow!(
"Missed some mints from the list.{}",
if debug {
""
} else {
" Run with --debug to see more details."
}
));
}
println!("All mints are in the collection!");
Ok(())
}
async fn get_mint_collection(
client: &AsyncRpcClient,
mint: String,
) -> AnyResult<(String, Option<MdCollection>)> {
let mint_pubkey = Pubkey::from_str(&mint)?;
let metadata_pubkey = derive_metadata_pda(&mint_pubkey);
let data = client.get_account_data(&metadata_pubkey).await?;
let md = <Metadata as BorshDeserialize>::deserialize(&mut data.as_slice())?;
Ok((mint, md.collection))
}