DAML Masterclass 시리즈의 두 번째 부분입니다. 아래 목록에서 다른 부분을 참조하십시오.
* DAML을 사용하는 애플리케이션에서 영감을 얻는 방법
이 시리즈에서는 내 프로젝트에 대한 영감을 얻기 위해 DAML 마켓 플레이스에 제공되는 DAML 전문가가 작성한 여러 고급 애플리케이션을 리버스 엔지니어링 합니다.
만약 일부 금융 refapp를 분석을 해봤다면, 일부는 DAML Marketplace에서 사용할 수 있는 Finance Library라는 라이브러리를 사용한다는 것을 알 수 있습니다. 그래서 먼저 이것을 잘 살펴보겠습니다.
금융 라이브러리(Finance Library)에는 내가 코드를 읽기 전에는 생각하지 못했던 여러 가지 고급 아이디어가 포함되어 있어, 나는 가장 유용하다고 판단한 것을 골라 빅 아이디어 라고 불렀습니다.
금융 라이브러리에서 5가지 큰 아이디어를 식별하고, 가독성을 위해 두 부분으로 나누었습니다. 이 게시물에서는 처음 세 가지를 안내합니다.
빅 아이디어 # 1 : 의미론 적으로 풍부한 식별자 (semantically rich identifiers)
금융 라이브러리에서 즉시 주목하게 될 한 가지는 계약서의 서명자를 포함한 여러 분야의 복잡한 ID가 계약서에 있다는 것입니다.
ID 데이터 유형은 속성을 조회하기 위한 데이터베이스 색인 역할을 하는 ID 문자열을 제공하는 것이 아니라 주체의 기본 속성을 직접 검사할 수 있는 ID 카드와 같습니다.
이는 또한 일부 금융 개체 (예 : 계정이나 자산)의 ID에 포함된 속성이 변경 불가능하며 (특정 버전의 범위 내에서) ID를 계약 인스턴스의 계약 키로 사용할 수도 있음을 의미합니다. Id 데이터 유형에는 서명자, 레이블, 버전 필드가 포함됩니다.
-- | A versioned identifier backed by a set of signatories. Can be used as
-- a primary key or foreign key of a contract.
data Id = Id
with
signatories : Set Party
-- ^ The parties that need to sign a contract with this id and
-- that are responsible to ensure primary key uniqueness.
label : Text
-- ^ A label that makes a contract unique for a given set of signatories.
version : Int
-- ^ Allows to model multiple revisions of the same contract.
deriving (Eq, Ord)
instance Show Id where
show Id{..} = "Sigs(" <> intercalate ";" (map show (Set.toList signatories)) <> ")-" <> label <> "-" <> show version
Id 데이터 레코드는 특정 자산 또는 계정 레코드의 id 필드에 제공된 값으로 사용됩니다. 예를 들어, 계정 필드의 값으로 계정 레코드가 있는 AssetDeposit 계약은 account.id.signatoriesfield에서 서명자를 가져오는것을 나중에 알게됩니다.
계약서의 서명자 필드처럼 서명자의 순서(Order)가 지정되지 않고 중복이 허용되지 않는경우, 금융 라이브러리는 컬렉션 유형으로 list이 아닌 Set을 사용합니다 (표준 라이브러리의 DA.Next.Set 모듈에서 가져옴).
빅 아이디어 # 2 : 모듈성 (modularity)
때로는 필수 계약과 하나 이상의 선택적 추가 계약으로 한 유형의 법적 관계를 모델링하는 것이 유용합니다. 이 모듈 식 접근 방식은 해당 관계의 참가자가 사용할 수 있는 선택(choice) 사항을 지정하는 데 더 많은 유연성을 제공합니다.
모듈 방식의 예는 다음과 같습니다.
계정에 예치된 일부 자산의 소유권은 AssetDeposit 계약으로 표시됩니다. AssetDeposit 계약에는 계약의 account.id.signatories 필드에 지정된 기본 계정과 동일한 서명자가 있습니다.
-- | Represents a deposit of an asset in an account. The `account.id` and `asset.id` fields
-- can be used to link the contract to other contracts that provide further information
-- such as the type of the asset or reference data for it. This allows new asset classes
-- to be added without having to modify business processes that operate on generic
-- asset deposits.
template AssetDeposit
with
account : Account
-- ^ A deposit is allocated to an account and backed by the account.id.signatories.
-- Depending on the desired trust model this might be (i) both the provider and the
-- owner, (ii) just the provider or (iii) a third party agent.
asset : Asset
-- ^ Specifies the id and the amount of assets deposited. The asset.id.signatories
-- are the parties that publish reference data and hence the details for how to
-- lifecycle the asset.
observers : Set Party
where
signatory account.id.signatories
observer insert account.provider observers
ensure asset.quantity > 0.0
let setQty (qty: Decimal) (ad: AssetDeposit) =
ad with asset = ad.asset with quantity = qty
controller account.owner can
AssetDeposit_SetObservers : ContractId AssetDeposit
with newObservers : Set Party
do create this with observers = newObservers
controller account.owner can
AssetDeposit_Split : [ContractId AssetDeposit]
-- ^ Splits an asset deposit according to the provided list of quantities.
with
quantities : [Decimal]
-- ^ The quantities of the newly created asset deposits. The total quantity
-- needs to be smaller or equal than the current quantity. If it does not add up,
-- an asset deposit with the remainder is created.
do
...
AssetDeposit_Merge : ContractId AssetDeposit
-- ^ Merges a list of asset deposits with the given one into one.
with
depositCids : [ContractId AssetDeposit]
-- ^ The asset deposits that will be consumed. All fields except for the quantity
-- need to match.
do
...
기본적으로 계정 소유자는 분할(split)및 병합(merge) 권한만 가지며 자산 예금을 양도할 수는 없습니다. 이는 자산의 양도 가능성을 제한해야 하는 실제 상황에 해당합니다 (예 : 자산이 시간이 지남에 따라 가득 차는 경우). 기본적으로 외부 당사자는 계정에 입금할 수 없습니다.
(각 선택(choice) 항목을 살펴보면 자산을 두 조각으로 분할및 하나로 병합하는 데 제한을 두지 않는다는 것을 알 수 있습니다.
그러나 하나의 자산을 분할하기 위해 수량을 무제한으로 지정하거나, 무제한의 서로 다른 자산을 하나의 자산으로 병합할 수 있는것을 알 수 있습니다. 선택(choice) 사항은 stQty (= set quantity) 유틸리티 기능을 사용하여 템플릿의 let 블록에 정의된 이러한 작업을 수행합니다.
선택 사항은 또한 두 개의 고급 라이브러리 기능, 즉 mapA 를 사용합니다. 일련의 원장 업데이트를 원자 적으로 생성하는 함수와 일련의 가져오기(fetch) 및 보관(archive) 업데이트 단계를 하나의 생성(create) 또는 업데이트(update) 단계로 결합하는 역할을 하는 foldlA 함수입니다.
이 포스트의 끝 부분에서 mapA 함수의 메커니즘에 대해 자세히 설명하겠습니다. (여기서 mapA 함수가 다단계 자산 결제 체인을 처리하는 데 어떻게 사용되는지 설명합니다.)
예치된 자산을 양도하거나 다른 당사자가 계정에 입금하도록 허용하는 옵션은 AssetSettlementRule 계약에 의해 계정에 추가됩니다. 자산예탁금과과 자산결산규칙은 기본 계정 ID를 통해 연결됩니다.
AssetSettlementRule 계약에는 다음 선택 사항이 포함됩니다.
- 자산을 보관하고 반환하는 AssetSettlement_Debit 선택(choice) 사항은 해당 계정에 예치됩니다.
- 자산 예금을 생성하는 AssetSettlement_Credit 선택(choice). 이 작업을 수행하려면 대상 계정에 활성 AssetSettlementRule 계약이 있어야 하며 Ctrl(controllers) 필드 에 해당 선택(choice) 의 controller가 있어야 합니다.
- 위의 두 선택 사항을 원자 적으로 결합하는 AssetSettlement_Transfer 선택(choice).
AssetDeposit과 AssetSettlementRule 계약이 서로를 보완하는 방식은 금융 라이브러리의 README 다이어그램에 표시됩니다 (그림에는 AssetSettlementRule 계약의 이전 이름이 표시되지만 메시지에는 영향을 주지 않음).
빅 아이디어 # 3 : 다단계 결제 체인 (multistep settlement chain)
실제 상황에서 우리는 때때로 직접 이체(direct transfer)보다는 다단계 결제 체인(multistep settlement chain)이 필요합니다.
금융 라이브러리에는 간단한 은행 내 이체부터 중앙은행 디지털 통화 (DBDC)를 포함한 은행 간 이체에 이르기까지 점차 복잡한 일련의 시나리오 형태의 튜토리얼이 포함되어 있습니다.
은행 간 이체는 이미 **"Acme Bank에 1000 USD 예금을 보유한 Alice"**에서 **"Genco Bank에 1000 USD 예금을 보유한 Bob"**으로의 상태 전환을 달성하기 위해 계정 계층 구조에서 여러 결제 단계를 오르내려야 합니다.
(DAML이 CDBC를 구현하는 데 적합한 도구 인 방법에 대한 주제에 대해서는 DAML 기반 블로그의 최근 게시물 두 개를 참조하십시오.
DAML은 함수형 언어이므로 다단계 결제 체인을 처리하기 위한 for 루프 또는 while 루프가 없습니다.
함수형 프로그래밍 언어와 절차적 프로그래밍 언어의 차이에 익숙하지 않은 분들을 위해 : DAML 또는 Haskell과 같은 함수형 언어의 함수는 내부 상태를 가질 수 없습니다. 루프 사용은 제외되지만 실제 단계의 인덱스를 포함하는 변수는 반복의 내부 상태 변수가 될 수 있습니다. 또한 함수 언어의 함수는 선언적이므로 해당 결과로 이어지는 단계가 아니라 계산 결과를 선언합니다.
루프 부족에 대한 보상으로 DAML (다른 기능 언어와 유사)에는 목록을 처리할 수 있는 광범위한 기능을 제공하고 있습니다. 주요 기능은 slicing, filtering, mapping functions onto lists, combining lists in various ways, monads를 통한 원자 적 방식의 작업 선언입니다. 보너스는 곧 살펴 보 겠지만 절차적 언어에 비해 간결함과 가독성이 더 높다는 것입니다.
DA.Finance.Trade.SettlementInstruction 모듈에 정의된 결제 체인의 DAML 구현에 대한 설명에서 이러한 기능에 대한 몇 가지 예를 보여 드리겠습니다.
결제 체인의 기본 메커니즘은 다음과 같은 사실에 의해 결정됩니다.
결제 체인이 원자적 전달대 지불 프로세스(DvP,Delivery vs Payment process)의 일부로 중단 없이 처리될 수 있도록 하기 위해 사전에 체인의 모든 단계에서 자금 (각 AssetDeposit 계약)을 할당해야 합니다.
결제 체인 구현은 다음 질문으로 이해할 수 있습니다.
- 개별 단계와 결제 체인의 순서가 표시되는 방법
- 시퀀스의 유효성을 확인하는 방법
- 중개 당사자가 송금인 역할을 하려는 단계에 자금을 할당하는 방법
- 체인 전체가 원자 방식으로 처리되는 방법
개별 단계 및 순서 표현
결제 체인의 개별 단계를 나타내는 것은 SettingsDetails 데이터 유형이며, 다음과 같은 필드를 가지고 있습니다.
- 필수 발신자 계정
- 필수 수신자 계정
- 선택적(옵션) 자산 예금 계약 ID.
자산예금계약 ID는 _AllocateNext_의 선택(choice)을 통해 자산 할당 단계에서 중개 당사자에 의해 특정 자산예금계약이 할당될 때만 채워지기 때문에 선택사항입니다.
-- | Data describing settlement details.
data SettlementDetails = SettlementDetails
with
senderAccount : Account
-- ^ The sender account.
receiverAccount : Account
-- ^ The receiver account.
depositCid : Optional (ContractId AssetDeposit)
-- ^ The allocated asset deposit.
deriving (Eq, Show)
-- | Represents a settlement instruction for a specific trade. It is typically
-- created at the same time than the trade is instructed. Allows to allocate assets
-- and, once done, settle the instruction.
-- If both counterparties have an account with the same provider, a single step,
-- i.e. a direct transfer, suffices. If assets need to be atomically transferred up
-- and down an account hierarchy, then multiple steps are required.
template SettlementInstruction
with
masterAgreement : MasterAgreement
-- ^ The master agreement to which the settlement applies.
tradeId : Id
-- ^ The trade under the master agreement to which the settlement applies.
asset : Asset
-- ^ The id and amount of the asset to be settled.
steps : [SettlementDetails]
-- ^ The steps in the settlement. If both counterparties have an account
-- with the same provider, a single step, i.e. a direct transfer, suffices.
observers : Set Party
where
signatory union masterAgreement.id.signatories $ fromList sendersDone
observer insert masterAgreement.party1 $ insert masterAgreement.party2
$ union observers $ fromList sendersPending
ensure length steps > 0 && asset.quantity > 0.0
&& all (\(s1,s2) -> s1.receiverAccount.owner == s2.senderAccount.owner) (zip steps $ tail steps)
key (masterAgreement.id, tradeId, asset.id) : (Id, Id, Id)
maintainer key._1.signatories
let (sendersDone, sendersPending) = partitionEithers $
map (\x -> if isSome x.depositCid then Left x.senderAccount.owner else Right x.senderAccount.owner) steps
choice SettlementInstruction_AllocateNext : ContractId SettlementInstruction
-- ^ Allocates an asset deposit to the next step of the settlement instruction.
-- In the simple case where both counterparties have an account with the same provider
-- a single allocation by the sender party is required.
with
depositCid : ContractId AssetDeposit
-- ^ Specifies the asset deposit contract to be allocated.
ctrl : Party
-- ^ The next sender.
controller ctrl
do
sender <- fromSomeNote "fully allocated already" $ nextSender this
assertMsg "expecting controller to be next sender" $ ctrl == sender
deposit <- fetch depositCid
let (done, curr :: next) = break (\x -> isNone x.depositCid) steps
deposit.account === curr.senderAccount
deposit.asset === asset
let currNew = curr with depositCid = Some depositCid
create this with steps = done ++ currNew :: next
controller masterAgreement.id.signatories can
SettlementInstruction_Process : [ContractId AssetDeposit]
-- ^ Processes a settlement instruction by transferring all allocated asset deposits.
-- This choice is often called from the trade itself to atomically settle all
-- assets involved.
do
mapA
(\x -> do
exerciseByKey @AssetSettlementRule x.senderAccount.id AssetSettlement_Transfer with
receiverAccountId = x.receiverAccount.id, depositCid = fromSome x.depositCid
)
steps
SettlementInstruction_Archive : () do return ()
-- | HIDE
nextSender : SettlementInstruction -> Optional Party
nextSender SettlementInstruction{..} = (.senderAccount.owner) <$> find (\step -> isNone step.depositCid) steps
개별 단계의 순서는 SettlementInstruction 계약으로 표시됩니다. 체인을 따라 이동하고 최종적으로 정산해야 하는 자산 유형 및 수량은 자산 필드에 명시되어 있습니다(따라서 전체 시퀀스에 따라 균일). 발신자, 수신자 및 - 선택적으로 - 각 단계의 할당된 자산 예금 계약은 SettlementDetails 레코드 목록으로 단계 필드에 포함됩니다.
단계 순서에 대한 유효성 검사
연속적인 "릴레이 실행(run)"의 전제 조건은, 한번에 전송하는 발신자가 이전 단계의 수신자와 동일하다는 것입니다. 이는 SettlementInstruction 템플릿의 ensure 필드에서 다음 기능으로 확인됩니다.
ensure length steps > 0 && asset.quantity > 0.0
&& all (\(s1,s2) -> s1.receiverAccount.owner == s2.senderAccount.owner) (zip steps $ tail steps)
이 함수는 DA.List 모듈의 tail 함수 (step 목록의 첫 번째 요소를 잘라 냄)와 zip 표준 라이브러리 함수 (두 목록의 요소에서 쌍을 생성)의 조합을 사용하여 튜플 목록을 다음과 같은 방식으로 만들어냅니다.
[step1, step2, step3, …] → [(step1, step2), (step2,step3), …]
그런 다음 위에서 설명한 대로 발신자와 수신자가 서로 일치하는지 확인합니다.
여기서 작동하는 메커니즘을 이해하고 싶다면 DAML REPL 대화형 환경에서 이 함수의 단순화된 버전을 사용해 보는것이 좋습니다. 아래는 숫자와 당자사를 표시하는것입니다.
daml> import DA.List
daml> let parties = [1..5]
daml> tail parties
[2,3,4,5]
daml> let steps = zip parties $ tail parties
daml> steps
[(1,2),(2,3),(3,4),(4,5)]
daml> let chain = zip steps $ tail steps
daml> chain
[((1,2),(2,3)),((2,3),(3,4)),((3,4),(4,5))]
daml>
리스트의 각 단계에서 우리는 party1이 party2로 이전하고, party2가 party3으로 이전한다는 것을 나타냅니다.
체인이라는 목록에서 갭이 없는 "릴레이 실행"상태를 확인할 수 있도록 인접한 단계를 조합했습니다.
이것은 튜플의 목록인데, 튜플의 첫 번째 요소와 두 번째 요소는 그 자체로 당사자의 튜플이며 한 당사자에서 다른 당사자로의 전송 단계를 나타냅니다.
이제 단계의 당사자가 결제 체인에 필요한 갭 리스 "릴레이 실행"조건을 충족하는지 위의 간단한 버전으로 확인할 수 있습니다 (물론, 애초에 그렇게 단계를 정의했기 때문에 그렇습니다.):
daml> all (\(s1,s2) -> snd s1 == fst s2) $ zip steps $ tail steps
True
daml>
물론 단계 목록을 엉망으로 만들면 두 번째 단계의 발신자로서 party2를 party6으로 바꾸면 부정적인 결과를 표시합니다.
daml> let steps' = [(1,2),(6,3),(3,4),(4,5)]
daml> zip steps' $ tail steps'
[((1,2),(6,3)),((6,3),(3,4)),((3,4),(4,5))]
daml> all (\(s1,s2) -> snd s1 == fst s2) $ zip steps' $ tail steps'
False
daml>
자산 할당
중개 당사자는 SettlementInstruction_Allocatenex 선택(choice)을 통해 사전에 자금을 할당할 수 있습니다. 이 선택은 자산 할당 프로세스의 "다음 발신자(next sender)"가 모든 단계에서 실행할 수 있습니다.
"다음 발신자"가 존재하거나 존재하지 않는 경우 :
- 단계 순서가 아직 완전히 할당되지 않은 경우 "다음 발신자"는 첫 번째 단계의 발신자 인 당사자이며 자산 예금 계약 ID 필드는 None 입니다.
- 일련의 단계가 이미 완전히 할당된 경우 "다음 발신자"가 존재하지 않습니다.
"다음 발신자"가 존재할 수도 있고 존재하지 않을 수도 있다는 불확실성은 nextSender 함수의 find monadic 함수에 의해 처리됩니다.
nextSender : SettlementInstruction -> Optional Party
nextSender SettlementInstruction{..} = (.senderAccount.owner) <$> find (\step -> isNone step.depositCid) steps
다시 말하지만, 무슨 일이 일어나고 있는지 제대로 이해하고 싶다면 다음과 같은 간단한 예를 확인하는 것이 좋습니다.
아래 예제는 find 기능의 작동 방식을 기반으로 합니다. 검색 기준에 해당하는 항목을 찾지 못할 가능성을 나타내는 선택적 반환 값이 있습니다. 아래 예에서는 step 및 step 목록에서 홀수 값을 찾고 있습니다. step 목록의 검색 결과는 Some 5 이고 단계 목록에는 None 이 있습니다. step 에는 홀수 값이 포함되어 있지 않기 때문입니다.
옵션 값을 반환하는 find 함수를 모든 숫자 값에 2를 곱하는 (* 2) 함수와 결합하여 기존 값만 곱하면 (<$> 연산자 사용) monadic 함수를 얻습니다.
존재하는 경우 첫 번째 홀수 값의 double 을 반환하고 없는 경우 None 을 반환합니다.
daml> import DA.List
daml> let steps = [2,4,5,6,8,10]
daml> find (\x -> x%2 == 1) steps
Some 5
daml> let steps' = [2,4,6,8,10]
daml> find (\x -> x%2 == 1) steps'
None
daml> (*2) <$> find (\x -> x%2 == 1) steps
Some 10
daml> (*2) <$> find (\x -> x%2 == 1) steps'
None
daml>
이제 우리는 다음과 같은 nextSender 함수를 유추하여 이해할 수 있습니다.
(.senderAccount.owner) <$> find (\step -> isNone step.depositCid) steps
이제 SettlementInstruction_AllocateNext 선택(choice)을 살펴 보겠습니다.
choice SettlementInstruction_AllocateNext : ContractId SettlementInstruction
-- ^ Allocates an asset deposit to the next step of the settlement instruction.
-- In the simple case where both counterparties have an account with the same provider
-- a single allocation by the sender party is required.
with
depositCid : ContractId AssetDeposit
-- ^ Specifies the asset deposit contract to be allocated.
ctrl : Party
-- ^ The next sender.
controller ctrl
do
sender <- fromSomeNote "fully allocated already" $ nextSender this
assertMsg "expecting controller to be next sender" $ ctrl == sender
deposit <- fetch depositCid
let (done, curr :: next) = break (\x -> isNone x.depositCid) steps
deposit.account === curr.senderAccount
deposit.asset === asset
let currNew = curr with depositCid = Some depositCid
create this with steps = done ++ currNew :: next
이제 마법과 같은일은 다음 줄에서 발생합니다.
let (done, curr :: next) = break (\x -> isNone x.depositCid) steps
이 줄을 이해하려면 break 함수를 살펴봐야 합니다.
필터링 함수와 목록에 적용된 break 함수는 튜플을 반환합니다.
- 튜플의 첫 번째 요소는 첫 번째와 일치하는 결과 앞에 있는 목록의 조각(slice)을 포함합니다.
- 튜플의 두 번째 요소는 일치하는 결과의 첫 번째 발생부터 시작하여 나머지 부분(목록)입니다.
우리의 습관처럼 간단한 예를 살펴보겠습니다.
예를 들어, 목록의 첫 번째 요소를 수정하여 검색을 완료한다고 가정해 보겠습니다. 목록이 있는 아래의 예를 봅겠습니다. 여기에서 양수인 첫 번째 요소에 100을 곱합니다. 이를 위해
- 첫째, 패턴 매칭과 break 함수를 통해 양수가 아닌 부분, 첫 번째 양수 요소 및 나머지 목록을 분리합니다.
- 둘째, 양수가 아닌 부분은 그대로 두고 나머지는 변경하지 않고 첫 번째 양수 요소만 변경하는 방식으로 목록을 다시 만듭니다.
daml> import DA.List
daml> let steps = [0,0,0,0,1,2,3,4]
daml> let (zeros,firstPositive::rest) = break (>0) steps
daml> zeros
[0,0,0,0]
daml> firstPositive
1
daml> rest
[2,3,4]
daml> zeros ++ 100*firstPositive :: rest
[0,0,0,0,100,2,3,4]
daml>
이제 다음 줄을 이해할 수 있습니다.
create this with steps = done ++ currNew :: next
코드에서 트리플 방정식 ===을 여러 번 볼 수 있는데, 이는 DA.Assert 모듈에 정의된 두 표현식의 동등성을 확인하는 assert 함수의 단축어 입니다.
전체 시퀀스를 원자 적으로 처리
여기서 원자 트랜잭션 처리의 마법이 일어나는 곳입니다. "원자"는 할당된 자금의 모든 이체가 성공했거나 둘 다 성공하지 않았기 때문에 시스템이 잘못된 상태로 고착될 수 없음을 의미합니다. 중개자가 자산예금계약을 할당한 후 자산을 다른 거래에 사용하는 경우 이전이 실패할 수 있습니다.
SettlementInstruction_Process 선택(choice)의 원자적 트랜잭션 처리는 mapA 함수에 의해 시작됩니다.
mapA 함수의 기능을 더 잘 이해하기 위해 다음을 수행하여 선택적 값을 반환하는 optionalHalf 함수가 있다고 가정해 보겠습니다.
- 정수가 짝수이면 반으로 나누고 결과를 Some n 값으로 반환하지만
- 정수가 홀수이면 None을 반환합니다.
이전에 선택적 값(optional value)을 반환하는 다른 함수를 보았습니다.
mapA 함수는 optionalHalf 함수를 목록(list)에 매핑하므로
- 모든 목록 항목이 짝수면 함수가 모든 항목을 반으로 나누고 원래 목록의 절반 목록을 Some xs 값으로 반환합니다.
- 원래 목록 항목이 홀수이면이 함수는 None을 반환합니다.
daml> let optionalHalf n = if n % 2 == 0 then Some (n / 2) else None
daml> optionalHalf 10
Some 5
daml> optionalHalf 11
None
daml> mapA optionalHalf [2,4,6,8,10]
Some [1,2,3,4,5]
daml> mapA optionalHalf [2,4,5,8,10]
None
daml>
SettlementInstruction_Process 선택(choice)은 유사하게 작동합니다.
- 이전에 할당된 자산 예금의 성공적인 이체는 짝수의 성공적인 절반에 해당합니다.
- 모든 전송으로 구성된 원장 업데이트는 요소 별 반감(halved)기 목록의 반환에 해당합니다.
controller masterAgreement.id.signatories can
SettlementInstruction_Process : [ContractId AssetDeposit]
-- ^ Processes a settlement instruction by transferring all allocated asset deposits.
-- This choice is often called from the trade itself to atomically settle all
-- assets involved.
do
mapA
(\x -> do
exerciseByKey @AssetSettlementRule x.senderAccount.id AssetSettlement_Transfer with
receiverAccountId = x.receiverAccount.id, depositCid = fromSome x.depositCid
)
steps
추가 지침 및 템플릿
README의 DVP (Delivery vs Payment) Trades 섹션에서 실제 DvP 워크플로우를 구현하고 자동화하기 위한 더 많은 템플릿 및 트리거에 대한 추가 지침과 참조를 찾을 수 있습니다.
출처 : The Finance Library — Part 1
https://medium.com/daml-masterclass/the-finance-library-part-1-2f94ed07c349
'블로그' 카테고리의 다른 글
DLT를 사용하여 대리 투표(proxy voting) 및 규제 보고 간소화 (0) | 2020.10.07 |
---|---|
금융 라이브러리 - 파트2 (0) | 2020.10.06 |
DAML을 사용하는 애플리케이션에서 영감을 얻는 방법 (0) | 2020.10.06 |
다중 참여 원장에서 DAML 앱 실행 (Canton) (0) | 2020.09.23 |
DAML 사용 사례 : 의료 시스템에서 스마트 계약을 구현하고 의료 오류를 방지하는 방법 (0) | 2020.09.21 |