use crate::imports::*;
use crate::tx::{MutableTransaction, PaymentOutputs, Transaction, TransactionInput, TransactionOutpoint, TransactionOutput};
use crate::utxo::{SelectionContext, UtxoEntry, UtxoEntryReference};
use kaspa_addresses::Address;
use kaspa_consensus_core::{subnets::SubnetworkId, tx};
use kaspa_txscript::pay_to_address_script;
#[derive(Clone, Debug)]
#[wasm_bindgen]
#[allow(dead_code)] pub struct VirtualTransaction {
transactions: Vec<MutableTransaction>,
payload: Vec<u8>,
}
#[wasm_bindgen]
impl VirtualTransaction {
#[wasm_bindgen(constructor)]
pub fn new(
utxo_selection: SelectionContext,
outputs: PaymentOutputs,
change_address: Address,
payload: Vec<u8>,
) -> crate::Result<VirtualTransaction> {
log_trace!("VirtualTransaction...");
log_trace!("utxo_selection.transaction_amount: {:?}", utxo_selection.transaction_amount);
log_trace!("outputs.outputs: {:?}", outputs.outputs);
log_trace!("change_address: {change_address:?}");
let entries = &utxo_selection.selected_entries;
let chunks = entries.chunks(80).collect::<Vec<&[UtxoEntryReference]>>();
let mut final_inputs = vec![];
let mut final_utxos = vec![];
let mut final_amount = 0;
let mut transactions = chunks
.into_iter()
.map(|chunk| {
let utxos = chunk.iter().map(|reference| reference.utxo.clone()).collect::<Vec<Arc<UtxoEntry>>>();
let mut amount = 0;
let mut entries = vec![];
let inputs = utxos
.iter()
.enumerate()
.map(|(sequence, utxo)| {
amount += utxo.utxo_entry.amount;
entries.push(utxo.as_ref().clone());
TransactionInput::new(utxo.outpoint.clone(), vec![], sequence as u64, 0)
})
.collect::<Vec<TransactionInput>>();
let amount_after_fee = amount - 500;
let script_public_key = pay_to_address_script(&change_address);
let tx = Transaction::new(
0,
inputs,
vec![TransactionOutput::new(amount_after_fee, &script_public_key)],
0,
SubnetworkId::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
0,
vec![],
)
.unwrap();
let transaction_id = tx.id();
final_amount += amount_after_fee;
final_utxos.push(UtxoEntry {
address: Some(change_address.clone()),
outpoint: TransactionOutpoint::new(&transaction_id, 0).unwrap(),
utxo_entry: tx::UtxoEntry {
amount: amount_after_fee,
script_public_key,
block_daa_score: u64::MAX,
is_coinbase: false,
},
});
final_inputs.push(TransactionInput::new(
TransactionOutpoint::new(&transaction_id, 0).unwrap(),
vec![],
final_inputs.len() as u64,
0,
));
MutableTransaction::new(&tx, &entries.into())
})
.collect::<Vec<MutableTransaction>>();
let fee = 500; let amount_after_fee = final_amount - fee;
let mut outputs_ = vec![];
let mut total_amount = 0;
for output in &outputs.outputs {
total_amount += output.amount;
outputs_.push(TransactionOutput::new(output.amount, &pay_to_address_script(&output.address)));
}
if total_amount > amount_after_fee {
return Err("total_amount({total_amount}) > amount_after_fee({amount_after_fee})".to_string().into());
}
let change = amount_after_fee - total_amount;
let dust = 500;
if change > dust {
outputs_.push(TransactionOutput::new(change, &pay_to_address_script(&change_address)));
}
let tx = Transaction::new(
0,
final_inputs,
outputs_,
0,
SubnetworkId::from_bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
0,
payload.clone(),
)?;
let mtx = MutableTransaction::new(&tx, &final_utxos.into());
transactions.push(mtx);
log_trace!("transactions: {transactions:#?}");
Ok(VirtualTransaction { transactions, payload })
}
}