Hi all,
Looking for a little advice around the selling of an object directly between buyer and seller with no intermediary.
The seller owns a Token
contract (with an amount
field), a Cash
contract, and has created a SellOffer
contract, stipulating he is willing to sell X
tokens for Y
cash.
The SellOffer
has a [Party]
field for the observers, but none are able to see the seller’s Token
or Cash
, and so I’m running in to an issue with regards to a buyer wishing to complete the trade.
The primary aim is to complete the transaction with only one user action. I could, for example, create a LockedToken
contract with the SellOffer
, and then the buyer archives that to receive tokens and creates a TransferredCash
contract for the seller to receive corresponding cash. But that would require a second action by the seller to retrieve the cash and update their Cash
contract.
Any insight would be greatly appreciated!
It’s hard to give a specific advice without knowing the details of your model. Particularly who are the signatories on the Token and the Cash templates.
The workflow you’re looking to implement has a name. It’s called atomic swap. As the name implies, in this workflow two assets (in your case the Token and the Cash) are exchanged between two parties in a single transaction. You may take a look at the example of atomic swap implementation in the Wallet Daml Sample App. The repository contains a writeup on the atomic swap workflow implementation in this app.
The asset model in the Wallet Daml Sample App utilizes an issuer party, whose role is to ensure the integrity of the system and its freedom from uncontrolled issuance or double spend. If in your model you don’t utilize an issuer party, the atomic swap implementation in the Wallet Daml Sample App may be overly complicated for your model.
From your description I assume that the owners of Token and Cash contracts are signatories on the Token and the Cash templates respectively. You need both seller’s and buyer’s authority to change the owner on the Token and the Cash contracts. You get the seller’s authority from seller being a signatory on the SellOffer contract. And you get the buyer’s authority from buyer being the controller on the Accept_SellOffer choice. You don’t need a TransferredCash or any other additional templates to satisfy the authorization and privacy rules.
The purpose of the LockedToken template, which you mentioned, would be to provide the buyer visibility of the token. If the buyer does not have visibility of the token, then the fetch of the token in the Accept_SellOffer choice will fail. This said, you don’t necessarily need a separate template for that. You could just add the buyer as an observer to the Token contract when the seller’s making the sell offer.
Here’s a complete example with a test script covering the happy path. Note that in this model there’s no protection against uncontrolled issuance. Any party can issue any amount of Token and Cash at any time.
template Token
with
owner : Party
amount : Decimal
observers : [Party]
where
signatory owner
observer observers
template Cash
with
owner : Party
amount : Decimal
where
signatory owner
template SellOffer
with
seller : Party
tokenCid : ContractId Token
buyer : Party
price : Decimal
where
signatory seller
observer buyer
choice Cancel_SellOffer : ContractId Token
controller seller
do
token <- fetch tokenCid
archive tokenCid
create token with
observers = []
choice Reject_SellOffer : ContractId Token
controller buyer
do
token <- fetch tokenCid
archive tokenCid
create token with
observers = []
choice Accept_SellOffer : ()
with
cashCid : ContractId Cash
controller buyer
do
token <- fetch tokenCid
archive tokenCid
create token with
owner = buyer
observers = []
cash <- fetch cashCid
assertMsg "The amount of cash provided does not match the price of the token" $
cash.amount == price
archive cashCid
create cash with owner = seller
return ()
testAtomicSwap = script do
buyerParty <- allocateParty "Buyer"
sellerParty <- allocateParty "Seller"
tokenCid <- submit sellerParty do
createCmd Token with
owner = sellerParty
amount = 10.0
observers = [buyerParty]
cashCid <- submit buyerParty do
createCmd Cash with
owner = buyerParty
amount = 100.0
sellOfferCid <- submit sellerParty do
createCmd SellOffer with
seller = sellerParty
tokenCid
buyer = buyerParty
price = 100.0
submit buyerParty do
exerciseCmd sellOfferCid Accept_SellOffer with
cashCid
return ()
Thanks for the reply!
You made some good points, and I definitely needed to look through the wallet example, but there’s two problems I’m seeing: visibility and merging.
Both in your little example and in the wallet repo, the traded assets are visible to both parties after the trade has completed. This will not be acceptable, and it’s a reasonable argument, if I purchase something from you with cash I should not be able to see what you do with that cash afterwards.
We also don’t want to have multiple instances of Cash, at least not when the currency is the same, and merging existing instances of Cash is not possible for the buyer because once again, they would need visibility of all the seller’s cash.
@lashenhurst
You’re correct in your observation that the transferred token is visible to the seller, even though the seller is not a stakeholder on the contract. Similarly the cash contract is visible to the buyer, even though the buyer is not a stakeholder on the transferred cash contract. This effect, where contracts may be visible to non-stakeholders, is known as Divulgence.
Divulgence is one reason we model assets as discrete contracts in Daml apps. The Wallet Daml Sample App repo contains a writeup on this topic. Another reason for implementing assets as a set of discrete contracts as opposed to a single contract representing the account balance, is that the discrete model (known as UTXO) reduces the contention on asset contracts and improves the performance of the system.
To summarize, contrary to your requirement, we strongly recommend modeling your system with multiple instances of Cash contracts. Our Daml Finance library, which supports the modeling of financial and non-financial use cases in Daml and provides the implementation of generic delivery-vs-payment and immediate, guaranteed settlement workflows, models assets as discrete contracts. Perhaps you’d like to look into utilizing Daml Finance library, which may accelerate the delivery of your project?
1 Like