ContractIds are just tagged strings to your ability to create a circular reference is dependent on being able to predict contractIds.
ContractIds on a production ledger are generally not predictable. If you could get a predict contractIds, you could generate a contractId clash which is bad. So in practice you will never get a circular dependency like you are hoping for.
If you did, it would not really be a problem though. Again, ContractIds are just tagged strings. You have a trivial “circular” dependency by having the. Implicit self available in the context of a contract anyway.
You can get circular dependencies through Contract Keys, which is my recommended way of referencing other contracts.
import Daml.Script
import DA.Assert
template Person with
owner : Party
name : Text
fullname : Text
spouse : Text
where
signatory owner
key (owner, name) : (Party, Text)
maintainer key._1
nonconsuming choice Invite : Text
with
controller owner
do
(_, s) <- fetchByKey @Person (owner, spouse)
pure $ fullname <> " and " <> s.fullname
test = script do
-- arrange
owner <- allocateParty "Owner"
johnny <- submit owner do
createCmd Person with
name = "Johnny", fullname = "Johnny Cash"
spouse = "June", ..
june <- submit owner do
createCmd Person with
name = "June", fullname = "June Carter"
spouse = "Johnny", ..
-- act
invite <- submit owner do exerciseCmd johnny Invite
-- assert
invite === "Johnny Cash and June Carter"
The abstract ledger model has no notion of one contract referencing another. Only exercise nodes reference contracts, and there the causality is clear. Exercise nodes referencing contracts as input come strictly after the create of the contract.