Cardano-client-lib: New composable functions to build transaction in Java— Part I

Cardano-client-lib is a Java client library for Cardano blockchain. It simplifies the interaction with Cardano blockchain from a Java application. Using this library, you can perform various types of transactions in a Java application.

Cardano-client-lib is a Java client library for Cardano blockchain. It simplifies the interaction with Cardano blockchain from a Java application. Using this library, you can perform various types of transactions in a Java application.

For example, you can do a transfer from one address to another, mint a token or an NFT and invoke a Plutus smart contract in Java.

In this post, I am going to explain the concept of newly introduced composable functions API.

In the next few posts of this series, I will go through specific examples from regular transfer to token minting to plutus contract calls using composable function apis.

If you are new to cardano-client-lib, it currently supports three types of apis to build / perform various transactions.

  1. High Level API : Provides simple interfaces to do transfer and token minting transaction. But some complex transactions may not be possible through high-level API.
  2. Low Level API : These are low level serialization api to build transaction for Cardano network. These APIs are flexible and good for complex scenarios. Basically, you can achieve any complexity with low level api, but at the same time these APIs are not beginner friendly.
  3. Composable Functions : These APIs were introduced in v0.2.0-beta2 which provide a balance between simple interface and flexibility. Using out-of-box composable functions, you can achieve any complexity and at the same time, you can write your own composable functions to customize the behavior during transaction building.

So in this post, I am going to focus on the general concepts of composable functions in the library.

Composable Functions

The current version (0.2.0-beta2 or later) of the library provides a set of FunctionalInterface which can be used to implement composable functions. These functions can be used to build various different types of transactions. The library provides many useful out-of-box implementations of these functions.

The followings are the main FunctionalInterface

  1. TxBuilder
  2. TxOutputBuilder
  3. TxInputBuilder
  4. TxSigner

TxBuilder : This functional interface helps to transform a transaction object. The build method in this interface takes a TxBuilderContext and a Transaction object as input parameters. The role of this function is to transform the input transaction object with additional attributes or update existing attributes.

TxOutputBuilder : This functional interface helps to build a TransactionOutput object and add that to the transaction output list. The accept method in this interface takes a TxBuilderContext and a list of TransactionOutput.

TxInputBuilder : This functional interface is responsible to build inputs from the expected outputs.

TxSigner : This interface is responsible to provide transaction signing capability.

TxBuilderContext

Each function takes TxBuilderContext as the first input parameter. The main responsibility of TxBuilderContext is to provide context data like BackendService, UtxoSelectionStrategy and some temporary data to the functions during the transaction building.

Function Helpers

The library provides many out-of-box functions through helper classes which help to build the transaction. So you don’t need to start from zero and the same time you can write your own functions if required.

In the current version (0.2.0-beta2) of the library, the following helper classes are available

  1. OutputBuilders : Provides a list of helper methods to create a TxOutputBuilder function which is used create a TransactionOutput from a TransactionOutput or Output object. The TxOutputBuilder functions verify min ada requirement and update the ada amount accordingly in the TransactionOutput.

Some of the helper methods available in this class are

- TxOutputBuilder createFromOutput(Output output) 
- TxOutputBuilder createFromOutput(TransactionOutput txnOutput) 
- TxOutputBuilder createFromMintOutput(Output output) 
- TxOutputBuilder createFromMintOutput(TransactionOutput txnOutput)

You can combine multiple TxOutputBuilder using “and” method.

TxOutputBuilder txOutputBuilder = createFromOutput(output1)                                      .and(createFromOutput(output2))

Note: For token minting transaction, to calculate min required ada in output accurately, mint outputs or createFromMintOutput() methods should be invoked after all regular outputs or createFromOutput() methods.

2. InputBuilders : Provides helper methods to create a TxInputBuilder function which is used to build required TransactionInput(s) from a list of TransactionOutput(s) using buildInputs() method of TxOutputBuilder.

TxBuilder txBuilder = txOutputBuilder1                        
.and(txOutputBuilder2)                        
.buildInputs(createFromSender(senderAddress, changeAddress))

Some of the helper methods available in this class are

- TxInputBuilder createFromSender(String sender, String changeAddress) 
- TxInputBuilder createFromUtxos(List<Utxo> utxos) 
- TxInputBuilder createFromUtxos(Supplier<List<Utxo>> supplier) 
- TxInputBuilder createFromUtxos(List<Utxo> utxos, String changeAddress) 
- TxInputBuilder createFromUtxos(List<Utxo> utxos, String changeAddress, Object datum) 
- TxInputBuilder createFromUtxos(List<Utxo> utxos, String changeAddress, String datumHash)

3. MintCreators : It provides helper methods to create a TxBuilder function which is used to add multiasset minting related data to the Transaction object.

TxBuilder txBuilder = txOuputBuilder1                         
.and(txOuputBuilder2)                         
.and(createFromMintOutput(mintOutput))                       
.buildInputs(txInputBuilder)                       
.andThen(mintCreator(script, multiAsset)

Note: For minting transaction, a TxOutputBuilder specific to mint output is required. This can be done by calling one of the OutputBuilders.createFromMintOutput method.

Some of the helper methods available in this class are

- TxBuilder mintCreator(Script script, MultiAsset multiAsset) 
- TxBuilder mintCreator(Script script, MultiAsset multiAsset, boolean inclScriptInAuxData)

4. AuxDataProviders : Provides helper methods to create a TxBuilder function which is used to add metadata to the Transaction object.

TxBuilder txBuilder = txOuputBuilder1                         
.and(txOuputBuilder2)                       
.buildInputs(txInputBuilder)                       
.andThen(metadataProvider(metadata))

5. CollateralBuilders : Provides helper methods to create a TxBuilder function which is used to add collateral(s) to the Transaction object in a plutus script transaction.

TxBuilder txBuilder = txOuputBuilder1                         
.and(txOuputBuilder2)                       
.buildInputs(txInputBuilder)                       
.andThen(collateralFrom(txHash, txIndex))

Some of the helper methods available in this class are

- TxBuilder collateralFrom(String txHash, int txIndex) 
- TxBuilder collateralFrom(List<Utxo> utxos) 
- TxBuilder collateralFrom(Supplier<List<Utxo>> supplier)

6. ScriptCallConextProviders : Provides helper methods to create a TxBuilder function which is used to add plutus script specific data to a Transaction object.

TxBuilder txBuilder = txOuputBuilder1               
.and(txOuputBuilder2)            
.buildInputs(txInputBuilder)            
.andThen(collateralFrom(txHash, txIndex))            
.andThen(createFromScriptCallContext(scriptCallContext))

Some of the helper methods available in this class are

- TxBuilder createFromScriptCallContext(ScriptCallContext sc) 
- TxBuilder scriptCallContext(PlutusScript plutusScript, Utxo utxo, T datum, K redeemerData,RedeemerTag tag, ExUnits exUnits) 
- TxBuilder scriptCallContext(PlutusScript plutusScript, int scriptInputIndex, T datum, K redeemerData, RedeemerTag tag, ExUnits exUnits)

7. FeeCalculators : Provides helper methods to create TxBuilder function to calculate fee and update Transaction object accordingly.

TxBuilder txBuilder = txOuputBuilder1                        
.and(txOuputBuilder2)                       
.buildInputs(txInputBuilder)                       
.andThen(otherTxBuilder)                       ...                       
.andThen(feeCalculator(changeAddress, noOfSigners))

Note: No of signers is required to calculate the fee accurately. For example, for a minting transaction with a policy script, min no of signers is 2. (Minting account, Policy script secret key)

Helper methods to create TxBuilder function in this helper class are

- TxBuilder feeCalculator(String changeAddress, int noOfSigners) 
- TxBuilder feeCalculator(int noOfSigners, UpdateOutputFunction updateOutputWithFeeFunc)

8. ChangeOutputAdjustments : Provides helper methods to create TxBuilder function which can adjust the change output. This function is used to verify if the ada amount in change output satisfies min ada requirement. If not, it tries to get additional inputs and adjust the change output after the fee calculation. This function is used after feeCalculation function.

TxBuilder txBuilder = txOuputBuilder1              
.and(txOuputBuilder2)              
.buildInputs(txInputBuilder)              
.andThen(otherTxBuilder)              ...              
.andThen(feeCalculator(changeAddress, noOfSigners))              
.andThen(adjustChangeOutput(senderAddress, changeAddress, noOfSigners))

9. SignerProviders : Provides helper methods to create TxSigner function to sign a transaction.

TxSigner signer = signerFrom(sender1, sender2, ..., sendern)            .andThen(signerFrom(secretKey1, ..., secretKeyn))

Some of the helper methods available in this class are

- TxSigner signerFrom(Account... signers)
- TxSigner signerFrom(SecretKey... secretKeys)
- TxSigner signerFrom(Policy... policies)

10. MinAdaCheckers : Provides a helper method to return MinAdaChecker function. MinAdaChecker function returns the additional lovelace amount required for a TransactionOutput to meet the min ada requirement. This function is used internally by implementations of other functions. You may not need to use this function directly.

Build and Sign Transaction

After TxBuilder is created by composing different functions, a Transaction object can be built and signed.

TxBuilder txBuilder = createFromOutput(output)         .buildInputs(createFromSender(senderAddress, senderAddress))         
.andThen(metadataProvider(metadata))         
.andThen(feeCalculator(senderAddress, 1))         
.andThen(adjustChangeOutput(senderAddress, 1));
TxSigner signer = SignerProviders.signerFrom(sender);
/**** Build and Sign in one step ****/ 
Transaction signedTxn = TxBuilderContext.init(backendService)                                 .buildAndSign(txBuilder, signer);
/**** OR, Build Txn and then sign using signer *****/ 
Transaction transaction = TxBuilderContext.init(backendService)                             
.build(txBuilder); 
Transaction signedTxn = signer.sign(transaction);

Submit a transaction to Cardano network

Once a transaction is signed, you can submit it to the network using TransactionService.

Result<String> result =           transactionService.submitTransaction(signedTxn.serialize());

Add your own TxBuilder implementation

In some scenarios, you need to provide your own TxBuilder and other functions to transform the transaction object.

Example :
Right now, there is no TxBuilder function to set ttl parameter in the transaction object. But you can easily provide your own implementation to set ttl or add/update other parameters.

TxBuilder txBuilder = txOuputBuilder1              
.and(txOuputBuilder2)              
.buildInputs(txInputBuilder)              
.andThen(otherTxBuilder)              
.andThen((context, transaction) -> {                    
transaction.getBody().setTtl(100000);                   
//set or update other transaction parameters              
})              
.andThen(feeCalculator(changeAddress, noOfSigners))

So in summary, to use the composable function API, follow these below steps to build a transaction

  1. Define expected outputs
  2. Create one or more TxOutputBuilder from outputs
  3. Create a TxInputBuilder which selects inputs for a sender
  4. Step-1 to Step-3 can be repeated for multiple senders
  5. Create additional TxBuilder(s) to add / update different attributes in the transaction.
  6. Create TxSigner
  7. Invoke TxBuilderContext.init() to initialize the context and then invoke buildAndSign() to build and sign the transaction.

In the next few posts of this series, I will go through specific examples from regular transfer to token minting to Plutus contract calls using composable function APIs.

Meanwhile, you can find different examples using composable functions in this GitHub repo

Resources

  1. Cardano Client Lib Project GitHub
  2. Cardano Client Example GitHub

Published on Java Code Geeks with permission by Satya Ranjan, partner at our JCG program. See the original article here: Cardano-client-lib: New composable functions to build transaction in Java— Part I

Opinions expressed by Java Code Geeks contributors are their own.

Exit mobile version