From 8e6cfcb424c74dba882de2220807aced9bcbd7ba Mon Sep 17 00:00:00 2001 From: rooooooooob Date: Wed, 24 Jul 2024 21:00:49 -0700 Subject: [PATCH] Docs update: Plutus Based on Aiken's Hello WOrld example. TODO: I couldn't get the redeem contract to be accepted by the network. We need to figure out why and update this example. Replaces the plutus part of #307 --- .../modules/builders/plutus_contracts.mdx | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 docs/docs/modules/builders/plutus_contracts.mdx diff --git a/docs/docs/modules/builders/plutus_contracts.mdx b/docs/docs/modules/builders/plutus_contracts.mdx new file mode 100644 index 00000000..2084331b --- /dev/null +++ b/docs/docs/modules/builders/plutus_contracts.mdx @@ -0,0 +1,139 @@ +--- +sidebar_position: 2 +--- + +# Aiken's Hello World Example + +Using Aiken's [hello world](https://aiken-lang.org/example--hello-world/basics) example we can see how to use datums and redeemers with the transaction builder. + +To deploy the contract datum we need to create an output to the contract with the datum attached like so: + +```rust +let change_addr: Address = todo!("Add your change address here"); +let sk1: PrivateKey = todo!("Add your own private key here that controls the input"); +let sk2: PrivateKey = todo!("Add your own private key here for the contract's datum"); + +let mut tx_builder = make_tx_builder(); + +// input needed to pay for the tx +tx_builder.add_input(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("1665fc34e312445884d752a557e6b3499e1fc10228de77ca712b6bda9078ced7").unwrap(), + 0 + ), + TransactionOutput::new( + addr.clone(), + Value::from(10000000000), + Some(DatumOption::new_hash(contract_datum.hash())), + None + ), +).payment_key().unwrap())?; + +// contract created from bytes from the Plutus.json generated by Aiken +let contract = PlutusV2Script::from_cbor_bytes(&hex::decode("58f2010000323232323232323222232325333008323232533300b002100114a06644646600200200644a66602200229404c8c94ccc040cdc78010028a511330040040013014002375c60240026eb0c038c03cc03cc03cc03cc03cc03cc03cc03cc020c008c020014dd71801180400399b8f375c6002600e00a91010d48656c6c6f2c20576f726c6421002300d00114984d958c94ccc020cdc3a400000226464a66601a601e0042930b1bae300d00130060041630060033253330073370e900000089919299980618070010a4c2c6eb8c030004c01401058c01400c8c014dd5000918019baa0015734aae7555cf2ab9f5742ae881").unwrap()).unwrap(); +let contract_addr = EnterpriseAddress::new( + change_addr.network_id().unwrap(), + StakeCredential::new_script(contract.hash()), +).to_address(); + +// contract datum +let contract_datum = PlutusData::new_constr_plutus_data( + ConstrPlutusData::new(0, vec![PlutusData::new_bytes(sk2.to_public().hash().to_raw_bytes().to_vec())]) +); + +// send funds to the contract +tx_builder.add_output(SingleOutputBuilderResult::new( + TransactionOutput::new(contract_addr.clone(), Value::from(5000000000), None, None) +)); + +let mut signed_tx_builder = tx_builder.build( + ChangeSelectionAlgo::Default, + &addr +)?; + +let tx_hash = hash_transaction(&signed_tx_builder.body()); +signed_tx_builder.add_vkey(make_vkey_witness(&tx_hash, &sk1)); +let tx = signed_tx_builder.build_checked().unwrap(); +``` + +After we've deployed this contract it is time to redeem it. + +```rust +// contract / tx_builder / contract_datum are same as above + +let redeemer_datum = PlutusData::new_constr_plutus_data( + ConstrPlutusData::new(0, vec![PlutusData::new_bytes("Hello, World!".as_bytes().to_vec())]) +); + +tx_builder.add_input(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("6079e89a1eeba9ef1d6a334f8edbf6029ff5299315a3fd55fce732da6a72fd9b").unwrap(), + 0 + ), + TransactionOutput::new( + addr.clone(), + Value::from(7500000), + None, + None + ), +).plutus_script( + PartialPlutusWitness::new( + PlutusScriptWitness::Script(contract.into()), + redeemer_datum.clone() + ), + vec![sk1.to_public().hash(), sk2.to_public().hash()], + contract_datum.clone(), +).unwrap())?; + +// In order to run plutus contracts we must supply collateral which will only be spent if the contract does +// not provide enough ADA specified via exunits to execute it. +tx_builder.add_collateral(SingleInputBuilder::new( + TransactionInput::new( + TransactionHash::from_hex("5acebd1bc82df3d6c8f50908c1a182d5fb2ff0525066fa5a3ec44fe8df80f005").unwrap(), + 0, + ), + TransactionOutput::new( + addr.clone(), + Value::from(5000000), + None, + None, + ), +).payment_key().unwrap())?; +let mut redeemer_builder = tx_builder.build_for_evaluation( + ChangeSelectionAlgo::Default, + &addr, +)?; + +// We must then send this draft tx for evaluation via ogmios, blockfrost, etc +// in order to figure out how many exunits are necessary to evaluate it +println!("draft tx: {}", hex::encode(redeemer_builder.draft_tx()?.to_cbor_bytes())); + +// once we get the exunits required back we can set it and be ready to finalize the signing of the tx +redeemer_builder.set_exunits( + RedeemerWitnessKey::new(RedeemerTag::Spend, 0), + ExUnits::new(5000000, 2000000000), +); +let redeemers = redeemer_builder.build()?; +tx_builder.set_exunits( + RedeemerWitnessKey::new(RedeemerTag::Spend, 0), + ExUnits::new(5000000, 2000000000), +); + + +let mut signed_tx_builder = tx_builder.build( + ChangeSelectionAlgo::Default, + &addr +)?; +let tx_hash = hash_transaction(&signed_tx_builder.body()); +signed_tx_builder + .add_vkey(make_vkey_witness( + &tx_hash, + &sk1, + )); +signed_tx_builder + .add_vkey(make_vkey_witness( + &tx_hash, + &sk2, + )); +let tx = signed_tx_builder.build_checked().unwrap(); +``` \ No newline at end of file