/
Account.daml
197 lines (170 loc) · 7.55 KB
/
Account.daml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
-- Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
{-# LANGUAGE AllowAmbiguousTypes #-}
-- | We recommend to import this module qualified.
module Daml.Finance.Interface.Account.Account where
import Daml.Finance.Interface.Holding.Base qualified as Base (I)
import Daml.Finance.Interface.Holding.Lockable qualified as Lockable (I)
import Daml.Finance.Interface.Holding.Factory qualified as Holding (F)
import Daml.Finance.Interface.Types.Common (Id, AccountKey(..), InstrumentKey(..), Parties, PartiesMap, Quantity)
import Daml.Finance.Interface.Util.Common (exerciseInterfaceByKeyHelper)
import Daml.Finance.Interface.Util.Disclosure qualified as Disclosure (AddObservers(..), GetView(..), I, Implementation, RemoveObservers(..), flattenObservers)
-- | Type synonym for `Account`.
type I = Account
-- | Type synonym for `AccountKey`.
type K = AccountKey
-- | Type synonym for `Reference`. This type is currently used as a work-around given the lack of interface keys.
type R = Reference
-- | Type synonym for `View`.
type V = View
-- | Controllers of the account (related to transfers).
data Controllers = Controllers
with
instructors : Parties
-- ^ Parties instructing a transfer (outgoing).
approvers : Parties
-- ^ Parties approving a transfer (incoming).
deriving (Eq, Ord, Show)
-- | Exercise interface by key.
-- This method can be used to exercise a choice on an `Account` given its `AccountKey`.
-- Requires as input the `AccountKey`, the actor fetching the account and the choice arguments. For example:
-- ```
-- exerciseInterfaceByKey @Account.I accountKey actor Account.Debit with holdingCid
-- ```
exerciseInterfaceByKey : forall t2 d r. (HasExercise t2 d r)
=> K -- ^ The account key.
-> Party -- ^ The actor fetching the account.
-> d -- ^ The choice arguments.
-> Update r
exerciseInterfaceByKey k actor arg = exerciseInterfaceByKeyHelper @R @t2 k (GetCid with viewer = actor) arg
-- | View for `Account`.
data View = View
with
custodian : Party
-- ^ Party providing accounting services.
owner : Party
-- ^ Party owning this account.
id : Id
-- ^ Identifier for the account.
description : Text
-- ^ Human readable description of the account.
holdingFactoryCid : ContractId Holding.F
-- ^ Associated holding factory.
controllers : Controllers
-- ^ Parties controlling transfers.
deriving (Eq, Show)
-- | Convert the account's 'View' to its key.
toKey : View -> AccountKey
toKey v = AccountKey with custodian = v.custodian; owner = v.owner; id = v.id
-- | An interface which represents an established relationship between a provider and an owner.
interface Account where
viewtype V
asDisclosure : Disclosure.I
-- ^ Conversion to `Disclosure` interface.
getKey : AccountKey
-- ^ Get the unique key of the `Account`.
credit : Credit -> Update (ContractId Base.I)
-- ^ Implementation of the `Credit` choice.
debit : Debit -> Update ()
-- ^ Implementation of the `Debit` choice.
creditAndDebit : CreditAndDebit -> Update (ContractId Lockable.I)
-- ^ Implementation of the `CreditAndDebit` choice.
nonconsuming choice GetView : View
-- ^ Retrieves the interface view.
with
viewer : Party
-- ^ The party fetching the view.
controller viewer
do
pure $ view this
nonconsuming choice Credit : ContractId Base.I
-- ^ Creates a new `Holding` in the corresponding `Account`.
with
quantity : Quantity InstrumentKey Decimal
-- ^ The target `Instrument` and corresponding amount.
controller (view this).custodian, (view this).owner
do
credit this arg
nonconsuming choice Debit : ()
-- ^ Removes an existing `Holding`.
with
holdingCid : ContractId Base.I
-- ^ The `Holding`'s contract id.
controller (view this).custodian, (view this).owner
do
debit this arg
nonconsuming choice CreditAndDebit : ContractId Lockable.I
-- ^ Used in the context of a `Transfer` to debit an existing holding, and
-- credit it to this `Account`.
with
actors : Parties
-- ^ Additional controllers of the choice.
lockableCid : ContractId Lockable.I
-- ^ The `Lockable`'s contract id to be debited.
controller actors, (view this).controllers.approvers, (view this).custodian
do
creditAndDebit this arg
-- | Type constraint for requiring templates to implement `Account` along with `Disclosure`.
type Implementation t = (HasToInterface t I, Disclosure.Implementation t)
instance HasToInterface I Disclosure.I where _toInterface = asDisclosure
class (Implementation t) => HasImplementation t
instance HasImplementation I
-- | HIDE
-- This template is used to key an Account contract. It allows for looking up this contract
-- by key then acquiring the Account contract by fetching its contract id on this contract.
-- As updates are made to an Account, this Reference contract is required to be kept in sync.
template Reference
with
accountView : View
-- ^ The default view for accounts.
cid : ContractId Account
-- ^ The contract id of the account.
observers : PartiesMap
where
signatory accountView.custodian, accountView.owner
observer Disclosure.flattenObservers observers
key toKey accountView : AccountKey
maintainer key.custodian
nonconsuming choice GetCid : ContractId Account
-- ^ Get the `Account`'s contract id.
with
viewer : Party
controller viewer
do
pure cid
choice SetCid : ContractId Reference
-- ^ Set the account cid. This choice should be called only from `Account` implementations.
with
newCid : ContractId Account
-- ^ The account cid.
controller accountView.custodian, accountView.owner
do
create this with cid = newCid
choice SetObservers : ContractId Reference
-- ^ Set observers. This choice should be called only from `Account` implementations.
with
newObservers : PartiesMap
-- ^ The new observers.
controller accountView.custodian, accountView.owner
do
create this with observers = newObservers
-- | Disclose account.
disclose : (Text, Parties) -> Party -> Parties -> K -> Update (ContractId I)
disclose observersToAdd actor disclosers account = coerceContractId <$> exerciseInterfaceByKey @Disclosure.I account actor Disclosure.AddObservers with disclosers; observersToAdd
-- | Undisclose account.
undisclose : (Text, Parties) -> Party -> Parties -> K -> Update (Optional (ContractId I))
undisclose observersToRemove actor disclosers account = fmap coerceContractId <$> exerciseInterfaceByKey @Disclosure.I account actor Disclosure.RemoveObservers with disclosers; observersToRemove
-- | HIDE
-- Create Reference for the account
createReference : Party -> ContractId Account -> Update (ContractId Reference)
createReference actor cid = do
accountView <- exercise cid GetView with viewer = actor
disclosureView <- exercise (toInterfaceContractId @Disclosure.I cid) Disclosure.GetView with viewer = actor
create Reference with accountView; cid; observers = disclosureView.observers
-- | HIDE
-- Helper function to update the account reference once observers are added to the account.
disclosureUpdateReference : PartiesMap -> AccountKey -> ContractId Account -> Update (ContractId Disclosure.I)
disclosureUpdateReference newObservers k iCid = do
exerciseByKey @Reference k SetCid with newCid = iCid
exerciseByKey @Reference k SetObservers with newObservers
pure $ toInterfaceContractId iCid