FromData and ToData
On-chain, all data is stored in a universal format called Data (Plutus Core Data). Datums, redeemers, and script context are all encoded as Data. Scalus provides type-safe conversions to work with your Scala types instead of raw Data.
Quick Usage
// Convert Data to a typed value
val owner = datum.to[PubKeyHash]
// Convert a typed value to Data
val data = myValue.toDataOn-chain, to[T] is a retag, not a decoder: it compiles to nothing. No constructor tag is
checked, no field count is verified, and fields are read from the underlying Data only when
your code touches them. In particular, never cast to a concrete variant of a sealed hierarchy
(redeemer.to[Order.Buy]) – match on the parent type instead. See
The Constructor Tag.
Using Data in Validators
In validators, you receive Data and convert it to typed values:
@Compile
object MyValidator extends Validator:
inline override def spend(datum: Option[Data], redeemer: Data, tx: TxInfo, outRef: TxOutRef): Unit =
// Extract typed values from Data
val owner = datum.getOrFail("Datum not found").to[PubKeyHash]
val action = redeemer.to[MyRedeemer]
// ... validation logicTwo equivalent ways to convert:
// Using .to[T] extension method (preferred)
val myDatum = datumData.to[MyDatumType]
// Using fromData function
import scalus.uplc.builtin.Data.fromData
val myDatum = fromData[MyDatumType](datumData)Deriving Instances
Scalus provides type classes FromData and ToData to convert between user-defined types and Data. Derive instances automatically using Scala 3’s derives syntax.
One caveat - you must always supply a companion object with @Compile annotation on it even if it is empty.
This is because Scalus needs to compile the companion object to include the FromData instance in the UPLC script.
import scalus.uplc.builtin.*, Builtins.*, Data.*
case class Account(hash: ByteString, balance: BigInt) derives FromData, ToData
@Compile
object Account
enum State derives FromData, ToData:
case Empty
case Active(account: Account)
@Compile
object State
Here’s an example of converting standard and user-defined types to and from Data.
import scalus.uplc.builtin.*, Builtins.*, Data.*
case class Account(hash: ByteString, balance: BigInt)
enum State:
case Empty
case Active(account: Account)
val fromDataExample = compile {
// The `fromData` function is used to convert a `Data` value to a typed Scala value.
val data = iData(123)
// fromData is a method that looks up the appropriate `FromData` instance for the type
// there are instances for all built-in types
val a = fromData[BigInt](data)
// also you can use extension method `to` on Data
val b = data.to[BigInt]
// you can define your own `FromData` instances
{
given FromData[Account] = (d: Data) => {
val args = unConstrData(d).snd
Account(args.head.to[ByteString], args.tail.head.to[BigInt])
}
val account = data.to[Account]
}
// or you can use a macro to derive the FromData instance
{
given FromData[Account] = FromData.derived
given FromData[State] = FromData.derived
}
}You can derive FromData instances for your case classes and enums via standard Scala 3 derives syntax.