Payment Methods: Send ADA and Native Tokens
TxBuilder provides several methods for creating transaction outputs with different levels of control. This guide covers the various ways to send ADA and native tokens.
How to Make Simple ADA Payment
The most basic payment sends ADA to an address without any datum:
TxBuilder(env)
.spend(utxo)
.payTo(recipientAddress, Value.ada(10)) // Send 10 ADA
.changeTo(changeAddress)
.build()Sending Native Tokens on Cardano
Send native tokens along with ADA:
import scalus.cardano.ledger.{AssetName, PolicyId, MultiAsset, Value}
import scalus.builtin.ByteString.hex
import scala.collection.immutable.SortedMap
val policyId = PolicyId(hex"...")
val assetName = AssetName(hex"...")
val tokenValue = Value.asset(
policyId,
assetName,
amount = 100L, // 100 tokens
ada = Coin.ada(2) // Plus 2 ADA
)
TxBuilder(env)
.spend(utxo)
.payTo(recipientAddress, tokenValue)
.changeTo(changeAddress)
.build()TxBuilder automatically ensures outputs meet the minimum ADA requirement based on the protocol parameters.
Payment with Inline Datum
Send funds to a script address with an inline datum:
import scalus.builtin.Data
import scalus.builtin.ToData
case class MyDatum(owner: PubKeyHash, amount: Long) derives ToData
val datum = MyDatum(ownerPubKeyHash, 1000)
TxBuilder(env)
.spend(utxo)
.payTo(scriptAddress, Value.ada(10), datum) // Datum is inlined
.changeTo(changeAddress)
.build()The datum is automatically serialized to Data and included inline in the output.
Payment with Datum Hash
Using attach
Send funds with a datum hash and attach the datum to the witness set:
val datum = MyDatum(ownerPubKeyHash, 1000)
TxBuilder(env)
.spend(utxo)
.attach(datum) // Add datum to witness set
.payTo(scriptAddress, Value.ada(10), datumHash)
.changeTo(changeAddress)
.build()The attach() method computes the datum hash and stores the datum for inclusion in the witness set.
Multiple Payments
Chain multiple payTo() calls to create multiple outputs:
TxBuilder(env)
.spend(utxo)
.payTo(recipient1, Value.ada(5))
.payTo(recipient2, Value.ada(3))
.payTo(recipient3, Value.ada(2))
.changeTo(changeAddress)
.build()Each call creates a separate UTxO at the recipient address.
Custom Transaction Output
For full control, use the output() method with a complete TransactionOutput:
import scalus.cardano.ledger.{TransactionOutput, DatumOption, Script}
val customOutput = TransactionOutput(
address = recipientAddress,
value = Value.ada(10),
datumOption = Some(DatumOption.Inline(myDatum.toData)),
scriptRef = Some(Script.PlutusV3(myScript)) // Attach reference script
)
TxBuilder(env)
.spend(utxo)
.output(customOutput)
.changeTo(changeAddress)
.build()This gives you complete control over all output fields, including attaching reference scripts.
Token Change Handling
When spending UTxOs with tokens, TxBuilder automatically returns unused tokens to the change address:
// UTxO contains 10 ADA + 1000 tokens
val utxoWithTokens: Utxo = // ...
TxBuilder(env)
.spend(utxoWithTokens)
.payTo(recipient, Value.ada(5)) // Only send ADA
.changeTo(changeAddress) // Tokens automatically returned here
.build()The change output will contain the remaining ADA and all 1000 tokens.
Next Steps
- Spending UTxOs - Learn about input selection and spending from scripts
- Minting & Burning - Create and destroy native tokens
- Validator Interactions - Work with Plutus scripts