- Overview
- Tutorials
- Getting started
- Get started with Canton and the JSON Ledger API
- Get Started with Canton, the JSON Ledger API, and TypeScript
- Get started with Canton Network App Dev Quickstart
- Get started with smart contract development
- Basic contracts
- Test templates using Daml scripts
- Build the Daml Archive (.dar) file
- Data types
- Transform contracts using choices
- Add constraints to a contract
- Parties and authority
- Compose choices
- Handle exceptions
- Work with dependencies
- Functional programming 101
- The Daml standard library
- Test Daml contracts
- Next steps
- Application development
- Getting started
- Development how-tos
- Component how-tos
- Explanations
- References
- Application development
- Smart contract development
- Daml language cheat sheet
- Daml language reference
- Daml standard library
- DA.Action.State.Class
- DA.Action.State
- DA.Action
- DA.Assert
- DA.Bifunctor
- DA.Crypto.Text
- DA.Date
- DA.Either
- DA.Exception
- DA.Fail
- DA.Foldable
- DA.Functor
- DA.Internal.Interface.AnyView.Types
- DA.Internal.Interface.AnyView
- DA.List.BuiltinOrder
- DA.List.Total
- DA.List
- DA.Logic
- DA.Map
- DA.Math
- DA.Monoid
- DA.NonEmpty.Types
- DA.NonEmpty
- DA.Numeric
- DA.Optional
- DA.Record
- DA.Semigroup
- DA.Set
- DA.Stack
- DA.Text
- DA.TextMap
- DA.Time
- DA.Traversable
- DA.Tuple
- DA.Validation
- GHC.Show.Text
- GHC.Tuple.Check
- Prelude
- Smart contract upgrading reference
- Glossary of concepts
How To Implement Time Constraints¶
Contract time constraints may be implemented using either:
ledger time primitives (i.e. isLedgerTimeLT, isLedgerTimeLE, isLedgerTimeGT and isLedgerTimeGE) or assertions (i.e. assertWithinDeadline and assertDeadlineExceeded)
the use of ledger time primitives and assertions do not constrain the time bound between transaction preparation and submission - e.g. they are suitable for workflows using external parties to sign transactions
or, by calling getTime
calls to getTime constrain transaction preparation and submission workflows to be (by default) within 1 minute.
the 1 minute value is the default value for the ledger time record time tolerance parameter (a dynamic synchronizer parameter).
The next subsections demonstrate how the following Coin and TransferProposal contracts can be modified to use different types of ledger time constraints to control when parties are allowed to perform ledger writes.
Coin contract
template Coin
with
owner: Party
issuer: Party
amount: Decimal
where
signatory issuer
signatory owner
ensure amount > 0.0
choice Transfer : ContractId TransferProposal
with
newOwner: Party
controller owner
do create TransferProposal
with coin=this; newOwner
TransferProposal contract
template TransferProposal
with
coin: Coin
newOwner: Party
where
signatory coin.owner
signatory coin.issuer
observer newOwner
choice AcceptTransfer : ContractId Coin
controller newOwner
do
create coin with owner = newOwner
choice WithdrawTransfer : ContractId Coin
controller coin.owner
do
create coin
Simple coin transfer with consent withdrawal¶
How to check that a deadline is valid¶
This design pattern demonstrates how to limit choices so that they must occur by a given deadline.
Motivation¶
When parties need to perform ledger writes by a given deadline.
Implementation¶
Transfer proposals can be accepted at any point in time. To restrict this behaviour so that acceptance must occur by a fixed time, a guard for AcceptTransfer choice execution can be added.
- TransferProposal contract
In the TransferProposal contract, the body of the AcceptTransfer choice is modified to assert that the contract deadline is valid.
choice AcceptTransfer : ContractId Coin controller newOwner do assertWithinDeadline "time-limited-transfer" timeLimit create coin with owner = newOwner
As transfer proposals are created when a Transfer choice is executed, the time by which an AcceptTransfer can be executed needs to be passed in as a choice parameter.
- Coin contract
In the Coin contract, the Transfer choice has an additional deadline argument, so that TransferProposal contracts can be given a fixed lifetime.
choice Transfer : ContractId TransferProposal with newOwner: Party timeLimit: Time controller owner do create TransferProposal with coin=this; newOwner; timeLimit
Time limited coin ownership transfer¶
How to check that a deadline has passed¶
This design pattern demonstrates how to ensure choices only occur after a given deadline.
Motivation¶
When parties need to perform ledger writes after a fixed time delay.
Implementation¶
Transfer proposals can be accepted at any point in time. To restrict this behaviour so that acceptance can only occur after a fixed delay, a guard for AcceptTransfer choice execution can be added.
- TransferProposal contract
In the TransferProposal contract, the body of the AcceptTransfer choice is modified to assert that the contract deadline has been exceeded or passed.
choice AcceptTransfer : ContractId Coin controller newOwner do assertDeadlineExceeded "delayed-transfer" delay create coin with owner = newOwner
As transfer proposals are created when a Transfer choice is executed, the delay time after which an AcceptTransfer can be executed needs to be passed in as a choice parameter.
- Coin contract
In the Coin contract, the Transfer choice has an additional deadline argument, so that TransferProposal contracts can be given a delay.
choice Transfer : ContractId TransferProposal with newOwner: Party delay: Time controller owner do create TransferProposal with coin=this; newOwner; delay
Delayed coin ownership transfer¶
Grant time-limited writes to parties¶
This design pattern demonstrates how to grant time-limited writes to parties.
Motivation¶
When parties need to be able to perform ledger writes, but writes need to only be granted for a specific time window.
Implementation¶
Transfer proposals can be accepted at any point in time. To restrict this behaviour so that acceptance can only occur within a given time window, a guard for AcceptTransfer choice execution can be added.
- TransferProposal contract
In the TransferProposal contract, the body of the AcceptTransfer choice is modified to assert that the contract deadline has been exceeded or passed.
choice AcceptTransfer : ContractId Coin controller newOwner do withinWindow <- isLedgerTimeGE startTime && isLedgerTimeLT endTime _ <- unless withinWindow $ failWithStatus $ FailureStatus "transfer-outside-time-window" InvalidGivenCurrentSystemStateOther ("Ledger time is outside permitted transfer time window [" <> show startTime <> ", " <> show endTime <> ")") (TextMap.fromList [("startTime", show startTime), ("endTime", show endTime)]) create coin with owner = newOwner
As transfer proposals are created when a Transfer choice is executed, the interval start and end times, during which an AcceptTransfer can be executed need to be passed in as choice parameters.
- Coin contract
In the Coin contract, the Transfer choice has an additional deadline argument, so that TransferProposal contracts can be given a delay.
choice Transfer : ContractId TransferProposal with newOwner: Party startTime: Time duration: RelTime controller owner do create TransferProposal with coin=this; newOwner; startTime; addRelTime startTime duration
Time limited coin ownership transfer¶
Where to use getTime¶
For workflows that prepare and submit transactions, care needs to be taken when using calls to getTime. This is because calls to getTime cause transactions to be bound to the ledger time, and in turn constrain how sequencers may re-order transactions. Global Synchronizers are configured such that the transaction prepare and submit time window is one minute, so any workflow using getTime must prepare and submit transactions within that one-minute time window.
For workflows where this constraint can not be met (e.g. workflows that sign transactions using external parties), it is recommended that workflows are designed to use the ledger time primitives and assertions.
Motivation¶
When parties need to perform ledger writes by a given deadline, but are able to prepare and submit a transaction within 1 minute.
Implementation¶
Transfer proposals can be accepted at any point in time. To require acceptance by a fixed time, you can add a guard for AcceptTransfer choice execution. Here you determine the current ledger time by calling getTime.
- TransferProposal contract
In the TransferProposal contract, the body of the AcceptTransfer choice is modified to assert that the contract deadline is valid relative to the ledger time returned by calling getTime.
choice AcceptTransfer : ContractId Coin controller newOwner do t <- getTime _ <- unless (t < timeLimit) $ failWithStatus $ FailureStatus "deadline-exceeded" InvalidGivenCurrentSystemStateOther ("Ledger time is after deadline " <> show timeLimit) (TextMap.fromList [("timeLimit", show timeLimit)]) create coin with owner = newOwner
As transfer proposals are created when a Transfer choice is executed, the time by which an AcceptTransfer can be executed needs to be passed in as a choice parameter.
- Coin contract
In the Coin contract, the Transfer choice has an additional deadline argument, so that TransferProposal contracts can be given a fixed lifetime.
choice Transfer : ContractId TransferProposal with newOwner: Party timeLimit: Time controller owner do create TransferProposal with coin=this; newOwner; timeLimit