DAML Studio¶
DAML Studio is an integrated development environment (IDE) for DAML. It is an extension on top of Visual Studio Code (VS Code), a cross-platform, open-source editor providing a rich code editing experience.
Installing¶
To install DAML Studio, install the SDK. DAML Studio isn’t currently available in the Visual Studio Marketplace.
Creating your first DAML file¶
Start DAML Studio by running
daml studio
in the current project.This command starts Visual Studio Code and (if needs be) installs the DAML Studio extension, or upgrades it to the latest version.
Make sure the DAML Studio extension is installed:
- Click on the Extensions icon at the bottom of the VS Code sidebar.
- Click on the DAML Studio extension that should be listed on the pane.
Open a new file (
⌘N
) and save it (⌘S
) asTest.daml
.Copy the following code into your file:
-- Copyright (c) 2020 The DAML Authors. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
daml 1.2
module Test where
double : Int -> Int
double x = 2 * x
Your screen should now look like the image below.
Introduce a parse error by deleting the
=
sign and then clicking the Ⓧ symbol on the lower-left corner. Your screen should now look like the image below.Remove the parse error by restoring the
=
sign.
We recommend reviewing the Visual Studio Code documentation to learn more about how to use it. To learn more about DAML, see Language reference docs.
Supported features¶
Visual Studio Code provides many helpful features for editing DAML files and Digital Asset recommends reviewing Visual Studio Code Basics and Visual Studio Code Keyboard Shortcuts for OS X. The DAML Studio extension for Visual Studio Code provides the following DAML-specific features:
Symbols and problem reporting¶
Use the commands listed below to navigate between symbols, rename them, and inspect any problems detected in your DAML files. Symbols are identifiers such as template names, lambda arguments, variables, and so on.
Command | Shortcut (OS X) |
---|---|
Go to Definition | F12 |
Peek Definition | ⌥F12 |
Rename Symbol | F2 |
Go to Symbol in File | ⇧⌘O |
Go to Symbol in Workspace | ⌘T |
Find all References | ⇧F12 |
Problems Panel | ⇧⌘M |
Note
You can also start a command by typing its name into the command palette (press ⇧⌘P
or F1
). The command palette
is also handy for looking up keyboard shortcuts.
Note
- Rename Symbol, Go to Symbol in File, Go to Symbol in Workspace, and Find all References work on: choices, record fields, top-level definitions, let-bound variables, lambda arguments, and modules
- Go to Definition and Peek Definition work on: top-level definitions, let-bound variables, lambda arguments, and modules
Hover tooltips¶
You can hover over most symbols in the code to display additional information such as its type.
Scenario results¶
Top-level declarations of type Scenario
are decorated with a Scenario results
code lens.
You can click on the Scenario results
code lens to inspect the transaction graph
or an error resulting from running that scenario.
The scenario results present a simplified view of a ledger, in the form of a transaction graph, after execution of the scenario. The transaction graph consists of transactions, each of which contain one or more updates to the ledger, that is creates and exercises. The transaction graph also records fetches of contracts.
For example a scenario for the Iou
module looks as follows:
Each transaction is the result of executing a step in the scenario. In the
image below, the transaction #0
is the result of executing the first
line of the scenario (line 20), where the Iou is created by the bank. The following
information can be gathered from the transaction:
- The result of the first scenario transaction
#0
was the creation of theIou
contract with the argumentsbank
,10
, and"USD"
. - The created contract is referenced in transaction
#1
, step0
. - The created contract was consumed in transaction
#1
, step0
. - A new contract was created in transaction
#1
, step1
, and has been divulged to parties ‘Alice’, ‘Bob’, and ‘Bank’. - At the end of the scenario only the contract created in
#1:1
remains. - The return value from running the scenario is the contract identifier
#1:1
. - And finally, the contract identifiers assigned in scenario execution correspond to
the scenario step that created them (e.g.
#1
).
You can navigate to the corresponding source code by clicking on the location
shown in parenthesis (e.g. Iou:20:12
, which means the Iou
module, line 20 and column 1).
You can also navigate between transactions by clicking on the transaction and contract ids (e.g. #1:0
).
DAML snippets¶
You can automatically complete a number of “snippets” when editing a DAML
source file. By default, hitting ^-Space
after typing a DAML keyword
displays available snippets that you can insert.
To define your own workflow around DAML snippets, adjust your user settings in Visual Studio Code to include the following options:
{
"editor.tabCompletion": true,
"editor.quickSuggestions": false
}
With those changes in place, you can simply hit Tab
after a keyword to insert the code pattern.
You can develop your own snippets by following the instructions in
Creating your own Snippets to create an appropriate daml.json
snippet file.
Common scenario errors¶
During DAML execution, errors can occur due to exceptions (e.g. use of “abort”, or division by zero), or due to authorization failures. You can expect to run into the following errors when writing DAML.
When a runtime error occurs in a scenario execution, the scenario result view shows the error together with the following additional information, if available:
- Last source location
- A link to the last source code location encountered before the error occurred.
- Environment
- The variables that are in scope when the error occurred. Note that contract identifiers are links that lead you to the transaction in which the contract was created.
- Ledger time
- The ledger time at which the error occurred.
- Call stack
- Call stack shows the function calls leading to the failing function. Updates and scenarios that do not take parameters are not included in the call stack.
- Partial transaction
- The transaction that is being constructed, but not yet committed to the ledger.
- Committed transaction
- Transactions that were successfully committed to the ledger prior to the error.
Abort, assert, and debug¶
The abort
, assert
and debug
inbuilt functions can be used in updates and scenarios. All three can be used to output messages, but abort
and assert
can additionally halt the execution:
abortTest = scenario do
debug "hello, world!"
abort "stop"
Scenario execution failed:
Aborted: stop
Ledger time: 1970-01-01T00:00:00Z
Partial transaction:
Trace:
"hello, world!"
Missing authorization on create¶
If a contract is being created without approval from all authorizing parties the commit will fail. For example:
template Example
with
party1 : Party; party2 : Party
where
signatory party1
signatory party2
example = scenario do
alice <- getParty "Alice"
bob <- getParty "Bob"
submit alice (create Example with party1=alice; party2=bob)
Execution of the example scenario fails due to ‘Bob’ being a signatory in the contract, but not authorizing the create:
Scenario execution failed:
#0: create of CreateAuthFailure:Example at unknown source
failed due to a missing authorization from 'Bob'
Ledger time: 1970-01-01T00:00:00Z
Partial transaction:
Sub-transactions:
#0
└─> create CreateAuthFailure:Example
with
party1 = 'Alice'; party2 = 'Bob'
To create the “Example” contract one would need to bring both parties to authorize the creation via a choice, for example ‘Alice’ could create a contract giving ‘Bob’ the choice to create the ‘Example’ contract.
Missing authorization on exercise¶
Similarly to creates, exercises can also fail due to missing authorizations when a party that is not a controller of a choice exercises it.
template Example
with
owner : Party
friend : Party
where
signatory owner
controller owner can
Consume : ()
do return ()
controller friend can
Hello : ()
do return ()
example = scenario do
alice <- getParty "Alice"
bob <- getParty "Bob"
cid <- submit alice (create Example with owner=alice; friend=bob)
submit bob do exercise cid Consume
The execution of the example scenario fails when ‘Bob’ tries to exercise the choice ‘Consume’ of which he is not a controller
Scenario execution failed:
#1: exercise of Consume in ExerciseAuthFailure:Example at unknown source
failed due to a missing authorization from 'Alice'
Ledger time: 1970-01-01T00:00:00Z
Partial transaction:
Sub-transactions:
#0
└─> fetch #0:0 (ExerciseAuthFailure:Example)
#1
└─> 'Alice' exercises Consume on #0:0 (ExerciseAuthFailure:Example)
with
Committed transactions:
TX #0 1970-01-01T00:00:00Z (unknown source)
#0:0
│ known to (since): 'Alice' (#0), 'Bob' (#0)
└─> create ExerciseAuthFailure:Example
with
owner = 'Alice'; friend = 'Bob'
From the error we can see that the parties authorizing the exercise (‘Bob’) is not a subset of the required controlling parties.
Contract not visible¶
Contract not being visible is another common error that can occur when a contract that is being fetched or exercised has not been disclosed to the committing party. For example:
template Example
with owner: Party
where
signatory owner
controller owner can
Consume : ()
do return ()
example = scenario do
alice <- getParty "Alice"
bob <- getParty "Bob"
cid <- submit alice (create Example with owner=alice)
submit bob do exercise cid Consume
In the above scenario the ‘Example’ contract is created by ‘Alice’ and makes no mention of the party ‘Bob’ and hence does not cause the contract to be disclosed to ‘Bob’. When ‘Bob’ tries to exercise the contract the following error would occur:
Scenario execution failed:
Attempt to fetch or exercise a contract not visible to the committer.
Contract: #0:0 (NotVisibleFailure:Example)
Committer: 'Bob'
Disclosed to: 'Alice'
Ledger time: 1970-01-01T00:00:00Z
Partial transaction:
Committed transactions:
TX #0 1970-01-01T00:00:00Z (unknown source)
#0:0
│ known to (since): 'Alice' (#0)
└─> create NotVisibleFailure:Example
with
owner = 'Alice'
To fix this issue the party ‘Bob’ should be made a controlling party in one of the choices.
Working with multiple packages¶
Often a DAML project consists of multiple packages, e.g., one
containing your templates and one containing a DAML trigger so that
you can keep the templates stable while modifying the trigger. It is
possible to work on multiple packages in a single session of DAML
studio but you have to keep some things in mind. You can see the
directory structure of a simple multi-package project consisting of
two packages pkga
and pkgb
below:
.
├── daml.yaml
├── pkga
│ ├── daml
│ │ └── A.daml
│ └── daml.yaml
└── pkgb
├── daml
│ └── B.daml
└── daml.yaml
pkga
and pkgb
are regular DAML projects with a daml.yaml
and a DAML module. In addition to the daml.yaml
files for the
respective packages, you also need to add a daml.yaml
to the root
of your project. This file only needs to specify the SDK
version. Replace X.Y.Z
by the SDK version you specified in the
daml.yaml
files of the individual packages. Note that this feature
is only available in SDK version 0.13.52
and newer.
sdk-version: X.Y.Z
You can then open DAML Studio once in the root of your project and
work on files in both packages. Note that if pkgb
refers to
pkga.dar
in its dependencies
field, changes will not be picked
up automatically. This is always the case even if you open DAML Studio
in pkgb
. However, for multi-package projects there is an
additional caveat: You have to both rebuild pkga.dar
using daml
build
and then build pkgb
using daml build
before restarting
DAML Studio.