1use crate::assets::{Asset, Assets, string_to_u64};
2use crate::constants::{Config, MAXIMUM_TOKENS_PER_UTXO, MAXIMUM_WALLET_UTXOS, get_config};
3use crate::display::seedelf_label;
4use crate::koios::{
5 UtxoResponse, address_utxos, contains_policy_id, credential_utxos, extract_bytes_with_logging,
6};
7use crate::register::Register;
8use crate::transaction::wallet_minimum_lovelace_with_assets;
9use blstrs::Scalar;
10use colored::Colorize;
11
12pub async fn collect_all_wallet_utxos(
14 sk: Scalar,
15 network_flag: bool,
16 variant: u64,
17) -> Vec<UtxoResponse> {
18 let mut all_utxos: Vec<UtxoResponse> = Vec::new();
19
20 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
21 eprintln!("Error: Invalid Variant");
22 std::process::exit(1);
23 });
24
25 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
26 Ok(utxos) => {
27 for utxo in utxos {
28 if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
29 if inline_datum.is_owned(sk) {
31 if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
33 {
34 all_utxos.push(utxo.clone());
35 }
36 }
37 }
38 }
39 }
40 Err(err) => {
41 eprintln!(
42 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
43 err
44 );
45 }
46 }
47 all_utxos
48}
49
50pub async fn find_seedelf_and_wallet_utxos(
52 sk: Scalar,
53 seedelf: String,
54 network_flag: bool,
55 variant: u64,
56) -> (Option<Register>, Vec<UtxoResponse>) {
57 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
58 eprintln!("Error: Invalid Variant");
59 std::process::exit(1);
60 });
61
62 let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
63 let mut number_of_utxos: u64 = 0;
64
65 let mut seedelf_datum: Option<Register> = None;
66 let mut found_seedelf: bool = false;
67 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
68 Ok(utxos) => {
69 for utxo in utxos {
70 if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
72 if !found_seedelf
73 && contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
74 {
75 let asset_name = utxo
76 .asset_list
77 .as_ref()
78 .and_then(|vec| {
79 vec.iter()
80 .find(|asset| {
81 asset.policy_id == config.contract.seedelf_policy_id
82 })
83 .map(|asset| &asset.asset_name)
84 })
85 .unwrap();
86 if asset_name == &seedelf {
87 found_seedelf = true;
88 seedelf_datum = Some(inline_datum.clone());
89 }
90 }
91 if inline_datum.is_owned(sk) {
93 if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
95 {
96 if number_of_utxos >= MAXIMUM_WALLET_UTXOS {
97 println!("Maximum UTxOs");
99 break;
100 }
101 usuable_utxos.push(utxo);
102 number_of_utxos += 1;
103 }
104 }
105 }
106 }
107 }
108 Err(err) => {
109 eprintln!(
110 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
111 err
112 );
113 }
114 }
115 (seedelf_datum, usuable_utxos)
116}
117
118pub async fn find_seedelf_utxo(
120 seedelf: String,
121 network_flag: bool,
122 variant: u64,
123) -> Option<UtxoResponse> {
124 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
125 eprintln!("Error: Invalid Variant");
126 std::process::exit(1);
127 });
128 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
129 Ok(utxos) => {
130 for utxo in utxos {
131 if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
132 let asset_name = utxo
133 .asset_list
134 .as_ref()
135 .and_then(|vec| {
136 vec.iter()
137 .find(|asset| asset.policy_id == config.contract.seedelf_policy_id)
138 .map(|asset| &asset.asset_name)
139 })
140 .unwrap();
141 if asset_name == &seedelf {
142 return Some(utxo);
144 }
145 }
146 }
147 }
148 Err(err) => {
149 eprintln!(
150 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
151 err
152 );
153 }
154 }
155 None
156}
157
158pub async fn collect_wallet_utxos(
160 sk: Scalar,
161 network_flag: bool,
162 variant: u64,
163) -> Vec<UtxoResponse> {
164 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
165 eprintln!("Error: Invalid Variant");
166 std::process::exit(1);
167 });
168 let mut number_of_utxos: u64 = 0;
169
170 let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
171
172 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
173 Ok(utxos) => {
174 for utxo in utxos {
175 if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
177 if inline_datum.is_owned(sk) {
179 if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
181 {
182 if number_of_utxos >= MAXIMUM_WALLET_UTXOS {
183 println!("Maximum UTxOs");
185 break;
186 }
187 usuable_utxos.push(utxo);
188 number_of_utxos += 1;
189 }
190 }
191 }
192 }
193 }
194 Err(err) => {
195 eprintln!(
196 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
197 err
198 );
199 }
200 }
201 usuable_utxos
202}
203
204pub async fn collect_address_utxos(address: &str, network_flag: bool) -> Vec<UtxoResponse> {
206 let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
207 match address_utxos(address, network_flag).await {
209 Ok(utxos) => {
210 for utxo in utxos {
212 let lovelace: u64 = utxo.value.parse::<u64>().expect("Invalid Lovelace");
214 if let Some(assets) = &utxo.asset_list {
215 if assets.is_empty() && lovelace == 5_000_000 {
216 } else {
218 usuable_utxos.push(utxo);
220 }
221 }
222 }
223 }
224 Err(err) => {
225 eprintln!(
226 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
227 err
228 );
229 }
230 }
231 usuable_utxos
232}
233
234pub async fn collect_all_address_utxos(address: &str, network_flag: bool) -> Vec<UtxoResponse> {
236 let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
237 match address_utxos(address, network_flag).await {
239 Ok(utxos) => {
240 for utxo in utxos {
242 usuable_utxos.push(utxo);
243 }
244 }
245 Err(err) => {
246 eprintln!(
247 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
248 err
249 );
250 }
251 }
252 usuable_utxos
253}
254
255pub fn select(utxos: Vec<UtxoResponse>, lovelace: u64, tokens: Assets) -> Vec<UtxoResponse> {
258 do_select(utxos, lovelace, tokens, lovelace)
259}
260pub fn do_select(
261 mut utxos: Vec<UtxoResponse>,
262 lovelace: u64,
263 tokens: Assets,
264 lovelace_goal: u64,
265) -> Vec<UtxoResponse> {
266 let mut selected_utxos: Vec<UtxoResponse> = Vec::new();
267
268 let mut current_lovelace_sum: u64 = 0;
269 let mut found_enough: bool = false;
270
271 let mut found_assets: Assets = Assets::new();
273
274 utxos.sort_by(|a, b| {
277 let a_group_key = a.asset_list.as_ref().is_some_and(|list| list.is_empty());
278 let b_group_key = b.asset_list.as_ref().is_some_and(|list| list.is_empty());
279
280 b_group_key
281 .cmp(&a_group_key)
282 .then_with(|| string_to_u64(b.value.clone()).cmp(&string_to_u64(a.value.clone())))
283 });
284
285 for utxo in utxos.clone() {
286 let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
288
289 let mut utxo_assets: Assets = Assets::new();
290 let mut added: bool = false;
291
292 if let Some(assets) = utxo.clone().asset_list {
294 if !assets.is_empty() {
295 for token in assets.clone() {
296 utxo_assets = utxo_assets.add(Asset::new(
297 token.policy_id,
298 token.asset_name,
299 string_to_u64(token.quantity).unwrap(),
300 ));
301 }
302 if utxo_assets.any(tokens.clone()) && !found_assets.contains(tokens.clone()) {
304 selected_utxos.push(utxo.clone());
305 current_lovelace_sum += value;
306 found_assets = found_assets.merge(utxo_assets.clone());
307 added = true;
308 }
309 } else {
310 if current_lovelace_sum < lovelace {
312 selected_utxos.push(utxo.clone());
313 current_lovelace_sum += value;
314 added = true;
315 }
316 }
317 }
318
319 if !added && current_lovelace_sum < lovelace && found_assets.contains(tokens.clone()) {
321 selected_utxos.push(utxo.clone());
322 current_lovelace_sum += value;
323 found_assets = found_assets.merge(utxo_assets);
324 }
325
326 if current_lovelace_sum >= lovelace && found_assets.contains(tokens.clone()) {
328 let change_assets: Assets = found_assets.separate(tokens.clone());
330 let number_of_change_assets: u64 = change_assets.len();
331 let minimum: u64 = wallet_minimum_lovelace_with_assets(change_assets.clone());
332 let multiplier: u64 = if number_of_change_assets > MAXIMUM_TOKENS_PER_UTXO {
334 (number_of_change_assets / MAXIMUM_TOKENS_PER_UTXO) + 1
336 } else {
337 1
338 };
339 if current_lovelace_sum - multiplier * minimum >= lovelace_goal {
341 found_enough = true;
343 break;
344 } else {
345 return do_select(
347 utxos.clone(),
348 lovelace + multiplier * minimum,
349 tokens.clone(),
350 lovelace_goal,
351 );
352 }
353 }
354 }
355 if found_enough {
356 selected_utxos
358 } else {
359 Vec::new()
361 }
362}
363
364pub fn assets_of(utxos: Vec<UtxoResponse>) -> (u64, Assets) {
366 let mut found_assets: Assets = Assets::new();
367 let mut current_lovelace_sum: u64 = 0;
368
369 for utxo in utxos.clone() {
370 let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
371 current_lovelace_sum += value;
372
373 if let Some(assets) = utxo.clone().asset_list {
374 if !assets.is_empty() {
375 let mut utxo_assets: Assets = Assets::new();
376
377 for token in assets.clone() {
378 utxo_assets = utxo_assets.add(Asset::new(
379 token.policy_id,
380 token.asset_name,
381 string_to_u64(token.quantity).unwrap(),
382 ));
383 }
384
385 found_assets = found_assets.merge(utxo_assets.clone());
386 }
387 }
388 }
389 (current_lovelace_sum, found_assets)
390}
391
392pub async fn find_and_print_all_seedelfs(label: String, network_flag: bool, variant: u64) {
394 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
395 eprintln!("Error: Invalid Variant");
396 std::process::exit(1);
397 });
398 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
399 Ok(utxos) => {
400 for utxo in utxos {
401 if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
402 let asset_name = utxo
403 .asset_list
404 .as_ref()
405 .and_then(|vec| {
406 vec.iter()
407 .find(|asset| asset.policy_id == config.contract.seedelf_policy_id)
408 .map(|asset| &asset.asset_name)
409 })
410 .unwrap();
411 if asset_name.to_lowercase().contains(&label.to_lowercase()) {
412 println!(
414 "\n{}: {}",
415 "Found Match:".bright_cyan(),
416 asset_name.bright_white()
417 );
418 seedelf_label(asset_name.to_string());
419 }
420 }
421 }
422 }
423 Err(err) => {
424 eprintln!(
425 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
426 err
427 );
428 }
429 }
430}
431
432pub async fn count_lovelace_and_utxos(network_flag: bool, variant: u64) {
434 let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
435 eprintln!("Error: Invalid Variant");
436 std::process::exit(1);
437 });
438
439 match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
440 Ok(utxos) => {
441 let mut total_lovelace: u64 = 0;
442 let mut total_seedelfs: u64 = 0;
443 for utxo in utxos.clone() {
444 if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
446 total_seedelfs += 1;
447 }
448 let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
450 total_lovelace += value;
451 }
452 println!(
453 "\nBalance: {} ₳",
454 format!("{:.6}", total_lovelace as f64 / 1_000_000.0).bright_yellow()
455 );
456 println!(
457 "Contract Has {} UTxOs",
458 utxos.len().to_string().bright_yellow()
459 );
460 println!(
461 "Contract Has {} Seedelfs",
462 total_seedelfs.to_string().bright_yellow()
463 );
464 }
465 Err(err) => {
466 eprintln!(
467 "Failed to fetch UTxOs: {}\nWait a few moments and try again.",
468 err
469 );
470 }
471 }
472}