Locking by safekeeping¶
Safekeeping is a realistic way to model locking as it is a common practice in many industries. For example, during a real estate transaction, purchase funds are transferred to the sellers lawyer’s escrow account after the contract is signed and before closing. To understand its implementation, review the original Coin template first.
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)
There is no need to make a change to the original contract. With two additional contracts, we can transfer the Coin ownership to a locker party.
- Introduce a separate contract template LockRequest with the following features:
- LockRequest has a locker party as the single signatory, allowing the locker party to unilaterally initiate the process and specify locking terms.
- Once owner exercises Accept on the lock request, the ownership of coin is transferred to the locker.
- The Accept choice also creates a LockedCoinV2 that represents Coin in locked state.
template LockRequest
with
locker: Party
maturity: Time
coin: Coin
where
signatory locker
observer coin.owner
choice Accept : LockResult
with coinCid : ContractId Coin
controller coin.owner
do
inputCoin <- fetch coinCid
assert (inputCoin == coin)
tpCid <- exercise coinCid Transfer with newOwner = locker
coinCid <- exercise tpCid AcceptTransfer
lockCid <- create LockedCoinV2 with locker; maturity; coin
return LockResult {coinCid; lockCid}
- LockedCoinV2 represents Coin in the locked state. It is fairly similar to the LockedCoin described in Consuming choice. The additional logic is to transfer ownership from the locker back to the owner when Unlock or Clawback is called.
template LockedCoinV2
with
coin: Coin
maturity: Time
locker: Party
where
signatory locker, coin.owner
choice UnlockV2
: ContractId Coin
with coinCid : ContractId Coin
controller locker
do
inputCoin <- fetch coinCid
assert (inputCoin.owner == locker)
tpCid <- exercise coinCid Transfer with newOwner = coin.owner
exercise tpCid AcceptTransfer
choice ClawbackV2
: ContractId Coin
with coinCid : ContractId Coin
controller coin.owner
do
currTime <- getTime
assert (currTime >= maturity)
inputCoin <- fetch coinCid
assert (inputCoin == coin with owner=locker)
tpCid <- exercise coinCid Transfer with newOwner = coin.owner
exercise tpCid AcceptTransfer
Trade-offs¶
Ownership transfer may give the locking party too much access on the locked asset. A rogue lawyer could run away with the funds. In a similar fashion, a malicious locker party could introduce code to transfer assets away while they are under their ownership.