Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.canton.network/llms.txt

Use this file to discover all available pages before exploring further.

This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/structure.rst Reviewers: Skip this section. Remove markers after final approval.

Overview: Template Structure

This page covers what a template looks like: what parts of a template there are, and where they go. For the structure of a Daml file outside a template, see file-structure.

Template Outline Structure

Here’s the structure of a Daml template:
template NameOfTemplate
  with
    exampleParty : Party
    exampleParty2 : Party
    exampleParty3 : Party
    exampleParameter : Text
    -- more parameters here
  where
    signatory exampleParty
    observer exampleParty2
    ensure
      -- boolean condition
      True
    key (exampleParty, exampleParameter) : (Party, Text)
    maintainer (exampleFunction key)
    -- a choice goes here; see next section
template name template keyword parameters with followed by the names of parameters and their types template body where keyword Can include: template-local definitions (deprecated) let keyword Lets you make definitions that have access to the contract arguments and are available in the rest of the template definition. signatories signatory keyword Required. The parties (see the Party type) who must consent to the creation of this contract. You won’t be able to create this contract until all of these parties have authorized it. observers observer keyword Optional. Parties that aren’t signatories but who you still want to be able to see this contract. a precondition ensure keyword Only create the contract if the conditions after ensure evaluate to true. a contract key key keyword Optional. Lets you specify a combination of a party and other data that uniquely identifies a contract of this template. See Contract Keys and Maintainers. maintainers maintainer keyword Required if you have specified a key. Keys are only unique to a maintainer. See Contract Keys and Maintainers. choices choice NameOfChoice : ReturnType controller nameOfParty do Defines choices that can be exercised. See Choice structure for what can go in a choice.

Choice Structure

Here is the structure of a choice inside a template:
choice NameOfChoice
  : () -- replace () with the actual return type
  with
    party : Party -- parameters here
  controller party
  do
    return () -- replace this line with the choice body
consumption annotation Optionally one of preconsuming, postconsuming, nonconsuming, which changes the behavior of the choice with respect to privacy and if and when the contract is archived. See contract consumption in choices for more details. a name Must begin with a capital letter. Must be unique - choices in different templates can’t have the same name. a return type after a :, the return type of the choice choice arguments with keyword If you include a Party as a choice argument, you can make that Party the controller of the choice. This means that the controller can be specified when the choice is exercised, rather than when the contract is created. For the exercise to work, the party needs to be able to see the contract, i.e. it must be an observer or a signatory. a controller (or controllers) controller keyword Who can exercise the choice. choice observers observer keyword Optional. Additional parties that are guaranteed to be informed of an exercise of the choice. To specify choice observers, you must start you choice with the choice keyword. The optional observer keyword must precede the mandatory controller keyword. a choice body After do keyword What happens when someone exercises the choice. A choice body can contain update statements: see Choice body structure below.

Choice Body Structure

A choice body contains Update expressions, wrapped in a do block. The update expressions are: create Create a new contract of this template. create NameOfContract with contractArgument1 = value1; contractArgument2 = value2; ... exercise Exercise a choice on a particular contract. exercise idOfContract NameOfChoiceOnContract with choiceArgument1 = value1; choiceArgument2 = value 2; ... fetch Fetch a contract using its ID. Often used with assert to check conditions on the contract’s content. fetchedContract <- fetch IdOfContract fetchByKey Like fetch, but uses a contract key rather than an ID. fetchedContract <- fetchByKey @ContractType contractKey lookupByKey Confirm that a contract with the given contract key exists. fetchedContractId <- lookupByKey @ContractType contractKey abort Stop execution of the choice, fail the update. if False then abort assert Fail the update unless the condition is true. Usually used to limit the arguments that can be supplied to a contract choice. assert (amount > 0) getTime Gets the ledger time. Usually used to restrict when a choice can be exercised. currentTime <- getTime return Explicitly return a value. By default, a choice returns the result of its last update expression. This means you only need to use return if you want to return something else. return ContractID ExampleTemplate The choice body can also contain: let keyword Used to assign values or functions. assign a value to the result of an update statement For example: contractFetched <- fetch someContractId
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/templates.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Templates

This page gives reference information on templates: For the structure of a template, see structure.

Template Name

template NameOfTemplate
  • This is the name of the template. It’s preceded by template keyword. Must begin with a capital letter.
  • This is the highest level of nesting.
  • The name is used when creating a contract of this template (usually, from within a choice).

Template Parameters

with
  exampleParty : Party
  exampleParty2 : Party
  exampleParty3 : Party
  exampleParam : Text
  -- more parameters here
  • with keyword. The parameters are in the form of a record type.
  • Passed in when creating a contract from this template. These are then in scope inside the template body.
  • A template parameter can’t have the same name as any choice arguments inside the template.
  • For all parties involved in the contract (whether they’re a signatory, observer, or controller) you must pass them in as parameters to the contract, whether individually or as a list ([Party]).

Implicit Record

Whenever a template is defined, a record is implicitly defined with the same name and fields as that template. This record structure is used in Daml code to represent the data of a contract based on that template. Note that in the general case, the existence of a local binding b of type T, where T is a template (and thus also a record), does not necessarily imply the existence of a contract with the same data as b on the ledger. You can only assume the existence of such a contract if b is the result of a fetch from the ledger within the same transaction. You can create a new instance of a record of type T without any interaction with the ledger; in fact, this is how you construct a create command.

this and self

Within the body of a template we implicitly define a local binding this to represent the data of the current contract. For a template T, this binding is of type T, i.e. the implicit record defined by the template. Within choices, you can additionally use the binding self to refer to the contract ID of the current contract (the one on which the choice is being executed). For a contract of template T, the self binding is of type ContractId T.

Template-local Definitions (Deprecated)

Fix or remove this literal include
  • let keyword. Starts a block and is followed by any number of definitions, just like any other let block.
  • Template parameters as well as this are in scope, but self is not.
  • Definitions from the let block can be used anywhere else in the template’s where block.
Since Daml 2.8.0, template-local definitions are deprecated and their presence will result in the following warning:
Template-local binding syntax ("template-let") is deprecated,
it will be removed in a future version of Daml.
Instead, use plain top level definitions, taking parameters
for the contract fields or body ("this") if necessary.
The reason for this deprecation is that some uses of the this keyword in template-local definitions would create implicit circular dependencies, causing an infinite loop upon evaluation.

Migration

Users are strongly encouraged to adapt their code to avoid this feature. This involves replacing each template-local definition with a regular top-level definition. If the old definition made use of contract fields or the contract body (“this”), the new definition should take them as parameters. Correspondingly, the use sites of these definitions should supply the appropriate values as arguments. For example, consider the template Person below. It defines and uses a template-local binding fullName, which now triggers the deprecation warning.
template Person
  with
    owner : Party
    first : Text
    last : Text
  where
    signatory owner
    let fullName = last <> ", " <> first
    nonconsuming choice GetDescription : ()
      controller owner
      do
        let desc = "An account owned by " <> fullName <> "."
        debug desc
To ensure this code keeps working after the feature is removed, fullName should be defined as a top-level function, and its use site now passes this explicitly.
fullName : Person -> Text
fullName Person {first, last} = last <> ", " <> first
-- takes 'Person' as an explicit parameter and unpacks required fields

template Person
  with
    owner : Party
    first : Text
    last : Text
  where
    signatory owner
    nonconsuming choice GetDescriptionV3 : ()
      controller owner
      do
        -- let bindings in choice bodies are unaffected
        let desc = "An account owned by " <> fullName this <> "."
                                             -- 'this' is passed explicitly
        debug desc

Turning off the warning

This warning is controlled by the warning flag template-let, which means that it can be toggled independently of other warnings. This is especially useful for gradually migrating code that used this syntax. To turn off the warning within a Daml file, add the following line at the top of the file:
{-# OPTIONS_GHC -Wno-template-let #-}
To turn it off for an entire Daml project, add the following entry to the build-options field of the project’s daml.yaml file
build-options:
- --ghc-option=-Wno-template-let
Within a project where the warning has been turned off via the daml.yaml file, it can be turned back on for individual Daml files by adding the following line at the top of each file:
{-# OPTIONS_GHC -Wtemplate-let #-}

Signatory Parties

signatory exampleParty
  • signatory keyword. After where. Followed by at least one Party.
  • Signatories are the parties (see the Party type) who must consent to the creation of this contract. They are the parties who would be put into an obligable position when this contract is created. Daml won’t let you put someone into an obligable position without their consent. So if the contract will cause obligations for a party, they must be a signatory. If they haven’t authorized it, you won’t be able to create the contract. In this situation, you may see errors like: NameOfTemplate requires authorizers Party1,Party2,Party, but only Party1 were given.
  • When a signatory consents to the contract creation, this means they also authorize the consequences of choices that can be exercised on this contract.
  • The contract is visible to all signatories (as well as the other stakeholders of the contract). That is, the compiler automatically adds signatories as observers.
  • Each template must have at least one signatory. A signatory declaration consists of the signatory keyword followed by a comma-separated list of one or more expressions, each expression denoting a Party or collection thereof.

Observers

observer exampleParty2
  • observer keyword. After where. Followed by at least one Party.
  • Observers are additional stakeholders, so the contract is visible to these parties (see the Party type).
  • Optional. You can have many, either as a comma-separated list or reusing the keyword. You could pass in a list (of type [Party]).
  • Use when a party needs visibility on a contract, or be informed or contract events, but is not a signatory or controller.
  • If you start your choice with choice rather than controller (see daml-ref-choices below), you must make sure to add any potential controller as an observer. Otherwise, they will not be able to exercise the choice, because they won’t be able to see the contract.

Choices

choice NameOfChoice
  : ()  -- replace () with the actual return type
  with
    exampleParameter : Text -- parameters here
  controller exampleParty
  do
    return () -- replace this line with the choice body
  • A right that the contract gives the controlling party. Can be exercised.
  • This is essentially where all the logic of the template goes.
  • By default, choices are consuming: that is, exercising the choice archives the contract, so no further choices can be exercised on it. You can make a choice non-consuming using the nonconsuming keyword.
  • See choices for full reference information.

Serializable Types

Every parameter to a template, choice argument, and choice result must have a serializable type. This does not merely mean “convertible to bytes”; it has a specific meaning in Daml. The serializability rule serves three purposes:
  1. Offer a stable means to store ledger values permanently.
  2. Provide a sensible encoding of them over the ledger-api.
  3. Provide sensible types that directly match their Daml counterparts in languages like Java for language codegen.
For example, certain kinds of type parameters Daml offers are compatible with (1) and (2), but have no proper counterpart in (3), so they are disallowed. Similarly, function types have sensible Java counterparts, satisfying (3), but no reliable way to store or share them via the API, thus failing (1) and (2). The following types are not serializable, and thus may not be used in templates.
  • Function types.
  • Record types with any non-serializable field.
  • Variant types with any non-serializable value case.
  • Variant and enum types with no constructors.
  • References to a parameterized data type with any non-serializable type argument. This applies whether or not the data type definition uses the type parameter.
  • Defined data types with any type parameter of kind Nat, or any kind other than *. This means higher-kinded types, and types that take a parameter just to pass to Numeric, are not serializable.

Migration

Users should remove any agreement declarations from their code, as this feature has been fully removed from the language.

Preconditions

ensure
  True -- a boolean condition goes here
  • ensure keyword, followed by a boolean condition.
  • Used on contract creation. ensure limits the values on parameters that can be passed to the contract: the contract can only be created if the boolean condition is true.

Contract Keys and Maintainers

key (exampleParty, exampleParam) : (Party, Text)
maintainer (exampleFunction key)
  • key and maintainer keywords.
  • This feature lets you specify a “key” that you can use to uniquely identify this contract as an instance of this template.
  • If you specify a key, you must also specify a maintainer. This is a Party that will ensure the uniqueness of all the keys it is aware of. Because of this, the key must include the maintainer Party or parties (for example, as part of a tuple or record), and the maintainer must be a signatory.
  • For a full explanation, see contractkeys.

Interface Instances

interface instance MyInterface for NameOfTemplate where
  view = MyInterfaceViewType "NameOfTemplate" 100
  method1 = field1
  method2 = field2
  method3 False _ _ = 0
  method3 True x y
    | x > 0 = x + y
    | otherwise = y
  • Used to make a template an instance of an existing interface.
  • The clause must start with the keywords interface instance, followed by the name of the interface, then the keyword for and the name of the template (which must match the enclosing declaration), and finally the keyword where, which introduces a block where all the methods of the interface must be implemented.
  • See interfaces for full reference information on interfaces, or section interface-instances for interface instances specifically.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/choices.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Choices

This page gives reference information on choices. For information on the high-level structure of a choice, see structure.

Choice Name

choice ExampleChoice
  : () -- replace () with the actual return type
  • choice keyword
  • The name of the choice. Must begin with a capital letter.
  • Must be unique in the module. Different templates defined in the same module cannot share a choice name.

Controllers

controller exampleParty
  • controller keyword
  • The controller is a comma-separated list of values, where each value is either a party or a collection of parties. The conjunction of all the parties are required to authorize when this choice is exercised.
You must make sure that the controller parties are observers (or signatories) of the contract, otherwise they cannot see the contract (and therefore cannot exercise the choice).

Choice Observers

Choice observers can be attached to a choice using the observer keyword. The choice observers are a list of parties who are not stakeholders but who see all the consequences of the action.
choice NameOfChoiceWithObserver
  : () -- replace () with the actual return type
  with
    party : Party -- parameters here
  observer party -- optional specification of choice observers
  controller exampleParty
  do
    return () -- replace this line with the choice body

Contract Consumption

If no qualifier is present, choices are consuming: the contract is archived before the evaluation of the choice body and both the controllers and all contract stakeholders see all consequences of the action.

Preconsuming Choices

preconsuming choice ExamplePreconsumingChoice
  : () -- replace () with the actual return type
  • preconsuming keyword. Optional.
  • Makes a choice pre-consuming: the contract is archived before the body of the exercise is executed.
  • The create arguments of the contract can still be used in the body of the exercise, but cannot be fetched by its contract id.
  • The archival behavior is analogous to the consuming default behavior.
  • Only the controllers and signatories of the contract see all consequences of the action. Other stakeholders merely see an archive action.
  • Can be thought as a non-consuming choice that implicitly archives the contract before anything else happens

Postconsuming Choices

postconsuming choice ExamplePostconsumingChoice
  : () -- replace () with the actual return type
  • postconsuming keyword. Optional.
  • Makes a choice post-consuming: the contract is archived after the body of the exercise is executed.
  • The create arguments of the contract can still be used in the body of the exercise as well as the contract id for fetching it.
  • Only the controllers and signatories of the contract see all consequences of the action. Other stakeholders merely see an archive action.
  • Can be thought as a non-consuming choice that implicitly archives the contract after the choice has been exercised

Non-consuming Choices

nonconsuming choice ExampleNonconsumingChoice
  : () -- replace () with the actual return type
  • nonconsuming keyword. Optional.
  • Makes a choice non-consuming: that is, exercising the choice does not archive the contract.
  • Only the controllers and signatories of the contract see all consequences of the action.
  • Useful in the many situations when you want to be able to exercise a choice more than once.

Return Type

  • Return type is written immediately after choice name.
  • All choices have a return type. A contract returning nothing should be marked as returning a “unit”, ie ().
  • If a contract is/contracts are created in the choice body, usually you would return the contract ID(s) (which have the type ContractId <name of template>). This is returned when the choice is exercised, and can be used in a variety of ways.

Choice Arguments

with
  exampleParameter : Text
  • with keyword.
  • Choice arguments are similar in structure to daml-ref-template-parameters: a record type.
  • A choice argument can’t have the same name as any parameter to the template the choice is in.
  • Optional - only if you need extra information passed in to exercise the choice.

Choice Body

  • Introduced with do
  • The logic in this section is what is executed when the choice gets exercised.
  • The choice body contains Update expressions. For detail on this, see updates.
  • By default, the last expression in the choice is returned. You can return multiple updates in tuple form or in a custom data type. To return something that isn’t of type Update, use the return keyword.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/updates.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Updates

This page gives reference information on Updates. For the structure around them, see structure.

Background

  • An Update is ledger update. There are many different kinds of these, and they’re listed below.
  • They are what can go in a choice body.

Binding Variables

boundVariable <- UpdateExpression1
  • One of the things you can do in a choice body is bind (assign) an Update expression to a variable. This works for any of the Updates below.

do

do
   updateExpression1
   updateExpression2
  • do can be used to group Update expressions. You can only have one update expression in a choice, so any choice beyond the very simple will use a do block.
  • Anything you can put into a choice body, you can put into a do block.
  • By default, do returns whatever is returned by the last expression in the block. So if you want to return something else, you’ll need to use return explicitly - see daml-ref-return for an example.

archive

archive ContractId
  • archive function.
  • Archives a contract already created and residing on the ledger. The contract is fetched by its unique contract identifier ContractId <name of template> and then exercises the Archive choice on it.
  • Returns unit.
  • Requires authorization from the contract controllers/signatories. Without the required authorization, the transaction fails. For more detail on authorization, see daml-ref-signatories.
  • All templates implicitly have an Archive choice that cannot be removed, which is equivalent to:
choice Archive : ()
  controller (signatory this)
  do return ()

create

create NameOfTemplate with exampleParameters
  • create function.
  • Creates a contract on the ledger. When a contract is committed to the ledger, it is given a unique contract identifier of type ContractId <name of template>.
  • Creating the contract returns that ContractId.
  • Use with to specify the template parameters.
  • Requires authorization from the signatories of the contract being created. This is given by being signatories of the contract from which the other contract is created, being the controller, or explicitly creating the contract itself. If the required authorization is not given, the transaction fails. For more detail on authorization, see daml-ref-signatories.

exercise

exercise IdOfContract NameOfChoiceOnContract with choiceArgument1 = value1
  • exercise function.
  • Exercises the specified choice on the specified contract.
  • Use with to specify the choice parameters.
  • Requires authorization from the controller(s) of the choice. If the authorization is not given, the transaction fails.

exerciseByKey

exerciseByKey @ContractType contractKey NameOfChoiceOnContract with choiceArgument1 = value1
  • exerciseByKey function.
  • Like exercise, but the contract is specified by contract key, instead of contract ID.
  • For details see Reference: Contract Keys: exerciseByKey

fetch

fetchedContract <- fetch IdOfContract
  • fetch function.
  • Fetches the contract with that ID. Usually used with a bound variable, as in the example above.
  • Often used to check the details of a contract before exercising a choice on that contract. Also used when referring to some reference data.
  • fetch cid fails if cid is not the contract id of an active contract, and thus causes the entire transaction to abort.
  • The submitting party must be an observer or signatory on the contract, otherwise fetch fails, and similarly causes the entire transaction to abort.

fetchByKey

fetchedContract <- fetchByKey @ContractType contractKey
  • fetchByKey function.
  • Like fetch, but fetches the contract with that contract key, instead of the contract ID.
  • For details see Reference: Contract Keys: fetchByKey.

visibleByKey

isVisible <- visibleByKey @ContractType contractKey
  • visibleByKey function.
  • Use this to check whether a contract with the given contract key exists.
  • For details see Reference: Contract Keys: visibleByKey

lookupByKey

fetchedContractId <- lookupByKey @ContractType contractKey
  • lookupByKey function.
  • Use this to confirm that a contract with the given contract key exists.
  • For details see Reference: Contract Keys: lookupByKey

abort

abort errorMessage
  • abort function.
  • Fails the transaction - nothing in it will be committed to the ledger.
  • errorMessage is of type Text. Use the error message to provide more context to an external system (e.g., it gets displayed in Daml Studio script results).
  • You could use assert False as an alternative.

assert

assert (condition == True)
  • assert keyword.
  • Fails the transaction if the condition is false. So the choice can only be exercised if the boolean expression evaluates to True.
  • Often used to restrict the arguments that can be supplied to a contract choice.
Here’s an example of using assert to prevent a choice being exercised if the Party passed as a parameter is on a blacklist:
choice Transfer : ContractId RestrictedPayout
  with newReceiver : Party
  controller receiver
  do
    assert (newReceiver /= blacklisted)
    create RestrictedPayout with receiver = newReceiver; giver; blacklisted; qty

getTime

currentTime <- getTime
  • getTime keyword.
  • Gets the ledger time. (You will usually want to immediately bind it to a variable in order to be able to access the value.)
  • Used to restrict when a choice can be made. For example, with an assert that the time is later than a certain time.
Here’s an example of a choice that uses a check on the current time:
choice Complete : ()
  controller party
  do
    -- bind the ledger effective time to the tchoose variable using getTime
    tchoose <- getTime
    -- assert that tchoose is no earlier than the begin time
    assert (begin <= tchoose && tchoose < addRelTime begin period)

return

return ()
  • return keyword.
  • Used to return a value from do block that is not of type Update.
Here’s an example where two contracts are created in a choice and both their ids are returned as a tuple:
do
  firstContract <- create SomeContractTemplate with arg1; arg2
  secondContract <- create SomeContractTemplate with arg1; arg2
  return (firstContract, secondContract)

let

See the documentation on daml-ref-let. Let looks similar to binding variables, but it’s very different! This code example shows how:
do
  -- defines a function, createdContract, taking a single argument that when
  -- called _will_ create the new contract using argument for issuer and owner
  let createContract x = create NameOfContract with issuer = x; owner = x

  createContract party1
  createContract party2

this

this lets you refer to the current contract from within the choice body. This refers to the contract, not the contract ID. It’s useful, for example, if you want to pass the current contract to a helper function outside the template.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/data-types.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Data Types

This page gives reference information on Daml’s data types.

Built-in Types

Table of built-in primitive types

TypeForExampleNotes
Intintegers1, 1000000, 1_000_000Int values are signed 64-bit integers which represent numbers between -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807 inclusive. Arithmetic operations raise an error on overflows and division by 0. To make long numbers more readable you can optionally add underscores.
Decimalshort for Numeric 101.0Decimal values are rational numbers with precision 38 and scale 10.
Numeric nfixed point decimal numbers1.0Numeric n values are rational numbers with 38 total digits. The scale parameter n controls the number of digits after the decimal point, so for example, Numeric 10 values have 28 digits before the decimal point and 10 digits after it, and Numeric 20 values have 18 digits before the decimal point and 20 digits after it. The value of n must be between 0 and 37 inclusive.
Textstrings"hello"Text values are strings of characters enclosed by double quotes.
Boolboolean valuesTrue, False
Partyunicode string representing a partyalice <- getParty "Alice"Every party in a Daml system has a unique identifier of type Party. To create a value of type Party, use binding on the result of calling getParty. The party text can only contain alphanumeric characters, -, _ and spaces.
Datemodels datesdate 2007 Apr 5Permissible dates range from 0001-01-01 to 9999-12-31 (using a year-month-day format). To create a value of type Date, use the function date (to get this function, import DA.Date).
Timemodels absolute time (UTC)time (date 2007 Apr 5) 14 30 05Time values have microsecond precision with allowed range from 0001-01-01 to 9999-12-31 (using a year-month-day format). To create a value of type Time, use a Date and the function time (to get this function, import DA.Time).
RelTimemodels differences between time valuesseconds 1, seconds (-2)RelTime values have microsecond precision with allowed range from -9,223,372,036,854,775,808ms to 9,223,372,036,854,775,807ms There are no literals for RelTime. Instead they are created using one of days, hours, minutes, seconds, milliseconds and microseconds (to get these functions, import DA.Time).

Escaping Characters

Text literals support backslash escapes to include their delimiter (\") and a backslash itself (\\).

Time

Definition of time on the ledger is a property of the execution environment. Daml assumes there is a shared understanding of what time is among the stakeholders of contracts.

Lists

[a] is the built-in data type for a list of elements of type a. The empty list is denoted by [] and [1, 3, 2] is an example of a list of type [Int]. You can also construct lists using [] (the empty list) and :: (which is an operator that appends an element to the front of a list). For example:
twoEquivalentListConstructions =
  script do
    assert ( [1, 2, 3] == 1 :: 2 :: 3 :: [] )

Sum a List

To sum a list, use a fold (because there are no loops in Daml). See daml-ref-folding for details.

Records and Record Types

You declare a new record type using the data and with keyword:
data MyRecord = MyRecord
  with
    label1 : type1
    label2 : type2
    ...
    labelN : typeN
  deriving (Eq, Show)
where:
  • label1, label2, …, labelN are labels, which must be unique in the record type
  • type1, type2, …, typeN are the types of the fields
There’s an alternative way to write record types:
data MyRecord = MyRecord { label1 : type1; label2 : type2; ...; labelN : typeN }
  deriving (Eq, Show)
The format using with and the format using { } are exactly the same syntactically. The main difference is that when you use with, you can use newlines and proper indentation to avoid the delimiting semicolons. The deriving (Eq, Show) ensures the data type can be compared (using ==) and displayed (using show). The line starting deriving is required for data types used in fields of a template. In general, add the deriving unless the data type contains function types (e.g. Int -> Int), which cannot be compared or shown. For example:
-- This is a record type with two fields, called first and second,
-- both of type `Int`
data MyRecord = MyRecord with first : Int; second : Int
  deriving (Eq, Show)

-- An example value of this type is:
newRecord = MyRecord with first = 1; second = 2

-- You can also write:
newRecord = MyRecord 1 2

Data Constructors

You can use data keyword to define a new data type, for example data Floor a = Floor a for some type a. The first Floor in the expression is the type constructor. The second Floor is a data constructor that can be used to specify values of the Floor Int type: for example, Floor 0, Floor 1. In Daml, data constructors may take at most one argument. An example of a data constructor with zero arguments is data Empty = Empty {}. The only value of the Empty type is Empty.
In data Confusing = Int, the Int is a data constructor with no arguments. It has nothing to do with the built-in Int type.

Access Record Fields

To access the fields of a record type, use dot notation. For example:
-- Access the value of the field `first`
val.first

-- Access the value of the field `second`
val.second

Update Record Fields

You can also use the with keyword to create a new record on the basis of an existing replacing select fields. For example:
myRecord = MyRecord with first = 1; second = 2

myRecord2 = myRecord with second = 5
produces the new record value MyRecord with first = 1; second = 5. If you have a variable with the same name as the label, Daml lets you use this without assigning it to make things look nicer:
-- if you have a variable called `second` equal to 5
second = 5

-- you could construct the same value as before with
myRecord2 = myRecord with second = second

-- or with
myRecord3 = MyRecord with first = 1; second = second

-- but Daml has a nicer way of putting this:
myRecord4 = MyRecord with first = 1; second

-- or even
myRecord5 = r with second
The with keyword binds more strongly than function application. So for a function, say return, either write return IntegerCoordinate with first = 1; second = 5 or return (IntegerCoordinate {first = 1; second = 5}), where the latter expression is enclosed in parentheses.

Parameterized Data Types

Daml supports parameterized data types. For example, to express a more general type for 2D coordinates:
-- Here, a and b are type parameters.
-- The Coordinate after the data keyword is a type constructor.
data Coordinate a b = Coordinate with first : a; second : b
An example of a type that can be constructed with Coordinate is Coordinate Int Int.

Type Synonyms

To declare a synonym for a type, use the type keyword. For example:
type IntegerTuple = (Int, Int)
This makes IntegerTuple and (Int, Int) synonyms: they have the same type and can be used interchangeably. You can use the type keyword for any type, including daml-ref-built-in-types.

Function Types

A function’s type includes its parameter and result types. A function foo with two parameters has type ParamType1 -> ParamType2 -> ReturnType. Note that this can be treated as any other type. You could for instance give it a synonym using type FooType = ParamType1 -> ParamType2 -> ReturnType.

Algebraic Data Types

An algebraic data type is a composite type: a type formed by a combination of other types. The enumeration data type is an example. This section introduces more powerful algebraic data types.

Product Types

The following data constructor is not valid in Daml: data AlternativeCoordinate a b = AlternativeCoordinate a b. This is because data constructors can only have one argument. To get around this, wrap the values in a record: data Coordinate a b = Coordinate {first: a; second: b}. These kinds of types are called product types. A way of thinking about this is that the Coordinate Int Int type has a first and second dimension (that is, a 2D product space). By adding an extra type to the record, you get a third dimension, and so on.

Sum Types

Sum types capture the notion of being of one kind or another. An example is the built-in data type Bool. This is defined by data Bool = True | False deriving (Eq,Show), where True and False are data constructors with zero arguments . This means that a Bool value is either True or False and cannot be instantiated with any other value. Please note that all types which you intend to use as template or choice arguments need to derive at least from (Eq, Show). A very useful sum type is data Optional a = None | Some a deriving (Eq,Show). It is part of the Daml standard library. Optional captures the concept of a box, which can be empty or contain a value of type a. Optional is a sum type constructor taking a type a as parameter. It produces the sum type defined by the data constructors None and Some. The Some data constructor takes one argument, and it expects a value of type a as a parameter.

Pattern Matching

You can match a value to a specific pattern using the case keyword. The pattern is expressed with data constructors. For example, the Optional Int sum type:
import Daml.Script
import DA.Assert

optionalIntegerToText (x : Optional Int) : Text =
  case x of
    None -> "Box is empty"
    Some val -> "The content of the box is " <> show val

optionalIntegerToTextTest =
  script do
In the optionalIntegerToText function, the case construct first tries to match the x argument against the None data constructor, and in case of a match, the "Box is empty" text is returned. In case of no match, a match is attempted for x against the next pattern in the list, i.e., with the Some data constructor. In case of a match, the content of the value attached to the Some label is bound to the val variable, which is then used in the corresponding output text string. Note that all patterns in the case construct need to be complete, i.e., for each x there must be at least one pattern that matches. The patterns are tested from top to bottom, and the expression for the first pattern that matches will be executed. Note that _ can be used as a catch-all pattern. You could also case distinguish a Bool variable using the True and False data constructors and achieve the same behavior as an if-then-else expression. As an example, the following is an expression for a Text:


tmp =
  let
    l = [1, 2, 3]
  in case l of
Notice the use of nested pattern matching above.
An underscore was used in place of a variable name. The reason for this is that Daml Studio produces a warning for all variables that are not being used. This is useful in detecting unused variables. You can suppress the warning by naming the variable with an initial underscore.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/expressions.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Expressions

This page gives reference information for Daml expressions that are not updates.

Definitions

Use assignment to bind values or functions at the top level of a Daml file or in a contract template body.

Values

For example:
-- Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

-- The TubeSurfaceArea example.

module TubeSurfaceArea where


pi = 3.1415926535

tubeSurfaceArea2 (r : Decimal) (h : Decimal) : Decimal =
  2.0 * pi * r * h

tubeSurfaceArea : Decimal -> Decimal -> Decimal 
tubeSurfaceArea r h  =
  2.0 * pi * r * h

tubeSurfaceArea3 = \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h
The fact that pi has type Decimal is inferred from the value. To explicitly annotate the type, mention it after a colon following the variable name:
-- Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

-- The TubeSurfaceArea example.

module TubeSurfaceArea2 where

-- Type synonym for Decimal -> Decimal -> Decimal
type BinaryDecimalFunction = Decimal -> Decimal -> Decimal

pi : Decimal = 3.1415926535

tubeSurfaceArea : BinaryDecimalFunction =
  \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h

Functions

You can define functions. Here’s an example: a function for computing the surface area of a tube:
tubeSurfaceArea : Decimal -> Decimal -> Decimal 
tubeSurfaceArea r h  =
  2.0 * pi * r * h
Here you see:
  • the name of the function
  • the function’s type signature Decimal -> Decimal -> Decimal This means it takes two Decimals and returns another Decimal.
  • the definition = 2.0 * pi * r * h (which uses the previously defined pi)

Arithmetic Operators

OperatorWorks for
+Int, Decimal, RelTime
-Int, Decimal, RelTime
*Int, Decimal
/ (integer division)Int
% (integer remainder operation)Int
^ (integer exponentiation)Int
The result of the modulo operation has the same sign as the dividend:
  • 7 / 3 and (-7) / (-3) evaluate to 2
  • (-7) / 3 and 7 / (-3) evaluate to -2
  • 7 % 3 and 7 % (-3) evaluate to 1
  • (-7) % 3 and (-7) % (-3) evaluate to -1
To write infix expressions in prefix form, wrap the operators in parentheses. For example, (+) 1 2 is another way of writing 1 + 2.

Comparison Operators

OperatorWorks for
<, <=, >, >=Bool, Text, Int, Decimal, Party, Time
==, /=Bool, Text, Int, Decimal, Party, Time, and identifiers of contracts stemming from the same contract template

Logical Operators

The logical operators in Daml are:
  • not for negation, e.g., not True == False
  • && for conjunction, where a && b == and a b
  • || for disjunction, where a || b == or a b
for Bool variables a and b.

If-then-else

You can use conditional if-then-else expressions, for example:
if owner == scroogeMcDuck then "sell" else "buy"

Let

To bind values or functions to be in scope beneath the expression, use the block keyword let:
doubled =
  -- let binds values or functions to be in scope beneath the expression
  let
    double (x : Int) = 2 * x
    up = 5
  in double up
You can also use let inside do blocks:
blah = script
  do
    let
      x = 1
      y = 2
      -- x and y are in scope for all subsequent expressions of the do block,
      -- so can be used in expression1 and expression2.
    expression1
    expression2
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/functions.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Functions

This page gives reference information on functions in Daml. Daml is a functional language. It lets you apply functions partially and also have functions that take other functions as arguments. This page discusses these higher-order functions.

Defining Functions

In expressions, the tubeSurfaceArea function was defined as:
tubeSurfaceArea : Decimal -> Decimal -> Decimal 
tubeSurfaceArea r h  =
  2.0 * pi * r * h
You can define this function equivalently using lambdas, involving \, a sequence of parameters, and an arrow -> as:
tubeSurfaceArea : BinaryDecimalFunction =
  \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h

Partial Application

The type of the tubeSurfaceArea function described previously, is Decimal -> Decimal -> Decimal. An equivalent, but more instructive, way to read its type is: Decimal -> (Decimal -> Decimal): saying that tubeSurfaceArea is a function that takes one argument and returns another function. So tubeSurfaceArea expects one argument of type Decimal and returns a function of type Decimal -> Decimal. In other words, this function returns another function. Only the last application of an argument yields a non-function. This is called currying: currying is the process of converting a function of multiple arguments to a function that takes just a single argument and returns another function. In Daml, all functions are curried. This doesn’t affect things that much. If you use functions in the classical way (by applying them to all parameters) then there is no difference. If you only apply a few arguments to the function, this is called partial application. The result is a function with partially defined arguments. For example:
multiplyThreeNumbers : Int -> Int -> Int -> Int
multiplyThreeNumbers xx yy zz =
  xx * yy * zz

multiplyTwoNumbersWith7 = multiplyThreeNumbers 7

multiplyWith21 = multiplyTwoNumbersWith7 3

multiplyWith18 = multiplyThreeNumbers 3 6
You could also define equivalent lambda functions:
multiplyWith18_v2 : Int -> Int
multiplyWith18_v2 xx =
  multiplyThreeNumbers 3 6 xx

Functions are Values

The function type can be explicitly added to the tubeSurfaceArea function (when it is written with the lambda notation):
-- Type synonym for Decimal -> Decimal -> Decimal
type BinaryDecimalFunction = Decimal -> Decimal -> Decimal

pi : Decimal = 3.1415926535

tubeSurfaceArea : BinaryDecimalFunction =
  \ (r : Decimal) (h : Decimal) -> 2.0 * pi * r * h
Note that tubeSurfaceArea : BinaryDecimalFunction = ... follows the same pattern as when binding values, e.g., pi : Decimal = 3.14159265359. Functions have types, just like values. Which means they can be used just like normal variables. In fact, in Daml, functions are values. This means a function can take another function as an argument. For example, define a function applyFilter: (Int -> Int -> Bool) -> Int -> Int -> Bool which applies the first argument, a higher-order function, to the second and the third arguments to yield the result.
applyFilter (filter : Int -> Int -> Bool)
    (x : Int)
    (y : Int) = filter x y

compute = script do
    applyFilter (<) 3 2 === False
    applyFilter (/=) 3 2 === True

    round (2.5 : Decimal) === 3
    round (3.5 : Decimal) === 4

    explode "me" === ["m", "e"]

    applyFilter (\a b -> a /= b) 3 2 === True
The daml-ref-folding section looks into two useful built-in functions, foldl and foldr, that also take a function as an argument.
Daml does not allow functions as parameters of contract templates and contract choices. However, a follow up of a choice can use built-in functions, defined at the top level or in the contract template body.

Generic Functions

A function is parametrically polymorphic if it behaves uniformly for all types, in at least one of its type parameters. For example, you can define function composition as follows:
compose (f : b -> c) (g : a -> b) (x : a) : c = f (g x)
where a, b, and c are any data types. Both compose ((+) 4) ((*) 2) 3 == 10 and compose not ((&&) True) False evaluate to True. Note that ((+) 4) has type Int -> Int, whereas not has type Bool -> Bool. You can find many other generic functions including this one in the Daml standard library.
Daml currently does not support generic functions for a specific set of types, such as Int and Decimal numbers. For example, sum (x: a) (y: a) = x + y is undefined when a equals the type Party. Bounded polymorphism might be added to Daml in a later version.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/file-structure.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Daml File Structure

This page gives reference information on the structure of Daml files outside of templates.

File Structure

  • This file’s module name (module NameOfThisFile where). Part of a hierarchical module system to facilitate code reuse. Must be the same as the Daml file name, without the file extension. For a file with path ./Scenarios/Demo.daml, use module Scenarios.Demo where.

Imports

  • You can import other modules (import OtherModuleName), including qualified imports (import qualified AndYetOtherModuleName, import qualified AndYetOtherModuleName as Signifier). Can’t have circular import references.
  • To import the Prelude module of ./Prelude.daml, use import Prelude.
  • To import a module of ./Scenarios/Demo.daml, use import Scenarios.Demo.
  • If you leave out qualified, and a module alias is specified, top-level declarations of the imported module are imported into the module’s namespace as well as the namespace specified by the given alias.

Libraries

A Daml library is a collection of related Daml modules. Define a Daml library using a LibraryModules.daml file: a normal Daml file that imports the root modules of the library. The library consists of the LibraryModules.daml file and all its dependencies, found by recursively following the imports of each module. Errors are reported in Daml Studio on a per-library basis. This means that breaking changes on shared Daml modules are displayed even when the files are not explicitly open.

Comments

Use -- for a single line comment. Use {- and -} for a comment extending over multiple lines.

Contract Identifiers

When an instance of a template (that is, a contract) is added to the ledger, it’s assigned a unique identifier, of type ContractId <name of template>. The runtime representation of these identifiers depends on the execution environment: a contract identifier from the Sandbox may look different to ones on other Daml Ledgers. You can use == and /= on contract identifiers of the same type.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/packages.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Daml Packages

This page gives reference information on Daml package dependencies.

Building Daml Archives

When a Daml project is compiled, the compiler produces a Daml archive. These are platform-independent packages of compiled Daml code that can be uploaded to a Daml ledger or imported in other Daml projects. Daml archives have a .dar file ending. By default, when you run dpm build, it will generate the .dar file in the .daml/dist folder in the project root folder. For example, running dpm build in project foo with project version 0.0.1 will result in a Daml archive .daml/dist/foo-0.0.1.dar. You can specify a different path for the Daml archive by using the -o flag:
dpm build -o foo.dar

The rest of this page will focus on how to import a Daml package in other Daml projects.

Inspecting DARs

Refer to the section on decoding DARs and DALF files.

Import Daml Packages

There are two ways to import a Daml package in a project: via dependencies, and via data-dependencies. They each have certain advantages and disadvantages. To summarize:
  • dependencies allow you to import a Daml archive as a library. The definitions in the dependency will all be made available to the importing project. However, the dependency must be compiled with the same SDK version, so this method is only suitable for breaking up large projects into smaller projects that depend on each other, or to reuse existing libraries.
  • data-dependencies allow you to import a Daml archive (.dar) or a Daml-LF package (.dalf), including packages that have already been deployed to a ledger. These packages can be compiled with any previous SDK version. On the other hand, not all definitions can be carried over perfectly, since the Daml interface needs to be reconstructed from the binary.
The following sections will cover these two approaches in more depth.

Import a Daml package via Dependencies

A Daml project can declare a Daml archive as a dependency in the dependencies field of daml.yaml. This lets you import modules and reuse definitions from another Daml project. The main limitation of this method is that the dependency must be built for the same SDK version as the importing project. Let’s go through an example. Suppose you have an existing Daml project foo, located at /home/user/foo, and you want to use it as a dependency in a project bar, located at /home/user/bar. To do so, you first need to generate the Daml archive of foo. Go into /home/user/foo and run dpm build -o foo.dar. This will create the Daml archive, /home/user/foo/foo.dar. Next, we will update the project config for bar to use the generated Daml archive as a dependency. Go into /home/user/bar and change the dependencies field in daml.yaml to point to the created `Daml archive`:
dependencies:
  - daml-prim
  - daml-stdlib
  - ../foo/foo.dar
The import path can also be absolute, for example, by changing the last line to:
- /home/user/foo/foo.dar
When you run dpm build in the bar project, the compiler will make the definitions in foo.dar available for importing. For example, if foo exports the module Foo, you can import it in the usual way:
import Foo
By default, all modules of foo are made available when importing foo as a dependency. To limit which modules of foo get exported, you may add an exposed-modules field in the daml.yaml file for foo:
exposed-modules:
- Foo

Import a Daml Archive via data-dependencies

You can import a Daml archive (.dar) or Daml-LF package (.dalf) using data-dependencies. Unlike dependencies, this can be used when the SDK versions do not match. For example, you can import foo.dar as follows:
dependencies:
- daml-prim
- daml-stdlib
data-dependencies:
- ../foo/foo.dar
When importing packages this way, the Daml compiler will try to reconstruct the original Daml interface from the compiled binaries. However, to allow data-dependencies to work across SDK versions, the compiler has to abstract over some details which are not compatible across SDK versions. This means that there are some Daml features that cannot be recovered when using data-dependencies. In particular:
  1. Export lists cannot be recovered, so imports via data-dependencies can access definitions that were originally hidden. This means it is up to the importing module to respect the data abstraction of the original module. Note that this is the same for all code that runs on the ledger, since the ledger does not provide special support for data abstraction.
  2. If you have a dependency that limits the modules that can be accessed via exposed-modules, you can get an error if you also have a data-dependency that references something from the hidden modules (even if it is only reexported). Since exposed-modules are not available on the ledger in general, we recommend to not make use of them and instead rely on naming conventions (e.g., suffix module names with .Internal) to make it clear which modules are part of the public API.
  3. Prior to Daml-LF version 1.8, typeclasses could not be reconstructed. This means if you have a package that is compiled with an older version of Daml-LF, typeclasses and typeclass instances will not be carried over via data-dependencies, and you won’t be able to call functions that rely on typeclass instances. This includes the template functions, such as create, signatory, and exercise, as these rely on typeclass instances.
  4. Starting from Daml-LF version 1.8, when possible, typeclass instances will be reconstructed by re-using the typeclass definitions from dependencies, such as the typeclasses exported in daml-stdlib. However, if the typeclass signature has changed, you will get an instance for a reconstructed typeclass instead, which will not interoperate with code from dependencies.

Transitive dependency management

The Daml compiler identifies each DAR dependency in the project by its packageId and fully qualified name (Daml project package name and version number). If you have a Daml project which contains multiple common transitive DAR dependencies, those common transitive dependencies must either:
  • Have identical contents if they have the same name and version specified in their Daml project’s daml.yaml file, or
  • Have a different value for the version entry in their respective daml.yaml files.
Otherwise, the Daml project cannot be built into a deployable DAR due to package identification conflicts. For example:
  • Daml project X (top-level) has dependencies DarA and DarB.
  • DarA and DarB both contain DAR dependency DarC
When compiling Daml project X, you must ensure that the DarC dependency referenced by both DarA and DarB either has identical Daml contents or has a different version number if the contents differ. The version number is defined in the daml.yaml file of the Daml project producing DarC, under the version key.

Reference Daml Packages Already On the Ledger

Daml packages that have been uploaded to a ledger can be imported as data dependencies, given you have the necessary permissions to download these packages. To import such a package, add the package name and version separated by a colon to the data-dependencies stanza as follows:
ledger:
  host: localhost
  port: 6865
dependencies:
- daml-prim
- daml-stdlib
data-dependencies:
- foo:1.0.0
If your ledger runs at the default host and port (localhost:6865), the ledger stanza can be omitted. This will fetch and install the package foo-1.0.0. A daml.lock file is created at the root of your project directory, pinning the resolved packages to their exact package ID:
dependencies:
- pkgId: 51255efad65a1751bcee749d962a135a65d12b87eb81ac961142196d8bbca535
  name: foo
  version: 1.0.0
The daml.lock file needs to be checked into version control of your project. This assures that package name/version tuples specified in your data dependencies are always resolved to the same package ID. To recreate or update your daml.lock file, delete it and run dpm build again.

Handling Module Name Collisions

Sometimes you will have multiple packages with the same module name. In that case, a simple import will fail, since the compiler doesn’t know which version of the module to load. Fortunately, there are a few tools you can use to approach this problem. The first is to use package qualified imports. Supposing you have packages with different names, foo and bar, which both expose a module X, you can select which one you want with a package qualified import. To get X from foo:
import "foo" X
To get X from bar:
import "bar" X
To get both, you need to rename the module as you perform the import:
import "foo" X as FooX
import "bar" X as BarX
Sometimes, package qualified imports will not help, because you are importing two packages with the same name. For example, if you’re loading different versions of the same package. To handle this case, you need the --package build option. Suppose you are importing packages foo-1.0.0 and foo-2.0.0. Notice they have the same name foo but different versions. To get modules that are exposed in both packages, you will need to provide module aliases. You can do this by passing the --package build option. Open daml.yaml and add the following build-options:
build-options:
- '--package'
- 'foo-1.0.0 with (X as Foo1.X)'
- '--package'
- 'foo-2.0.0 with (X as Foo2.X)'
This will alias the X in foo-1.0.0 as Foo1.X, and alias the X in foo-2.0.0 as Foo2.X. Now you will be able to import both X by using the new names:
import qualified Foo1.X
import qualified Foo2.X
It is also possible to add a prefix to all modules in a package using the module-prefixes field in your daml.yaml. This is particularly useful for upgrades where you can map all modules of version v of your package under V$v. For the example above you can use the following:
module-prefixes:
  foo-1.0.0: Foo1
  foo-2.0.0: Foo2
That will allow you to import module X from package foo-1.0.0 as Foo1.X and X from package foo-2.0.0 as Foo2. You can also use more complex module prefixes, e.g., foo-1.0.0: Foo1.Bar which will make module X available under Foo1.Bar.X.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/contract-keys.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Contract Keys

Contract keys are not supported in this release of Canton 3.x. Support for non-unique contract keys will come in a future release of Canton 3.x. Unique contract keys are not planned be supported in Canton 3.x, as there are no known good solutions for enforcing uniqueness constraints at the infrastructure level when there are multiple independent subnetworks that can be (dis)connected at any point in time.The documentation below is kept for completeness, but can be ignored for this release of Canton 3.x.

Unsupported feature

Contract keys are an optional addition to templates. They let you specify a way of uniquely identifying contracts, using the parameters to the template - similar to a primary key for a database. Contract keys do not change and can be used to refer to a contract even when the contract id changes. Here’s an example of setting up a contract key for a bank account, to act as a bank account ID:
type AccountKey = (Party, Text)

template Account with
    bank : Party
    number : Text
    owner : Party
    balance : Decimal
    observers : [Party]
  where
    signatory [bank, owner]
    observer observers

    key (bank, number) : AccountKey
    maintainer key._1

What Can Be a Contract Key

The key can be an arbitrary serializable expression that does not contain contract IDs. However, it must include every party that you want to use as a maintainer (see Specify Maintainers below). It’s best to use simple types for your keys like Text or Int, rather than a list or more complex type.

Specify Maintainers

If you specify a contract key for a template, you must also specify a maintainer or maintainers, in a similar way to specifying signatories or observers. The maintainers “own” the key in the same way the signatories “own” a contract. Just like signatories of contracts prevent double spends or use of false contract data, maintainers of keys prevent double allocation or incorrect lookups. Since the key is part of the contract, the maintainers must be signatories of the contract. However, maintainers are computed from the key instead of the template arguments. In the example above, the bank is ultimately the maintainer of the key. Uniqueness of keys is guaranteed per template. Since multiple templates may use the same key type, some key-related functions must be annotated using the @ContractType as shown in the examples below. When you are writing Daml models, the maintainers matter since they affect authorization — much like signatories and observers. You don’t need to do anything to “maintain” the keys. In the above example, it is guaranteed that there can only be one Account with a given number at a given bank. Checking of the keys is done automatically at execution time, by the Daml execution engine: if someone tries to create a new contract that duplicates an existing contract key, the execution engine will cause that creation to fail.

Contract Lookups

The primary purpose of contract keys is to provide a stable, and possibly meaningful, identifier that can be used in Daml to fetch contracts. There are two functions to perform such lookups: fetchbykey and lookupbykey. Both types of lookup are performed at interpretation time on the submitting Participant Node, on a best-effort basis. Currently, that best-effort means lookups only return contracts if the submitting Party is a stakeholder of that contract. In particular, the above means that if multiple commands are submitted simultaneously, all using contract lookups to find and consume a given contract, there will be contention between these commands, and at most one will succeed. For more information, see the section on Avoiding Contention. Limiting key usage to stakeholders also means that keys cannot be used to access a divulged contract, i.e. there can be cases where fetch succeeds and fetchByKey does not. See the example at the end of this section for details.

fetchByKey

(fetchedContractId, fetchedContract) <- fetchByKey @ContractType contractKey Use fetchByKey to fetch the ID and data of the contract with the specified key. It is an alternative to fetch and behaves the same in most ways. It returns a tuple of the ID and the contract object (containing all its data). Like fetch, fetchByKey needs to be authorized by at least one stakeholder. fetchByKey fails and aborts the transaction if:
  • The submitting Party is not a stakeholder on a contract with the given key, or
  • A contract was found, but the fetchByKey violates the authorization rule, meaning no stakeholder authorized the fetch.
This means that if it fails, it doesn’t guarantee that a contract with that key doesn’t exist, just that the submitting Party doesn’t know about it, or there are issues with authorization.

visibleByKey

boolean <- visibleByKey @ContractType contractKey Use visibleByKey to check whether you can see an active contract for the given key with the current authorizations. If the contract exists and you have permission to see it, returns True, otherwise returns False. To clarify, ignoring contention:
  1. visibleByKey will return True if all of these are true: there exists a contract for the given key, the submitter is a stakeholder on that contract, and at the point of call we have the authorization of all of the maintainers of the key.
  2. visibleByKey will return False if all of those are true: there is no contract for the given key, and at the point of call we have authorization from all the maintainers of the key.
  3. visibleByKey will abort the transaction at interpretation time if, at the point of call, we are missing the authorization from any one maintainer of the key.
  4. visibleByKey will fail at validation time (after returning False at interpretation time) if all of these are true: at the point of call, we have the authorization of all the maintainers, and a valid contract exists for the given key, but the submitter is not a stakeholder on that contract.
While it may at first seem too restrictive to require all maintainers to authorize the call, this is actually required in order to validate negative lookups. In the positive case, when you can see the contract, it’s easy for the transaction to mention which contract it found, and therefore for validators to check that this contract does indeed exist, and is active as of the time of executing the transaction. For the negative case, however, the transaction submitted for execution cannot say which contract it has not found (as, by definition, it has not found it, and it may not even exist). Still, validators have to be able to reproduce the result of not finding the contract, and therefore they need to be able to look for it, which means having the authorization to ask the maintainers about it.

lookupByKey

optionalContractId <- lookupByKey @ContractType contractKey Use lookupByKey to check whether a contract with the specified key exists. If it does exist, lookupByKey returns the Some contractId, where contractId is the ID of the contract; otherwise, it returns None. lookupByKey is conceptually equivalent to
lookupByKey : forall c k. (HasFetchByKey c k) => k -> Update (Optional (ContractId c))
lookupByKey k = do
  visible <- visibleByKey @c k
  if visible then do
    (contractId, _ignoredContract) <- fetchByKey @c k
    return $ Some contractId
  else
    return None
Therefore, lookupByKey needs all the same authorizations as visibleByKey, for the same reasons, and fails in the same cases. To get the data from the contract once you’ve confirmed it exists, you’ll still need to use fetch.

exerciseByKey

exerciseByKey @ContractType contractKey Use exerciseByKey to exercise a choice on a contract identified by its key (compared to exercise, which lets you exercise a contract identified by its ContractId). Just like exercise, running exerciseByKey requires visibility of the contract (either through divulgence, readAs or being a stakeholder) and authorization from the controllers of the choice.

Example

A complete example of possible success and failure scenarios of fetchByKey and lookupByKey is shown below.
-- Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0

module Keys where

import Daml.Script
import DA.Assert
import DA.Optional

template Keyed
  with
    sig : Party
    obs : Party
  where
    signatory sig
    observer obs

    key sig : Party
    maintainer key

template Divulger
  with
    divulgee : Party
    sig : Party
  where
    signatory divulgee
    observer sig

    nonconsuming choice DivulgeKeyed
      : Keyed
      with
        keyedCid : ContractId Keyed
      controller sig
      do
        fetch keyedCid

template Delegation
  with
    sig : Party
    delegees : [Party]
  where
    signatory sig
    observer delegees

    nonconsuming choice CreateKeyed
      : ContractId Keyed
      with
        delegee : Party
        obs : Party
      controller delegee
      do
        create Keyed with sig; obs

    nonconsuming choice ArchiveKeyed
      : ()
      with
        delegee : Party
        keyedCid : ContractId Keyed
      controller delegee
      do
        archive keyedCid

    nonconsuming choice UnkeyedFetch
      : Keyed
      with
        cid : ContractId Keyed
        delegee : Party
      controller delegee
      do
        fetch cid

    nonconsuming choice VisibleKeyed
      : Bool
      with
        key : Party
        delegee : Party
      controller delegee
      do
        visibleByKey @Keyed key

    nonconsuming choice LookupKeyed
      : Optional (ContractId Keyed)
      with
        lookupKey : Party
        delegee : Party
      controller delegee
      do
        lookupByKey @Keyed lookupKey

    nonconsuming choice FetchKeyed
      : (ContractId Keyed, Keyed)
      with
        lookupKey : Party
        delegee : Party
      controller delegee
      do
        fetchByKey @Keyed lookupKey

template Helper
  with
    p : Party
  where
    signatory p

    choice FetchByKey : (ContractId Keyed, Keyed)
      with
        keyedKey : Party
      controller p
      do fetchByKey @Keyed keyedKey

    choice VisibleByKey : Bool
      with
        keyedKey : Party
      controller p
      do visibleByKey @Keyed keyedKey

    choice LookupByKey : (Optional (ContractId Keyed))
      with
        keyedKey : Party
      controller p
      do lookupByKey @Keyed keyedKey

    choice AssertNotVisibleKeyed : ()
      with
        delegationCid : ContractId Delegation
        delegee : Party
        key : Party
      controller p
      do
        b <- exercise delegationCid VisibleKeyed with
          delegee
          key
        assert $ not b

    choice AssertLookupKeyedIsNone : ()
      with
        delegationCid : ContractId Delegation
        delegee : Party
        lookupKey : Party
      controller p
      do
        b <- exercise delegationCid LookupKeyed with
          delegee
          lookupKey
        assert $ isNone b

    choice AssertFetchKeyedEqExpected : ()
      with
        delegationCid : ContractId Delegation
        delegee : Party
        lookupKey : Party
        expectedCid : ContractId Keyed
      controller p
      do
        (cid, keyed) <- exercise delegationCid FetchKeyed with
          delegee
          lookupKey
        cid === expectedCid


lookupTest = script do

  -- Put four parties in the four possible relationships with a `Keyed`
  sig <- allocateParty "s" -- Signatory
  obs <- allocateParty "o" -- Observer
  divulgee <- allocateParty "d" -- Divulgee
  blind <- allocateParty "b" -- Blind

  keyedCid <- submit sig do createCmd Keyed with ..
  divulgercid <- submit divulgee do createCmd Divulger with ..
  submit sig do exerciseCmd divulgercid DivulgeKeyed with ..

  -- Now the signatory and observer delegate their choices
  sigDelegationCid <- submit sig do
    createCmd Delegation with
      sig
      delegees = [obs, divulgee, blind]
  obsDelegationCid <- submit obs do
    createCmd Delegation with
      sig = obs
      delegees = [divulgee, blind]

  -- TESTING LOOKUPS AND FETCHES

  -- Maintainer can fetch
  (cid, keyed) <- submit sig do
    Helper sig `createAndExerciseCmd` FetchByKey sig
  cid === keyedCid
  -- Maintainer can see
  b <- submit sig do
    Helper sig `createAndExerciseCmd` VisibleByKey sig
  assert b
  -- Maintainer can lookup
  mcid <- submit sig do
    Helper sig `createAndExerciseCmd` LookupByKey sig
  mcid === Some keyedCid


  -- Stakeholder can fetch
  (cid, l) <- submit obs do
    Helper obs `createAndExerciseCmd` FetchByKey sig
  keyedCid === cid
  -- Stakeholder can't see without authorization
  submitMustFail obs do
    Helper obs `createAndExerciseCmd` VisibleByKey sig

  -- Stakeholder can see with authorization
  b <- submit obs do
    exerciseCmd sigDelegationCid VisibleKeyed with
      delegee = obs
      key = sig
  assert b
  -- Stakeholder can't lookup without authorization
  submitMustFail obs do
    Helper obs `createAndExerciseCmd` LookupByKey sig
  -- Stakeholder can lookup with authorization
  mcid <- submit obs do
    exerciseCmd sigDelegationCid LookupKeyed with
      delegee = obs
      lookupKey = sig
  mcid === Some keyedCid

  -- Divulgee can't fetch the contract directly
  submitMustFail divulgee do
    exerciseCmd obsDelegationCid UnkeyedFetch with
        delegee = divulgee
        cid = keyedCid
  -- Divulgee can't fetch through the key
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` FetchByKey sig
  -- Divulgee can't see
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` VisibleByKey sig
  -- Divulgee can't see with stakeholder authority
  submitMustFail divulgee do
    exerciseCmd obsDelegationCid VisibleKeyed with
        delegee = divulgee
        key = sig
  -- Divulgee can't lookup
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` LookupByKey sig
  -- Divulgee can't lookup with stakeholder authority
  submitMustFail divulgee do
    exerciseCmd obsDelegationCid LookupKeyed with
        delegee = divulgee
        lookupKey = sig
  -- Divulgee can't do positive lookup with maintainer authority.
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` AssertNotVisibleKeyed with
      delegationCid = sigDelegationCid
      delegee = divulgee
      key = sig
  -- Divulgee can't do positive lookup with maintainer authority.
  -- Note that the lookup returns `None` so the assertion passes.
  -- If the assertion is changed to `isSome`, the assertion fails,
  -- which means the error message changes. The reason is that the
  -- assertion is checked at interpretation time, before the lookup
  -- is checked at validation time.
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` AssertLookupKeyedIsNone with
      delegationCid = sigDelegationCid
      delegee = divulgee
      lookupKey = sig
  -- Divulgee can't fetch with stakeholder authority
  submitMustFail divulgee do
    Helper divulgee `createAndExerciseCmd` AssertFetchKeyedEqExpected with
      delegationCid = obsDelegationCid
      delegee = divulgee
      lookupKey = sig
      expectedCid = keyedCid

  -- Blind party can't fetch
  submitMustFail blind do
    Helper blind `createAndExerciseCmd` FetchByKey sig
  -- Blind party can't see
  submitMustFail blind do
    Helper blind `createAndExerciseCmd` VisibleByKey sig
  -- Blind party can't see with stakeholder authority
  submitMustFail blind do
    exerciseCmd obsDelegationCid VisibleKeyed with
      delegee = blind
      key = sig
  -- Blind party can't see with maintainer authority
  submitMustFail blind do
    Helper blind `createAndExerciseCmd` AssertNotVisibleKeyed with
      delegationCid = sigDelegationCid
      delegee = blind
      key = sig
  -- Blind party can't lookup
  submitMustFail blind do
    Helper blind `createAndExerciseCmd` LookupByKey sig
  -- Blind party can't lookup with stakeholder authority
  submitMustFail blind do
    exerciseCmd obsDelegationCid LookupKeyed with
      delegee = blind
      lookupKey = sig
  -- Blind party can't lookup with maintainer authority.
  -- The lookup initially returns `None`, but is rejected at
  -- validation time
  submitMustFail blind do
    Helper blind `createAndExerciseCmd` AssertLookupKeyedIsNone with
      delegationCid = sigDelegationCid
      delegee = blind
      lookupKey = sig
  -- Blind party can't fetch with stakeholder authority as lookup is negative
  submitMustFail blind do
    exerciseCmd obsDelegationCid FetchKeyed with
      delegee = blind
      lookupKey = sig
  -- Blind party can see nonexistence of a contract
  submit blind do
    Helper blind `createAndExerciseCmd` AssertNotVisibleKeyed with
      delegationCid = obsDelegationCid
      delegee = blind
      key = obs
  -- Blind can do a negative lookup on a truly nonexistant contract
  submit blind do
    Helper blind `createAndExerciseCmd` AssertLookupKeyedIsNone with
      delegationCid = obsDelegationCid
      delegee = blind
      lookupKey = obs

  -- TESTING CREATES AND ARCHIVES

  -- Divulgee can't archive
  submitMustFail divulgee do
    exerciseCmd sigDelegationCid ArchiveKeyed with
      delegee = divulgee
      keyedCid

  submit sig do
     exerciseCmd keyedCid Archive

  -- Divulgee can create
  keyedCid2 <- submit divulgee do
    exerciseCmd sigDelegationCid CreateKeyed with
      delegee = divulgee
      obs

  -- Stakeholder can archive
  submit obs do
    exerciseCmd sigDelegationCid ArchiveKeyed with
      delegee = obs
      keyedCid = keyedCid2
  -- Stakeholder can create
  keyedCid3 <- submit obs do
    exerciseCmd sigDelegationCid CreateKeyed with
      delegee = obs
      obs

  return ()
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/interfaces.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Interfaces

In Daml, an interface defines an abstract type together with a behavior specified by its view type, method signatures, and choices. For a template to conform to this interface, there must be a corresponding interface instance definition where all the methods of the interface (including the special view method) are implemented. This allows decoupling such behavior from its implementation, so other developers can write applications in terms of the interface instead of the concrete template.

Configuration

To use this feature your Daml project must target Daml-LF version 1.15 or higher, which is the current default. If using Canton, the protocol version of the sync domain should be 4 or higher, see Canton protocol version for more details.

Interface Declaration

An interface declaration is somewhat similar to a template declaration.

Interface Name

interface MyInterface where
  • This is the name of the interface.
  • It’s preceded by the keyword interface and followed by the keyword where.
  • It must begin with a capital letter, like any other type name.

Implicit abstract type

  • Whenever an interface is defined, an abstract type is defined with the same name. “Abstract” here means:
    • Values of this type cannot be created using a data constructor. Instead, they are constructed by applying the function toInterface to a template value.
    • Values of this type cannot be inspected directly via case analysis. Instead, use functions such as fromInterface.
    • See daml-ref-interface-functions for more information on these and other functions for interacting with interface values.
  • An interface value carries inside it the type and parameters of the template value from which it was constructed.
  • As for templates, the existence of a local binding b of type I, where I is an interface does not necessarily imply the existence on the ledger of a contract with the template type and parameters used to construct b. This can only be assumed if b the result of a fetch from the ledger within the same transaction.

Interface Methods

method1 : Party
method2 : Int
method3 : Bool -> Int -> Int -> Int
  • An interface may define any number of methods.
  • A method definition consists of the method name and the method type, separated by a single colon :. The name of the method must be a valid identifier beginning with a lowercase letter or an underscore.
  • A method definition introduces a top level function of the same name:
    • If the interface is called I, the method is called m, and the method type is M (which might be a function type), this introduces the function m : I -> M:
<DamlInterfacesINTERFACEMETHODSTOPLEVEL />
  • The first argument’s type I means that the function can only be applied to values of the interface type I itself. Methods cannot be applied to template values, even if there exists an interface instance of I for that template. To use an interface method on a template value, first convert it using the toInterface function.
  • Applying the function to such argument results in a value of type M, corresponding to the implementation of m in the interface instance of I for the underlying template type t (the type of the template value from which the interface value was constructed).
  • One special method, view, must be defined for the viewtype. (see interface-viewtype below).

Interface View Type

data MyInterfaceViewType =
  MyInterfaceViewType { name : Text, value : Int }
viewtype MyInterfaceViewType
  • All interface instances must implement a special view method which returns a value of the type declared by viewtype.
  • The type must be a record.
  • This type is returned by subscriptions on interfaces.

Interface Choices

choice MyChoice : (ContractId MyInterface, Int)
  with
    argument1 : Bool
    argument2 : Int
  controller method1 this
  do
    let n0 = method2 this
    let n1 = method3 this argument1 argument2 n0
    pure (self, n1)

nonconsuming choice MyNonConsumingChoice : Int
  controller method1 this
  do
    pure $ method2 this
  • Interface choices work in a very similar way to template choices. Any contract of a template type for which an interface instance exists will grant the choice to the controlling party.
  • Interface choices can only be exercised on values of the corresponding interface type. To exercise an interface choice on a template value, first convert it using the toInterface function.
  • Interface methods can be used to define the controller of a choice (e.g. method1) as well as the actions that run when the choice is exercised (e.g. method2 and method3).
  • As for template choices, the choice keyword can be optionally prefixed with the nonconsuming keyword to specify that the contract will not be consumed when the choice is exercised. If not specified, the choice will be consuming. Note that the preconsuming and postconsuming qualifiers are not supported on interface choices.
  • See choices for full reference information, but note that controller-first syntax is not supported for interface choices.

Empty Interfaces

data EmptyInterfaceView = EmptyInterfaceView {}

interface YourInterface where
  viewtype EmptyInterfaceView
  • It is possible (though not necessarily useful) to define an interface without methods, precondition or choices. However, a view type must always be defined, though it can be set to unit.

Interface Instances

For context, a simple template definition:
template NameOfTemplate
  with
    field1 : Party
    field2 : Int
  where
    signatory field1

interface instance clause

interface instance MyInterface for NameOfTemplate where
  view = MyInterfaceViewType "NameOfTemplate" 100
  method1 = field1
  method2 = field2
  method3 False _ _ = 0
  method3 True x y
    | x > 0 = x + y
    | otherwise = y
  • To make a template an instance of an existing interface, an interface instance clause must be defined in the template declaration.
  • The template of the clause must match the enclosing declaration. In other words, a template T declaration can only contain interface instance clauses where the template is T.
  • The clause must start with the keywords interface instance, followed by the name of the interface, then the keyword for and the name of the template, and finally the keyword where, which introduces a block where all the methods of the interface must be implemented.
  • Within the clause, there’s an implicit local binding this referring to the contract on which the method is applied, which has the type of the template’s data record. The template parameters of this contract are also in scope.
  • Method implementations can be defined using the same syntax as for top level functions, including pattern matches and guards (e.g. method3).
  • Each method implementation must return the same type as specified for that method in the interface declaration.
  • The implementation of the special view method must return the type specified as the viewtype in the interface declaration.

Empty interface instance clause

  • If the interface has no methods, the interface instance only needs to implement the view method:
interface instance YourInterface for NameOfTemplate where
  view = EmptyInterfaceView

Interface Functions

interfaceTypeRep

Type

HasInterfaceTypeRep i =>
i -> TemplateTypeRep

Instantiated TypeMyInterface -> TemplateTypeRep
NotesThe value of the resulting TemplateTypeRep indicates what template was used to construct the interface value.

toInterface

Type

forall i t.
HasToInterface t i =>
t -> i

Instantiated TypeMyTemplate -> MyInterface
NotesConverts a template value into an interface value.

fromInterface

Type

HasFromInterface t i =>
i -> Optional t

Instantiated TypeMyInterface -> Optional MyTemplate
NotesAttempts to convert an interface value back into a template value. The result is None if the expected template type doesn’t match the underlying template type used to construct the contract.

toInterfaceContractId

Type

forall i t.
HasToInterface t i =>
ContractId t -> ContractId i

Instantiated TypeContractId MyTemplate -> ContractId MyInterface
NotesConverts a template Contract ID into an Interface Contract ID.

fromInterfaceContractId

Type

forall t i.
HasFromInterface t i =>
ContractId i -> ContractId t

Instantiated TypeContractId MyInterface -> ContractId MyTemplate
NotesConverts an interface contract id into a template contract id. This function does not verify that the given contract id actually points to a contract of the resulting type; if that is not the case, a subsequent fetch, exercise or archive will fail. Therefore, this should only be used when the underlying contract is known to be of the resulting type, or when the result is immediately used by a fetch, exercise or archive action and a transaction failure is the desired behavior in case of mismatch. In all other cases, consider using fetchFromInterface instead.

coerceInterfaceContractId

Type

forall j i.
(HasInterfaceTypeRep i, HasInterfaceTypeRep j) =>
ContractId i -> ContractId j

Instantiated TypeContractId SourceInterface -> ContractId TargetInterface
NotesConverts an interface contract id into a contract id of a different interface. This function does not verify that the given contract id actually points to a contract of the resulting type; if that is not the case, a subsequent fetch, exercise or archive will fail. Therefore, this should only be used when the underlying contract is known to be of the resulting type, or when the result is immediately used by a fetch, exercise or archive action and a transaction failure is the desired behavior in case of mismatch.

fetchFromInterface

Type

forall t i.
(HasFromInterface t i, HasFetch i) =>
ContractId i -> Update (Optional (ContractId t, t))

Instantiated Type

ContractId MyInterface ->
Update (Optional (ContractId MyTemplate, MyTemplate))

NotesAttempts to fetch and convert an interface contract id into a template, returning both the converted contract and its contract id if the conversion is successful, or None otherwise.

Required Interfaces

interface OurInterface requires MyInterface, YourInterface where
  viewtype EmptyInterfaceView
  • An interface can depend on other interfaces. These are specified with the requires keyword after the interface name but before the where keyword, separated by commas.
  • For an interface declaration to be valid, its list of required interfaces must be transitively closed. In other words, an interface I cannot require an interface J without also explicitly requiring all the interfaces required by J. The order, however, is irrelevant. For example, given
    interface Shape where
      viewtype EmptyInterfaceView
    
    interface Rectangle requires Shape where
      viewtype EmptyInterfaceView
    
    This declaration for interface Square would cause a compiler error
    -- Compiler error! "Interface Square is missing requirement [Shape]"
    interface Square requires Rectangle where
      viewtype EmptyInterfaceView
    
    Explicitly adding Shape to the required interfaces fixes the error
    interface Square requires Rectangle, Shape where
      viewtype EmptyInterfaceView
    
  • For a template T to be a valid interface instance of an interface I, T must also be an interface instance of each of the interfaces required by I.

Interface Functions

FunctionNotes
toInterfaceCan also be used to convert an interface value to one of its required interfaces.
fromInterfaceCan also be used to convert a value of an interface type to one of its requiring interfaces.
toInterfaceContractIdCan also be used to convert an interface contract id into a contract id of one of its required interfaces.
fromInterfaceContractIdCan also be used to convert an interface contract id into a contract id of one of its requiring interfaces.
fetchFromInterfaceCan also be used to fetch and convert an interface contract id into a contract and contract id of one of its requiring interfaces.
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/exceptions.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Exceptions (Deprecated)

User-defined Daml Exceptions, catching, and throwing have been deprecated and are being phased out in favour of the Canton error framework, which represents Daml errors as InvalidGivenCurrentSystemStateOther.
Exceptions are a deprecated Daml feature which provides a way to handle certain errors that arise during interpretation instead of aborting the transaction, and to roll back the state changes that lead to the error. There are two types of errors:

Builtin Errors

Exception typeThrown on
GeneralErrorCalls to error and abort
ArithmeticErrorArithmetic errors like overflows and division by zero
PreconditionFailedensure statements that return False
AssertionFailedFailed assert calls (or other functions from DA.Assert)
Note that other errors cannot be handled via exceptions, e.g., an exercise on an inactive contract will still result in a transaction abort.

User-defined Exceptions

Users can define their own exception types which can be thrown and caught. The definition looks similar to templates, and just like with templates, the definition produces a record type of the given name as well as instances to make that type throwable and catchable. In addition to the record fields, exceptions also need to define a message function.
exception MyException
  with
    field1 : Int
    field2 : Text
  where
    message "MyException(" <> show field1 <> ", " <> show field2 <> ")"

Throw Exceptions

There are two ways to throw exceptions:
  1. Inside of an Action like Update or Script you can use throw from DA.Exception. This works for any Action that is an instance of ActionThrow.
  2. Outside of ActionThrow you can throw exceptions using throwPure.
If both are an option, it is generally preferable to use throw since it is easier to reason about when exactly the exception will get thrown.

Catch Exceptions

Exceptions are caught in try-catch blocks similar to those found in languages like Java. The try block defines the scope within which errors should be handled while the catch clauses defines which types of errors are handled and how the program should continue. If an exception gets caught, the subtransaction between the try and the the point where the exception is thrown is rolled back. The actions under rollback nodes are still validated, so, e.g., you can never fetch a contract that is inactive at that point or have two contracts with the same key active at the same time. However, all ledger state changes (creates, consuming exercises) are rolled back to the state before the rollback node. Each try-catch block can have multiple catch clauses with the first one that applies taking precedence. In the example below the create of T will be rolled back and the first catch clause applies which will create an Error contract.
try do
  _ <- create (T p)
  throw MyException with
    field1 = 0
    field2 = "42"
catch
  (MyException field1 field2) ->
    create Error with
      p = p
      msg = "MyException"
  (ArithmeticError _) ->
    create Error with
      p = p
      msg = "ArithmeticError"
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/daml/working-with.rst Reviewers: Skip this section. Remove markers after final approval.

Reference: Built-in Functions

This page gives reference information on built-in functions for working with a variety of common concepts.

Work with Time

Daml has these built-in functions for working with time:
  • datetime: creates a Time given year, month, day, hours, minutes, and seconds as argument.
  • subTime: subtracts one time from another. Returns the RelTime difference between time1 and time2.
  • addRelTime: add times. Takes a Time and RelTime and adds the RelTime to the Time.
  • days, hours, minutes, seconds: constructs a RelTime of the specified length.
  • pass: (in Daml Script tests only) use pass : RelTime -> Script Time to advance the ledger time by the argument amount. Returns the new time.

Work with Numbers

Daml has these built-in functions for working with numbers:
  • round: rounds a Decimal number to Int. round d is the nearest Int to d. Tie-breaks are resolved by rounding away from zero, for example:
    round 2.5 == 3    round (-2.5) == -3
    round 3.4 == 3    round (-3.7) == -4
    
  • truncate: converts a Decimal number to Int, truncating the value towards zero, for example:
    truncate 2.2 == 2    truncate (-2.2) == -2
    truncate 4.9 == 4    v (-4.9) == -4
    
  • intToDecimal: converts an Int to Decimal.
The set of numbers expressed by Decimal is not closed under division as the result may require more than 10 decimal places to represent. For example, 1.0 / 3.0 == 0.3333... is a rational number, but not a Decimal.

Work with Text

Daml has these built-in functions for working with text:
  • <> operator: concatenates two Text values.
  • show converts a value of the primitive types (Bool, Int, Decimal, Party, Time, RelTime) to a Text.
To escape text in Daml strings, use \:
How to escape it
\\\
"\"
''
Newline\n
Tab\t
Carriage return\r
Unicode (using ! as an example)
  • Decimal code: \33
  • Octal code: \o41
  • Hexadecimal code: \x21

Work with Lists

Daml has these built-in functions for working with lists:
  • foldl and foldr: see daml-ref-folding below.

Fold

A fold takes:
  • a binary operator
  • a first accumulator value
  • a list of values
The elements of the list are processed one-by-one (from the left in a foldl, or from the right in a foldr).
We’d usually recommend using foldl, as foldr is usually slower. This is because it needs to traverse the whole list before starting to discharge its elements.
Processing goes like this:
  1. The binary operator is applied to the first accumulator value and the first element in the list. This produces a second accumulator value.
  2. The binary operator is applied to the second accumulator value and the second element in the list. This produces a third accumulator value.
  3. This continues until there are no more elements in the list. Then, the last accumulator value is returned.
As an example, to sum up a list of integers in Daml:
sumList =
  script do
    assert (foldl (+) 0 [1, 2, 3] == 6)
This section was copied from existing reviewed documentation. Source: docs/replicated/daml/3.4/sdk/reference/fixity.rst Reviewers: Skip this section. Remove markers after final approval.

Fixity, Associativity and Precedence

With normal, prefix operators (e.g. functions), the semantics of f g h is clear: f is a function that takes g and h as parameters. If we want f to take the result of applying g to h we write f (g h). In the case of infix operators (e.g. symbol operators such as + and *, or functions surrounded by backticks, for example `elem ```), it is less clear. What doesx - y - zmean? Subtractingxfromyfirst (i.e.(x - y) - z) generally yields different results than subtracting zfromyfirst (i.e.x - (y - z)). In Daml, the subtraction operator -is defined as a \*left-assocative\* operator. That is, when we writex - y - z - …the parser associates \*to the left\*, meaning the parser interprets this as((x - y) - z) - …\. Some operators are right-associative. We have already encountered one: function application! A function signature of a -> b -> c -> ... is parsed as (a -> (b -> (c -> ...))). Finally, some operators are non-associative. A good example are comparison operators such as == and >. This means any ambiguous usage of these operators (e.g. a == b == c or a > b > c) results in a parse error.
Non-associative operators are not to be confused with operators that are both left- and right-associative, such as + (since (x + y) + z = x + (y + z))). To obtain a deterministic parser, such operators must be declared as one of either left-associative or right-associative. In Daml the + operator has been declared as left-associative
The precedence of operators defines, when combining different operators, which operator is processed first. For example, in general (and in Daml), multiplication takes precedence over addition. That is, x + y * z is parsed as x + (y * z). Operator precedence is expressed as a number, where a higher number indicates a higher precedence. Operators of same precedence are associated to the left (e.g. x + y - z is parsed as (x + y) - z. The fixity and precedence of an operator are declared using the infixl, infix, and infixr keywords (denoting left-, non-, and right-associativity, respectfully) that take an integer between 0 and 9 inclusive and an operator the fixity applies to. For example, infixl 6 + declares that + is a left-associative operator with precedence 6. These keywords can be used for user-defined operators as well. The following table shows the fixity and precedence for operators that are built-in to the Daml language, such as + and -:
PrecedenceLeft-associativeNon-associativeRight-associative

9 8 7 6 5 4 3 2 1 0

!!</code></p> <p><code>*</code>, <code>/</code>, <code>%</code> <code>+</code>, <code>-</code></p> <p><code>&lt;$</code>, <code>&lt;$&gt;</code>, <code>$&gt;</code>, <code>&lt;*</code>, <code>&lt;*&gt;</code>, <code>*&gt;</code></p> <p><code>&gt;&gt;</code>, <code>&gt;&gt;=</code>, <code>&lt;&amp;&gt;</code></p></td> <td><p><code>==</code>, <code>/=</code>, <code>&lt;</code>, <code>&lt;=</code>, <code>&gt;==</code>, <code>&gt;</code>, <code>===</code>, <code>=/=</code></p></td> <td><p><code>.</code> <code>&lt;#&amp;&amp;&gt;</code>, <code>^</code>, <code>**</code></p> <p><code>&lt;&gt;</code> <code>++, ::

&&, &&& ||, ||| =<<, <=<, >=> $