partner_chains_cardano_offchain/reserve/
mod.rs

1//! All smart-contracts related to Rewards Token Reserve Management
2
3use 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}