partner_chains_cardano_offchain/reserve/
handover.rs

1//!
2//! Specification for deposit transaction:
3//!
4//! Consumes:
5//! - UTXO at the Reserve Validator address
6//!
7//! Outputs:
8//! - UTXO at the illiquid supply validator address with all the Reserve Tokens, plutus data Constr 0 []
9//! - UTXO at the payment address with change and governance token
10//!
11//! Mints:
12//! - Governance Token
13//! - Reserve Auth Policy Token token -1 (burn)
14//!
15//! Reference UTOXs:
16//! - Version Oracle Validator script
17//! - Reserve Auth Policy script
18//! - Reserve Validator script
19//! - Illiquid Supply Validator script
20
21use super::{ReserveUtxo, TokenAmount, reserve_utxo_input_with_validator_script_reference};
22use crate::{
23	await_tx::AwaitTx,
24	cardano_keys::CardanoPaymentSigningKey,
25	csl::{
26		AssetIdExt, CostStore, Costs, OgmiosUtxoExt, Script, TransactionBuilderExt,
27		TransactionContext, TransactionExt, TransactionOutputAmountBuilderExt, get_builder_config,
28		unit_plutus_data,
29	},
30	governance::GovernanceData,
31	multisig::{MultiSigSmartContractResult, submit_or_create_tx_to_sign},
32	reserve::ReserveData,
33	scripts_data::ReserveScripts,
34};
35use cardano_serialization_lib::*;
36use ogmios_client::{
37	query_ledger_state::{QueryLedgerState, QueryUtxoByUtxoId},
38	query_network::QueryNetwork,
39	transactions::Transactions,
40	types::OgmiosUtxo,
41};
42use partner_chains_plutus_data::reserve::ReserveRedeemer;
43use sidechain_domain::UtxoId;
44
45/// Spends current UTXO at validator address to illiquid supply validator and burn reserve auth policy token, preventing further operations.
46pub async fn handover_reserve<
47	T: QueryLedgerState + Transactions + QueryNetwork + QueryUtxoByUtxoId,
48	A: AwaitTx,
49>(
50	genesis_utxo: UtxoId,
51	payment_key: &CardanoPaymentSigningKey,
52	client: &T,
53	await_tx: &A,
54) -> anyhow::Result<MultiSigSmartContractResult> {
55	let ctx = TransactionContext::for_payment_key(payment_key, client).await?;
56	let governance = GovernanceData::get(genesis_utxo, client).await?;
57	let reserve = ReserveData::get(genesis_utxo, &ctx, client).await?;
58
59	let ref reserve_utxo @ ReserveUtxo { ref utxo, .. } =
60		reserve.get_reserve_utxo(&ctx, client).await?;
61	let amount = get_amount_to_release(reserve_utxo);
62
63	submit_or_create_tx_to_sign(
64		&governance,
65		ctx,
66		|costs, ctx| build_tx(&amount, utxo, &reserve, &governance, costs, &ctx),
67		"Handover Reserve",
68		client,
69		await_tx,
70	)
71	.await
72}
73
74fn get_amount_to_release(reserve_utxo: &ReserveUtxo) -> TokenAmount {
75	let token = reserve_utxo.datum.immutable_settings.token.clone();
76	let amount = reserve_utxo.utxo.get_asset_amount(&token);
77	TokenAmount { token, amount }
78}
79
80fn build_tx(
81	handover_amount: &TokenAmount,
82	reserve_utxo: &OgmiosUtxo,
83	reserve: &ReserveData,
84	governance: &GovernanceData,
85	costs: Costs,
86	ctx: &TransactionContext,
87) -> anyhow::Result<Transaction> {
88	let mut tx_builder = TransactionBuilder::new(&get_builder_config(ctx)?);
89
90	let reserve_auth_policy_spend_cost = costs.get_one_spend();
91
92	// mint goveranance token
93	tx_builder.add_mint_one_script_token_using_reference_script(
94		&governance.policy.script(),
95		&governance.utxo_id_as_tx_input(),
96		&costs,
97	)?;
98
99	// Spends UTXO with Reserve Auth Policy Token and Reserve (Reward) tokens
100	tx_builder.set_inputs(&reserve_utxo_input_with_validator_script_reference(
101		reserve_utxo,
102		reserve,
103		ReserveRedeemer::Handover,
104		&reserve_auth_policy_spend_cost,
105	)?);
106
107	// burn reserve auth policy token
108	tx_builder.add_mint_script_token_using_reference_script(
109		&Script::Plutus(reserve.scripts.auth_policy.clone()),
110		&reserve.auth_policy_version_utxo.to_csl_tx_input(),
111		&Int::new_i32(-1),
112		&costs,
113	)?;
114
115	tx_builder.add_output(&illiquid_supply_validator_output(
116		handover_amount,
117		&reserve.scripts,
118		ctx,
119	)?)?;
120	tx_builder.add_script_reference_input(
121		&reserve.illiquid_circulation_supply_validator_version_utxo.to_csl_tx_input(),
122		reserve.scripts.illiquid_circulation_supply_validator.bytes.len(),
123	);
124	Ok(tx_builder.balance_update_and_build(ctx)?.remove_native_script_witnesses())
125}
126
127// Creates output with reserve token and updated deposit
128fn illiquid_supply_validator_output(
129	output_value: &TokenAmount,
130	scripts: &ReserveScripts,
131	ctx: &TransactionContext,
132) -> Result<TransactionOutput, JsError> {
133	let tx_output_builder = TransactionOutputBuilder::new()
134		.with_address(&scripts.illiquid_circulation_supply_validator.address(ctx.network));
135	if output_value.amount > 0 {
136		let ma = output_value.token.to_multi_asset(output_value.amount)?;
137		let amount_builder = tx_output_builder
138			.with_plutus_data(&illiquid_supply_validator_redeemer())
139			.next()?;
140		amount_builder.with_minimum_ada_and_asset(&ma, ctx)?.build()
141	} else {
142		// Smart-contract requires to deposit exactly one UTXO in the illiquid supply validator,
143		// otherwise it returns ERROR-RESERVE-16: No unique output utxo at the illiquid circulation supply address
144		let amount_builder = tx_output_builder.next()?;
145		amount_builder.with_minimum_ada(ctx)?.build()
146	}
147}
148
149fn illiquid_supply_validator_redeemer() -> PlutusData {
150	unit_plutus_data()
151}