diff --git a/Cargo.lock b/Cargo.lock index adee592..76bcf7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -229,6 +229,47 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cardano-connect" +version = "0.0.0" +dependencies = [ + "anyhow", + "cardano-tx-builder", +] + +[[package]] +name = "cardano-connect-blockfrost" +version = "0.0.0" +dependencies = [ + "anyhow", + "blockfrost", + "blockfrost-openapi", + "cardano-connect", + "cardano-tx-builder", + "hex", + "minicbor", + "reqwest", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "cardano-tx-builder" +version = "0.0.0" +dependencies = [ + "anyhow", + "bech32 0.11.0", + "cryptoxide 0.5.1", + "hex", + "minicbor", + "pallas-addresses", + "pallas-codec", + "pallas-crypto", + "pallas-primitives", + "uplc", +] + [[package]] name = "cc" version = "1.2.38" @@ -1206,6 +1247,17 @@ dependencies = [ "uplc", ] +[[package]] +name = "konduit-tx" +version = "0.0.0" +dependencies = [ + "anyhow", + "cardano-tx-builder", + "hex", + "minicbor", + "rand", +] + [[package]] name = "libc" version = "0.2.176" diff --git a/Cargo.toml b/Cargo.toml index 6a71ba5..d0a5d03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,21 @@ -[package] -name = "konduit-cli" +[workspace] +members = ["crates/*"] +resolver = "2" + +[workspace.package] +description = "Konduit-tools" +documentation = "https://github.com/cardano-lightning/konduit" version = "0.0.0" -edition = "2021" -publish = false -rust-version = "1.86.0" -# license = "MIT" -- set to apache +edition = "2024" +repository = "https://github.com/cardano-lightning/konduit" +homepage = "https://github.com/cardano-lightning/konduit" +# license = "Apache-2.0" // TBC +authors = [ + "waalge", + "KtorZ ", +] +rust-version = "1.90.0" -[package.metadata.release] -release = false - -[dependencies] -anyhow = "1.0.100" -bech32 = "0.11.0" -blockfrost = "1.1.0" -blockfrost-openapi = "0.1.75" -clap = { version = "4.5.18", features = ["cargo", "derive"] } -cryptoxide = "0.5.1" -hex = "0.4.3" -minicbor = { version = "0.25.1", features = ["alloc", "derive"] } -pallas-addresses = "0.33.0" -pallas-crypto = "0.33.0" -pallas-codec = "0.33.0" -pallas-primitives = "0.33.0" -rand = "0.9.2" -reqwest = { version = "0.12.23", features = ["json"] } -serde = { version = "1.0.213", features = ["derive"] } -serde_json = "1.0.138" -tokio = { version = "1.47.1", features = ["full"] } -uplc = "1.1.19" +[workspace.metadata.release] +shared-version = true +tag-name = "v{{version}}" diff --git a/README.md b/README.md index ecc3bc3..a98e7ef 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -# Konduit cli +# Konduit tools -> A first stab at the konduit cli +> A collection to crates to run konduit in the wild ## Workspace -- [ ] cardano-tx-builder - Pure cardano transaction builder utils -- [ ] cardano-connect - Traits for cardano connector -- [ ] cardano-connect-blockfrost - Impl for blockfrost -- [ ] konduit-core - Pure konduit transaction builders. +- cardano-tx-builder - Pure cardano transaction builder utils +- cardano-connect - Traits for cardano connector +- cardano-connect-blockfrost - Impl for blockfrost +- konduit-core - Pure konduit transaction builders. Depends only on cardano-tx-builder. - - [ ] test round trip data - - [ ] compiles to wasm -- [ ] konduit-cli - cli wrapping of core + - test round trip data + - compiles to wasm +- konduit-cli - cli wrapping of core ## TODOs diff --git a/crates/cardano-connect-blockfrost/Cargo.toml b/crates/cardano-connect-blockfrost/Cargo.toml new file mode 100644 index 0000000..2737209 --- /dev/null +++ b/crates/cardano-connect-blockfrost/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "cardano-connect-blockfrost" +version.workspace = true +edition.workspace = true +description.workspace = true +# license.workspace = true // TBC +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +rust-version.workspace = true + +[package.metadata.release] +release = false + +[dependencies] +anyhow = "1.0.100" +blockfrost = "1.1.0" +blockfrost-openapi = "0.1.75" +hex = "0.4.3" +minicbor = { version = "0.25.1", features = ["alloc", "derive"] } +reqwest = { version = "0.12.23", features = ["json"] } +serde = { version = "1.0.213", features = ["derive"] } +serde_json = "1.0.138" +tokio = { version = "1.47.1", features = ["full"] } +cardano-tx-builder = { path = "../cardano-tx-builder" } +cardano-connect = { path = "../cardano-connect" } diff --git a/crates/cardano-connect-blockfrost/README.md b/crates/cardano-connect-blockfrost/README.md new file mode 100644 index 0000000..53bede4 --- /dev/null +++ b/crates/cardano-connect-blockfrost/README.md @@ -0,0 +1,3 @@ +# Cardano connect blockfrost + +> An implementation of Cardano connect for blockfrost diff --git a/crates/cardano-connect-blockfrost/src/lib.rs b/crates/cardano-connect-blockfrost/src/lib.rs new file mode 100644 index 0000000..eb3f019 --- /dev/null +++ b/crates/cardano-connect-blockfrost/src/lib.rs @@ -0,0 +1,337 @@ +// ORIGINAL SOURCE : https://github.com/CardanoSolutions/zhuli/blob/main/cli/src/cardano.rs + +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use std::collections::{BTreeMap, HashMap}; + +use anyhow::{Result, anyhow}; + +use blockfrost::{BlockfrostAPI, Pagination}; +use blockfrost_openapi::models::tx_content_output_amount_inner::TxContentOutputAmountInner; +use cardano_connect::CardanoConnect; +use cardano_tx_builder::{ + Address, BuildParameters, Credential, DatumOrHash, Input, Network, Output, PlutusData, Utxo, + Value, +}; + +pub struct Config { + pub project_id: String, +} + +impl Config { + pub fn from_env(env: &HashMap) -> Self { + let project_id = env + .get("project_id") + .expect("Blockfrost requires `project_id`") + .clone(); + Self { project_id } + } +} + +pub struct Blockfrost { + api: BlockfrostAPI, + base_url: String, + client: reqwest::Client, + network: Network, + project_id: String, +} + +const UNIT_LOVELACE: &str = "lovelace"; + +const MAINNET_PREFIX: &str = "mainnet"; +const PREPROD_PREFIX: &str = "preprod"; +const PREVIEW_PREFIX: &str = "preview"; + +#[derive(Debug)] +pub struct ProtocolParameters { + pub collateral_percent: f64, + pub cost_model_v3: Vec, + pub drep_deposit: u64, + pub fee_constant: u64, + pub fee_coefficient: u64, + pub min_utxo_deposit_coefficient: u64, + pub price_mem: f64, + pub price_steps: f64, +} + +impl From<&ProtocolParameters> for BuildParameters { + fn from(params: &ProtocolParameters) -> BuildParameters { + BuildParameters { + fee_constant: params.fee_constant, + fee_coefficient: params.fee_coefficient, + price_mem: params.price_mem, + price_steps: params.price_steps, + } + } +} + +impl Blockfrost { + pub fn new(project_id: String) -> Self { + let network_prefix = if project_id.starts_with(MAINNET_PREFIX) { + MAINNET_PREFIX.to_string() + } else if project_id.starts_with(PREPROD_PREFIX) { + PREPROD_PREFIX.to_string() + } else if project_id.starts_with(PREVIEW_PREFIX) { + PREVIEW_PREFIX.to_string() + } else { + panic!("unexpected project id prefix") + }; + let base_url = format!("https://cardano-{}.blockfrost.io/api/v0", network_prefix,); + let api = BlockfrostAPI::new(project_id.as_str(), Default::default()); + Self { + api, + base_url, + client: reqwest::Client::new(), + network: if project_id.starts_with(MAINNET_PREFIX) { + Network::Mainnet + } else { + Network::Testnet + }, + project_id, + } + } + + pub async fn plutus_data_from_hash(&self, datum_hash: &str) -> Result { + let x = self.api.scripts_datum_hash_cbor(datum_hash).await?; + let data = x + .as_object() + .expect("Expect an object") + .get("cbor") + .expect("Expect key `cbor`") + .as_str() + .expect("Expect value to be string"); + plutus_data_from_inline(&data) + } + + pub async fn resolve_datum( + &self, + datum_hash: &Option, + inline_datum: &Option, + ) -> Result> { + if let Some(inline_datum) = inline_datum { + Ok(Some(DatumOrHash::Data(plutus_data_from_inline( + inline_datum, + )?))) + } else { + if let Some(datum_hash) = datum_hash { + Ok(Some(DatumOrHash::Data( + self.plutus_data_from_hash(&datum_hash).await?, + ))) + } else { + Ok(None) + } + } + } + + /// Blockfrost client has the wrong type. + pub async fn scripts_hash_cbor(&self, script_hash: &str) -> Result> { + let response = self + .client + .get(&format!("{}/scripts/{}", self.base_url, script_hash)) + .header("Accept", "application/json") + .header("project_id", self.project_id.as_str()) + .send() + .await + .unwrap(); + + match response.status() { + reqwest::StatusCode::OK => { + let ResponseCbor { cbor } = response.json::().await.unwrap(); + let bytes = hex::decode(cbor)?; + Ok(bytes) + } + _ => Err(anyhow!("No script found")), + } + } + + // /// Blockfrost client has incomplete type + // pub async fn script_type(&self, script_hash : &str) -> Result{ + // let response = self + // .client + // .get(&format!( "{}/scripts/{}/cbor", self.base_url, script_hash)) + // .header("Accept", "application/json") + // .header("project_id", self.project_id.as_str()) + // .send() + // .await + // .unwrap(); + + // match response.status() { + // reqwest::StatusCode::OK => { + // let ResponseScript { plutus_type,.. } = response.json::().await.unwrap(); + // } + // _ => Err(anyhow!("No script found")), + // } + // } +} + +impl CardanoConnect for Blockfrost { + async fn build_parameters(&self) -> BuildParameters { + let params = self + .api + .epochs_latest_parameters() + .await + .expect("failed to fetch protocol parameters"); + + let pp = ProtocolParameters { + collateral_percent: (params + .collateral_percent + .expect("protocol parameters are missing collateral percent") + as f64) + / 1e2, + // NOTE: Blockfrost returns cost models out of order. They must be ordered by their + // "ParamName" according to how Plutus defines it, but they are usually found ordered + // by ascending keys, unfortunately. Given that they are unlikely to change anytime + // soon, I am going to bundle them as-is. + cost_model_v3: vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, + 769, 4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, + 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, + 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, + 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, + 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, + 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, + 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, + 962335, 18, 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, + 18, 52948122, 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, + 36, 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, + 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, + 0, 1, + ], + drep_deposit: 500_000_000, // NOTE: Missing from Blockfrost + fee_constant: params.min_fee_b as u64, + fee_coefficient: params.min_fee_a as u64, + min_utxo_deposit_coefficient: params + .coins_per_utxo_size + .expect("protocol parameters are missing min utxo deposit coefficient") + .parse() + .unwrap(), + price_mem: params + .price_mem + .expect("protocol parameters are missing price mem") as f64, + price_steps: params + .price_step + .expect("protocol parameters are missing price step") + as f64, + }; + (&pp).into() + } + + async fn utxos_at( + &self, + payment: &Credential, + delegation: &Option, + ) -> Result> { + let addr = Address::new(self.network_id(), payment.clone(), delegation.clone()); + let response = self + .api + .addresses_utxos(&addr.to_bech32()?, Pagination::all()) + .await?; + response + .iter() + .map(|o| { + // FIXME: This should pull the datum and script reference + let datum = None; + let script_ref = None; + Ok(Utxo { + input: Input { + transaction_id: v2a(hex::decode(&o.tx_hash)?)?.into(), + index: o.tx_index as u64, + }, + output: Output { + address: Address::from_bech32(&o.address).into(), + value: from_tx_content_output_amounts(&o.amount[..]), + datum, + script_ref, + }, + }) + }) + .collect() + } + + async fn submit(&self, tx: Vec) -> Result { + let tx_hash = self.api.transactions_submit(tx).await?; + Ok(tx_hash) + } + + fn network(&self) -> Network { + self.network + } + + fn health(&self) -> impl std::future::Future> + Send { + todo!() + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct ResponseCbor { + cbor: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug)] +struct ResponseScript { + script_hash: String, + #[serde(rename = "type")] + plutus_type: String, + serialised_size: u64, +} + +fn from_tx_content_output_amounts(xs: &[TxContentOutputAmountInner]) -> Value { + /// FIXME : Using the sane version of value + let mut lovelaces = 0; + let mut assets = BTreeMap::new(); + + for asset in xs { + let quantity: u64 = asset.quantity.parse().unwrap(); + if asset.unit == UNIT_LOVELACE { + lovelaces += quantity; + } else { + let policy_id: PolicyId = asset.unit[0..56].parse().unwrap(); + let asset_name: AssetName = hex::decode(&asset.unit[56..]).unwrap().into(); + assets + .entry(policy_id) + .and_modify(|m: &mut BTreeMap| { + m.entry(asset_name.clone()) + .and_modify(|q| *q += quantity) + .or_insert(quantity); + }) + .or_insert_with(|| BTreeMap::from([(asset_name, quantity)])); + } + } + + if assets.is_empty() { + Value::Coin(lovelaces) + } else { + Value::Multiasset( + lovelaces, + NonEmptyKeyValuePairs::Def( + assets + .into_iter() + .map(|(policy_id, policies)| { + ( + policy_id, + NonEmptyKeyValuePairs::Def( + policies + .into_iter() + .map(|(asset_name, quantity)| { + (asset_name, quantity.try_into().unwrap()) + }) + .collect::>(), + ), + ) + }) + .collect::>(), + ), + ) + } +} + +pub fn plutus_data_from_inline(inline_datum: &str) -> Result { + Ok(minicbor::decode(&hex::decode(inline_datum)?)?) +} diff --git a/crates/cardano-connect/Cargo.toml b/crates/cardano-connect/Cargo.toml new file mode 100644 index 0000000..b723a4d --- /dev/null +++ b/crates/cardano-connect/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "cardano-connect" +version.workspace = true +edition.workspace = true +description.workspace = true +# license.workspace = true // TBC +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +rust-version.workspace = true + +[package.metadata.release] +release = false + +[dependencies] +anyhow = "1.0.100" +cardano-tx-builder = { path = "../cardano-tx-builder" } diff --git a/crates/cardano-connect/README.md b/crates/cardano-connect/README.md new file mode 100644 index 0000000..f17a718 --- /dev/null +++ b/crates/cardano-connect/README.md @@ -0,0 +1,5 @@ +# Cardano connect + +> No frills interface to Cardano + +Just enough to get konduit running diff --git a/src/cardano.rs b/crates/cardano-connect/cardano.rs similarity index 100% rename from src/cardano.rs rename to crates/cardano-connect/cardano.rs diff --git a/src/cardano/blockfrost.rs b/crates/cardano-connect/cardano/blockfrost.rs similarity index 100% rename from src/cardano/blockfrost.rs rename to crates/cardano-connect/cardano/blockfrost.rs diff --git a/crates/cardano-connect/src/lib.rs b/crates/cardano-connect/src/lib.rs new file mode 100644 index 0000000..1040c02 --- /dev/null +++ b/crates/cardano-connect/src/lib.rs @@ -0,0 +1,20 @@ +use anyhow::Result; + +use cardano_tx_builder::{ + Credential, + Network, + Utxo, + BuildParameters, +}; + +pub trait CardanoConnect { + fn network(&self) -> Network; + fn build_parameters(&self) -> impl std::future::Future + Send; + fn utxos_at( + &self, + payment: &Credential, + delegation: Option<&Credential>, + ) -> impl std::future::Future>> + Send; + fn health(&self) -> impl std::future::Future> + Send; + fn submit(&self, tx: Vec) -> impl std::future::Future> + Send; +} diff --git a/crates/cardano-tx-builder/Cargo.toml b/crates/cardano-tx-builder/Cargo.toml new file mode 100644 index 0000000..1e8f143 --- /dev/null +++ b/crates/cardano-tx-builder/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "cardano-tx-builder" +version.workspace = true +edition.workspace = true +description.workspace = true +# license.workspace = true // TBC +authors.workspace = true +repository.workspace = true +homepage.workspace = true +documentation.workspace = true +rust-version.workspace = true + +[package.metadata.release] +release = false + +[dependencies] +anyhow = "1.0.100" +bech32 = "0.11.0" +cryptoxide = "0.5.1" +hex = "0.4.3" +minicbor = { version = "0.25.1", features = ["alloc", "derive"] } +pallas-addresses = "0.33.0" +pallas-crypto = "0.33.0" +pallas-codec = "0.33.0" +pallas-primitives = "0.33.0" +uplc = "1.1.19" diff --git a/crates/cardano-tx-builder/README.md b/crates/cardano-tx-builder/README.md new file mode 100644 index 0000000..c5004bf --- /dev/null +++ b/crates/cardano-tx-builder/README.md @@ -0,0 +1,9 @@ +# Cardano tx builder + +> The bits of cardano we need for konduit. No more, no less. + +## Aims + +- Provide an ergonomic interface for handling cardano data. +- Contain nothing konduit specific. +- Downstream Konduit tools depend on this crate, and not any pallas crate. diff --git a/src/cardano_types/address.rs b/crates/cardano-tx-builder/src/address.rs similarity index 71% rename from src/cardano_types/address.rs rename to crates/cardano-tx-builder/src/address.rs index 0069b74..f39e92a 100644 --- a/src/cardano_types/address.rs +++ b/crates/cardano-tx-builder/src/address.rs @@ -1,7 +1,8 @@ /// Seems like quite a lot of code just to pullout `from_bech32` on address /// This is mostly extracted from private function of pallas_addresses -use anyhow::{anyhow, Result}; -use pallas_addresses::{self, Network}; +use anyhow::{Result, anyhow}; +use pallas_addresses; +pub use pallas_addresses::Network; use pallas_crypto::hash::Hash; use pallas_primitives::NetworkId; @@ -26,7 +27,6 @@ fn parse_network(header: u8) -> Network { } } - /// Credential #[derive(Debug, PartialEq)] @@ -65,12 +65,15 @@ pub struct Address { } impl Address { - pub fn new(network: Network, payment: Credential, delegation: Option) -> Self { - Self { network, payment, delegation } + Self { + network, + payment, + delegation, + } } - pub fn from_bech32(s : &str) -> Result { + pub fn from_bech32(s: &str) -> Result { let (_, bytes) = bech32::decode(s)?; let x = Address::try_from(bytes)?; Ok(x) @@ -78,16 +81,32 @@ impl Address { } impl TryFrom> for Address { - fn try_from(bytes : Vec) -> std::result::Result { + fn try_from(bytes: Vec) -> std::result::Result { let header = *bytes.first().expect("Missing bytes"); let network = parse_network(header); - let payment : Hash<28> = v2a(bytes[1..=28].to_vec())?.into(); + let payment: Hash<28> = v2a(bytes[1..=28].to_vec())?.into(); let delegation = v2a(bytes[28..].to_vec()); match header & 0b1111_0000 { - 0b0000_0000 => Ok(Address::new(network, Credential::Key(payment), Some(Credential::Key(delegation?.into())))), - 0b0001_0000 => Ok(Address::new(network, Credential::Script(payment),Some(Credential::Key(delegation?.into())))), - 0b0010_0000 => Ok(Address::new(network, Credential::Key(payment), Some(Credential::Script(delegation?.into())))), - 0b0011_0000 => Ok(Address::new(network, Credential::Script(payment), Some(Credential::Script(delegation?.into())))), + 0b0000_0000 => Ok(Address::new( + network, + Credential::Key(payment), + Some(Credential::Key(delegation?.into())), + )), + 0b0001_0000 => Ok(Address::new( + network, + Credential::Script(payment), + Some(Credential::Key(delegation?.into())), + )), + 0b0010_0000 => Ok(Address::new( + network, + Credential::Key(payment), + Some(Credential::Script(delegation?.into())), + )), + 0b0011_0000 => Ok(Address::new( + network, + Credential::Script(payment), + Some(Credential::Script(delegation?.into())), + )), 0b0110_0000 => Ok(Address::new(network, Credential::Key(payment), None)), 0b0111_0000 => Ok(Address::new(network, Credential::Script(payment), None)), _ => Err(anyhow!("Header not supported")), @@ -109,7 +128,6 @@ impl Into for Address { } } - #[cfg(test)] mod tests { use super::*; @@ -120,10 +138,10 @@ mod tests { let addr0 = Address::from_bech32(testnet).unwrap(); let mainnet = "addr1vypymjsk49r6p9f2f6d3gm04ther0ka7ts8jsv8dukg4nvgr9x6tq"; let addr1 = Address::from_bech32(mainnet).unwrap(); - assert_eq!(addr0.delegation,addr1.delegation, "oops"); - let sa0 : pallas_addresses::ShelleyAddress = addr0.into(); + assert_eq!(addr0.delegation, addr1.delegation, "oops"); + let sa0: pallas_addresses::ShelleyAddress = addr0.into(); assert_eq!(sa0.to_bech32().unwrap(), testnet, "oops"); - let sa1 : pallas_addresses::ShelleyAddress = addr1.into(); + let sa1: pallas_addresses::ShelleyAddress = addr1.into(); assert_eq!(sa1.to_bech32().unwrap(), mainnet, "oops"); } } diff --git a/crates/cardano-tx-builder/src/datum.rs b/crates/cardano-tx-builder/src/datum.rs new file mode 100644 index 0000000..f8f8dff --- /dev/null +++ b/crates/cardano-tx-builder/src/datum.rs @@ -0,0 +1,24 @@ +use pallas_codec::utils::CborWrap; + +use super::pallas::era; +use super::plutus_data::PlutusData; + +/// Datum +/// We are not being too clever with this. +/// We make use of rust's `Optional` for the no datum case. +/// We are yet to distinguish cases where we need the data, +/// in place of the hash. +/// It should be documented. +pub enum DatumOrHash { + Hash([u8; 32]), // Check this + Data(PlutusData), +} + +impl Into for DatumOrHash { + fn into(self) -> era::DatumOption { + match self { + DatumOrHash::Hash(hash) => era::DatumOption::Hash(hash.into()), + DatumOrHash::Data(plutus_data) => era::DatumOption::Data(CborWrap(plutus_data)), + } + } +} diff --git a/crates/cardano-tx-builder/src/input.rs b/crates/cardano-tx-builder/src/input.rs new file mode 100644 index 0000000..3ef4302 --- /dev/null +++ b/crates/cardano-tx-builder/src/input.rs @@ -0,0 +1,3 @@ +/// Input +/// No change from pallas primitives here. +pub type Input = pallas_primitives::TransactionInput; diff --git a/crates/cardano-tx-builder/src/lib.rs b/crates/cardano-tx-builder/src/lib.rs new file mode 100644 index 0000000..93ece1e --- /dev/null +++ b/crates/cardano-tx-builder/src/lib.rs @@ -0,0 +1,29 @@ +mod pallas; +mod utils; + +pub mod plutus_data; +pub use plutus_data::PlutusData; + +pub mod input; +pub use input::Input; + +pub mod address; +pub use address::{Address, Credential, Network}; + +pub mod value; +pub use value::{MultiAsset, Value}; + +pub mod datum; +pub use datum::DatumOrHash; + +pub mod script; +pub use script::Script; + +pub mod output; +pub use output::Output; + +pub mod utxo; +pub use utxo::Utxo; + +pub mod tx; +pub use tx::{BuildParameters, Tx}; diff --git a/crates/cardano-tx-builder/src/output.rs b/crates/cardano-tx-builder/src/output.rs new file mode 100644 index 0000000..701b0f1 --- /dev/null +++ b/crates/cardano-tx-builder/src/output.rs @@ -0,0 +1,32 @@ +use super::pallas::era; + +use super::address::Address; +use super::datum::DatumOrHash; +use super::script::Script; +use super::value::Value; + +/// Output +pub struct Output { + pub address: Address, + pub value: Value, + pub datum: Option, + pub script_ref: Option