use crate::contract::error::CompilationError;
use bitcoin::hashes::sha256;
use bitcoin::util::amount::Amount;
use sapio_base::simp::SIMPAttachableAt;
use sapio_base::simp::SIMPError;
use sapio_base::simp::TemplateInputLT;
use sapio_base::simp::TemplateLT;
use sapio_base::Clause;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
pub mod input;
pub mod output;
pub use output::{Output, OutputMeta};
pub mod builder;
pub use builder::Builder;
use self::input::InputMetadata;
#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug, PartialEq, Eq)]
pub struct TemplateMetadata {
#[serde(skip_serializing_if = "Option::is_none", default)]
pub label: Option<String>,
#[serde(flatten)]
pub extra: BTreeMap<String, serde_json::Value>,
pub simp: BTreeMap<i64, serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub color: Option<String>,
}
impl TemplateMetadata {
pub fn skip_serializing(&self) -> bool {
*self == TemplateMetadata::new()
}
pub fn new() -> Self {
TemplateMetadata {
simp: BTreeMap::new(),
color: None,
label: None,
extra: BTreeMap::new(),
}
}
pub fn set_extra<I, J>(mut self, i: I, j: J) -> Result<Self, CompilationError>
where
I: Into<String>,
J: Into<serde_json::Value>,
{
let s: String = i.into();
match s.as_str() {
"color" | "label" => Err(CompilationError::TerminateWith(
"Don't Set label or color through the extra API".into(),
)),
_ => {
if self.extra.insert(s.clone(), j.into()).is_some() {
return Err(CompilationError::OverwriteMetadata(s));
}
Ok(self)
}
}
}
pub fn set_color<I>(mut self, i: I) -> Result<Self, CompilationError>
where
I: Into<String>,
{
if self.color.is_some() {
return Err(CompilationError::OverwriteMetadata("color".into()));
}
self.color = Some(i.into());
Ok(self)
}
pub fn set_label<I>(mut self, i: I) -> Result<Self, CompilationError>
where
I: Into<String>,
{
if self.label.is_some() {
return Err(CompilationError::OverwriteMetadata("label".into()));
}
self.label = Some(i.into());
Ok(self)
}
pub fn add_simp<S: SIMPAttachableAt<TemplateLT>>(mut self, s: S) -> Result<Self, SIMPError> {
let old = self.simp.insert(s.get_protocol_number(), s.to_json()?);
if let Some(old) = old {
Err(SIMPError::AlreadyDefined(old))
} else {
Ok(self)
}
}
}
#[derive(Serialize, Deserialize, JsonSchema, Clone, Debug)]
pub struct Template {
#[serde(rename = "additional_preconditions")]
pub guards: Vec<Clause>,
#[serde(rename = "precomputed_template_hash")]
pub ctv: sha256::Hash,
#[serde(rename = "precomputed_template_hash_idx")]
pub ctv_index: u32,
#[serde(
rename = "max_amount_sats",
with = "bitcoin::util::amount::serde::as_sat"
)]
#[schemars(with = "i64")]
pub max: Amount,
#[serde(
rename = "min_feerate_sats_vbyte",
with = "bitcoin::util::amount::serde::as_sat::opt"
)]
#[schemars(with = "Option<i64>")]
pub min_feerate_sats_vbyte: Option<Amount>,
#[serde(
skip_serializing_if = "TemplateMetadata::skip_serializing",
default = "TemplateMetadata::new"
)]
pub metadata_map_s2s: TemplateMetadata,
#[serde(rename = "transaction_literal")]
pub tx: bitcoin::Transaction,
#[serde(rename = "outputs_info")]
pub outputs: Vec<Output>,
#[serde(rename = "inputs_info")]
pub inputs: Vec<InputMetadata>,
}
impl Template {
pub fn hash(&self) -> sha256::Hash {
self.ctv
}
pub fn total_amount(&self) -> Amount {
self.outputs
.iter()
.map(|o| o.amount)
.fold(Amount::from_sat(0), |b, a| b + a)
}
}