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