use serde::{Deserialize, Serialize};
use tari_bor::BorTag;
use tari_template_abi::{
EngineOp,
call_engine,
rust::{collections::BTreeSet, fmt, prelude::*},
};
use tari_template_lib_types::{BinaryTag, NonFungibleId, ResourceAddress};
use crate::{
args::{InvokeResult, ProofAction, ProofInvokeArg, ProofRef},
types::{Amount, ResourceType},
};
const TAG: u64 = BinaryTag::ProofId.as_u64();
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct ProofId(BorTag<u32, TAG>);
impl From<u32> for ProofId {
fn from(value: u32) -> Self {
Self(BorTag::new(value))
}
}
impl fmt::Display for ProofId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ProofId({})", self.0.inner())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct Proof {
id: ProofId,
}
impl Proof {
pub const fn from_id(id: ProofId) -> Self {
Self { id }
}
pub fn id(&self) -> ProofId {
self.id
}
pub fn resource_address(&self) -> ResourceAddress {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::GetResourceAddress,
args: invoke_args![],
});
resp.decode()
.expect("Proof GetResourceAddress returned invalid resource address")
}
pub fn resource_type(&self) -> ResourceType {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::GetResourceType,
args: invoke_args![],
});
resp.decode()
.expect("Proof GetResourceType returned invalid resource type")
}
pub fn get_non_fungibles(&self) -> BTreeSet<NonFungibleId> {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::GetNonFungibles,
args: invoke_args![],
});
resp.decode()
.expect("Proof GetNonFungibles returned invalid non-fungibles")
}
pub fn amount(&self) -> Amount {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::GetAmount,
args: invoke_args![],
});
resp.decode().expect("Proof GetAmount returned invalid amount")
}
#[must_use = "ProofAccess must used"]
pub fn authorize(&self) -> ProofAccess {
self.try_authorize().expect("Proof authorization failed")
}
pub fn authorize_with<F: FnOnce() -> R, R>(&self, f: F) -> R {
let _auth = self.try_authorize().expect("Proof authorization failed");
f()
}
pub fn try_authorize(&self) -> Result<ProofAccess, NotAuthorized> {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::Authorize,
args: invoke_args![],
});
resp.decode::<Result<(), NotAuthorized>>()
.expect("Proof Access error")?;
Ok(ProofAccess { id: self.id })
}
pub fn drop(self) {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::Drop,
args: invoke_args![],
});
resp.decode().expect("Proof drop error")
}
pub fn assert_resource(&self, resource_address: ResourceAddress) {
assert_eq!(
self.resource_address(),
resource_address,
"Proof of resource did not match {resource_address}"
);
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct NotAuthorized;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProofAccess {
pub id: ProofId,
}
impl Drop for ProofAccess {
fn drop(&mut self) {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::DropAuthorize,
args: invoke_args![],
});
resp.decode::<()>()
.unwrap_or_else(|_| panic!("Drop failed for proof {}", self.id));
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProofAuth {
pub id: ProofId,
}
impl Drop for ProofAuth {
fn drop(&mut self) {
let resp: InvokeResult = call_engine(EngineOp::ProofInvoke, &ProofInvokeArg {
proof_ref: ProofRef::Ref(self.id),
action: ProofAction::Drop,
args: invoke_args![],
});
resp.decode::<()>()
.unwrap_or_else(|_| panic!("Drop failed for proof {}", self.id));
}
}