use std::{cell::RefCell, fmt::Display, rc::Rc, str::FromStr};
use inquire::{validator::Validation, Confirm, Select, Text};
use itertools::Itertools;
use shadow_drive_sdk::{Pubkey, Signer};
use shadow_nft_standard::accounts::CreateGroup as CreateGroupAccounts;
use shadow_nft_standard::common::get_creator_group_pda;
use shadow_nft_standard::instruction::CreateGroup as CreateGroupInstruction;
use shadow_nft_standard::instructions::create_group::CreateGroupArgs;
use shadowy_super_minter::state::file_type::{InstructionData, ToAccountMetas};
use solana_sdk::{instruction::Instruction, system_program, transaction::Transaction};
use strum::{EnumIter, EnumString, IntoEnumIterator, IntoStaticStr};
#[derive(PartialEq, Debug, Clone, Copy, EnumString, EnumIter, IntoStaticStr)]
pub enum MemberOptions {
SingleMember,
Multisig,
}
pub(crate) async fn process(
signer: &impl Signer,
rpc_url: &str,
) -> anyhow::Result<(Pubkey, Vec<Pubkey>)> {
let options: Vec<MemberOptions> = MemberOptions::iter().collect_vec();
let Ok(is_single_member) = Select::new(
"What kind of creator group would you like to create?",
options,
).prompt().map(|option| option == MemberOptions::SingleMember)
else {
panic!()
};
let name = Text::new("What would you like to name your group").prompt()?;
let other_members = Rc::new(RefCell::new(vec![]));
let other_members_scope = Rc::clone(&other_members);
let member_label = Rc::new(RefCell::new(1)); let member_label_loop = Rc::clone(&member_label);
let keep_going = Rc::new(RefCell::new(true));
let keep_going_loop = Rc::clone(&keep_going);
let signer_pubkey = signer.pubkey();
if !is_single_member {
let prompt_text = format!(
"Add Member {} Pubkey (enter if done):",
*member_label.borrow() + 1
);
let text_prompt = Text::new(&prompt_text).with_validator(move |input: &str| {
if input == "" {
*keep_going.borrow_mut() = false;
return Ok(Validation::Valid);
}
if let Ok(other_member) = Pubkey::from_str(&*input) {
let is_duplicate_member =
other_members.borrow().contains(&other_member) || other_member == signer_pubkey;
if is_duplicate_member {
Ok(Validation::Invalid(
inquire::error::InquireError::Custom(
"Pubkey already present in group".into(),
)
.into(),
))
} else {
*member_label.borrow_mut() += 1;
other_members.borrow_mut().push(other_member);
Ok(Validation::Valid)
}
} else {
Ok(Validation::Invalid("Invalid Pubkey".into()))
}
});
while *member_label_loop.borrow() <= 8 && *keep_going_loop.borrow() {
let mut text_prompt_updated = text_prompt.clone();
let text_prompt_updated_message = format!(
"Add Member {} Pubkey (enter if done):",
*member_label_loop.borrow() + 1
);
text_prompt_updated.message = &text_prompt_updated_message;
if let Err(e) = text_prompt_updated.prompt() {
return Err(anyhow::Error::msg(e));
};
}
drop(text_prompt.validators);
}
let (creator_group, all_creators_sorted): (Pubkey, Vec<Pubkey>) = {
let mut all_creators_sorted = other_members_scope.borrow().clone();
all_creators_sorted.push(signer.pubkey());
all_creators_sorted.sort();
(
get_creator_group_pda(&all_creators_sorted).expect("length is validated"),
all_creators_sorted,
)
};
match Confirm::new(&format!(
"Send and confirm transaction (signing with {})?",
signer.pubkey()
))
.prompt()
{
Ok(true) => {}
_ => return Err(anyhow::Error::msg("Discarded Request")),
}
let args = CreateGroupArgs { name };
let create_group_ix_data = CreateGroupInstruction { args };
let create_group_accounts = CreateGroupAccounts {
creator_group,
creator: signer.pubkey(),
system_program: system_program::ID,
}
.to_account_metas(None);
let create_group_ix = Instruction::new_with_bytes(
shadow_nft_standard::ID,
&create_group_ix_data.data(),
create_group_accounts,
);
let client = solana_client::nonblocking::rpc_client::RpcClient::new(rpc_url.to_string());
let create_group_tx = Transaction::new_signed_with_payer(
&[create_group_ix],
Some(&signer.pubkey()),
&[signer],
client.get_latest_blockhash().await?,
);
println!("Sending create group tx. May take a while to confirm.");
match client.send_and_confirm_transaction(&create_group_tx).await {
Ok(sig) => {
println!("Successful: https://explorer.solana.com/tx/{sig}")
}
Err(e) => return Err(anyhow::Error::msg(format!("{e:#?}"))),
};
println!("Initialized {creator_group}");
Ok((creator_group, all_creators_sorted))
}
impl Display for MemberOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s: &'static str = self.into();
write!(f, "{}", s)
}
}