Lock by Archiving¶
Pre-condition: there exists a contract that needs to be locked and unlocked. In this section, Coin is used as the original contract to demonstrate locking and unlocking.
template Coin
with
owner: Party
issuer: Party
amount: Decimal
delegates : [Party]
where
signatory issuer, owner
observer delegates
choice Transfer : ContractId TransferProposal
with newOwner: Party
controller owner
do
create TransferProposal
with coin=this; newOwner
--a coin can only be archived by the issuer under the condition that the issuer is the owner of the coin. This ensures the issuer cannot archive coins at will.
choice Archives
: ()
controller issuer
do assert (issuer == owner)
Archiving is a straightforward choice for locking because once a contract is archived, all choices on the contract become unavailable. Archiving can be done either through consuming choice or archiving contract.
Consuming Choice¶
The steps below show how to use a consuming choice in the original contract to achieve locking:
- Add a consuming choice, Lock, to the Coin template that creates a LockedCoin.
- The controller party on the Lock may vary depending on business context. In this example, owner is a good choice.
- The parameters to this choice are also subject to business use case. Normally, it should at least have locking terms (e.g. lock expiry time) and a party authorized to unlock.
choice Lock : ContractId LockedCoin
with maturity: Time; locker: Party
controller owner
do create LockedCoin with coin=this; maturity; locker
Create a LockedCoin to represent Coin in the locked state. LockedCoin has the following characteristics, all in order to be able to recreate the original Coin:
- The signatories are the same as the original contract.
- It has all data of Coin, either through having a Coin as a field, or by replicating all data of Coin.
- It has an Unlock choice to lift the lock.
template LockedCoin with coin: Coin maturity: Time locker: Party where signatory coin.issuer, coin.owner observer locker
choice Unlock : ContractId Coin controller locker do create coin
Archiving Contract¶
In the event that changing the original contract is not desirable and assuming the original contract already has an Archive choice, you can introduce another contract, CoinCommitment, to archive Coin and create LockedCoin.
- Examine the controller party and archiving logic in the Archives choice on the Coin contract. A coin can only be archived by the issuer under the condition that the issuer is the owner of the coin. This ensures the issuer cannot archive any coin at will.
--a coin can only be archived by the issuer under the condition that the issuer is the owner of the coin. This ensures the issuer cannot archive coins at will.
choice Archives
: ()
controller issuer
do assert (issuer == owner)
- Since we need to call the Archives choice from CoinCommitment, its signatory has to be Issuer.
template CoinCommitment
with
owner: Party
issuer: Party
amount: Decimal
where
signatory issuer
observer owner
- The controller party and parameters on the Lock choice are the same as described in locking by consuming choice. The additional logic required is to transfer the asset to the issuer, and then explicitly call the Archive choice on the Coin contract.
- Once a Coin is archived, the Lock choice creates a LockedCoin that represents Coin in locked state.
nonconsuming choice LockCoin
: ContractId LockedCoin
with coinCid: ContractId Coin
maturity: Time
locker: Party
controller owner
do
inputCoin <- fetch coinCid
assert (inputCoin.owner == owner && inputCoin.issuer == issuer && inputCoin.amount == amount )
--the original coin firstly transferred to issuer and then archived
prop <- exercise coinCid Transfer with newOwner = issuer
do
id <- exercise prop AcceptTransfer
exercise id Archives
--create a lockedCoin to represent the coin in locked state
create LockedCoin with
coin=inputCoin with owner; issuer; amount
maturity; locker
Trade-offs¶
This pattern achieves locking in a fairly straightforward way. However, there are some tradeoffs.
- Locking by archiving disables all choices on the original contract. Usually for consuming choices this is exactly what is required. But if a party needs to selectively lock only some choices, remaining active choices need to be replicated on the LockedCoin contract, which can lead to code duplication.
- The choices on the original contract need to be altered for the lock choice to be added. If this contract is shared across multiple participants, it will require agreement from all involved.