Skip to content

Commit 84b78f3

Browse files
authored
add support for ics authority tokens (#981)
* add support for ics authority tokens * remove debug prints * minor refactor * minor refactor * prevent user from setting ics_auth_token_amount to 0 * use NonZero for ics_initial_utxos_amount
1 parent 0147a6f commit 84b78f3

File tree

14 files changed

+825
-570
lines changed

14 files changed

+825
-570
lines changed

Cargo.lock

Lines changed: 526 additions & 502 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ substrate-prometheus-endpoint = { package = "substrate-prometheus-endpoint", git
260260
substrate-test-runtime-client = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2506" }
261261
substrate-wasm-builder = { default-features = false, git = "https://github.com/paritytech/polkadot-sdk.git", tag = "polkadot-stable2506" }
262262

263-
raw-scripts = { git = "https://github.com/input-output-hk/partner-chains-smart-contracts.git", tag = "v8.0.0" }
263+
raw-scripts = { git = "https://github.com/input-output-hk/partner-chains-smart-contracts.git", rev = "bc728b23c4fce131a4fdf7db801c89d2afdf88bd" }
264+
264265
# local dependencies
265266

266267
# utils

deny.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ allow = [
9595
"CC0-1.0",
9696
"Unicode-3.0",
9797
"CDLA-Permissive-2.0",
98-
"Apache-2.0 WITH LLVM-exception"
98+
"Apache-2.0 WITH LLVM-exception",
9999
]
100100
# The confidence threshold for detecting a license from license text.
101101
# The higher the value, the more closely the license text must be to the
@@ -118,7 +118,7 @@ exceptions = [
118118
# Run `cargo deny check licenses` locally; its output will guide you.
119119

120120
[[licenses.clarify]]
121-
crate = "ring" # Or "ring@<version-constraint>" e.g. "ring@^0.16"
121+
crate = "ring" # Or "ring@<version-constraint>" e.g. "ring@^0.16"
122122
expression = "ISC AND MIT AND OpenSSL" # SPDX conjunction
123123
# Correct paths and hashes are CRITICAL for this clarification to be accepted.
124124
# `cargo-deny` will tell you what it found when you run `cargo deny check licenses`.
@@ -265,4 +265,4 @@ github = []
265265
# gitlab.com organizations to allow git sources for
266266
gitlab = []
267267
# bitbucket.org organizations to allow git sources for
268-
bitbucket = []
268+
bitbucket = []

toolkit/smart-contracts/commands/src/reserve.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ pub struct CreateReserveCmd {
9191
#[arg(long)]
9292
/// Reserve token asset id encoded in form <policy_id_hex>.<asset_name_hex>.
9393
token: AssetId,
94+
#[arg(long, default_value = "1")]
95+
/// Amount of illiquid circulation supply authority tokens to mint.
96+
ics_initial_utxos_amount: NonZero<u64>,
9497
}
9598

9699
impl CreateReserveCmd {
@@ -103,6 +106,7 @@ impl CreateReserveCmd {
103106
total_accrued_function_script_hash: self.total_accrued_function_script_hash,
104107
token: self.token,
105108
initial_deposit: self.initial_deposit_amount,
109+
ics_initial_utxos_amount: self.ics_initial_utxos_amount,
106110
},
107111
self.genesis_utxo.into(),
108112
&payment_key,

toolkit/smart-contracts/offchain/src/reserve/create.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
//! * Governance Policy Script
1717
1818
use super::ReserveData;
19+
use crate::csl::unit_plutus_data;
20+
use crate::reserve::create::Script::Plutus;
1921
use crate::{
2022
await_tx::AwaitTx,
2123
cardano_keys::CardanoPaymentSigningKey,
@@ -28,7 +30,7 @@ use crate::{
2830
scripts_data::ReserveScripts,
2931
};
3032
use cardano_serialization_lib::{
31-
JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
33+
Int, JsError, MultiAsset, Transaction, TransactionBuilder, TransactionOutput,
3234
TransactionOutputBuilder,
3335
};
3436
use ogmios_client::{
@@ -40,6 +42,7 @@ use partner_chains_plutus_data::reserve::{
4042
ReserveDatum, ReserveImmutableSettings, ReserveMutableSettings, ReserveStats,
4143
};
4244
use sidechain_domain::{AssetId, PolicyId, UtxoId};
45+
use std::num::NonZero;
4346

4447
/// Creates new reserve with the given [ReserveParameters].
4548
pub async fn create_reserve_utxo<
@@ -75,6 +78,8 @@ pub struct ReserveParameters {
7578
pub token: AssetId,
7679
/// Initial deposit amount.
7780
pub initial_deposit: u64,
81+
/// Amount of illiquid circulation supply authority tokens to mint.
82+
pub ics_initial_utxos_amount: NonZero<u64>,
7883
}
7984

8085
impl From<&ReserveParameters> for ReserveDatum {
@@ -118,7 +123,24 @@ fn create_reserve_tx(
118123
&reserve.validator_version_utxo.to_csl_tx_input(),
119124
reserve.scripts.validator.bytes.len(),
120125
);
121-
Ok(tx_builder.balance_update_and_build(ctx)?.remove_native_script_witnesses())
126+
127+
tx_builder.add_mint_script_token_using_reference_script(
128+
&Plutus(reserve.scripts.illiquid_circulation_supply_auth_token_policy.clone()),
129+
&reserve
130+
.illiquid_circulation_supply_authority_token_policy_version_utxo
131+
.to_csl_tx_input(),
132+
&Int::new(&parameters.ics_initial_utxos_amount.get().into()),
133+
&costs,
134+
)?;
135+
// Create ICS Authorized Outputs. These contain special ICS Authority Token,
136+
// that prevents UTxOs from being merged all into one.
137+
for _ in 0..parameters.ics_initial_utxos_amount.into() {
138+
tx_builder.add_output(&ics_validator_output(&reserve.scripts, ctx)?)?;
139+
}
140+
141+
let tx = tx_builder.balance_update_and_build(ctx)?.remove_native_script_witnesses();
142+
143+
Ok(tx)
122144
}
123145

124146
// Creates output with reserve token and the initial deposit
@@ -137,3 +159,19 @@ fn reserve_validator_output(
137159

138160
amount_builder.with_minimum_ada_and_asset(&ma, ctx)?.build()
139161
}
162+
163+
fn ics_validator_output(
164+
scripts: &ReserveScripts,
165+
ctx: &TransactionContext,
166+
) -> Result<TransactionOutput, JsError> {
167+
let amount_builder = TransactionOutputBuilder::new()
168+
.with_address(&scripts.illiquid_circulation_supply_validator.address(ctx.network))
169+
.with_plutus_data(&unit_plutus_data())
170+
.next()?;
171+
let ma = MultiAsset::new().with_asset_amount(
172+
&scripts.illiquid_circulation_supply_auth_token_policy.empty_name_asset(),
173+
1u64,
174+
)?;
175+
176+
amount_builder.with_minimum_ada_and_asset(&ma, ctx)?.build()
177+
}

toolkit/smart-contracts/offchain/src/reserve/handover.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use crate::{
2525
csl::{
2626
AssetIdExt, CostStore, Costs, OgmiosUtxoExt, Script, TransactionBuilderExt,
2727
TransactionContext, TransactionExt, TransactionOutputAmountBuilderExt, get_builder_config,
28-
unit_plutus_data,
2928
},
3029
governance::GovernanceData,
3130
multisig::{MultiSigSmartContractResult, submit_or_create_tx_to_sign},
@@ -39,7 +38,7 @@ use ogmios_client::{
3938
transactions::Transactions,
4039
types::OgmiosUtxo,
4140
};
42-
use partner_chains_plutus_data::reserve::ReserveRedeemer;
41+
use partner_chains_plutus_data::{bridge::TokenTransferDatumV1, reserve::ReserveRedeemer};
4342
use sidechain_domain::UtxoId;
4443

4544
/// Spends current UTXO at validator address to illiquid supply validator and burn reserve auth policy token, preventing further operations.
@@ -135,7 +134,7 @@ fn illiquid_supply_validator_output(
135134
if output_value.amount > 0 {
136135
let ma = output_value.token.to_multi_asset(output_value.amount)?;
137136
let amount_builder = tx_output_builder
138-
.with_plutus_data(&illiquid_supply_validator_redeemer())
137+
.with_plutus_data(&TokenTransferDatumV1::ReserveTransfer.into())
139138
.next()?;
140139
amount_builder.with_minimum_ada_and_asset(&ma, ctx)?.build()
141140
} else {
@@ -145,7 +144,3 @@ fn illiquid_supply_validator_output(
145144
amount_builder.with_minimum_ada(ctx)?.build()
146145
}
147146
}
148-
149-
fn illiquid_supply_validator_redeemer() -> PlutusData {
150-
unit_plutus_data()
151-
}

toolkit/smart-contracts/offchain/src/reserve/init.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
//! * a script reference input of the current Goveranance UTXO
1515
//! * signature of the current goveranance
1616
17+
use crate::plutus_script;
1718
use crate::{
1819
await_tx::AwaitTx,
1920
cardano_keys::CardanoPaymentSigningKey,
@@ -38,7 +39,8 @@ use ogmios_client::{
3839
};
3940
use partner_chains_plutus_data::version_oracle::VersionOracleDatum;
4041
use raw_scripts::{
41-
ILLIQUID_CIRCULATION_SUPPLY_VALIDATOR, RESERVE_AUTH_POLICY, RESERVE_VALIDATOR, ScriptId,
42+
ILLIQUID_CIRCULATION_SUPPLY_AUTHORITY_TOKEN_POLICY, ILLIQUID_CIRCULATION_SUPPLY_VALIDATOR,
43+
RESERVE_AUTH_POLICY, RESERVE_VALIDATOR, ScriptId,
4244
};
4345
use sidechain_domain::UtxoId;
4446

@@ -71,10 +73,23 @@ pub async fn init_reserve_management<
7173
ILLIQUID_CIRCULATION_SUPPLY_VALIDATOR.0.to_vec(),
7274
ScriptId::IlliquidCirculationSupplyValidator,
7375
);
76+
let ics_auth_token_policy = ScriptData::new(
77+
"Illiquid Circulation Supply Auth Token Policy",
78+
plutus_script![
79+
ILLIQUID_CIRCULATION_SUPPLY_AUTHORITY_TOKEN_POLICY,
80+
ScriptId::IlliquidCirculationSupplyAuthorityTokenPolicy
81+
]?
82+
.bytes
83+
.to_vec(),
84+
ScriptId::IlliquidCirculationSupplyAuthorityTokenPolicy,
85+
);
86+
7487
Ok(vec![
7588
initialize_script(reserve_validator, genesis_utxo, payment_key, client, await_tx).await?,
7689
initialize_script(reserve_policy, genesis_utxo, payment_key, client, await_tx).await?,
7790
initialize_script(ics_validator, genesis_utxo, payment_key, client, await_tx).await?,
91+
initialize_script(ics_auth_token_policy, genesis_utxo, payment_key, client, await_tx)
92+
.await?,
7893
]
7994
.into_iter()
8095
.flatten()

toolkit/smart-contracts/offchain/src/reserve/mod.rs

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use crate::{
66
};
77
use anyhow::anyhow;
88
use cardano_serialization_lib::{
9-
ExUnits, JsError, PlutusScriptSource, PlutusWitness, Redeemer, RedeemerTag, TxInputsBuilder,
9+
BigInt, ExUnits, JsError, PlutusData, PlutusScriptSource, PlutusWitness, Redeemer, RedeemerTag,
10+
TxInputsBuilder,
1011
};
1112
use init::find_script_utxo;
1213
use ogmios_client::{query_ledger_state::QueryLedgerState, types::OgmiosUtxo};
@@ -26,6 +27,7 @@ pub(crate) struct ReserveData {
2627
pub(crate) auth_policy_version_utxo: OgmiosUtxo,
2728
pub(crate) validator_version_utxo: OgmiosUtxo,
2829
pub(crate) illiquid_circulation_supply_validator_version_utxo: OgmiosUtxo,
30+
pub(crate) illiquid_circulation_supply_authority_token_policy_version_utxo: OgmiosUtxo,
2931
}
3032

3133
#[derive(Clone, Debug)]
@@ -75,12 +77,23 @@ impl ReserveData {
7577
.ok_or_else(|| {
7678
anyhow!("Illiquid Circulation Supply Validator Version Utxo not found, is the Reserve Token Management initialized?")
7779
})?;
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+
})?;
7890
let scripts = scripts_data::reserve_scripts(genesis_utxo, ctx.network)?;
7991
Ok(ReserveData {
8092
scripts,
8193
auth_policy_version_utxo,
8294
validator_version_utxo,
8395
illiquid_circulation_supply_validator_version_utxo,
96+
illiquid_circulation_supply_authority_token_policy_version_utxo,
8497
})
8598
}
8699

@@ -113,6 +126,33 @@ impl ReserveData {
113126

114127
Ok(ReserveUtxo { utxo: reserve_utxo, datum: reserve_settings })
115128
}
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+
}
116156
}
117157

118158
pub(crate) struct TokenAmount {
@@ -127,6 +167,23 @@ pub(crate) fn reserve_utxo_input_with_validator_script_reference(
127167
cost: &ExUnits,
128168
) -> Result<TxInputsBuilder, JsError> {
129169
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+
180+
pub(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> {
130187
let input = reserve_utxo.to_csl_tx_input();
131188
let amount = reserve_utxo.value.to_csl()?;
132189
let script = &reserve.scripts.validator;
@@ -140,5 +197,32 @@ pub(crate) fn reserve_utxo_input_with_validator_script_reference(
140197
&Redeemer::new(&RedeemerTag::new_spend(), &0u32.into(), &redeemer.into(), cost),
141198
);
142199
inputs.add_plutus_script_input(&witness, &input, &amount);
143-
Ok(inputs)
200+
Ok(())
201+
}
202+
203+
pub(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(())
144228
}

0 commit comments

Comments
 (0)