#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
use noop_proc_macro::wasm_bindgen;
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
use wasm_bindgen::prelude::*;
use crate::Datum;
use crate::TransactionOutput;
use crate::address::Address;
use crate::error::JsError;
use crate::ledger::common::value::BigNum;
use crate::ledger::common::value::Coin;
use crate::ledger::common::value::Value;
use crate::ledger::common::value::to_bignum;
use crate::plutus::ScriptRef;
#[deprecated(
since = "1.0.0",
note = "If you don't need to support Alonzo, you don't need this function"
)]
#[wasm_bindgen]
pub fn compatible_min_ada_required(
output: &TransactionOutput,
coins_per_utxo_byte: &BigNum, coins_per_utxo_word: &BigNum, ) -> Result<Coin, JsError> {
let babbage_min = min_ada_required(output, coins_per_utxo_byte)?;
let alonzo_min = crate::ledger::alonzo::min_ada::min_ada_required(&output.amount(), output.datum_option.is_some(), coins_per_utxo_word)?;
Ok(std::cmp::max(babbage_min, alonzo_min))
}
#[wasm_bindgen]
pub fn min_ada_required(
output: &TransactionOutput,
coins_per_utxo_byte: &BigNum, ) -> Result<BigNum, JsError> {
let constant_overhead = 160_u64;
let old_coin_size = output.amount.coin.to_bytes().len();
let mut latest_size = old_coin_size;
loop {
let size_diff = latest_size as i128 - old_coin_size as i128;
let tentative_min_ada = to_bignum((output.to_bytes().len() as i128 + constant_overhead as i128 + size_diff) as u64)
.checked_mul(coins_per_utxo_byte)?;
let new_coin_size = tentative_min_ada
.to_bytes()
.len();
let is_done = latest_size == new_coin_size;
latest_size = new_coin_size;
if is_done {
break;
}
}
let size_change = latest_size as i128 - old_coin_size as i128;
let adjusted_min_ada = to_bignum((output.to_bytes().len() as i128 + constant_overhead as i128 + size_change) as u64)
.checked_mul(coins_per_utxo_byte)?;
Ok(adjusted_min_ada)
}
pub fn min_pure_ada(coins_per_utxo_byte: &BigNum, address: &Address, datum: &Option<Datum>, script_ref: &Option<ScriptRef>) -> Result<BigNum, JsError> {
let mut output = TransactionOutput::new(
address,
&Value::new(&Coin::from_str("1000000")?),
);
if let Some(d) = &datum {
output.set_datum(d);
}
if let Some(s) = &script_ref {
output.set_script_ref(s);
}
min_ada_required(
&output,
coins_per_utxo_byte,
)
}
#[cfg(test)]
mod tests {
use crate::{crypto::{ScriptHash, Bip32PrivateKey}, address::{StakeCredential, BaseAddress, NetworkInfo}, MultiAsset, Assets, PolicyID, AssetName, ledger::common::value::{from_bignum, Value}};
use super::*;
const COINS_PER_UTXO_BYTE: u64 = 4310;
fn test_output() -> TransactionOutput {
fn harden(index: u32) -> u32 {
index | 0x80_00_00_00
}
fn root_key_15() -> Bip32PrivateKey {
let entropy = [
0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22,
0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12,
];
Bip32PrivateKey::from_bip39_entropy(&entropy, &[])
}
let spend = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(0)
.derive(0)
.to_public();
let stake = root_key_15()
.derive(harden(1852))
.derive(harden(1815))
.derive(harden(0))
.derive(2)
.derive(0)
.to_public();
let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash());
let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash());
let address = BaseAddress::new(
NetworkInfo::testnet().network_id(),
&spend_cred,
&stake_cred,
)
.to_address();
TransactionOutput::new(&address, &Value::new(&to_bignum(0)))
}
fn one_policy_one_0_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(0),
multiasset: Some(token_bundle),
}
}
fn one_policy_one_1_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![1]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(1407406),
multiasset: Some(token_bundle),
}
}
fn one_policy_three_1_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![1]),
&BigNum::from(1)
);
asset_list.insert(
&AssetName(vec![2]),
&BigNum::from(1)
);
asset_list.insert(
&AssetName(vec![3]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(1555554),
multiasset: Some(token_bundle),
}
}
fn two_policies_one_0_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
token_bundle.insert(
&PolicyID::from([1; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(1592591),
multiasset: Some(token_bundle),
}
}
fn two_policies_one_1_char_asset() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![1]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
token_bundle.insert(
&PolicyID::from([1; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(1592591),
multiasset: Some(token_bundle),
}
}
fn three_policies_96_1_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
fn add_policy(token_bundle: &mut MultiAsset, index: u8) {
let mut asset_list = Assets::new();
for i in 0..32 {
asset_list.insert(
&AssetName(vec![index * 32 + i]),
&BigNum::from(1)
);
}
token_bundle.insert(
&PolicyID::from([index; ScriptHash::BYTE_COUNT]),
&asset_list
);
}
add_policy(&mut token_bundle, 1);
add_policy(&mut token_bundle, 2);
add_policy(&mut token_bundle, 3);
Value {
coin: BigNum::from(7592585),
multiasset: Some(token_bundle),
}
}
fn one_policy_three_32_char_assets() -> Value {
let mut token_bundle = MultiAsset::new();
let mut asset_list = Assets::new();
asset_list.insert(
&AssetName(vec![1; 32]),
&BigNum::from(1)
);
asset_list.insert(
&AssetName(vec![2; 32]),
&BigNum::from(1)
);
asset_list.insert(
&AssetName(vec![3; 32]),
&BigNum::from(1)
);
token_bundle.insert(
&PolicyID::from([0; ScriptHash::BYTE_COUNT]),
&asset_list
);
Value {
coin: BigNum::from(1555554),
multiasset: Some(token_bundle),
}
}
#[test]
fn min_ada_value_no_multiasset() {
let check_output = test_output();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
969750,
);
}
#[test]
fn min_ada_value_one_policy_one_0_char_asset() {
let mut check_output = test_output();
check_output.amount = one_policy_one_0_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1120600,
);
}
#[test]
fn min_ada_value_one_policy_one_1_char_asset() {
let mut check_output = test_output();
check_output.amount = one_policy_one_1_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1124910,
);
}
#[test]
fn min_ada_value_one_policy_three_1_char_assets() {
let mut check_output = test_output();
check_output.amount = one_policy_three_1_char_assets();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1150770,
);
}
#[test]
fn min_ada_value_two_policies_one_0_char_asset() {
let mut check_output = test_output();
check_output.amount = two_policies_one_0_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1262830,
);
}
#[test]
fn min_ada_value_two_policies_one_1_char_asset() {
let mut check_output = test_output();
check_output.amount = two_policies_one_1_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1271450,
);
}
#[test]
fn min_ada_value_three_policies_96_1_char_assets() {
let mut check_output = test_output();
check_output.amount = three_policies_96_1_char_assets();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
2633410,
);
}
#[test]
fn min_ada_value_one_policy_one_0_char_asset_datum_hash() {
let mut check_output = test_output();
check_output.amount = one_policy_one_0_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1120600,
);
}
#[test]
fn min_ada_value_one_policy_three_32_char_assets_datum_hash() {
let mut check_output = test_output();
check_output.amount = one_policy_three_32_char_assets();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1564530,
);
}
#[test]
fn min_ada_value_two_policies_one_0_char_asset_datum_hash() {
let mut check_output = test_output();
check_output.amount = two_policies_one_0_char_asset();
assert_eq!(
from_bignum(&min_ada_required(&check_output, &to_bignum(COINS_PER_UTXO_BYTE)).unwrap()),
1262830,
);
}
}