Features announced to be deprecated: "controller first" choice syntax, contract key uniqueness - anything else?
Some features were announced to be deprecated together with the announcement of Daml 2.0. I have found two such features, is there anything else I have missed?
"Controller first" choice syntax
- The syntax
controller … canwill be deprecated in favor of the choiceX controller …syntax. If the deprecated syntax is used then the following warning will be shown in the IDE:
The syntax 'controller ... can' is deprecated, it will be removed in a future version of Daml. Instead, use 'choice ... with ... controller' syntax. Note that 'choice ... with ... controller' syntax does not implicitly add the controller as an observer, so it must be added explicitly as one (or as a signatory).

Release of Daml 2.0
Integrating Canton into Daml - introducing Canton-enabled drivers; Improvements in the developer experience to build and test distributed applications; Addition of the User Management Service; Supporting newer versions of dependencies
Contract key uniqueness
contract key uniqueness will soon be deprecated, as uniqueness can not be enforced among multiple domains. We encourage to build your models already anticipating this change.
https://docs.daml.com/canton/usermanual/contract_keys.html
I guess with the deprecation of key uniqueness, some functions like exerciseByKey (used extensively in React UI examples for Daml) will also be deprecated.
Thank you!
I’ve deleted my answer since it contained inaccuracies, I’ll let someone else write down an accurate answer, apologies for the confusion.
The only deprecations I’m aware off are the controller … can syntax and the ledger identity service.
Note that both of those are deprecated not removed, you can still use them for now and migrate away from them over time.
Contract key uniqueness is not yet deprecated. In fact, it’s the only supported option atm since multi-domain Canton is not yet GA. It may still be useful to consider where you rely on contract key uniqueness to ease the migration later but for now uniqueness is the only option on GA ledgers.
Yes, thank you!
Thank you, Stefano!
Hey I have the same issue but didn’t understand how to fix it. could you please help me?
The syntax ‘controller … can’ is deprecated, it will be removed in a future version of Daml. Instead, use ‘choice … with … controller’ syntax. Note that ‘choice … with … controller’ syntax does not implicitly add the controller as an observer, so it must be added explicitly as one (or as a signatory).
@Nimra_Fatima can you share the example you’re having trouble adjusting?
I got this error in my .daml files
The syntax ‘controller … can’ is deprecated, it will be removed in a future version of Daml. Instead, use ‘choice … with … controller’ syntax. Note that ‘choice … with … controller’ syntax does not implicitly add the controller as an observer, so it must be added explicitly as one (or as a signatory).
please help me to fix this issue
Hi @Nimra_Fatima,
It’s difficult to give you more guidance than what’s already said in the error message without having access to your code. Can you share the template that produces that warning?
Payment.daml
module Payment where
template Payable
with
amount: Decimal
currency: Text
from: Party
to: Party
reference: Text
where
signatory from
observer to
controller from can
ClaimPaid: ContractId PaymentClaim
with
transactionId: Text
do
create PaymentClaim with ..
template PaymentClaim
with
amount: Decimal
currency: Text
from: Party
to: Party
reference: Text
transactionId: Text
where
signatory from
controller to can
Receive: ContractId Receipt
do
received <- getTime
create Receipt
with ..
template Receipt
with
amount: Decimal
currency: Text
from: Party
to: Party
reference: Text
transactionId: Text
received: Time
where
signatory to, from
controller from can
Dismiss: ()
do return ()
here is the code in which I got this error
Hi @Nimra_Fatima,
As the error message says, the controller ... can syntax is deprecated and needs to be replaced with choice ... controller .... The two syntaxes are explained in the documentation of choices.
As a concrete example, this means replacing:
controller from can
ClaimPaid: ContractId PaymentClaim
with
transactionId: Text
do
create PaymentClaim with ..
which is in "controller first" form, to the equivalent "choice first" form:
choice ClaimPaid : ContractId PaymentClaim
with transactionId: Text
controller from
do
create PaymentClaim with ..
which is the same choice but expressed with the choice keyword first.
The "choice first" form has a couple advantages:
- It can specificy the consuming behaviour of a choice.
- It can enable flexible controllers by having the controlling
Partyas an argument to the choice, as explained in this old blog post. - It’s not deprecated.
Importantly, while not all "choice first" choices can be translated to controller first" form, all "controller first" choices can easily be transformed to "choice first" by basically moving the controller X words a couple lines down, with the minor exception that when the controller was neither a signatory nor an observer, it needs to be manually added to the observers.
@Gary_Verhaegen Thank you so much Sir
Hey @Gary_Verhaegen
can you tell me how to add controller keyword in nonconsuming choice
controller issuer can
nonconsuming MintToken: ContractId Token
with
description: Text
initialPrice: Decimal
currency: Text
royaltyRate: Decimal
thumbnail: Thumbnail
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..controller issuer can nonconsuming MintToken: ContractId Token with description: Text initialPrice: Decimal currency: Text royaltyRate: Decimal thumbnail: Thumbnail do issued <- getTime create Token with lastPrice = initialPrice owner = issuer ..
@Nimra_Fatima
Here you go
nonconsuming choice MintToken: ContractId Token
with
description: Text
InitialPrice: Decimal
currency: Text
royaltyRate: Decimal
thumbnail: Thumbnail
controller issuer
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..
@a_putkov I have already tried this but when I write controller then it gives me the error
nonconsuming choice MintToken: ContractId Token
with
description: Text
initialPrice: Decimal
currency: Text
royaltyRate: Decimal
controller issuer
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..
error: /home/nimra/private-nft/nft/daml/UserAdmin.daml:27:9: error:
parse error on input ‘controller’parser
@Nimra_Fatima can you share the full file please?
That looks like it should work, but as @cocreature implied, it may depend on the rest of the file, particularly the overall indentation.
@cocreature here is the code
module UserAdmin where
import Token
import Payment
template Issuer
with
userAdmin: Party
issuer: Party
where
signatory userAdmin
ensure userAdmin /= issuer
nonconsuming choice MintToken: ContractId Token
with
description: Text
initialPrice: Decimal
currency: Text
royaltyRate: Decimal
thumbnail: Thumbnail
controller issuer
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..
controller userAdmin can
RevokeIssuer: ()
do return ()
template IssuerRequest
with
userAdmin: Party
issuer: Party
reason: Text
where
signatory issuer
key (userAdmin, issuer): (Party, Party)
maintainer key._2
controller userAdmin can
GrantIssuerRights: ContractId Issuer
do
create Issuer with ..
RejectIssuerRequest: ()
do return ()
template Owner
with
userAdmin: Party
owner: Party
where
signatory userAdmin
ensure userAdmin /= owner
key (userAdmin, owner): (Party, Party)
maintainer key._1
controller owner can
nonconsuming AcceptTokenAsNewOwner: (ContractId Token, ContractId Payable, Optional (ContractId Payable))
with
offerId: ContractId TokenOffer
do
exercise offerId AcceptToken
nonconsuming AcceptTokenByKey: (ContractId Token, ContractId Payable, Optional (ContractId Payable))
with
issuer: Party
currentOwner: Party
description: Text
do
exerciseByKey @TokenOffer (issuer, currentOwner, description) AcceptToken
controller userAdmin can
RevokeOwnerRights: ()
do return ()
template OwnerRequest
with
userAdmin: Party
owner: Party
reason: Text
where
signatory owner
key (userAdmin, owner): (Party, Party)
maintainer key._2
controller userAdmin can
GrantOwnerRights: ContractId Owner
do
create Owner with ..
RejectOwnerRequest: ()
do return ()Please post this as a code block by wrapping it in triple backticks
```
yourcodehere
```
Otherwise the indentation gets messed up which makes it hard to find issues related to that.
@Nimra_Fatima
I’m guessing you’re missing indentation for the choice RevokeIssuer in the Issuer template (and/or possibly elsewhere). I get similar error to the one you mentioned (error: parse error on input ‘controller’) for example if I make the choice RevokeIssuer of the Issuer template insufficiently indented (e.g. if I place it on the same indentation level with the “where” keyword). Choice declarations and bodies are part of a template body and therefore must be indented from the “where” keyword, which declares the template body.
If my guess is incorrect, then please, as @cocreature suggested, post your code in a preformatted text block by wrapping it with triple backticks on both ends. Alternatively you can upload the Daml file for your module in your post.
module UserAdmin where
import Token
import Payment
template Issuer
with
userAdmin: Party
issuer: Party
where
signatory userAdmin
nonconsuming choice MintToken: ContractId Token
with
description: Text
initialPrice: Decimal
currency: Text
royaltyRate: Decimal
controller issuer
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..
choice
RevokeIssuer: ()
controller userAdmin
do return ()
template IssuerRequest
with
userAdmin: Party
issuer: Party
reason: Text
where
signatory issuer
choice
GrantIssuerRights: ContractId Issuer
controller userAdmin
do
create Issuer with ..
RejectIssuerRequest: ()
do return()
template Owner
with
userAdmin: Party
owner: Party
where
signatory userAdmin
nonconsuming choice AcceptTokenAsNewOwner: (ContractId Token, ContractId Payable, ContractId Payable)
with
offerId: ContractId TokenOffer
controller owner
do
exercise offerId AcceptToken
nonconsuming AcceptTokenByKey: (ContractId Token, ContractId Payable, ContractId Payable)
with
issuer: Party
currentOwner: Party
description: Text
do
exerciseByKey @TokenOffer (issuer, currentOwner, description) AcceptToken
controller userAdmin can
RevokeOwnerRights: ()
do return()
template OwnerRequest
with
userAdmin: Party
owner: Party
reason: Text
where
signatory owner
choice
GrantOwnerRights: ContractId Owner
controller userAdmin
do
create Owner with ..
RejectOwnerRequest: ()
do return()
it shows me the error of RejectIssuerRequest() in IssuerRequest Template
There are a few issues in there:
- The indentation still seems messed up. I suspect this is probably breaking during copy paste somewhere and your actual code is fine in that regard.
-
RejectIssuerRequestis missing the controller. -
AcceptTokenByKeyis missing thechoicekeyword and the controller. -
RevokeOwnerRightsstill usescontroller … can. -
RejectOwnerRequestis missing the choice keyword and the controller.
Here is the fixed up version
module UserAdmin where
import Token
import Payment
template Issuer
with
userAdmin: Party
issuer: Party
where
signatory userAdmin
nonconsuming choice MintToken: ContractId Token
with
description: Text
initialPrice: Decimal
currency: Text
royaltyRate: Decimal
controller issuer
do
issued <- getTime
create Token
with
lastPrice = initialPrice
owner = issuer
..
choice RevokeIssuer: ()
controller userAdmin
do return ()
template IssuerRequest
with
userAdmin: Party
issuer: Party
reason: Text
where
signatory issuer
choice GrantIssuerRights: ContractId Issuer
controller userAdmin
do create Issuer with ..
choice RejectIssuerRequest: ()
controller userAdmin
do return ()
template Owner
with
userAdmin: Party
owner: Party
where
signatory userAdmin
nonconsuming choice AcceptTokenAsNewOwner: (ContractId Token, ContractId Payable, ContractId Payable)
with
offerId: ContractId TokenOffer
controller owner
do exercise offerId AcceptToken
nonconsuming choice AcceptTokenByKey: (ContractId Token, ContractId Payable, ContractId Payable)
with
issuer: Party
currentOwner: Party
description: Text
controller owner
do exerciseByKey @TokenOffer (issuer, currentOwner, description) AcceptToken
choice RevokeOwnerRights : ()
controller userAdmin
do return ()
template OwnerRequest
with
userAdmin: Party
owner: Party
reason: Text
where
signatory owner
choice GrantOwnerRights: ContractId Owner
controller userAdmin
do create Owner with ..
choice RejectOwnerRequest: ()
controller owner
do return()
@cocreature
@a_putkov
@Gary_Verhaegen
thank you so much