use crate::address;
use crate::assets::Assets;
use crate::constants::{
CPU_COST_DENOMINATOR, CPU_COST_NUMERATOR, MAINNET_COLLATERAL_UTXO, MEM_COST_DENOMINATOR,
MEM_COST_NUMERATOR, OVERHEAD_COST, PREPROD_COLLATERAL_UTXO, UTXO_COST_PER_BYTE,
};
use anyhow::{Context, Result, anyhow};
use hex_literal::hex;
use pallas_addresses::Address;
use pallas_crypto::hash::Hash;
use pallas_primitives::Fragment;
use pallas_txbuilder::{Input, Output};
use seedelf_crypto::{register::Register, schnorr};
use serde_json::Value;
pub fn calculate_min_required_utxo(output: Output) -> Result<u64> {
let output_cbor_length: u64 = output
.build_babbage_raw()
.context("Failed To Construct Babbage CBOR")?
.encode_fragment()
.map_err(|e| anyhow!("Failed to encode PlutusData fragment: {e}"))?
.len()
.try_into()
.context("Failed To Get CBOR Length")?;
Ok((OVERHEAD_COST + output_cbor_length) * UTXO_COST_PER_BYTE)
}
pub fn collateral_input(network_flag: bool) -> Input {
let utxo: [u8; 32] = if network_flag {
PREPROD_COLLATERAL_UTXO
} else {
MAINNET_COLLATERAL_UTXO
};
Input::new(pallas_crypto::hash::Hash::new(utxo), 0)
}
pub fn reference_utxo(reference_utxo: [u8; 32]) -> Input {
Input::new(Hash::new(reference_utxo), 1)
}
pub fn seedelf_token_name(label: String, inputs: Option<&Vec<Input>>) -> Result<Vec<u8>> {
let mut label_hex: String = hex::encode(label);
label_hex.truncate(30);
let smallest_input: &Input = inputs
.and_then(|inputs| {
inputs.iter().min_by(|a, b| {
a.tx_hash
.0
.cmp(&b.tx_hash.0)
.then(a.txo_index.cmp(&b.txo_index))
})
})
.context("Smallest Input Not Found")?;
let formatted_index: String = format!("{:02x}", smallest_input.txo_index);
let tx_hash_hex: String = hex::encode(smallest_input.tx_hash.0);
let prefix: String = "5eed0e1f".to_string();
let concatenated: String = format!("{prefix}{label_hex}{formatted_index}{tx_hash_hex}");
hex::decode(&concatenated[..64.min(concatenated.len())]).context("Can Decode Token Name")
}
pub fn computation_fee(mem_units: u64, cpu_units: u64) -> u64 {
(MEM_COST_NUMERATOR * mem_units / MEM_COST_DENOMINATOR)
+ (CPU_COST_NUMERATOR * cpu_units / CPU_COST_DENOMINATOR)
}
pub fn extract_budgets(value: &Value) -> Vec<(u64, u64)> {
let mut budgets: Vec<(u64, u64)> = Vec::new();
if let Some(result_array) = value.get("result").and_then(|r| r.as_array()) {
for item in result_array {
if let Some(budget) = item.get("budget") {
if let (Some(cpu), Some(memory)) = (
budget.get("cpu").and_then(|c| c.as_u64()),
budget.get("memory").and_then(|m| m.as_u64()),
) {
budgets.push((cpu, memory));
}
}
}
}
budgets
}
pub fn total_computation_fee(budgets: Vec<(u64, u64)>) -> u64 {
let mut fee: u64 = 0;
for (cpu, mem) in budgets.into_iter() {
fee += computation_fee(mem, cpu);
}
fee
}
pub fn seedelf_minimum_lovelace() -> Result<u64> {
let token_name: Vec<u8> = [
94, 237, 14, 31, 1, 66, 250, 134, 20, 230, 198, 12, 121, 19, 73, 107, 154, 156, 226, 154,
138, 103, 76, 134, 93, 156, 23, 169, 169, 167, 201, 55,
]
.to_vec();
let policy_id: [u8; 28] = hex!("84967d911e1a10d5b4a38441879f374a07f340945bcf9e7697485255");
let staging_output: Output = Output::new(address::dummy_base_address(), 5_000_000)
.set_inline_datum(
Register::create(schnorr::random_scalar())
.context("Failed To Construct Points")?
.rerandomize()
.context("Failed To Randomize Points")?
.to_vec()?,
)
.add_asset(Hash::new(policy_id), token_name, 1)
.context("Staging Output Failed")?;
calculate_min_required_utxo(staging_output)
}
pub fn wallet_minimum_lovelace_with_assets(tokens: Assets) -> Result<u64> {
let mut staging_output: Output = Output::new(address::dummy_base_address(), 5_000_000)
.set_inline_datum(
Register::create(schnorr::random_scalar())
.context("Failed To Construct Points")?
.rerandomize()
.context("Failed To Randomize Points")?
.to_vec()?,
);
for asset in tokens.items {
staging_output = staging_output
.add_asset(asset.policy_id, asset.token_name, asset.amount)
.context("Staging Output Failed")?;
}
calculate_min_required_utxo(staging_output)
}
pub fn address_minimum_lovelace_with_assets(address: &str, tokens: Assets) -> Result<u64> {
let addr: Address =
Address::from_bech32(address).context("Address Failed To Convert To Bech32")?;
let mut staging_output: Output = Output::new(addr, 5_000_000);
for asset in tokens.items {
staging_output = staging_output
.add_asset(asset.policy_id, asset.token_name, asset.amount)
.context("Staging Output Failed")?;
}
calculate_min_required_utxo(staging_output)
}