diff --git a/docs-main/appdev/modules/images/patterns/authorization.png b/docs-main/appdev/modules/images/patterns/authorization.png
new file mode 100644
index 000000000..dc13372e9
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/authorization.png differ
diff --git a/docs-main/appdev/modules/images/patterns/delegation.png b/docs-main/appdev/modules/images/patterns/delegation.png
new file mode 100644
index 000000000..f92517949
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/delegation.png differ
diff --git a/docs-main/appdev/modules/images/patterns/initiateaccept.png b/docs-main/appdev/modules/images/patterns/initiateaccept.png
new file mode 100644
index 000000000..70484db5f
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/initiateaccept.png differ
diff --git a/docs-main/appdev/modules/images/patterns/legends.png b/docs-main/appdev/modules/images/patterns/legends.png
new file mode 100644
index 000000000..c95af21d0
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/legends.png differ
diff --git a/docs-main/appdev/modules/images/patterns/lockingByArchiving1.png b/docs-main/appdev/modules/images/patterns/lockingByArchiving1.png
new file mode 100644
index 000000000..dc6fe1ac1
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/lockingByArchiving1.png differ
diff --git a/docs-main/appdev/modules/images/patterns/lockingByArchiving2.png b/docs-main/appdev/modules/images/patterns/lockingByArchiving2.png
new file mode 100644
index 000000000..7560e4dcd
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/lockingByArchiving2.png differ
diff --git a/docs-main/appdev/modules/images/patterns/lockingBySafekeeping.png b/docs-main/appdev/modules/images/patterns/lockingBySafekeeping.png
new file mode 100644
index 000000000..c69e470b3
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/lockingBySafekeeping.png differ
diff --git a/docs-main/appdev/modules/images/patterns/lockingByStateChange.png b/docs-main/appdev/modules/images/patterns/lockingByStateChange.png
new file mode 100644
index 000000000..64c34949e
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/lockingByStateChange.png differ
diff --git a/docs-main/appdev/modules/images/patterns/multiplepartyAgreement.png b/docs-main/appdev/modules/images/patterns/multiplepartyAgreement.png
new file mode 100644
index 000000000..be37a9697
Binary files /dev/null and b/docs-main/appdev/modules/images/patterns/multiplepartyAgreement.png differ
diff --git a/docs-main/appdev/modules/m3-design-patterns.mdx b/docs-main/appdev/modules/m3-design-patterns.mdx
index 288c94bb0..1b58ab24d 100644
--- a/docs-main/appdev/modules/m3-design-patterns.mdx
+++ b/docs-main/appdev/modules/m3-design-patterns.mdx
@@ -16,7 +16,13 @@ import DamlAppdevModulesM3DesignPatternsL464 from "/snippets/daml-docs/appdev_mo
import DamlAppdevModulesM3DesignPatternsL491 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L491.mdx";
import DamlAppdevModulesM3DesignPatternsL504 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L504.mdx";
import DamlAppdevModulesM3DesignPatternsL521 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L521.mdx";
+import DamlAppdevModulesM3DesignPatternsL530 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L530.mdx";
+import DamlAppdevModulesM3DesignPatternsL533 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L533.mdx";
+import DamlAppdevModulesM3DesignPatternsL535 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L535.mdx";
+import DamlAppdevModulesM3DesignPatternsL536 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L536.mdx";
import DamlAppdevModulesM3DesignPatternsL541 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L541.mdx";
+import DamlAppdevModulesM3DesignPatternsL555 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L555.mdx";
+import DamlAppdevModulesM3DesignPatternsL565 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L565.mdx";
import DamlAppdevModulesM3DesignPatternsL587 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L587.mdx";
import DamlAppdevModulesM3DesignPatternsL598 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L598.mdx";
import DamlAppdevModulesM3DesignPatternsL629 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L629.mdx";
@@ -24,11 +30,6 @@ import DamlAppdevModulesM3DesignPatternsL74 from "/snippets/daml-docs/appdev_mod
import DamlAppdevModulesM3DesignPatternsL80 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L80.mdx";
import DamlAppdevModulesM3DesignPatternsL86 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L86.mdx";
import DamlAppdevModulesM3DesignPatternsL92 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L92.mdx";
-import DamlAppdevModulesM3DesignPatternsL454 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L454.mdx";
-import DamlAppdevModulesM3DesignPatternsL461 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L461.mdx";
-import DamlAppdevModulesM3DesignPatternsL498 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L498.mdx";
-import DamlAppdevModulesM3DesignPatternsL599 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L599.mdx";
-import DamlAppdevModulesM3DesignPatternsL673 from "/snippets/daml-docs/appdev_modules_m3-design-patterns_L673.mdx";
@@ -225,29 +226,21 @@ Just writing `(alice, bob, bank, aha, ahb) <- setupRoles` would also be legal, b
Daml's execution model is fairly easy to understand, but has some important consequences. You can imagine the life of a transaction as follows:
-Command submission
-A user submits a list of commands via the Ledger API of a participant node, acting as a `Party` hosted on that node. That party is called the requester.
+1. **Command submission**: A user submits a list of commands via the Ledger API of a participant node, acting as a `Party` hosted on that node. That party is called the requester.
-Interpretation
-Each command corresponds to one or more actions. During this step, the `Update` corresponding to each action is evaluated in the context of the ledger to calculate all consequences, including transitive ones (consequences of consequences, etc.). The result of this is a complete transaction. Together with its requestor, this is also known as a commit.
+2. **Interpretation**: Each command corresponds to one or more actions. During this step, the `Update` corresponding to each action is evaluated in the context of the ledger to calculate all consequences, including transitive ones (consequences of consequences, etc.). The result of this is a complete transaction. Together with its requestor, this is also known as a commit.
-Blinding
-On ledgers with strong privacy, projections (see [Privacy Model](/overview/learn/privacy-model)) for all involved parties are created. This is also called *projecting*.
+3. **Blinding**: On ledgers with strong privacy, projections (see [Privacy Model](/overview/learn/privacy-model)) for all involved parties are created. This is also called *projecting*.
-Transaction submission
-The transaction/commit is submitted to the network.
+4. **Transaction submission**: The transaction/commit is submitted to the network.
-Validation
-The transaction/commit is validated by the network. Who exactly validates can differ from implementation to implementation. Validation also involves scheduling and collision detection, ensuring that the transaction has a well-defined place in the (partial) ordering of commits, and no double spends occur.
+5. **Validation**: The transaction/commit is validated by the network. Who exactly validates can differ from implementation to implementation. Validation also involves scheduling and collision detection, ensuring that the transaction has a well-defined place in the (partial) ordering of commits, and no double spends occur.
-Commitment
-The commit is actually committed according to the commit or consensus protocol of the ledger.
+6. **Commitment**: The commit is actually committed according to the commit or consensus protocol of the ledger.
-Confirmation
-The network sends confirmations of the commitment back to all involved participant nodes.
+7. **Confirmation**: The network sends confirmations of the commitment back to all involved participant nodes.
-Completion
-The user gets back a confirmation through the Ledger API of the submitting participant node.
+8. **Completion**: The user gets back a confirmation through the Ledger API of the submitting participant node.
The first important consequence of the above is that all transactions are committed atomically. Either a transaction is committed as a whole and for all participants, or it fails.
@@ -295,7 +288,7 @@ The consequences contain, next to some `fetch` actions, two `exercise` actions o
Each of the two involved `TransferApproval` contracts is signed by a different `issuer`, which see the action on "their" contract. So the EUR_Bank sees the `TransferApproval_Transfer` action for the EUR `Asset` and the USD_Bank sees the `TransferApproval_Transfer` action for the USD `Asset`.
-Some Daml ledgers, like the script runner and the Sandbox, work on the principle of "data minimization", meaning nothing more than the above information is distributed. That is, the "projection" of the overall transaction that gets distributed to EUR_Bank in step 4 of `execution_model` would consist only of the `TransferApproval_Transfer` and its consequences.
+Some Daml ledgers, like the script runner and the Sandbox, work on the principle of "data minimization", meaning nothing more than the above information is distributed. That is, the "projection" of the overall transaction that gets distributed to EUR_Bank in step 4 (transaction submission) of [Daml's execution model](#damls-execution-model) would consist only of the `TransferApproval_Transfer` and its consequences.
Other implementations, in particular those on public blockchains, may have weaker privacy constraints.
@@ -316,12 +309,28 @@ This is because the `create` action of these contracts are in the transitive con
Beyond the composition patterns above, this section covers common multi-party workflow patterns used in Daml. All examples below use a `Coin` asset model to illustrate each pattern.
+
+You can check out the examples locally by running `dpm new daml-patterns --template daml-patterns`.
+
+
+The diagrams below use a shared visual key for contracts, signatories, and choices:
+
+
+
{/* COPIED_START source="docs-website:docs/replicated/daml/3.4/sdk/sdlc-howtos/smart-contracts/develop/patterns/" hash="patterns-all" */}
### Propose-Accept
The most common way to get multiple parties to agree on a shared contract. One party creates a proposal contract that the other party can accept, reject, or let expire. The `IouProposal` [in the authorization module](/appdev/modules/m3-authorization#use-propose-accept-workflow-for-one-off-authorization) is another example of this pattern.
+It takes two to tango, but one party has to propose. It is no different in the business world. The contractual relationship between two businesses often starts with an invite, a business proposal, a bid offering, etc.
+
+**Invite** — When a market operator wants to set up a market, they need to go through an onboarding process in which they invite participants to sign master service agreements and fulfill different roles in the market. Receiving participants need to evaluate the rights and responsibilities of each role and respond accordingly.
+
+**Propose** — When issuing an asset, an issuer is making a business proposal to potential buyers. The proposal lays out what is expected from buyers, and what they can expect from the issuer. Buyers need to evaluate all aspects of the offering, e.g. price, return, and tax implications, before making a decision.
+
+The Propose and Accept pattern demonstrates how to write a Daml program to model the initiation of an inter-company contractual relationship. Daml modelers often have to follow this pattern to ensure that no participant is forced into an obligation.
+
The issuer creates a `CoinMaster` contract, then uses it to invite an owner. The invitation is a proposal contract with the issuer as signatory and the owner as observer:
@@ -334,24 +343,38 @@ When the owner accepts, the result contract has both parties as signatories —
+
+
This pattern can be verbose when more than two signatures are needed — see Multiple Party Agreement below for that case.
### Delegation
Gives one party the right to exercise a choice on behalf of another. The principal creates a delegation contract that authorizes an agent to act for them, without the principal committing each action. This models real-world custodian relationships where a bank holds securities and settles transactions on a client's behalf.
+Delegation is prevalent in the business world. In fact, the entire custodian business is based on delegation. When a company chooses a custodian bank, it is effectively giving the bank the rights to hold their securities and settle transactions on their behalf. The securities are not legally possessed by the custodian banks, but the banks should have full rights to perform actions in the client's name, such as making payments or changing investments.
+
+The Delegation pattern enables Daml modelers to model the real-world business contractual agreements between custodian banks and their customers. Ownership and administration rights can be segregated easily and clearly.
+
The delegation contract (`CoinPoA` — Power of Attorney) has the principal as signatory. The attorney controls a `TransferCoin` choice that exercises `Transfer` on the principal's coin:
+Whether or not the attorney should be a signatory of `CoinPoA` is subject to the business agreements between principal and attorney. For simplicity, in this example, the attorney is not a signatory.
+
The coin must be disclosed to the attorney before they can exercise the delegated choice. This is done by adding them as an observer via a `Disclose` choice on `Coin`:
+
+
### Authorization
Verifies that a controlling party has the right permissions before they take certain actions. An authorization contract serves as proof — the choice body checks for its existence and validity before proceeding.
+Authorization is a universal concept in the business world, as access to most business resources is a privilege and not given freely. For example, security trading may seem to be a plain bilateral agreement between the two trading counterparties, but this could not be further from the truth. To be able to trade, the trading parties need to go through a series of authorization processes and gain permission from a list of service providers such as exchanges, market data streaming services, clearing houses, and security registrars.
+
+The Authorization pattern shows how to model these authorization checks prior to a business transaction.
+
For example, an issuer wants to ensure that only accredited parties can receive coin transfers. The issuer creates an authorization token for approved owners:
@@ -362,310 +385,116 @@ The `AcceptTransfer` choice on `TransferProposal` requires the new owner to supp
If the issuer withdraws the authorization before the transfer is accepted, the transfer fails.
+
+
### Locking
Prevents choices from being exercised on a contract while it is in a locked state. Useful for scenarios like securities settlement where assets must be frozen during clearing.
-One approach is **locking by state change** — the contract carries a `locker` field. When `owner == locker`, the coin is unlocked and can be transferred. When they differ, a third-party locker controls the unlock:
-
-
-
-Two other approaches exist: **locking by archiving** (archive the original contract and create a `LockedCoin` wrapper with `Unlock` and `Clawback` choices) and **locking by safekeeping** (transfer custody to a trusted third party who controls the unlock).
-
-### Multiple party agreement
-
-Collects signatures from more than two parties. A `Pending` contract wraps the final `Agreement` and tracks who has signed. Each party signs by exercising a `Sign` choice, and once all parties have signed, any of them can `Finalize` to create the agreement.
-
-The final agreement contract has multiple signatories:
-
-
+Locking is a common real-life requirement in business transactions. During the clearing and settlement process, once a trade is registered and novated to a central clearing house, the trade is considered locked-in. This means the securities under the ownership of the seller need to be locked so they cannot be used for other purposes, and so should the funds on the buyer's account. The locked state should remain throughout the settlement payment-versus-delivery process. Once the ownership is exchanged, the lock is lifted for the new owner to have full access.
-The `Pending` contract collects signatures one by one. It is observable by all required signatories, so each can see when it is their turn to sign:
+There are three ways to achieve locking:
-
+#### Locking by archiving
-One party kicks off the workflow by creating a `Pending` contract listing only themselves as signed. The others sign in any order, and once complete, any signatory can finalize:
+Archiving is a straightforward choice for locking because once a contract is archived, all choices on the contract become unavailable. Archiving can be done either through a consuming choice or an archiving contract.
-
+**Consuming choice**
-{/* COPIED_END */}
-{/* COPIED_START source="docs-website:docs/replicated/daml/3.4/sdk/tutorials/smart-contracts/compose.rst" hash="f782af8e" */}
+The steps below show how to use a consuming choice in the original contract to achieve locking:
+- Add a consuming choice, `Lock`, to the `Coin` template that creates a `LockedCoin`.
+- The controller party on `Lock` may vary depending on business context. In this example, `owner` is a good choice.
+- The parameters to this choice are also subject to business use case. Normally, it should at least have locking terms (e.g. lock expiry time) and a party authorized to unlock.
-# Compose choices
+
-It's time to put everything you've learned so far together into a complete and secure Daml model for asset issuance, management, transfer, and trading. This application will have capabilities similar to the one in the [CN Quickstart](/sdks-tools/reference-projects/cn-quickstart). In the process you will learn about a few more concepts:
+Create a `LockedCoin` to represent `Coin` in the locked state. `LockedCoin` has the following characteristics, all in order to be able to recreate the original `Coin`:
+- The signatories are the same as the original contract.
+- It has all data of `Coin`, either through having a `Coin` as a field, or by replicating all data of `Coin`.
+- It has an `Unlock` choice to lift the lock.
-- Daml projects, packages, and modules
-- Composition of transactions
-- Observers and stakeholders
-- Daml's execution model
-- Privacy
+
-The model in this section is not a single Daml file, but a Daml project consisting of several files that depend on each other.
+
-
-Remember that you can load all the code for this section into a folder called `intro-compose` by running `dpm new intro-compose --template daml-intro-compose`
-
+**Archiving contract**
-## Daml projects
+In the event that changing the original contract is not desirable, and assuming the original contract already has an `Archive` choice, you can introduce another contract, `CoinCommitment`, to archive `Coin` and create `LockedCoin`.
-Daml is organized in projects, packages, and modules. A Daml project is specified using a single `daml.yaml` file, and compiles into a package in Daml's intermediate language, or bytecode equivalent, Daml-LF. Each Daml file within a project becomes a Daml module, which is a bit like a namespace. Each Daml project has a source root specified in the `source` parameter in the project's `daml.yaml` file. The package will include all modules specified in `*.daml` files beneath that source directory.
+Examine the controller party and archiving logic in the `Archives` choice on the `Coin` contract. A coin can only be archived by the issuer under the condition that the issuer is the owner of the coin. This ensures the issuer cannot archive any coin at will:
-You can start a new project with a skeleton structure using `dpm new project-name` in the terminal. A minimal project would contain just a `daml.yaml` file and an empty directory of source files.
+
-> Take a look at the `daml.yaml` for the this chapter's project:
+Since we need to call the `Archives` choice from `CoinCommitment`, its signatory has to be the issuer. The controller party and parameters on the `Lock` choice are the same as described above for locking by consuming choice — the additional logic required is to transfer the asset to the issuer, and then explicitly call the `Archive` choice on the `Coin` contract. Once a `Coin` is archived, the `Lock` choice creates a `LockedCoin` that represents `Coin` in the locked state:
-```yaml
-sdk-version: __VERSION__
-name: __PROJECT_NAME__
-source: daml
-version: 1.0.0
-dependencies:
- - daml-prim
- - daml-stdlib
- - daml-script
-```
+
-You can generally set `name` and `version` freely to describe your project. `dependencies` does what the name suggests: it includes dependencies. You should always include `daml-prim` and `daml-stdlib`. The former contains internals of the compiler and the Daml Runtime, the latter gives access to the Daml standard library. `daml-script` contains the types and functions for Daml Script.
+
-You compile a Daml project by running `dpm build` from the project root directory. This creates a DAR file in `.daml/dist/dist/${project_name}-${project_version}.dar`. A DAR file is Daml's equivalent of a JAR file in Java: it's the artifact that gets deployed to a ledger to load the package and its dependencies. `dar` files are fully self-contained in that they contain all dependencies of the main package. More on all of this in `dependencies`.
+This pattern achieves locking in a fairly straightforward way. However, there are some trade-offs:
+- Locking by archiving disables all choices on the original contract. Usually for consuming choices this is exactly what is required, but if a party needs to selectively lock only some choices, remaining active choices need to be replicated on the `LockedCoin` contract, which can lead to code duplication.
+- The choices on the original contract need to be altered for the lock choice to be added. If this contract is shared across multiple participants, it will require agreement from all involved.
-## Project structure
+#### Locking by state change
-This project contains an asset holding model for transferable, fungible assets and a separate trade workflow. The templates are structured in three modules: `Intro.Asset`, `Intro.Asset.Role`, and `Intro.Asset.Trade`.
+In its original form, all choices on `Coin` are actionable as long as the contract is active. Locking by state requires introducing fields to track state. This allows for the creation of an active contract in two possible states: locked or unlocked. A Daml modeler can selectively make certain choices actionable only if the contract is in an unlocked state. This effectively makes the asset lockable.
-In addition, there are tests in modules `Test.Intro.Asset`, `Test.Intro.Asset.Role`, and `Test.Intro.Asset.Trade`.
+The state can be stored in many ways. This example demonstrates how to create a `LockableCoin` through a party. Alternatively, you can add a lock contract to the asset contract, use a boolean flag, or include lock activation and expiry terms as part of the template parameters.
-All but the last `.`-separated segment in module names correspond to paths relative to the project source directory, and the last one to a file name. The folder structure therefore looks like this:
+Here are the changes made to the original `Coin` contract to make it lockable:
+- Add a `locker` party to the template parameters.
+- Define the states: if `owner == locker`, the coin is unlocked; if `owner != locker`, the coin is in a locked state.
+- The contract state is checked on choices: `Transfer` is only actionable if the coin is unlocked; `Lock` is only actionable if the coin is unlocked and a third-party locker is supplied; `Unlock` is available to the locker party only if the coin is locked.
-``` none
-.
-├── daml
-│ ├── Intro
-│ │ ├── Asset
-│ │ │ ├── Role.daml
-│ │ │ └── Trade.daml
-│ │ └── Asset.daml
-│ └── Test
-│ └── Intro
-│ ├── Asset
-│ │ ├── Role.daml
-│ │ └── Trade.daml
-│ └── Asset.daml
-└── daml.yaml
-```
-
-Each file contains a module header. For example, `daml/Intro/Asset/Role.daml`:
-
-
-
-You can import one module into another using the `import` keyword. The `LibraryModules` module imports all six modules:
-
-
-
-Imports always have to appear just below the module declaration. You can optionally add a list of names after the import to import only the selected names:
-
-
-
-If your module contains any Daml Scripts, you need to import the corresponding functionality:
-
-
-
-## Project overview
-
-The project both changes and adds to the `Iou` model presented in `parties`:
-
-- Assets are fungible in the sense that they have `Merge` and `Split` choices that allow the `owner` to manage their holdings.
-
-- Transfer proposals now need the authorities of both `issuer` and `newOwner` to accept. This makes `Asset` safer than `Iou` from the issuer's point of view.
-
- With the `Iou` model, an `issuer` could end up owing cash to anyone as transfers were authorized by just `owner` and `newOwner`. In this project, only parties having an `AssetHolder` contract can end up owning assets. This allows the `issuer` to determine which parties may own their assets.
-
-- The `Trade` template adds a swap of two assets to the model.
-
-## Composed choices and scripts
-
-This project showcases how you can put the `Update` and `Script` actions you learned about in `parties` to good use. For example, the `Merge` and `Split` choices each perform several actions in their consequences.
-
-- Two create actions in case of `Split`
-- One create and one archive action in case of `Merge`
-
-
-
-The `return` function used in `Split` is available in any `Action` context. The result of `return x` is a no-op containing the value `x`. It has an alias `pure`, indicating that it's a pure value, as opposed to a value with side-effects. The `return` name makes sense when it's used as the last statement in a `do` block as its argument is indeed the "return"-value of the `do` block in that case.
-
-Taking transaction composition a step further, the `Trade_Settle` choice on `Trade` composes two `exercise` actions:
-
-
-
-The resulting transaction, with its two nested levels of consequences, can be seen in the `test_trade` script in `Test.Intro.Asset.Trade`:
-
-``` none
-TX 14 1970-01-01T00:00:00Z (Test.Intro.Asset.Trade:79:23)
-#14:0
-│ disclosed to (since): 'Alice' (14), 'Bob' (14)
-└─> 'Bob' exercises Trade_Settle on #12:0 (Intro.Asset.Trade:Trade)
- with
- quoteAssetCid = #9:1; baseApprovalCid = #13:1
- children:
- #14:1
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'USD_Bank' (14)
- └─> 'Alice' and 'USD_Bank' fetch #10:1 (Intro.Asset:Asset)
-
- #14:2
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'EUR_Bank' (14)
- └─> 'Bob' and 'EUR_Bank' fetch #9:1 (Intro.Asset:Asset)
-
- #14:3
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'USD_Bank' (14)
- └─> 'Alice' and 'Bob' exercise TransferApproval_Transfer on #13:1 (Intro.Asset:TransferApproval)
- with
- assetCid = #10:1
- children:
- #14:4
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'USD_Bank' (14)
- └─> 'Alice' and 'USD_Bank' fetch #10:1 (Intro.Asset:Asset)
-
- #14:5
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'USD_Bank' (14)
- └─> 'Alice' and 'USD_Bank' exercise Archive on #10:1 (Intro.Asset:Asset)
-
- #14:6
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'USD_Bank' (14)
- └─> 'Bob' and 'USD_Bank' create Intro.Asset:Asset
- with
- issuer = 'USD_Bank';
- owner = 'Bob';
- symbol = "USD";
- quantity = 100.0000000000;
- observers = []
-
- #14:7
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'EUR_Bank' (14)
- └─> 'Alice',
- 'Bob' exercises TransferApproval_Transfer on #11:1 (Intro.Asset:TransferApproval)
- with
- assetCid = #9:1
- children:
- #14:8
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'EUR_Bank' (14)
- └─> 'Bob' and 'EUR_Bank' fetch #9:1 (Intro.Asset:Asset)
-
- #14:9
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'EUR_Bank' (14)
- └─> 'Bob' and 'EUR_Bank' exercise Archive on #9:1 (Intro.Asset:Asset)
-
- #14:10
- │ disclosed to (since): 'Alice' (14), 'Bob' (14), 'EUR_Bank' (14)
- └─> 'Alice' and 'EUR_Bank' create Intro.Asset:Asset
- with
- issuer = 'EUR_Bank';
- owner = 'Alice';
- symbol = "EUR";
- quantity = 90.0000000000;
- observers = []
-```
-
-Similar to choices, you can see how the scripts in this project are built up from each other:
-
-
-
-In the above, the `test_issuance` script in `Test.Intro.Asset.Role` uses the output of the `setupRoles` script in the same module.
-
-The same line shows a new kind of pattern matching. Rather than writing `setupResult <- setupRoles` and then accessing the components of `setupResult` using `_1`, `_2`, etc., you can give them names. It's equivalent to writing:
-
-
-
-Just writing `(alice, bob, bank, aha, ahb) <- setupRoles` would also be legal, but `setupResult` is used in the return value of `test_issuance` so it makes sense to give it a name, too. The notation with `@` allows you to give both the whole value as well as its constituents names in one go.
-
-## Daml's execution model
-
-Daml's execution model is fairly easy to understand, but has some important consequences. You can imagine the life of a transaction as follows:
-
-Command submission
-A user submits a list of commands via the Ledger API of a participant node, acting as a `Party` hosted on that node. That party is called the requester.
-
-Interpretation
-Each command corresponds to one or more actions. During this step, the `Update` corresponding to each action is evaluated in the context of the ledger to calculate all consequences, including transitive ones (consequences of consequences, etc.). The result of this is a complete transaction. Together with its requestor, this is also known as a commit.
-
-Blinding
-On ledgers with strong privacy, projections (see `privacy`) for all involved parties are created. This is also called *projecting*.
-
-Transaction submission
-The transaction/commit is submitted to the network.
-
-Validation
-The transaction/commit is validated by the network. Who exactly validates can differ from implementation to implementation. Validation also involves scheduling and collision detection, ensuring that the transaction has a well-defined place in the (partial) ordering of commits, and no double spends occur.
-
-Commitment
-The commit is actually committed according to the commit or consensus protocol of the ledger.
-
-Confirmation
-The network sends confirmations of the commitment back to all involved participant nodes.
-
-Completion
-The user gets back a confirmation through the Ledger API of the submitting participant node.
-
-The first important consequence of the above is that all transactions are committed atomically. Either a transaction is committed as a whole and for all participants, or it fails.
-
-That's important in the context of the `Trade_Settle` choice shown above. The choice transfers a `baseAsset` one way and a `quoteAsset` the other way. Thanks to transaction atomicity, there is no chance that either party is left out of pocket.
-
-The second consequence is that the requester of a transaction knows all consequences of their submitted transaction -- there are no surprises in Daml. However, it also means that the requester must have all the information to interpret the transaction. We also refer to this as Principle 2 a bit later on this page.
-
-That's also important in the context of `Trade`. In order to allow Bob to interpret a transaction that transfers Alice's cash to Bob, Bob needs to know both about Alice's `Asset` contract, as well as about some way for `Alice` to accept a transfer -- remember, accepting a transfer needs the authority of `issuer` in this example.
-
-## Observers
-
-*Observers* are Daml's mechanism to disclose contracts to other parties. They are declared just like signatories, but using the `observer` keyword, as shown in the `Asset` template:
-
-
-
-The `Asset` template also gives the `owner` a choice to set the observers, and you can see how Alice uses it to show her `Asset` to Bob just before proposing the trade. You can try out what happens if she didn't do that by removing that transaction:
+
-
+
-Observers have guarantees in Daml. In particular, they are guaranteed to see actions that create and archive the contract on which they are an observer.
+Trade-offs:
+- It requires changes made to the original contract template. Furthermore, every choice intended to be locked needs to change too.
+- If locking and unlocking terms (e.g. lock triggering event, expiry time, etc.) need to be added to the template parameters to track the state change, the template can get overloaded.
-Since observers are calculated from the arguments of the contract, they always know about each other. That's why, rather than adding Bob as an observer on Alice's `AssetHolder` contract, and using that to authorize the transfer in `Trade_Settle`, Alice creates a one-time authorization in the form of a `TransferAuthorization`. If Alice had lots of counterparties, she would otherwise end up leaking them to each other.
+#### Locking by safekeeping
-Choice controllers are not automatically made observers, as they can only be calculated at the point in time when the choice arguments are known.
+Safekeeping is a realistic way to model locking, as it is a common practice in many industries. For example, during a real estate transaction, purchase funds are transferred to the seller's lawyer's escrow account after the contract is signed and before closing.
-## Privacy
+There is no need to make a change to the original contract. With two additional contracts, we can transfer the `Coin` ownership to a locker party:
+- `LockRequest` has a locker party as the single signatory, allowing the locker party to unilaterally initiate the process and specify locking terms.
+- Once the owner exercises `Accept` on the lock request, the ownership of the coin is transferred to the locker.
+- The `Accept` choice also creates a `LockedCoinV2` that represents `Coin` in the locked state.
-Daml's privacy model is based on two principles:
+
-Principle 1. Parties see those actions that they have a stake in. Principle 2. Every party that sees an action sees its (transitive) consequences.
+`LockedCoinV2` represents `Coin` in the locked state. It is fairly similar to the `LockedCoin` described above for locking by consuming choice. The additional logic is to transfer ownership from the locker back to the owner when `Unlock` or `Clawback` is called:
-Principle 2 is necessary to ensure that every party can independently verify the validity of every transaction they see.
+
-A party has a stake in an action if
+
-- they are a required authorizer of it
-- they are a signatory of the contract on which the action is performed
-- they are an observer on the contract, and the action creates or archives it
+Ownership transfer may give the locking party too much access to the locked asset. A rogue lawyer could run away with the funds. In a similar fashion, a malicious locker party could introduce code to transfer assets away while they are under their ownership.
-What does that mean for the `exercise tradeCid Trade_Settle` action from `test_trade`?
+### Multiple party agreement
-Alice is the signatory of `tradeCid` and Bob a required authorizer of the `Trade_Settled` action, so both of them see it. According to principle 2 above, that means they get to see everything in the transaction.
+Collects signatures from more than two parties. A `Pending` contract wraps the final `Agreement` and tracks who has signed. Each party signs by exercising a `Sign` choice, and once all parties have signed, any of them can `Finalize` to create the agreement.
-The consequences contain, next to some `fetch` actions, two `exercise` actions of the choice `TransferApproval_Transfer`.
+Propose-Accept (above) shows how to create bilateral agreements in Daml. However, a project or a workflow often requires more than two parties to reach a consensus and put their signatures on a multi-party contract. For example, in a large construction project, there are at least three major stakeholders: owner, architect, and builder. All three parties need to establish agreement on key responsibilities and project success criteria before starting the construction.
-Each of the two involved `TransferApproval` contracts is signed by a different `issuer`, which see the action on "their" contract. So the EUR_Bank sees the `TransferApproval_Transfer` action for the EUR `Asset` and the USD_Bank sees the `TransferApproval_Transfer` action for the USD `Asset`.
+If such an agreement were modeled as three separate bilateral agreements, no party could be sure if there are conflicts between their two contracts and the third contract between their partners. If Propose-Accept were used to collect three signatures on a multi-party agreement, unnecessary restrictions would be put on the order of consensus, and a number of additional contract templates would be needed as intermediate steps. Both solutions are suboptimal.
-Some Daml ledgers, like the script runner and the Sandbox, work on the principle of "data minimization", meaning nothing more than the above information is distributed. That is, the "projection" of the overall transaction that gets distributed to EUR_Bank in step 4 of `execution_model` would consist only of the `TransferApproval_Transfer` and its consequences.
+Following the Multiple Party Agreement pattern, it is easy to write an agreement contract with multiple signatories and have each party accept explicitly.
-Other implementations, in particular those on public blockchains, may have weaker privacy constraints.
+The final agreement contract has multiple signatories:
-### Divulgence
+
-Note that principle 2 of the privacy model means that sometimes parties see contracts that they are not signatories or observers on. If you look at the final ledger state of the `test_trade` script, for example, you may notice that both Alice and Bob now see both assets, as indicated by the Xs in their respective columns:
+The `Pending` contract collects signatures one by one. It is observable by all required signatories, so each can see when it is their turn to sign:
-
+
-This is because the `create` action of these contracts are in the transitive consequences of the `Trade_Settle` action both of them have a stake in. This kind of disclosure is often called "divulgence" and needs to be considered when designing Daml models for privacy sensitive applications.
+One party kicks off the workflow by creating a `Pending` contract listing only themselves as signed. The others sign in any order, and once complete, any signatory can finalize:
-## Next up
+
-In `exceptions`, we will learn about how errors in your model can be handled in Daml.
+
{/* COPIED_END */}
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L530.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L530.mdx
new file mode 100644
index 000000000..10d128295
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L530.mdx
@@ -0,0 +1,6 @@
+```haskell
+choice Lock : ContractId LockedCoin
+ with maturity: Time; locker: Party
+ controller owner
+ do create LockedCoin with coin=this; maturity; locker
+```
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L533.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L533.mdx
new file mode 100644
index 000000000..0a1ebb497
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L533.mdx
@@ -0,0 +1,15 @@
+```haskell
+template LockedCoin
+ with
+ coin: Coin
+ maturity: Time
+ locker: Party
+ where
+ signatory coin.issuer, coin.owner
+ observer locker
+
+ choice Unlock
+ : ContractId Coin
+ controller locker
+ do create coin
+```
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L535.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L535.mdx
new file mode 100644
index 000000000..84e2c76d6
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L535.mdx
@@ -0,0 +1,7 @@
+```haskell
+-- a coin can only be archived by the issuer under the condition that the issuer is the owner of the coin. This ensures the issuer cannot archive coins at will.
+choice Archives
+ : ()
+ controller issuer
+ do assert (issuer == owner)
+```
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L536.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L536.mdx
new file mode 100644
index 000000000..edc0863aa
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L536.mdx
@@ -0,0 +1,28 @@
+```haskell
+template CoinCommitment
+ with
+ owner: Party
+ issuer: Party
+ amount: Decimal
+ where
+ signatory issuer
+ observer owner
+
+ nonconsuming choice LockCoin
+ : ContractId LockedCoin
+ with
+ coinCid: ContractId Coin
+ maturity: Time
+ locker: Party
+ controller owner
+ do
+ inputCoin <- fetch coinCid
+ assert (inputCoin.owner == owner && inputCoin.issuer == issuer && inputCoin.amount == amount)
+ -- the original coin is transferred to the issuer, then archived
+ prop <- exercise coinCid Transfer with newOwner = issuer
+ id <- exercise prop AcceptTransfer
+ exercise id Archives
+ create LockedCoin with
+ coin = inputCoin with owner; issuer; amount
+ maturity; locker
+```
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L555.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L555.mdx
new file mode 100644
index 000000000..43ce3b166
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L555.mdx
@@ -0,0 +1,21 @@
+```haskell
+template LockRequest
+ with
+ locker: Party
+ maturity: Time
+ coin: Coin
+ where
+ signatory locker
+ observer coin.owner
+
+ choice Accept : LockResult
+ with coinCid : ContractId Coin
+ controller coin.owner
+ do
+ inputCoin <- fetch coinCid
+ assert (inputCoin == coin)
+ tpCid <- exercise coinCid Transfer with newOwner = locker
+ coinCid <- exercise tpCid AcceptTransfer
+ lockCid <- create LockedCoinV2 with locker; maturity; coin
+ return LockResult {coinCid; lockCid}
+```
diff --git a/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L565.mdx b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L565.mdx
new file mode 100644
index 000000000..cb4a0b8fe
--- /dev/null
+++ b/docs-main/snippets/daml-docs/appdev_modules_m3-design-patterns_L565.mdx
@@ -0,0 +1,31 @@
+```haskell
+template LockedCoinV2
+ with
+ coin: Coin
+ maturity: Time
+ locker: Party
+ where
+ signatory locker, coin.owner
+
+ choice UnlockV2
+ : ContractId Coin
+ with coinCid : ContractId Coin
+ controller locker
+ do
+ inputCoin <- fetch coinCid
+ assert (inputCoin.owner == locker)
+ tpCid <- exercise coinCid Transfer with newOwner = coin.owner
+ exercise tpCid AcceptTransfer
+
+ choice ClawbackV2
+ : ContractId Coin
+ with coinCid : ContractId Coin
+ controller coin.owner
+ do
+ currTime <- getTime
+ assert (currTime >= maturity)
+ inputCoin <- fetch coinCid
+ assert (inputCoin == coin with owner=locker)
+ tpCid <- exercise coinCid Transfer with newOwner = coin.owner
+ exercise tpCid AcceptTransfer
+```