There are a few things transient or sometimes ephemeral contracts are useful for, but let me focus on the important one: Compositionality
Let’s say you have a transfer flow with a TransferProposal
step. A corresponding Trade
usually creates and archives the TransferProposal
in the Settle
choice.
template Asset
with
issuer : Party
owner : Party
symbol : Text
quantity : Decimal
where
signatory issuer, owner
ensure quantity > 0.0
controller owner can
ProposeTransfer
: ContractId TransferProposal
with
newOwner : Party
do
create TransferProposal with
asset = this
newOwner
template TransferProposal
with
asset : Asset
newOwner : Party
where
signatory (signatory asset)
controller newOwner can
TransferProposal_Accept
: ContractId Asset
do
create asset with
owner = newOwner
template Trade
with
baseAssetCid : ContractId Asset
baseAsset : Asset
quoteAsset : Asset
where
signatory baseAsset.owner
controller quoteAsset.owner can
Trade_Settle
: (ContractId Asset, ContractId Asset)
with
quoteAssetCid : ContractId Asset
do
fetchedBaseAsset <- fetch baseAssetCid
assertMsg
"Base asset mismatch"
(baseAsset == fetchedBaseAsset with
observers = baseAsset.observers)
fetchedQuoteAsset <- fetch quoteAssetCid
assertMsg
"Quote asset mismatch"
(quoteAsset == fetchedQuoteAsset with
observers = quoteAsset.observers)
tpBase <- exercise baseAssetCid ProposeTransfer with
newOwner = quoteAsset.owner
newBaseCid <- exercise tpBase TransferProposal_Accept
tpQuote <- exercise quoteAssetCid ProposeTransfer with
newOwner = quoteAsset.owner
newQuoteCid <- exercise tpQuote TransferProposal_Accept
return (newBaseCid, newQuoteCid)
Usually the TransferProposal
is used as part of the propose-accept pattern to collect the agreements of owner
and newOwner
. In the Trade_Settle
choice we already have the authorities of owner
and newOwner
so the TradeProposal workflow is unnecessary, but can’t make use of that without adding a new choice to Asset
:
choice BilateralTransfer
: ContractId TransferProposal
with
newOwner : Party
controller [owner, newOwner]
do
create this with
owner = newOwner
The issuer would need to agree to that model change, and if Trade
is an add-on to the basic Asset
, it may not be practical even then.
So - to replace this type to transient contract, we’d need an entirely new mechanism to transfer authority and compose existing contracts. But that new feature would probably introduce new nodes into the Ledger Model, which you may have to persist.
Which leads me to a different question: How do you decide what to persist in the first place? The essential state of a participant’s projection are
- The root nodes of each received transaction
- A map
ContractId -> ContractArgs
containing all divulged, but otherwise unknown contracts.
Everything else can be reinterpreted from that. In other words, transient contracts like the above are not essential state for any of the parties. The exercise ProposeTransfer
nodes are. Persisting or not persisting the TransferProposal
contract is a question of optimization, just like it is for persisting Fetch
and NoSuchKey
nodes.
I do think that we should have a way get ones hands on Ledger as per the Ledger Model, including Fetch
, NoSuchKey
and “transient” contracts. So maybe the questions we should be asking are
- What data should the Ledger API be able to serve in a performant manner
- How do we enable external ledger validation and exploration without bogging down the Ledger API Server in persistence tasks
Maybe the answer to 1. is “some version of the flat transaction stream” and the answer to 2. is “Ledger API can serve essential state, which has to be expanded using an external process”?