Locking 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 controller owner can Transfer : ContractId TransferProposal with newOwner: Party 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. controller issuer can Archives : () 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.
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 have at least locking terms (eg. lock expiry time) and a party authorized to unlock.
Lock : ContractId LockedCoin with maturity: Time; locker: Party 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 controller locker can Unlock : ContractId Coin do create coin
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.
controller issuer can Archives : () 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
- 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.
controller owner can nonconsuming LockCoin : ContractId LockedCoin with coinCid: ContractId Coin maturity: Time locker: Party do inputCoin <- fetch coinCid assert (inputCoin.owner == owner && inputCoin.issuer == issuer && inputCoin.amount == amount ) --the original coin firstly transferred to issuer and then archivaed 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
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.