Scala bindings

This page provides a basic Scala programmer’s introduction to working with Digital Asset distributed ledger, using the Scala programming language and the Ledger API.

Introduction

The Scala bindings is a client implementation of the Ledger API. The Scala bindings library lets you write applications that connect to the Digital Asset distributed ledger using the Scala programming language.

There are two main components:

  • Scala codegen
    DAML to Scala code generator. Use this to generate Scala classes from DAML models. The generated Scala code provides a type safe way of creating contracts (CreateCommand) and exercising contract choices (ExerciseCommand).
  • Akka Streams-based API
    The API that you use to send commands to the ledger and receive transactions back.

In order to use the Scala bindings, you should be familiar with:

Getting started

If this is your first experience with the Scala bindings library, we recommend that you start by looking at the quickstart-scala example.

To use the Scala bindings, set up the following dependencies in your project:

lazy val codeGenDependencies = Seq(
  "com.daml.scala" %% "bindings" % daSdkVersion,
)

lazy val applicationDependencies = Seq(
  "com.daml.scala" %% "bindings-akka" % daSdkVersion,
)

We recommend separating generated code and application code into different modules. There are two modules in the quickstart-scala example:

  • scala-codegen
    This module will contain only generated Scala classes.
  • application
    This is the application code that makes use of the generated Scala classes.
lazy val `scala-codegen` = project
  .in(file("scala-codegen"))
  .settings(
    name := "scala-codegen",
    commonSettings,
    libraryDependencies ++= codeGenDependencies,
  )

lazy val `application` = project
  .in(file("application"))
  .settings(
    name := "application",
    commonSettings,
    libraryDependencies ++= codeGenDependencies ++ applicationDependencies,
  )
  .dependsOn(`scala-codegen`)

Generating Scala code

  1. Install the latest version of the DAML SDK.
  2. Build a DAR file from a DAML model. Refer to Building DAML projects for more instructions.
  3. Configure codegen in the daml.yaml (for more details see DAML codegen documentation).
codegen:
  scala:
    package-prefix: com.digitalasset.quickstart.iou.model
    output-directory: scala-codegen/src/main/scala
    verbosity: 2
  1. Run Scala codegen:

    $ daml codegen scala
    

If the command is successful, it should print:

Scala codegen
Reading configuration from project configuration file
[INFO ] Scala Codegen verbosity: INFO
[INFO ] decoding archive with Package ID: 5c96aa21d5f38386833ff47fe1a7562afb5b3fe5be520f289c42892dfb0ef42b
[INFO ] decoding archive with Package ID: 748d55be531976e941076a44fe8c06ad4a7bdb36160711dd0204b5ab8dc77e44
[INFO ] decoding archive with Package ID: d841a5e45897aea965ab7699f3e51613c9d00b9fbd1bb09658d7fb00486f5b57
[INFO ] Scala Codegen result:
Number of generated templates: 3
Number of not generated templates: 0
Details:

The output above tells that Scala codegen read configuration from daml.yaml and produced Scala classes for 3 templates without errors (empty Details: line).

Example code

In this section we will demonstrate how to use the Scala bindings library.

This section refers to the IOU DAML example from the Quickstart guide and quickstart-scala example that we already mentioned above.

Please keep in mind that quickstart-scala example compiles with -Xsource:2.13 scalac option, this is to activate the fix for a Scala bug that forced users to add extra imports for implicits that should not be needed.

Create a contract and send a CreateCommand

To create a Scala class representing an IOU contract, you need the following imports:

import com.digitalasset.ledger.client.binding.{Primitive => P}
import com.digitalasset.quickstart.iou.model.{Iou => M}

the definition of the issuer Party:

  private val issuer = P.Party("Alice")

and the following code to create an instance of the M.Iou class:

  val iou = M.Iou(
    issuer = issuer,
    owner = issuer,
    currency = "USD",
    amount = BigDecimal("1000.00"),
    observers = List())

To send a CreateCommand (keep in mind the following code snippet is part of the Scala for comprehension expression):

    createCmd = iou.create
    _ <- clientUtil.submitCommand(issuer, issuerWorkflowId, createCmd)
    _ = logger.info(s"$issuer created IOU: $iou")
    _ = logger.info(s"$issuer sent create command: $createCmd")

For more details on how to submit a command, please refer to the implementation of com.digitalasset.quickstart.iou.ClientUtil#submitCommand.

Receive a transaction, exercise a choice and send an ExerciseCommand

To receive a transaction as a newOwner and decode a CreatedEvent for IouTransfer contract, you need the definition of the newOwner Party:

  private val newOwner = P.Party("Bob")

and the following code that handles subscription and decoding:

    _ <- clientUtil.subscribe(newOwner, offset0, None) { tx =>
      logger.info(s"$newOwner received transaction: $tx")
      decodeCreated[M.IouTransfer](tx).foreach { contract: Contract[M.IouTransfer] =>
        logger.info(s"$newOwner received contract: $contract")

To exercise IouTransfer_Accept choice on the IouTransfer contract that you received and send a corresponding ExerciseCommand:

        val exerciseCmd = contract.contractId.exerciseIouTransfer_Accept(actor = newOwner)
        clientUtil.submitCommand(newOwner, newOwnerWorkflowId, exerciseCmd) onComplete {
          case Success(_) =>
            logger.info(s"$newOwner sent exercise command: $exerciseCmd")
            logger.info(s"$newOwner accepted IOU Transfer: $contract")
          case Failure(e) =>
            logger.error(s"$newOwner failed to send exercise command: $exerciseCmd", e)
        }

Fore more details on how to subscribe to receive events for a particular party, please refer to the implementation of com.digitalasset.quickstart.iou.IouMain#newOwnerAcceptsAllTransfers.

Authentication

Some ledgers will require you to send an access token along with each request. To learn more about authentication, read the Authentication overview.

To use the same token for all ledger API requests, use the token field of LedgerClientConfiguration:

  private val clientConfig = LedgerClientConfiguration(
    applicationId = ApplicationId.unwrap(applicationId),
    ledgerIdRequirement = LedgerIdRequirement("", enabled = false),
    commandClient = CommandClientConfiguration.default,
    sslContext = None,
    token = None
  )

To specify the token for an individual call, use the token parameter:

transactionClient.getLedgerEnd() // Uses the token specified in LedgerClientConfiguration
transactionClient.getLedgerEnd(token = acessToken) // Uses the given token

Note that if your tokens can change at run time (e.g., because they expire or because you switch users), you will need to specify them on a per-call basis as shown above.