partner_chains_cardano_offchain/reserve/
mod.rs1use crate::{
4 csl::{OgmiosUtxoExt, OgmiosValueExt, TransactionContext},
5 scripts_data,
6};
7use anyhow::anyhow;
8use cardano_serialization_lib::{
9 BigInt, ExUnits, JsError, PlutusData, PlutusScriptSource, PlutusWitness, Redeemer, RedeemerTag,
10 TxInputsBuilder,
11};
12use init::find_script_utxo;
13use ogmios_client::{query_ledger_state::QueryLedgerState, types::OgmiosUtxo};
14use partner_chains_plutus_data::reserve::{ReserveDatum, ReserveRedeemer};
15use sidechain_domain::{AssetId, AssetName, UtxoId};
16
17pub mod create;
18pub mod deposit;
19pub mod handover;
20pub mod init;
21pub mod release;
22pub mod update_settings;
23
24#[derive(Clone, Debug)]
25pub(crate) struct ReserveData {
26 pub(crate) scripts: scripts_data::ReserveScripts,
27 pub(crate) auth_policy_version_utxo: OgmiosUtxo,
28 pub(crate) validator_version_utxo: OgmiosUtxo,
29 pub(crate) illiquid_circulation_supply_validator_version_utxo: OgmiosUtxo,
30 pub(crate) illiquid_circulation_supply_authority_token_policy_version_utxo: OgmiosUtxo,
31}
32
33#[derive(Clone, Debug)]
34pub(crate) struct ReserveUtxo {
35 pub(crate) utxo: OgmiosUtxo,
36 pub(crate) datum: ReserveDatum,
37}
38
39impl ReserveData {
40 pub(crate) async fn get<T: QueryLedgerState>(
41 genesis_utxo: UtxoId,
42 ctx: &TransactionContext,
43 client: &T,
44 ) -> Result<Self, anyhow::Error> {
45 let version_oracle = scripts_data::version_oracle(genesis_utxo, ctx.network)?;
46 let auth_policy_version_utxo = find_script_utxo(
47 raw_scripts::ScriptId::ReserveAuthPolicy as u32,
48 &version_oracle,
49 ctx,
50 client,
51 )
52 .await?
53 .ok_or_else(|| {
54 anyhow!(
55 "Reserve Auth Version Utxo not found, is the Reserve Token Management initialized?"
56 )
57 })?;
58 let validator_version_utxo = find_script_utxo(
59 raw_scripts::ScriptId::ReserveValidator as u32,
60 &version_oracle,
61 ctx,
62 client,
63 )
64 .await?
65 .ok_or_else(|| {
66 anyhow!(
67 "Reserve Validator Version Utxo not found, is the Reserve Token Management initialized?"
68 )
69 })?;
70 let illiquid_circulation_supply_validator_version_utxo = find_script_utxo(
71 raw_scripts::ScriptId::IlliquidCirculationSupplyValidator as u32,
72 &version_oracle,
73 ctx,
74 client,
75 )
76 .await?
77 .ok_or_else(|| {
78 anyhow!("Illiquid Circulation Supply Validator Version Utxo not found, is the Reserve Token Management initialized?")
79 })?;
80 let illiquid_circulation_supply_authority_token_policy_version_utxo = find_script_utxo(
81 raw_scripts::ScriptId::IlliquidCirculationSupplyAuthorityTokenPolicy as u32,
82 &version_oracle,
83 ctx,
84 client,
85 )
86 .await?
87 .ok_or_else(|| {
88 anyhow!("Illiquid Circulation Supply Authority Token Policy Version Utxo not found, is the Reserve Token Management initialized?")
89 })?;
90 let scripts = scripts_data::reserve_scripts(genesis_utxo, ctx.network)?;
91 Ok(ReserveData {
92 scripts,
93 auth_policy_version_utxo,
94 validator_version_utxo,
95 illiquid_circulation_supply_validator_version_utxo,
96 illiquid_circulation_supply_authority_token_policy_version_utxo,
97 })
98 }
99
100 pub(crate) async fn get_reserve_utxo<T: QueryLedgerState>(
101 &self,
102 ctx: &TransactionContext,
103 client: &T,
104 ) -> Result<ReserveUtxo, anyhow::Error> {
105 let validator_address = self.scripts.validator.address(ctx.network).to_bech32(None)?;
106 let validator_utxos = client.query_utxos(&[validator_address]).await?;
107
108 let auth_token_asset_id = AssetId {
109 policy_id: self.scripts.auth_policy.policy_id(),
110 asset_name: AssetName::empty(),
111 };
112
113 let (reserve_utxo, reserve_settings) = validator_utxos
114 .into_iter()
115 .find_map(|utxo| {
116 if utxo.get_asset_amount(&auth_token_asset_id) != 1u64 {
117 return None;
118 }
119 utxo.get_plutus_data()
120 .and_then(|d| ReserveDatum::try_from(d).ok())
121 .map(|d| (utxo, d))
122 })
123 .ok_or_else(|| {
124 anyhow!("Reserve Utxo not found, is the Reserve Token Management initialized?")
125 })?;
126
127 Ok(ReserveUtxo { utxo: reserve_utxo, datum: reserve_settings })
128 }
129
130 pub(crate) async fn get_illiquid_circulation_supply_utxo<T: QueryLedgerState>(
131 &self,
132 ctx: &TransactionContext,
133 client: &T,
134 ) -> Result<OgmiosUtxo, anyhow::Error> {
135 let validator_address = self
136 .scripts
137 .illiquid_circulation_supply_validator
138 .address(ctx.network)
139 .to_bech32(None)?;
140 let validator_utxos = client.query_utxos(&[validator_address]).await?;
141
142 let auth_token_asset_id = AssetId {
143 policy_id: self.scripts.illiquid_circulation_supply_auth_token_policy.policy_id(),
144 asset_name: AssetName::empty(),
145 };
146
147 let ics_utxo = validator_utxos
148 .into_iter()
149 .find(|utxo| utxo.get_asset_amount(&auth_token_asset_id) == 1u64)
150 .ok_or_else(|| {
151 anyhow!("Could not find any UTXO with ICS Auth token at ICS Validator, is the Reserve Token Management initialized?")
152 })?;
153
154 Ok(ics_utxo.clone())
155 }
156}
157
158pub(crate) struct TokenAmount {
159 pub token: AssetId,
160 pub amount: u64,
161}
162
163pub(crate) fn reserve_utxo_input_with_validator_script_reference(
164 reserve_utxo: &OgmiosUtxo,
165 reserve: &ReserveData,
166 redeemer: ReserveRedeemer,
167 cost: &ExUnits,
168) -> Result<TxInputsBuilder, JsError> {
169 let mut inputs = TxInputsBuilder::new();
170 add_reserve_utxo_input_with_validator_script_reference(
171 &mut inputs,
172 reserve_utxo,
173 reserve,
174 redeemer,
175 cost,
176 )?;
177 Ok(inputs)
178}
179
180pub(crate) fn add_reserve_utxo_input_with_validator_script_reference(
181 inputs: &mut TxInputsBuilder,
182 reserve_utxo: &OgmiosUtxo,
183 reserve: &ReserveData,
184 redeemer: ReserveRedeemer,
185 cost: &ExUnits,
186) -> Result<(), JsError> {
187 let input = reserve_utxo.to_csl_tx_input();
188 let amount = reserve_utxo.value.to_csl()?;
189 let script = &reserve.scripts.validator;
190 let witness = PlutusWitness::new_with_ref_without_datum(
191 &PlutusScriptSource::new_ref_input(
192 &script.csl_script_hash(),
193 &reserve.validator_version_utxo.to_csl_tx_input(),
194 &script.language,
195 script.bytes.len(),
196 ),
197 &Redeemer::new(&RedeemerTag::new_spend(), &0u32.into(), &redeemer.into(), cost),
198 );
199 inputs.add_plutus_script_input(&witness, &input, &amount);
200 Ok(())
201}
202
203pub(crate) fn add_ics_utxo_input_with_validator_script_reference(
204 inputs: &mut TxInputsBuilder,
205 ics_utxo: &OgmiosUtxo,
206 reserve: &ReserveData,
207 cost: &ExUnits,
208) -> Result<(), JsError> {
209 let input = ics_utxo.to_csl_tx_input();
210 let amount = ics_utxo.value.to_csl()?;
211 let script = &reserve.scripts.illiquid_circulation_supply_validator;
212 let witness = PlutusWitness::new_with_ref_without_datum(
213 &PlutusScriptSource::new_ref_input(
214 &script.csl_script_hash(),
215 &reserve.illiquid_circulation_supply_validator_version_utxo.to_csl_tx_input(),
216 &script.language,
217 script.bytes.len(),
218 ),
219 &Redeemer::new(
220 &RedeemerTag::new_spend(),
221 &0u32.into(),
222 &PlutusData::new_integer(&BigInt::zero()),
223 cost,
224 ),
225 );
226 inputs.add_plutus_script_input(&witness, &input, &amount);
227 Ok(())
228}