Creating your own bindings¶
This page gets you started with creating custom bindings for a Daml Ledger.
Bindings for a language consist of two main components:
- Ledger API
- Client “stubs” for the programming language, – the remote API that allows sending ledger commands and receiving ledger transactions. You have to generate Ledger API from the gRPC protobuf definitions in the daml repository on GitHub. Ledger API is documented on this page: gRPC. The gRPC tutorial explains how to generate client “stubs”.
- Codegen
- A code generator is a program that generates classes representing Daml contract templates in the language. These classes incorporate all boilerplate code for constructing: CreateCommand and ExerciseCommand corresponding for each Daml contract template.
Technically codegen is optional. You can construct the commands manually from the auto-generated Ledger API classes. However, it is very tedious and error-prone. If you are creating ad hoc bindings for a project with a few contract templates, writing a proper codegen may be overkill. On the other hand, if you have hundreds of contract templates in your project or are planning to build language bindings that you will share across multiple projects, we recommend including a codegen in your bindings. It will save you and your users time in the long run.
Note that for different reasons we chose codegen, but that is not the only option. There is really a broad category of metaprogramming features that can solve this problem just as well or even better than codegen; they are language-specific, but often much easier to maintain (i.e. no need to add a build step). Some examples are:
- F# Type Providers
- Template Haskell
- Scala macro annotations (not future-proof enough to use when implementing the last Scala codegen)
Building Ledger Commands¶
No matter what approach you take, either manually building commands or writing a codegen to do this, you need to understand how ledger commands are structured. This section demonstrates how to build create and exercise commands manually and how it can be done using contract classes generated by Scala codegen.
Create Command¶
Let’s recall an IOU example from the Quickstart guide, where Iou template is defined like this:
template Iou
with
issuer : Party
owner : Party
currency : Text
amount : Decimal
observers : [Party]
Here is how to manually build a CreateCommand for the above contract template in Scala:
def iouCreateCommand(
templateId: Identifier,
issuer: String,
owner: String,
currency: String,
amount: BigDecimal,
): Command.Create = {
val fields = Seq(
RecordField("issuer", Some(Value(Value.Sum.Party(issuer)))),
RecordField("owner", Some(Value(Value.Sum.Party(owner)))),
RecordField("currency", Some(Value(Value.Sum.Text(currency)))),
RecordField("amount", Some(Value(Value.Sum.Numeric(amount.toString)))),
RecordField("observers", Some(Value(Value.Sum.List(List())))),
)
Command.Create(
CreateCommand(
templateId = Some(templateId),
createArguments = Some(Record(Some(templateId), fields)),
)
)
}
If you do not specify any of the above fields or type their names or values incorrectly, or do not order them exactly as they are in the Daml template, the above code will compile but fail at run-time because you did not structure your create command correctly.
Codegen should simplify the command construction by providing auto-generated utilities to help you construct commands. For example, when you use Scala codegen to generate contract classes, a similar contract instantiation would look like this:
val iou = M.Iou(
issuer = issuer,
owner = issuer,
currency = "USD",
amount = BigDecimal("1000.00"),
observers = List(),
)
Exercise Command¶
To build ExerciseCommand for Iou_Transfer:
controller owner can
Iou_Transfer : ContractId IouTransfer
with
newOwner : Party
do create IouTransfer with iou = this; newOwner
manually in Scala:
def iouTransferExerciseCommand(
templateId: Identifier,
contractId: String,
newOwner: String,
): Command.Exercise = {
val transferTemplateId = Identifier(
packageId = templateId.packageId,
moduleName = templateId.moduleName,
entityName = "Iou_Transfer",
)
val fields = Seq(RecordField("newOwner", Some(Value(Value.Sum.Party(newOwner)))))
Command.Exercise(
ExerciseCommand(
templateId = Some(templateId),
contractId = contractId,
choice = "Iou_Transfer",
choiceArgument = Some(Value(Value.Sum.Record(Record(Some(transferTemplateId), fields)))),
)
)
}
versus creating the same command using a value class generated by Scala codegen:
exerciseCmd = iouContract.contractId.exerciseIou_Transfer(actor = issuer, newOwner = newOwner)
Summary¶
When creating custom bindings for Daml Ledgers, you will need to:
- generate Ledger API from the gRPC definitions
- decide whether to write a codegen to generate ledger commands or manually build them for all contracts defined in your Daml model.
The above examples should help you get started. If you are creating custom binding or have any questions, see the Getting Help page for how to get in touch with us.
Links¶
- A Scala example that demonstrates how to manually construct ledger commands: https://github.com/digital-asset/daml/tree/main/language-support/scala/examples/iou-no-codegen
- A Scala codegen example: https://github.com/digital-asset/daml/tree/main/language-support/scala/examples/quickstart-scala
- gRPC documentation: https://grpc.io/docs/
- Documentation for Protobuf “well known types”: https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
- Daml Ledger API gRPC Protobuf definitions
- current main: https://github.com/digital-asset/daml/tree/main/ledger-api/grpc-definitions
- for specific versions: https://github.com/digital-asset/daml/releases