use crate::assets::{Asset, Assets, string_to_u64};
use crate::constants::{Config, MAXIMUM_TOKENS_PER_UTXO, MAXIMUM_WALLET_UTXOS, get_config};
use crate::display::seedelf_label;
use crate::koios::{
UtxoResponse, address_utxos, contains_policy_id, credential_utxos, extract_bytes_with_logging,
};
use crate::register::Register;
use crate::transaction::wallet_minimum_lovelace_with_assets;
use blstrs::Scalar;
use colored::Colorize;
pub async fn collect_all_wallet_utxos(
sk: Scalar,
network_flag: bool,
variant: u64,
) -> Vec<UtxoResponse> {
let mut all_utxos: Vec<UtxoResponse> = Vec::new();
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
if inline_datum.is_owned(sk) {
if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
{
all_utxos.push(utxo.clone());
}
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
all_utxos
}
pub async fn find_seedelf_and_wallet_utxos(
sk: Scalar,
seedelf: String,
network_flag: bool,
variant: u64,
) -> (Option<Register>, Vec<UtxoResponse>) {
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
let mut number_of_utxos: u64 = 0;
let mut seedelf_datum: Option<Register> = None;
let mut found_seedelf: bool = false;
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
if !found_seedelf
&& contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
{
let asset_name = utxo
.asset_list
.as_ref()
.and_then(|vec| {
vec.iter()
.find(|asset| {
asset.policy_id == config.contract.seedelf_policy_id
})
.map(|asset| &asset.asset_name)
})
.unwrap();
if asset_name == &seedelf {
found_seedelf = true;
seedelf_datum = Some(inline_datum.clone());
}
}
if inline_datum.is_owned(sk) {
if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
{
if number_of_utxos >= MAXIMUM_WALLET_UTXOS {
println!("Maximum UTxOs");
break;
}
usuable_utxos.push(utxo);
number_of_utxos += 1;
}
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
(seedelf_datum, usuable_utxos)
}
pub async fn find_seedelf_utxo(
seedelf: String,
network_flag: bool,
variant: u64,
) -> Option<UtxoResponse> {
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
let asset_name = utxo
.asset_list
.as_ref()
.and_then(|vec| {
vec.iter()
.find(|asset| asset.policy_id == config.contract.seedelf_policy_id)
.map(|asset| &asset.asset_name)
})
.unwrap();
if asset_name == &seedelf {
return Some(utxo);
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
None
}
pub async fn collect_wallet_utxos(
sk: Scalar,
network_flag: bool,
variant: u64,
) -> Vec<UtxoResponse> {
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
let mut number_of_utxos: u64 = 0;
let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
if let Some(inline_datum) = extract_bytes_with_logging(&utxo.inline_datum) {
if inline_datum.is_owned(sk) {
if !contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id)
{
if number_of_utxos >= MAXIMUM_WALLET_UTXOS {
println!("Maximum UTxOs");
break;
}
usuable_utxos.push(utxo);
number_of_utxos += 1;
}
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
usuable_utxos
}
pub async fn collect_address_utxos(address: &str, network_flag: bool) -> Vec<UtxoResponse> {
let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
match address_utxos(address, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
let lovelace: u64 = utxo.value.parse::<u64>().expect("Invalid Lovelace");
if let Some(assets) = &utxo.asset_list {
if assets.is_empty() && lovelace == 5_000_000 {
} else {
usuable_utxos.push(utxo);
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
usuable_utxos
}
pub async fn collect_all_address_utxos(address: &str, network_flag: bool) -> Vec<UtxoResponse> {
let mut usuable_utxos: Vec<UtxoResponse> = Vec::new();
match address_utxos(address, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
usuable_utxos.push(utxo);
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
usuable_utxos
}
pub fn select(utxos: Vec<UtxoResponse>, lovelace: u64, tokens: Assets) -> Vec<UtxoResponse> {
do_select(utxos, lovelace, tokens, lovelace)
}
pub fn do_select(
mut utxos: Vec<UtxoResponse>,
lovelace: u64,
tokens: Assets,
lovelace_goal: u64,
) -> Vec<UtxoResponse> {
let mut selected_utxos: Vec<UtxoResponse> = Vec::new();
let mut current_lovelace_sum: u64 = 0;
let mut found_enough: bool = false;
let mut found_assets: Assets = Assets::new();
utxos.sort_by(|a, b| {
let a_group_key = a.asset_list.as_ref().is_some_and(|list| list.is_empty());
let b_group_key = b.asset_list.as_ref().is_some_and(|list| list.is_empty());
b_group_key
.cmp(&a_group_key)
.then_with(|| string_to_u64(b.value.clone()).cmp(&string_to_u64(a.value.clone())))
});
for utxo in utxos.clone() {
let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
let mut utxo_assets: Assets = Assets::new();
let mut added: bool = false;
if let Some(assets) = utxo.clone().asset_list {
if !assets.is_empty() {
for token in assets.clone() {
utxo_assets = utxo_assets.add(Asset::new(
token.policy_id,
token.asset_name,
string_to_u64(token.quantity).unwrap(),
));
}
if utxo_assets.any(tokens.clone()) && !found_assets.contains(tokens.clone()) {
selected_utxos.push(utxo.clone());
current_lovelace_sum += value;
found_assets = found_assets.merge(utxo_assets.clone());
added = true;
}
} else {
if current_lovelace_sum < lovelace {
selected_utxos.push(utxo.clone());
current_lovelace_sum += value;
added = true;
}
}
}
if !added && current_lovelace_sum < lovelace && found_assets.contains(tokens.clone()) {
selected_utxos.push(utxo.clone());
current_lovelace_sum += value;
found_assets = found_assets.merge(utxo_assets);
}
if current_lovelace_sum >= lovelace && found_assets.contains(tokens.clone()) {
let change_assets: Assets = found_assets.separate(tokens.clone());
let number_of_change_assets: u64 = change_assets.len();
let minimum: u64 = wallet_minimum_lovelace_with_assets(change_assets.clone());
let multiplier: u64 = if number_of_change_assets > MAXIMUM_TOKENS_PER_UTXO {
(number_of_change_assets / MAXIMUM_TOKENS_PER_UTXO) + 1
} else {
1
};
if current_lovelace_sum - multiplier * minimum >= lovelace_goal {
found_enough = true;
break;
} else {
return do_select(
utxos.clone(),
lovelace + multiplier * minimum,
tokens.clone(),
lovelace_goal,
);
}
}
}
if found_enough {
selected_utxos
} else {
Vec::new()
}
}
pub fn assets_of(utxos: Vec<UtxoResponse>) -> (u64, Assets) {
let mut found_assets: Assets = Assets::new();
let mut current_lovelace_sum: u64 = 0;
for utxo in utxos.clone() {
let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
current_lovelace_sum += value;
if let Some(assets) = utxo.clone().asset_list {
if !assets.is_empty() {
let mut utxo_assets: Assets = Assets::new();
for token in assets.clone() {
utxo_assets = utxo_assets.add(Asset::new(
token.policy_id,
token.asset_name,
string_to_u64(token.quantity).unwrap(),
));
}
found_assets = found_assets.merge(utxo_assets.clone());
}
}
}
(current_lovelace_sum, found_assets)
}
pub async fn find_and_print_all_seedelfs(label: String, network_flag: bool, variant: u64) {
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
for utxo in utxos {
if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
let asset_name = utxo
.asset_list
.as_ref()
.and_then(|vec| {
vec.iter()
.find(|asset| asset.policy_id == config.contract.seedelf_policy_id)
.map(|asset| &asset.asset_name)
})
.unwrap();
if asset_name.to_lowercase().contains(&label.to_lowercase()) {
println!(
"\n{}: {}",
"Found Match:".bright_cyan(),
asset_name.bright_white()
);
seedelf_label(asset_name.to_string());
}
}
}
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
}
pub async fn count_lovelace_and_utxos(network_flag: bool, variant: u64) {
let config: Config = get_config(variant, network_flag).unwrap_or_else(|| {
eprintln!("Error: Invalid Variant");
std::process::exit(1);
});
match credential_utxos(config.contract.wallet_contract_hash, network_flag).await {
Ok(utxos) => {
let mut total_lovelace: u64 = 0;
let mut total_seedelfs: u64 = 0;
for utxo in utxos.clone() {
if contains_policy_id(&utxo.asset_list, config.contract.seedelf_policy_id) {
total_seedelfs += 1;
}
let value: u64 = string_to_u64(utxo.value.clone()).unwrap();
total_lovelace += value;
}
println!(
"\nBalance: {} ₳",
format!("{:.6}", total_lovelace as f64 / 1_000_000.0).bright_yellow()
);
println!(
"Contract Has {} UTxOs",
utxos.len().to_string().bright_yellow()
);
println!(
"Contract Has {} Seedelfs",
total_seedelfs.to_string().bright_yellow()
);
}
Err(err) => {
eprintln!(
"Failed to fetch UTxOs: {}\nWait a few moments and try again.",
err
);
}
}
}