1use ::oura::model::{MintRecord, OutputAssetRecord};
2use anyhow::Context;
3use data_encoding::HEXLOWER;
4use num_bigint::BigInt;
5use plutus_ledger_api::csl::{csl_to_pla::TryToPLA, lib as csl};
6use plutus_ledger_api::v3::{
7 address::Address,
8 crypto::LedgerBytes,
9 datum::{Datum, DatumHash},
10 redeemer::Redeemer,
11 script::{MintingPolicyHash, ScriptHash},
12 transaction::TransactionHash,
13 value::{CurrencySymbol, TokenName, Value},
14};
15use std::fmt::Debug;
16
17#[derive(thiserror::Error, Debug)]
18pub enum OuraParseError {
19 #[error(transparent)]
20 ParseError(#[from] anyhow::Error),
21
22 #[error("Unable to convert current time: {0}")]
23 TimeConversionError(tx_bakery::error::Error),
24}
25
26pub trait FromOura<T> {
28 fn from_oura(value: T) -> Result<Self, OuraParseError>
29 where
30 Self: Sized;
31}
32
33impl FromOura<String> for LedgerBytes {
34 fn from_oura(value: String) -> Result<Self, OuraParseError> {
35 Ok(LedgerBytes(
36 HEXLOWER
37 .decode(&value.clone().into_bytes()[..])
38 .with_context(|| "Parsing LedgerBytes from Oura")?,
39 ))
40 }
41}
42
43impl FromOura<String> for TransactionHash {
44 fn from_oura(value: String) -> Result<Self, OuraParseError> {
45 Ok(TransactionHash(
46 LedgerBytes::from_oura(value).with_context(|| "Parsing TransactionHash from Oura")?,
47 ))
48 }
49}
50
51impl FromOura<String> for DatumHash {
52 fn from_oura(value: String) -> Result<Self, OuraParseError> {
53 Ok(DatumHash(LedgerBytes::from_oura(value)?))
54 }
55}
56
57impl FromOura<String> for ScriptHash {
58 fn from_oura(value: String) -> Result<Self, OuraParseError> {
59 Ok(ScriptHash(LedgerBytes::from_oura(value)?))
60 }
61}
62
63impl FromOura<String> for CurrencySymbol {
64 fn from_oura(value: String) -> Result<Self, OuraParseError> {
65 Ok(if value.is_empty() {
66 CurrencySymbol::Ada
67 } else {
68 CurrencySymbol::NativeToken(MintingPolicyHash(ScriptHash(LedgerBytes::from_oura(
69 value,
70 )?)))
71 })
72 }
73}
74
75impl FromOura<String> for TokenName {
76 fn from_oura(value: String) -> Result<Self, OuraParseError> {
77 Ok(if value.is_empty() {
78 TokenName::ada()
79 } else {
80 TokenName(LedgerBytes::from_oura(value)?)
81 })
82 }
83}
84
85impl FromOura<serde_json::Value> for Datum {
86 fn from_oura(value: serde_json::Value) -> Result<Self, OuraParseError> {
87 let csl_plutus_data =
88 csl::encode_json_value_to_plutus_datum(value, csl::PlutusDatumSchema::DetailedSchema)
89 .with_context(|| "Parsing Datum from Oura")?;
90
91 Ok(Datum(
92 csl_plutus_data
93 .try_to_pla()
94 .with_context(|| "Parsing Datum from Oura")?,
95 ))
96 }
97}
98
99impl FromOura<serde_json::Value> for Redeemer {
100 fn from_oura(value: serde_json::Value) -> Result<Self, OuraParseError> {
101 let csl_plutus_data =
102 csl::encode_json_value_to_plutus_datum(value, csl::PlutusDatumSchema::DetailedSchema)
103 .with_context(|| "Parsing Redeemer from Oura")?;
104
105 Ok(Redeemer(
106 csl_plutus_data
107 .try_to_pla()
108 .with_context(|| "Parsing Redeemer from Oura")?,
109 ))
110 }
111}
112
113impl FromOura<String> for Address {
114 fn from_oura(value: String) -> Result<Self, OuraParseError> {
115 let csl_addr = csl::Address::from_bech32(&value)
116 .or_else(|_| {
117 csl::ByronAddress::from_base58(&value).map(|byron_addr| byron_addr.to_address())
118 })
119 .with_context(|| "Parsing Address from Oura")?;
120
121 Ok(csl_addr
122 .try_to_pla()
123 .with_context(|| "Parsing Address from Oura")?)
124 }
125}
126
127impl FromOura<Vec<OutputAssetRecord>> for Value {
128 fn from_oura(value: Vec<OutputAssetRecord>) -> Result<Self, OuraParseError> {
129 value.iter().try_fold(Value::new(), |acc, x| {
130 let amt = BigInt::from(x.amount);
131 Ok(acc.insert_token(
132 &CurrencySymbol::from_oura(x.policy.clone())?,
133 &TokenName::from_oura(x.asset.clone())?,
134 &amt,
135 ))
136 })
137 }
138}
139
140impl FromOura<Vec<MintRecord>> for Value {
141 fn from_oura(value: Vec<MintRecord>) -> Result<Self, OuraParseError> {
142 value.iter().try_fold(Value::new(), |acc, x| {
143 let amt = BigInt::from(x.quantity);
144 Ok(acc.insert_token(
145 &CurrencySymbol::from_oura(x.policy.clone())?,
146 &TokenName::from_oura(x.asset.clone())?,
147 &amt,
148 ))
149 })
150 }
151}