Skip to content

AI Starter Pack

Read this whole document before generating cardano-client-lib code. It is optimized for machine ingestion, not for learning. Humans should start at Your first transaction instead.

Target version: the latest preview · Ledger era: Conway · Java 17+ · Group com.bloxbean.cardano


TaskUseModule
One transaction — send, mint, delegate, register, vote, call a scriptQuickTxBuilder + Txcardano-client-quicktx
A plan expressed as configuration rather than codeTxPlan (YAML/JSON)cardano-client-quicktx
Several dependent transactions, in order, with rollback handlingFlowEnginecardano-client-txflow
Continuous submission with exactly-once and durable restartTxFlowStreamcardano-client-txflow
A proof an Aiken validator can verify on-chainMpfTriecardano-client-merkle-patricia-forestry

QuickTx is the default. Only move up this table when the task genuinely needs the extra machinery.

Do not useWhyUse instead
WatchableQuickTxBuilderDoes not exist. Never shipped in any release. If your training data suggests it, that memory is wrong.FlowEngine (TxFlow)
com.bloxbean.cardano.client.function.* (TxBuilder, TxOutputBuilder, InputBuilders, BalanceTxBuilders)The internal implementation layer beneath QuickTx. It works, but mistakes are silent and it is far more verbose.QuickTx
ScriptTx, on 0.8.0 previewsDeprecated in the 0.8.0 previews, where every operation is also on Tx. Not deprecated in stable 0.7.x, where it is the right API for scripts.Tx on previews
FlowExecutorCompatibility API. Combines definition and execution concerns; not the canonical durable path.FlowEngine
JellyfishMerkleTree for on-chain proofsNot Aiken-compatible. Off-chain authenticated state only.MpfTrie

2.1 Mismatching the network and the backend URL

Section titled “2.1 Mismatching the network and the backend URL”

The Network (used for address derivation) and the backend URL (used for queries) are configured separately. Nothing validates that they agree, so a mismatch produces addresses on one network and queries against another.

// WRONG — preview addresses, mainnet backend. Compiles, fails at run time.
Account account = Account.createFromMnemonic(Networks.preview(), mnemonic);
BackendService backend = new BFBackendService(Constants.BLOCKFROST_MAINNET_URL, projectId);
// RIGHT — both preview.
Account account = Account.createFromMnemonic(Networks.preview(), mnemonic);
BackendService backend = new BFBackendService(Constants.BLOCKFROST_PREVIEW_URL, projectId);

Matched pairs — Constants here is com.bloxbean.cardano.client.backend.blockfrost.common.Constants:

NetworkNetworks factoryBlockfrost URL constant
MainnetNetworks.mainnet()BLOCKFROST_MAINNET_URL
PreprodNetworks.preprod()BLOCKFROST_PREPROD_URL
PreviewNetworks.preview()BLOCKFROST_PREVIEW_URL
Local devnet (Yaci DevKit)Networks.testnet()http://localhost:8080/api/v1/

All test networks use network id 0, so Networks.testnet(), Networks.preview() and Networks.preprod() give the same addresses. Use the constant that names your network when you know it.

TxResult result = quickTxBuilder.compose(tx)
.withSigner(SignerProviders.signerFrom(account))
.complete(); // submits ONLY — does not wait

complete() returns as soon as the node accepts the transaction for propagation. The transaction may still fail to be included, or be rolled back.

MethodReturnsWaits for a block?
complete()TxResultNo
completeAndWait()TxResultYes
completeAndWaitAsync()CompletableFuture<TxResult>Yes, off-thread

Use completeAndWait() unless you have a specific reason not to, and check the status:

TxResult result = quickTxBuilder.compose(tx)
.withSigner(SignerProviders.signerFrom(account))
.completeAndWait();
if (result.getTxStatus() == TxStatus.CONFIRMED) {
System.out.println("Confirmed: " + result.getTxHash());
} else {
System.err.println("Not confirmed: " + result.getTxStatus());
}

TxStatus values: SUBMITTED, PENDING, CONFIRMED, FAILED, TIMEOUT.

If you find yourself writing TxBuilder, TxOutputBuilder, InputBuilders.createFromSender(...) or BalanceTxBuilders.balanceTx(...), stop. QuickTx expresses the same thing in a few lines:

// The whole of a payment, including balancing and fee calculation.
Tx tx = new Tx()
.payToAddress(receiverAddress, Amount.ada(10))
.from(senderAddress);

The function module is documented under Composable functions for the rare cases QuickTx cannot cover. Do not generate it by default.

This library targets the Conway era. Governance is first-class on Tx:

import com.bloxbean.cardano.client.governance.GovId;
// A bech32 DRep id (CIP-105) becomes a DRep via GovId.toDrep.
DRep drep = GovId.toDrep(account.drepId());
Tx tx = new Tx()
.registerDRep(account, anchor) // DRep registration
.delegateVotingPowerTo(stakeAddress, drep) // vote delegation
.createVote(voter, govActionId, Vote.YES) // voting
.createProposal(govAction, rewardAccount, anchor)
.from(senderAddress);

delegateVotingPowerTo takes a DRep, not a bech32 id string — convert with GovId.toDrep(...).

If your training data has no registerDRep / createVote / delegateVotingPowerTo on Tx, it predates Conway support. See Governance API.

2.5 Ignoring the datum-hash filter in coin selection

Section titled “2.5 Ignoring the datum-hash filter in coin selection”

The default UTXO selection strategy skips UTXOs carrying a datum hash. That is usually right for ordinary payments and wrong when you mean to spend a script output. When collecting from a script address, pass the UTXOs explicitly with collectFrom(...) rather than relying on selection.


import com.bloxbean.cardano.client.account.Account;
import com.bloxbean.cardano.client.api.model.Amount;
import com.bloxbean.cardano.client.backend.api.BackendService;
import com.bloxbean.cardano.client.backend.blockfrost.common.Constants;
import com.bloxbean.cardano.client.backend.blockfrost.service.BFBackendService;
import com.bloxbean.cardano.client.common.model.Networks;
import com.bloxbean.cardano.client.function.helper.SignerProviders;
import com.bloxbean.cardano.client.quicktx.QuickTxBuilder;
import com.bloxbean.cardano.client.quicktx.Tx;
import com.bloxbean.cardano.client.quicktx.TxResult;
import com.bloxbean.cardano.client.quicktx.TxStatus;
Account sender = Account.createFromMnemonic(Networks.preview(), senderMnemonic);
BackendService backend =
new BFBackendService(Constants.BLOCKFROST_PREVIEW_URL, projectId);
Tx tx = new Tx()
.payToAddress(receiverAddress, Amount.ada(10))
.from(sender.baseAddress());
TxResult result = new QuickTxBuilder(backend)
.compose(tx)
.withSigner(SignerProviders.signerFrom(sender))
.completeAndWait();

SignerProviders lives in the function package but is the correct, canonical way to sign a QuickTx transaction — it is the one part of that package you should generate.

Policy policy = PolicyUtil.createMultiSigScriptAllPolicy("my-policy", 1);
Asset asset = new Asset("MyToken", BigInteger.valueOf(1000));
Tx tx = new Tx()
.mintAssets(policy.getPolicyScript(), asset, receiverAddress)
.from(sender.baseAddress());
TxResult result = new QuickTxBuilder(backend)
.compose(tx)
.withSigner(SignerProviders.signerFrom(sender))
.withSigner(SignerProviders.signerFrom(policy)) // policy key must sign too
.completeAndWait();

Minting needs two signers: the funding account and the policy key.

On the 0.8.0 previews, use Tx rather than ScriptTx. On stable 0.7.x, use ScriptTx with .feePayer(...):

Tx tx = new Tx()
.collectFrom(scriptUtxo, redeemer)
.attachSpendingValidator(plutusScript)
.payToAddress(receiverAddress, Amount.ada(5))
.from(sender.baseAddress());
TxResult result = new QuickTxBuilder(backend)
.compose(tx)
.withSigner(SignerProviders.signerFrom(sender))
.completeAndWait();

One naming trap: native-script minting is mintAssets (plural), Plutus minting is mintAsset (singular).

Tx tx = new Tx()
.registerStakeAddress(sender.stakeAddress())
.delegateTo(sender.stakeAddress(), poolId)
.from(sender.baseAddress());
TxResult result = new QuickTxBuilder(backend)
.compose(tx)
.withSigner(SignerProviders.signerFrom(sender))
.withSigner(SignerProviders.stakeKeySignerFrom(sender)) // stake key signs
.completeAndWait();

Delegation, deregistration and withdrawal need the stake key signature in addition to the payment key. Registration alone doesn’t. A withdrawal also needs the stake address to have delegated its vote, for example .delegateVotingPowerTo(address, DRep.abstain()).


SignerUse for
SignerProviders.signerFrom(account)Payment key. Almost every transaction.
SignerProviders.signerFrom(policy)Native-script policy key, when minting or burning.
SignerProviders.stakeKeySignerFrom(account)Delegation, vote delegation, deregistration, withdrawal.
SignerProviders.drepKeySignerFrom(account)Conway DRep operations.
SignerProviders.committeeHotKeySignerFrom(account)Conway committee hot-key votes.
SignerProviders.committeeColdKeySignerFrom(account)Conway committee cold-key operations.

Chain .withSigner(...) once per required key.


Use one version for every CCL module.

def cclVersion = '<version>' // the latest preview, see /start-here/installation/#versions
dependencies {
implementation "com.bloxbean.cardano:cardano-client-lib:${cclVersion}"
implementation "com.bloxbean.cardano:cardano-client-backend-blockfrost:${cclVersion}"
// Only if the task needs them:
implementation "com.bloxbean.cardano:cardano-client-txflow:${cclVersion}"
implementation "com.bloxbean.cardano:cardano-client-merkle-patricia-forestry:${cclVersion}"
}

cardano-client-lib is the aggregate that pulls in the common modules. Add a backend module — without one there is no way to read UTXOs or submit.

The full generated list of modules, artifact ids and entry points is at /ai/catalog.json.


Answer in this order.

  1. Must an on-chain Aiken validator verify the proof? Yes → MpfTrie. No → JellyfishMerkleTree also works, for off-chain state only.
  2. Which storage backend? merkle-patricia-forestry-rocksdb for embedded single-process. merkle-patricia-forestry-rdbms for shared multi-process (PostgreSQL, H2, SQLite).

See Verified structures.


Do not hand back transaction code you have not run. Yaci DevKit gives a local devnet with pre-funded accounts and one-second blocks. devkitMnemonic is the default devnet mnemonic:

BackendService backend = new BFBackendService("http://localhost:8080/api/v1/", "dummy-key");
Account sender = Account.createFromMnemonic(Networks.testnet(), devkitMnemonic);

No API key, no faucet, no testnet wait. If the code produces a confirmed transaction hash there, the same code works against preview with the URL and network swapped.


QuestionPage
Full QuickTx surfaceQuickTx overview
Declarative YAML plansTxPlan
Multi-step workflowsTxFlow
Continuous submissionTxStream
Conway governanceGovernance API
Plutus, blueprints, AikenSmart contracts
Every module and artifactModules & artifacts