From 852da3b5c7c09ab62170f6cfeae2b117dd2619ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20Ludue=C3=B1a?= Date: Thu, 5 Feb 2026 10:23:12 -0300 Subject: [PATCH] feat: reference inputs handled as inputs --- crates/tx3-cardano/src/compile/mod.rs | 4 +- crates/tx3-lang/src/analyzing.rs | 17 +- crates/tx3-lang/src/ast.rs | 9 + crates/tx3-lang/src/lowering.rs | 29 +- crates/tx3-lang/src/parsing.rs | 36 ++- crates/tx3-lang/src/tx3.pest | 9 +- crates/tx3-resolver/src/inputs/narrow.rs | 2 +- crates/tx3-tir/src/model/v1beta0.rs | 68 ++++- crates/tx3-tir/src/reduce/mod.rs | 51 ++++ examples/buidler_fest_2026.buy_ticket.tir | 13 +- examples/env_vars.mint_from_env.tir | 13 +- examples/lang_tour.my_tx.tir | 23 +- examples/reference_input_datum.ast | 355 ++++++++++++++++++++++ examples/reference_input_datum.swap.tir | 257 ++++++++++++++++ examples/reference_input_datum.tx3 | 23 ++ 15 files changed, 872 insertions(+), 37 deletions(-) create mode 100644 examples/reference_input_datum.ast create mode 100644 examples/reference_input_datum.swap.tir create mode 100644 examples/reference_input_datum.tx3 diff --git a/crates/tx3-cardano/src/compile/mod.rs b/crates/tx3-cardano/src/compile/mod.rs index 7b8a86e0..ad02818f 100644 --- a/crates/tx3-cardano/src/compile/mod.rs +++ b/crates/tx3-cardano/src/compile/mod.rs @@ -434,7 +434,9 @@ fn compile_reference_inputs(tx: &tir::Tx) -> Result, _>>()? + .into_iter() .flatten() .map(|x| primitives::TransactionInput { transaction_id: x.txid.as_slice().into(), diff --git a/crates/tx3-lang/src/analyzing.rs b/crates/tx3-lang/src/analyzing.rs index 55e50b02..bdb3d13c 100644 --- a/crates/tx3-lang/src/analyzing.rs +++ b/crates/tx3-lang/src/analyzing.rs @@ -172,6 +172,7 @@ impl Error { Symbol::Function(name) => format!("Function({})", name), Symbol::LocalExpr(_) => "LocalExpr".to_string(), Symbol::Input(_) => "Input".to_string(), + Symbol::Reference(_) => "Reference".to_string(), Symbol::Output(_) => "Output".to_string(), Symbol::Fees => "Fees".to_string(), } @@ -387,6 +388,11 @@ impl Scope { .insert(name.to_string(), Symbol::Input(Box::new(input))); } + pub fn track_reference(&mut self, name: &str, reference: ReferenceBlock) { + self.symbols + .insert(name.to_string(), Symbol::Reference(Box::new(reference))); + } + pub fn track_output(&mut self, index: usize, output: OutputBlock) { if let Some(n) = output.name { self.symbols.insert(n.value, Symbol::Output(index)); @@ -1140,11 +1146,13 @@ impl Analyzable for SignersBlock { impl Analyzable for ReferenceBlock { fn analyze(&mut self, parent: Option>) -> AnalyzeReport { - self.r#ref.analyze(parent) + let ref_report = self.r#ref.analyze(parent.clone()); + let datum_report = self.datum_is.analyze(parent); + ref_report + datum_report } fn is_resolved(&self) -> bool { - self.r#ref.is_resolved() + self.r#ref.is_resolved() && self.datum_is.is_resolved() } } @@ -1243,6 +1251,10 @@ impl TxDef { scope.track_input(&input.name, input.clone()) } + for reference in self.references.iter() { + scope.track_reference(&reference.name, reference.clone()); + } + for (index, output) in self.outputs.iter().enumerate() { scope.track_output(index, output.clone()) } @@ -1250,6 +1262,7 @@ impl TxDef { let scope_snapshot = Rc::new(scope); let _ = self.locals.analyze(Some(scope_snapshot.clone())); let _ = self.inputs.analyze(Some(scope_snapshot.clone())); + let _ = self.references.analyze(Some(scope_snapshot.clone())); let _ = self.outputs.analyze(Some(scope_snapshot.clone())); Scope::new(Some(scope_snapshot)) diff --git a/crates/tx3-lang/src/ast.rs b/crates/tx3-lang/src/ast.rs index 54578928..d58296eb 100644 --- a/crates/tx3-lang/src/ast.rs +++ b/crates/tx3-lang/src/ast.rs @@ -24,6 +24,7 @@ pub enum Symbol { LocalExpr(Box), Output(usize), Input(Box), + Reference(Box), PartyDef(Box), PolicyDef(Box), AssetDef(Box), @@ -124,6 +125,7 @@ impl Symbol { Symbol::ParamVar(_, ty) => Some(ty.as_ref().clone()), Symbol::RecordField(x) => Some(x.r#type.clone()), Symbol::Input(x) => x.datum_is().cloned(), + Symbol::Reference(x) => x.datum_is().cloned(), x => { dbg!(x); None @@ -347,9 +349,16 @@ impl InputBlockField { pub struct ReferenceBlock { pub name: String, pub r#ref: DataExpr, + pub datum_is: Option, pub span: Span, } +impl ReferenceBlock { + pub(crate) fn datum_is(&self) -> Option<&Type> { + self.datum_is.as_ref() + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct MetadataBlockField { pub key: DataExpr, diff --git a/crates/tx3-lang/src/lowering.rs b/crates/tx3-lang/src/lowering.rs index a49dfe79..43f6c698 100644 --- a/crates/tx3-lang/src/lowering.rs +++ b/crates/tx3-lang/src/lowering.rs @@ -193,6 +193,27 @@ impl IntoLower for ast::Identifier { Ok(out) } + ast::Symbol::Reference(ref_block) => { + let ref_expr = ref_block.r#ref.into_lower(ctx)?; + let query = ir::InputQuery { + address: ir::Expression::None, + min_amount: ir::Expression::None, + r#ref: ref_expr, + many: false, + collateral: false, + }; + let inner = + ir::Param::ExpectInput(ref_block.name.to_lowercase().clone(), query).into(); + let out = if ctx.is_asset_expr() { + ir::Coerce::IntoAssets(inner).into() + } else if ctx.is_datum_expr() { + ir::Coerce::IntoDatum(inner).into() + } else { + inner + }; + + Ok(out) + } ast::Symbol::Fees => Ok(ir::Param::ExpectFees.into()), ast::Symbol::EnvVar(n, ty) => { Ok(ir::Param::ExpectValue(n.to_lowercase().clone(), ty.into_lower(ctx)?).into()) @@ -786,10 +807,13 @@ impl IntoLower for ast::ChainSpecificBlock { } impl IntoLower for ast::ReferenceBlock { - type Output = ir::Expression; + type Output = ir::Reference; fn into_lower(&self, ctx: &Context) -> Result { - self.r#ref.into_lower(ctx) + Ok(ir::Reference { + name: self.name.to_lowercase().clone(), + utxos: self.r#ref.into_lower(ctx)?, + }) } } @@ -1018,6 +1042,7 @@ mod tests { test_lowering!(cardano_witness); test_lowering!(reference_script); + test_lowering!(reference_input_datum); test_lowering!(withdrawal); test_lowering!(map); diff --git a/crates/tx3-lang/src/parsing.rs b/crates/tx3-lang/src/parsing.rs index 2b34fc1c..bbde5e0f 100644 --- a/crates/tx3-lang/src/parsing.rs +++ b/crates/tx3-lang/src/parsing.rs @@ -355,20 +355,39 @@ impl AstNode for ReferenceBlock { const RULE: Rule = Rule::reference_block; fn parse(pair: Pair) -> Result { - let span = pair.as_span().into(); + let span: Span = pair.as_span().into(); let mut inner = pair.into_inner(); let name = inner.next().unwrap().as_str().to_string(); - let pair = inner.next().unwrap(); - match pair.as_rule() { - Rule::input_block_ref => { - let pair = pair.into_inner().next().unwrap(); - let r#ref = DataExpr::parse(pair)?; - Ok(ReferenceBlock { name, r#ref, span }) + let mut r#ref = None; + let mut datum_is = None; + for field in inner { + match field.as_rule() { + Rule::input_block_ref => { + let pair = field.into_inner().next().unwrap(); + r#ref = Some(DataExpr::parse(pair)?); + } + Rule::input_block_datum_is => { + let pair = field.into_inner().next().unwrap(); + datum_is = Some(Type::parse(pair)?); + } + x => unreachable!("Unexpected rule in reference_block: {:?}", x), } - x => unreachable!("Unexpected rule in ref_input_block: {:?}", x), } + + let r#ref = r#ref.ok_or_else(|| Error { + message: "reference block requires 'ref' field".to_string(), + src: String::new(), + span: span.clone(), + })?; + + Ok(ReferenceBlock { + name, + r#ref, + datum_is, + span, + }) } fn span(&self) -> &Span { @@ -2809,6 +2828,7 @@ mod tests { test_parsing!(cardano_witness); test_parsing!(reference_script); + test_parsing!(reference_input_datum); test_parsing!(map); test_parsing!(burn); diff --git a/crates/tx3-lang/src/tx3.pest b/crates/tx3-lang/src/tx3.pest index a01471ff..9e5e89ca 100644 --- a/crates/tx3-lang/src/tx3.pest +++ b/crates/tx3-lang/src/tx3.pest @@ -238,9 +238,14 @@ collateral_block = { "}" } +reference_block_field = _{ + input_block_ref | + input_block_datum_is +} + reference_block = { - "reference" ~ identifier ~ ("*")? ~ "{" ~ - input_block_ref ~ "," ~ + "reference" ~ identifier ~ "{" ~ + (reference_block_field ~ ",")* ~ "}" } diff --git a/crates/tx3-resolver/src/inputs/narrow.rs b/crates/tx3-resolver/src/inputs/narrow.rs index 48f32797..9b085fce 100644 --- a/crates/tx3-resolver/src/inputs/narrow.rs +++ b/crates/tx3-resolver/src/inputs/narrow.rs @@ -83,7 +83,7 @@ pub struct SearchSpace { } impl SearchSpace { - fn new() -> Self { + pub fn new() -> Self { Self { union: Subset::NotSet, intersection: Subset::NotSet, diff --git a/crates/tx3-tir/src/model/v1beta0.rs b/crates/tx3-tir/src/model/v1beta0.rs index 1d0ad338..e93d0a00 100644 --- a/crates/tx3-tir/src/model/v1beta0.rs +++ b/crates/tx3-tir/src/model/v1beta0.rs @@ -8,8 +8,10 @@ //! [`lower`](crate::lower) for lowering an AST to the intermediate //! representation. +use serde::de::{self, Deserializer, SeqAccess, Visitor as SerdeVisitor}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; +use std::fmt; use crate::{ encoding::{TirRoot, TirVersion}, @@ -292,6 +294,12 @@ pub struct InputQuery { pub collateral: bool, } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct Reference { + pub name: String, + pub utxos: Expression, +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct Input { pub name: String, @@ -335,10 +343,57 @@ pub struct Signers { pub signers: Vec, } +/// Legacy TIR stored references as Vec. We accept both formats when deserializing. +#[derive(Deserialize)] +#[serde(untagged)] +enum ReferenceOrLegacy { + New(Reference), + Legacy(Expression), +} + +fn deserialize_references<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct ReferencesVisitor; + + impl<'de> SerdeVisitor<'de> for ReferencesVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("array of Reference or legacy array of Expression") + } + + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut refs = Vec::new(); + let mut index = 0usize; + while let Some(item) = seq.next_element::()? { + match item { + ReferenceOrLegacy::New(r) => refs.push(r), + ReferenceOrLegacy::Legacy(utxos) => { + refs.push(Reference { + name: format!("ref_{}", index), + utxos, + }); + } + } + index += 1; + } + Ok(refs) + } + } + + deserializer.deserialize_seq(ReferencesVisitor) +} + #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Tx { pub fees: Expression, - pub references: Vec, + #[serde(deserialize_with = "deserialize_references")] + pub references: Vec, pub inputs: Vec, pub outputs: Vec, pub validity: Option, @@ -416,6 +471,17 @@ impl Node for InputQuery { } } +impl Node for Reference { + fn apply(self, visitor: &mut V) -> Result { + let visited = Self { + utxos: self.utxos.apply(visitor)?, + ..self + }; + + Ok(visited) + } +} + impl Node for Param { fn apply(self, visitor: &mut V) -> Result { let visited = match self { diff --git a/crates/tx3-tir/src/reduce/mod.rs b/crates/tx3-tir/src/reduce/mod.rs index eb60cf4c..6a646001 100644 --- a/crates/tx3-tir/src/reduce/mod.rs +++ b/crates/tx3-tir/src/reduce/mod.rs @@ -756,6 +756,57 @@ impl Composite for Input { } } +impl Apply for Reference { + fn apply_args(self, args: &BTreeMap) -> Result { + Ok(Self { + utxos: self.utxos.apply_args(args)?, + ..self + }) + } + + fn apply_inputs(self, args: &BTreeMap>) -> Result { + Ok(Self { + utxos: self.utxos.apply_inputs(args)?, + ..self + }) + } + + fn apply_fees(self, fees: u64) -> Result { + Ok(Self { + utxos: self.utxos.apply_fees(fees)?, + ..self + }) + } + + fn is_constant(&self) -> bool { + self.utxos.is_constant() + } + + fn params(&self) -> BTreeMap { + self.utxos.params() + } + + fn queries(&self) -> BTreeMap { + BTreeMap::from([( + self.name.clone(), + InputQuery { + address: Expression::None, + min_amount: Expression::None, + r#ref: self.utxos.clone(), + many: false, + collateral: false, + }, + )]) + } + + fn reduce(self) -> Result { + Ok(Self { + utxos: self.utxos.reduce()?, + ..self + }) + } +} + impl Composite for InputQuery { fn components(&self) -> Vec<&Expression> { vec![&self.address, &self.min_amount, &self.r#ref] diff --git a/examples/buidler_fest_2026.buy_ticket.tir b/examples/buidler_fest_2026.buy_ticket.tir index 28354443..8d504ab0 100644 --- a/examples/buidler_fest_2026.buy_ticket.tir +++ b/examples/buidler_fest_2026.buy_ticket.tir @@ -4,11 +4,14 @@ }, "references": [ { - "EvalParam": { - "ExpectValue": [ - "issuer_script_ref", - "UtxoRef" - ] + "name": "issuer_script", + "utxos": { + "EvalParam": { + "ExpectValue": [ + "issuer_script_ref", + "UtxoRef" + ] + } } } ], diff --git a/examples/env_vars.mint_from_env.tir b/examples/env_vars.mint_from_env.tir index b2b72adb..be48ceb7 100644 --- a/examples/env_vars.mint_from_env.tir +++ b/examples/env_vars.mint_from_env.tir @@ -4,11 +4,14 @@ }, "references": [ { - "EvalParam": { - "ExpectValue": [ - "mint_script", - "UtxoRef" - ] + "name": "myscript", + "utxos": { + "EvalParam": { + "ExpectValue": [ + "mint_script", + "UtxoRef" + ] + } } } ], diff --git a/examples/lang_tour.my_tx.tir b/examples/lang_tour.my_tx.tir index 525dcb75..348a841d 100644 --- a/examples/lang_tour.my_tx.tir +++ b/examples/lang_tour.my_tx.tir @@ -4,16 +4,19 @@ }, "references": [ { - "UtxoRefs": [ - { - "txid": [ - 171, - 205, - 239 - ], - "index": 2 - } - ] + "name": "ref_block", + "utxos": { + "UtxoRefs": [ + { + "txid": [ + 171, + 205, + 239 + ], + "index": 2 + } + ] + } } ], "inputs": [ diff --git a/examples/reference_input_datum.ast b/examples/reference_input_datum.ast new file mode 100644 index 00000000..57c2e32d --- /dev/null +++ b/examples/reference_input_datum.ast @@ -0,0 +1,355 @@ +{ + "env": null, + "txs": [ + { + "name": { + "value": "swap", + "span": { + "dummy": false, + "start": 76, + "end": 80 + } + }, + "parameters": { + "parameters": [], + "span": { + "dummy": false, + "start": 80, + "end": 82 + } + }, + "locals": null, + "references": [ + { + "name": "pool", + "ref": { + "UtxoRef": { + "txid": [ + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52 + ], + "index": 0, + "span": { + "dummy": false, + "start": 119, + "end": 187 + } + } + }, + "datum_is": { + "Custom": { + "value": "PoolState", + "span": { + "dummy": true, + "start": 0, + "end": 0 + } + } + }, + "span": { + "dummy": false, + "start": 89, + "end": 223 + } + } + ], + "inputs": [ + { + "name": "user_input", + "many": false, + "fields": [ + { + "From": { + "Identifier": { + "value": "User", + "span": { + "dummy": false, + "start": 262, + "end": 266 + } + } + } + }, + { + "MinAmount": { + "FnCall": { + "callee": { + "value": "Ada", + "span": { + "dummy": false, + "start": 288, + "end": 291 + } + }, + "args": [ + { + "Number": 1000000 + } + ], + "span": { + "dummy": false, + "start": 288, + "end": 300 + } + } + } + } + ], + "span": { + "dummy": false, + "start": 229, + "end": 307 + } + } + ], + "outputs": [ + { + "name": null, + "optional": false, + "fields": [ + { + "To": { + "Identifier": { + "value": "User", + "span": { + "dummy": false, + "start": 334, + "end": 338 + } + } + } + }, + { + "Amount": { + "FnCall": { + "callee": { + "value": "Ada", + "span": { + "dummy": false, + "start": 356, + "end": 359 + } + }, + "args": [ + { + "AddOp": { + "lhs": { + "PropertyOp": { + "operand": { + "Identifier": { + "value": "pool", + "span": { + "dummy": false, + "start": 360, + "end": 364 + } + } + }, + "property": { + "Identifier": { + "value": "reserve_a", + "span": { + "dummy": false, + "start": 365, + "end": 374 + } + } + }, + "span": { + "dummy": false, + "start": 364, + "end": 374 + } + } + }, + "rhs": { + "PropertyOp": { + "operand": { + "Identifier": { + "value": "pool", + "span": { + "dummy": false, + "start": 377, + "end": 381 + } + } + }, + "property": { + "Identifier": { + "value": "reserve_b", + "span": { + "dummy": false, + "start": 382, + "end": 391 + } + } + }, + "span": { + "dummy": false, + "start": 381, + "end": 391 + } + } + }, + "span": { + "dummy": false, + "start": 375, + "end": 376 + } + } + } + ], + "span": { + "dummy": false, + "start": 356, + "end": 392 + } + } + } + } + ], + "span": { + "dummy": false, + "start": 313, + "end": 399 + } + } + ], + "validity": null, + "mints": [], + "burns": [], + "signers": null, + "adhoc": [], + "span": { + "dummy": false, + "start": 73, + "end": 401 + }, + "collateral": [], + "metadata": null + } + ], + "types": [ + { + "name": { + "value": "PoolState", + "span": { + "dummy": false, + "start": 5, + "end": 14 + } + }, + "cases": [ + { + "name": { + "value": "Default", + "span": { + "dummy": true, + "start": 0, + "end": 0 + } + }, + "fields": [ + { + "name": { + "value": "reserve_a", + "span": { + "dummy": false, + "start": 21, + "end": 30 + } + }, + "type": "Int", + "span": { + "dummy": false, + "start": 21, + "end": 35 + } + }, + { + "name": { + "value": "reserve_b", + "span": { + "dummy": false, + "start": 41, + "end": 50 + } + }, + "type": "Int", + "span": { + "dummy": false, + "start": 41, + "end": 55 + } + } + ], + "span": { + "dummy": false, + "start": 0, + "end": 58 + } + } + ], + "span": { + "dummy": false, + "start": 0, + "end": 58 + } + } + ], + "aliases": [], + "assets": [], + "parties": [ + { + "name": { + "value": "User", + "span": { + "dummy": false, + "start": 66, + "end": 70 + } + }, + "span": { + "dummy": false, + "start": 60, + "end": 71 + } + } + ], + "policies": [], + "span": { + "dummy": false, + "start": 0, + "end": 402 + } +} \ No newline at end of file diff --git a/examples/reference_input_datum.swap.tir b/examples/reference_input_datum.swap.tir new file mode 100644 index 00000000..63675cf5 --- /dev/null +++ b/examples/reference_input_datum.swap.tir @@ -0,0 +1,257 @@ +{ + "fees": { + "EvalParam": "ExpectFees" + }, + "references": [ + { + "name": "pool", + "utxos": { + "UtxoRefs": [ + { + "txid": [ + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52 + ], + "index": 0 + } + ] + } + } + ], + "inputs": [ + { + "name": "user_input", + "utxos": { + "EvalParam": { + "ExpectInput": [ + "user_input", + { + "address": { + "EvalParam": { + "ExpectValue": [ + "user", + "Address" + ] + } + }, + "min_amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "Number": 1000000 + } + } + ] + }, + "ref": "None", + "many": false, + "collateral": false + } + ] + } + }, + "redeemer": "None" + } + ], + "outputs": [ + { + "address": { + "EvalParam": { + "ExpectValue": [ + "user", + "Address" + ] + } + }, + "datum": "None", + "amount": { + "Assets": [ + { + "policy": "None", + "asset_name": "None", + "amount": { + "EvalBuiltIn": { + "Add": [ + { + "EvalBuiltIn": { + "Property": [ + { + "EvalCoerce": { + "IntoAssets": { + "EvalParam": { + "ExpectInput": [ + "pool", + { + "address": "None", + "min_amount": "None", + "ref": { + "UtxoRefs": [ + { + "txid": [ + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52 + ], + "index": 0 + } + ] + }, + "many": false, + "collateral": false + } + ] + } + } + } + }, + { + "Number": 0 + } + ] + } + }, + { + "EvalBuiltIn": { + "Property": [ + { + "EvalCoerce": { + "IntoAssets": { + "EvalParam": { + "ExpectInput": [ + "pool", + { + "address": "None", + "min_amount": "None", + "ref": { + "UtxoRefs": [ + { + "txid": [ + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52, + 86, + 120, + 144, + 18, + 52 + ], + "index": 0 + } + ] + }, + "many": false, + "collateral": false + } + ] + } + } + } + }, + { + "Number": 1 + } + ] + } + } + ] + } + } + } + ] + }, + "optional": false + } + ], + "validity": null, + "mints": [], + "burns": [], + "adhoc": [], + "collateral": [], + "signers": null, + "metadata": [] +} \ No newline at end of file diff --git a/examples/reference_input_datum.tx3 b/examples/reference_input_datum.tx3 new file mode 100644 index 00000000..6291240a --- /dev/null +++ b/examples/reference_input_datum.tx3 @@ -0,0 +1,23 @@ +type PoolState { + reserve_a: Int, + reserve_b: Int, +} + +party User; + +tx swap() { + reference pool { + ref: 0x1234567890123456789012345678901234567890123456789012345678901234#0, + datum_is: PoolState, + } + + input user_input { + from: User, + min_amount: Ada(1000000), + } + + output { + to: User, + amount: Ada(pool.reserve_a + pool.reserve_b), + } +}