shadow_drive_cli/command/nft/
utils.rs1use inquire::validator::Validation;
2use serde_json::json;
3use serde_json::Value;
4use solana_sdk::{pubkey::Pubkey, transaction::VersionedTransaction};
5use std::str::FromStr;
6
7pub fn validate_json_compliance(json: &Value) -> bool {
24 let has_name = json.get("name").is_some();
25 let has_symbol = json.get("symbol").is_some();
26 let has_description = json.get("description").is_some();
27 let has_image = json.get("image").is_some();
28 let has_external_url = json.get("external_url").is_some();
29
30 has_name & has_symbol & has_description & has_image & has_external_url
31}
32
33pub(crate) async fn swap_sol_for_shdw_tx(
34 shades: u64,
35 user: Pubkey,
36) -> anyhow::Result<VersionedTransaction> {
37 let Ok(quote) = Value::from_str("e_sol_to_shdw(shades).await?) else {
39 return Err(anyhow::Error::msg("Failed to parse jup.ag quote response as json"))
40 };
41
42 let request_body = json!({
44 "route": dbg!("e[0]),
45 "userPublicKey": user.to_string(),
46 "wrapUnwrapSOL": true,
47 });
49 let client = reqwest::Client::new();
50 let response = client
51 .post("https://quote-api.jup.ag/v4/swap")
52 .header("Content-Type", "application/json")
53 .json(&request_body)
54 .send()
55 .await?;
56
57 let Ok(body) = serde_json::Value::from_str(&response.text().await?) else {
59 return Err(anyhow::Error::msg("Failed to parse jup.ag swap_tx response as json"))
60 };
61
62 let Some(Some(tx_body))= body.get("swapTransaction").map(|b| b.as_str()) else {
64 return Err(anyhow::Error::msg("Unexpected response from jup.ag swap_tx endpoint"))
65 };
66 #[allow(deprecated)]
67 let Ok(Ok(transaction)) = base64::decode(tx_body).map(|bytes| bincode::deserialize(&bytes)) else {
68 return Err(anyhow::Error::msg("Invalid base64 encoding from jup.ag swap_tx endpoint"))
69 };
70
71 Ok(transaction)
72}
73
74pub(crate) const SHDW_MINT: &'static str = "SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y";
75pub(crate) const SOL_MINT: &'static str = "So11111111111111111111111111111111111111112";
76pub(crate) const SHDW_MINT_PUBKEY: Pubkey = Pubkey::new_from_array([
77 6, 121, 219, 1, 206, 42, 132, 247, 28, 19, 158, 124, 153, 66, 246, 218, 59, 51, 31, 222, 195,
78 49, 157, 2, 248, 153, 235, 167, 1, 52, 115, 126,
79]);
80
81async fn quote_sol_to_shdw(shades: u64) -> anyhow::Result<String> {
82 const SLIPPAGE_BPS: u16 = 5;
83
84 let url = format!(
85 "https://quote-api.jup.ag/v4/quote?inputMint={}&outputMint={}&amount={}&slippageBps={SLIPPAGE_BPS}&swapMode=ExactOut",
86 SOL_MINT, SHDW_MINT, shades
87 );
88
89 let response = reqwest::Client::new()
90 .get(&url)
91 .header("accept", "application/json")
92 .send()
93 .await?;
94
95 let body = response.text().await?;
96
97 Ok(body)
98}
99
100pub(crate) fn pubkey_validator(
101 input: &str,
102) -> Result<Validation, Box<dyn std::error::Error + Send + Sync>> {
103 if Pubkey::from_str(input).is_ok() {
105 Ok(Validation::Valid)
106 } else {
107 Ok(Validation::Invalid("Invalid Pubkey".into()))
108 }
109}
110
111pub(crate) fn validate_and_convert_to_half_percent(input: &str) -> Result<u8, &'static str> {
112 let input = input.trim().trim_end_matches('%');
114
115 let value = input.parse::<f64>();
117
118 match value {
119 Ok(v) => {
120 if v < 0.0 {
122 Err("Value must be positive.")
123 } else if (2.0 * v).fract() != 0.0 {
124 Err("Value must be a whole or half number.")
125 } else {
126 Ok((2.0 * v).round() as u8)
128 }
129 }
130 Err(_) => Err("Invalid input, not a number."),
131 }
132}
133
134#[test]
135fn test_validate_and_convert_to_half_percent() {
136 assert_eq!(validate_and_convert_to_half_percent("1"), Ok(2));
137 assert_eq!(validate_and_convert_to_half_percent("1%"), Ok(2));
138 assert_eq!(validate_and_convert_to_half_percent("1.5"), Ok(3));
139 assert_eq!(validate_and_convert_to_half_percent("1.5%"), Ok(3));
140 assert_eq!(validate_and_convert_to_half_percent("2"), Ok(4));
141 assert_eq!(validate_and_convert_to_half_percent("2%"), Ok(4));
142 assert_eq!(validate_and_convert_to_half_percent("2.0"), Ok(4));
143 assert_eq!(validate_and_convert_to_half_percent("2.0%"), Ok(4));
144 assert_eq!(validate_and_convert_to_half_percent("2.5"), Ok(5));
145 assert_eq!(validate_and_convert_to_half_percent("2.5%"), Ok(5));
146
147 assert_eq!(
148 validate_and_convert_to_half_percent("2.4"),
149 Err("Value must be a whole or half number.")
150 );
151 assert_eq!(
152 validate_and_convert_to_half_percent("-1"),
153 Err("Value must be positive.")
154 );
155 assert_eq!(
156 validate_and_convert_to_half_percent("-1.5"),
157 Err("Value must be positive.")
158 );
159 assert_eq!(
160 validate_and_convert_to_half_percent("not a number"),
161 Err("Invalid input, not a number.")
162 );
163 assert_eq!(
164 validate_and_convert_to_half_percent(""),
165 Err("Invalid input, not a number.")
166 );
167}