-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -- | Daml script test utilities for simulating the actions of wallet client -- based on the token standard. -- -- NOTE: there are likely more functions that could/should be added to this module. PRs welcome. module Splice.Testing.TokenStandard.WalletClient ( -- * Reading/checking holdings listHoldings, listHoldingCids, listLockedHoldings, checkHoldingWithAmountExists, checkBalanceBounds, checkBalance, checkBalanceApprox, checkHolding, checkHoldingBounds, checkHoldingApprox, -- * Reading transfer instructions listTransferOffers, -- * Reading allocations listRequestedAllocations, ) where import DA.Action (unless) import DA.Optional (isSome) import DA.TextMap qualified as TextMap import Splice.Api.Token.AllocationV1 as Api.Token.AllocationV1 import Splice.Api.Token.TransferInstructionV1 import Splice.Api.Token.AllocationRequestV1 import Splice.Api.Token.HoldingV1 as Api.Token.HoldingV1 import Daml.Script -- | List the hodlings of a party of a specific instrument. listHoldings : Party -> InstrumentId -> Script [(ContractId Holding, HoldingView)] listHoldings p instrumentId = do holdings <- queryInterface @Holding p let instrumendHoldings = do (cid, Some holding) <- holdings guard (holding.instrumentId == instrumentId) guard (holding.owner == p) pure (cid, holding) pure instrumendHoldings listLockedHoldings : Party -> InstrumentId -> Script [(ContractId Holding, HoldingView)] listLockedHoldings p instrumentId = filter (\(_, holding) -> isSome (holding.lock)) <$> listHoldings p instrumentId -- | List the cids of the hodlings of a party of a specific instrument. listHoldingCids : Party -> InstrumentId -> Script [ContractId Holding] listHoldingCids p instrumentId = (map fst) <$> listHoldings p instrumentId -- | Check that a holding with a specific amount exists for the given owner. checkHoldingWithAmountExists : Party -> InstrumentId -> Decimal -> Script () checkHoldingWithAmountExists p instrumentId amount = do holdings <- map snd <$> listHoldings p instrumentId unless (any (\holding -> holding.amount == amount) holdings) $ fail (show p <> " is missing holding of value " <> show amount <> " in " <> show holdings) -- | Check the bounds on a party's total balance of all holdings of the given instrument. checkBalanceBounds : Party -> InstrumentId -> (Decimal, Decimal) -> Script () checkBalanceBounds p instrumentId (lb, ub) = do holdings <- listHoldings p instrumentId let total = sum $ map (._2.amount) holdings unless (total >= lb && total <= ub) $ fail $ "Wallet " <> show p <> ": balance of " <> show total <> " for " <> show instrumentId <> " is not within the expected range [" <> show lb <> ", " <> show ub <> "]" -- | Check the exact value of on an individual holding's amount. checkHolding : Party -> ContractId Holding -> Decimal -> Script () checkHolding p holdingCid amount = checkHoldingBounds p holdingCid (amount, amount) -- | Check the bounds on an individual holding's amount. checkHoldingBounds : Party -> ContractId Holding -> (Decimal, Decimal) -> Script () checkHoldingBounds p holdingCid (lb, ub) = do holdingO <- queryInterfaceContractId p holdingCid debug holdingO let holding = case holdingO of None -> error $ "Holding " <> show holdingCid <> " was not found" Some holding -> holding unless (holding.amount >= lb && holding.amount <= ub) $ fail $ "Holding " <> show holding <> " is not within the expected range [" <> show lb <> ", " <> show ub <> "]" -- | Check the approximate value (+/- 1.0) a party's total balance of all holdings of the given instrument. checkBalanceApprox : Party -> InstrumentId -> Decimal -> Script () checkBalanceApprox p instrumentId approximateBalance = checkBalanceBounds p instrumentId (approximateBalance - 1.0, approximateBalance + 1.0) -- | Check the approximate (+/- 1.0) amount of an individual holding. checkHoldingApprox : Party -> ContractId Holding -> Decimal -> Script () checkHoldingApprox p holdingCid approximateAmount = checkHoldingBounds p holdingCid (approximateAmount - 1.0, approximateAmount + 1.0) -- | Check the exact value a party's total balance of all holdings of the given instrument. checkBalance : Party -> InstrumentId -> Decimal -> Script () checkBalance p instrumentId balance = checkBalanceBounds p instrumentId (balance, balance) -- | List pending transfer offers (as sender or receiver) listTransferOffers : Party -> InstrumentId -> Script [(ContractId TransferInstruction, TransferInstructionView)] listTransferOffers p instrumentId = do instrs <- queryInterface @TransferInstruction p let pendingOffers = do (cid, Some instr) <- instrs guard (instr.transfer.instrumentId == instrumentId) guard (instr.status == TransferPendingReceiverAcceptance) guard (p == instr.transfer.sender || p == instr.transfer.receiver) pure (cid, instr) pure pendingOffers -- | List all allocations requested from the owner for a specific instrument. listRequestedAllocations : Party -> InstrumentId -> Script [AllocationSpecification] listRequestedAllocations p instrumentId = do reqs <- queryInterface @AllocationRequest p trace reqs $ pure () let amuletAllocs = do (_reqCid, Some req) <- reqs (tfId, tf) <- TextMap.toList req.transferLegs guard (tf.instrumentId == instrumentId) guard (p == tf.sender) pure AllocationSpecification with settlement = req.settlement transferLegId = tfId transferLeg = tf pure amuletAllocs