Skip to content
Permalink
escrowAndTrust…
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
title author
Single Claim Hash Time Lock Escrow
Luke

Simple Summary

Hash time lock escrow interface with a single claim and a preset destination.

Abstract

This document defines the interface for a single claim hash time lock escrow on an Accounts-based DLT. This proposed standard assumes each newly escrowed funds can only be claimed in full once (not partially claimed) and the escrowed funds can have a pre-set destination address. This proposed standard also allows for fungible and non-fungible tokens to be escrowed.

Motivation

To standardise hash time lock escrow smart contracts across Accounts-based DLTs.

Specification

Methods

NOTES:

  • Compulsory variables are marked with *
  • DestinationId is compulsory to avoid front running in DLTs where this is a possibility
  • the addToken function should be protected by permissions of the escrow contract owner

addToken

Adds a new token that can be locked by this escrow.

Input Variables:

- string unit*: the unit of this token (e.g. BTC, ETH, QNT,...)
- string type*: the token type of this (e.g. native, ERC20, ERC721) 
- integer decimalPlaces*: the number of decimal places for this token
- accountId issuer: unique reference to the issuer of the token that is being escrowed (e.g. smart contract) . It is not required for the native token type.

e.g.:
addToken("QNT","ERC20",18,0x4a220e6096b25eadb88358cb44068a3248254675)

Requirements:

  • If the type parameter is not equal to native, then the issuer parameter must be provided.

Functionality:

  • The following tuple is saved <unit*, type*, decimalPlaces*, issuer>

Note:

  • The decimalPlaces parameter is added here so that integer can always be used for any token lock later.

lockFungibleTokens

Locks fungible tokens of a given amount.

Input Variables:

- bytes hashlock*: the hash of the secret.
- unixtime timelock*: the time that the escrow expires.
- integer amount*: the amount of the fungible tokens to lock. 
- string unit*: the unit of this token.
- accountId destinationId*: a unique reference to the account who will receive the funds if they are unlocked before the expiry time.
- string escrowId: a unique reference to the escrow that is being created. If not provided, the smart contract will assign one.

e.g.:
lockFungibleTokens("8C762F9491E5017B0934DE9E32C44459974876F8A2E369B4302738CAD7DBF2E4",629644383,1000000000000000000,"QNT",0x4f6742badb049791cd9a37ea913f2bac38d01279,"myUniqueEscrowId123")

Requirements:

  • The timelock parameter must be in the future.
  • The sender must provide to the escrow contract the correct amount of tokens (in the DLT specific manner).
  • The unit parameter must correspond to a token added to the contract.
  • The escrowId parameter provided must not have already been used.

Functionality:

  • Creates a fungible token escrow object that includes all the parameters provided as input, as well as the creator of the escrow as the transaction sender and an empty variable to store the secret later. I.e., escrow = {id = escrowId OR generated by smart contract, status = 1, hashlock = hashlock, timelock = timelock, amount = amount, unit = unit, destinationId = destinationId, originId = transaction sender, secret = ""}
  • This escrow object locks the funds until the ledger time is greater than the escrow timelock or an unlock transaction is confirmed that includes the secret (which hashes to the hashlock) - see the unlockClaim function below.
  • Triggers the locked event locked(escrowId, true).

Note:

  • For the list of escrow statuses, see the getStatus function below

lockNonFungibleTokens

Locks non-fungible tokens.

Input Variables:

- bytes hashlock*: the hash of the secret.
- unixtime timelock*: the time that the escrow expires.
- integer[] tokenIds*: a list of the non-fungible tokens to lock. 
- string unit*: the unit code of this token.
- accountId destinationId*: a unique reference to the account who will receive the funds if they are unlocked before the expiry time.
- string lockEscrowId: a unique reference to the escrow that is being created. If not provided, the smart contract will assign one.

e.g.:
lockNonFungibleTokens("8C762F9491E5017B0934DE9E32C44459974876F8A2E369B4302738CAD7DBF2E4",629644383,[1,512],"CK",0x4f6742badb049791cd9a37ea913f2bac38d01279,"myUniqueCryptoKittiesEscrowId123")

Requirements:

  • The timelock parameter must be in the future.
  • The sender must provide to the escrow contract the correct tokens (in the DLT specific manner).
  • The unit parameter must correspond to a token added to the contract.
  • The escrowId parameter provided must not have already been used.

Functionality:

  • Creates a fungible token escrow object that includes all the parameters provided as input, as well as the creator of the escrow as the transaction sender and an empty variable to store the secret later. I.e., escrow = {id = escrowId OR generated by smart contract, status = 1, hashlock = hashlock, timelock = timelock, amount = amount, unit = unit, destinationId = destinationId, originId = transaction sender, secret = ""}
  • This escrow object locks the funds until the ledger time is greater than the escrow timelock or an unlock transaction is confirmed that includes the secret (which hashes to the hashlock) - see the unlockClaim function below.
  • Triggers the locked event locked(escrowId, false).

unlockClaim

unlocks the escrow before the timelock expires. This function is called for unlocking an escrow regardless on if the escrow is for fungible or non fungible tokens.

Input Variables:

- string escrowId*: a unique reference to the escrow to claim the funds from.
- string secret*: the secret that was used to generate the hashlock.

e.g.:
unlockClaim("myUniqueEscrowId123","MyAwesomeSecret")

Requirements:

  • The escrowId parameter must correspond to an escrow that is currently locked (i.e. it does exist and the funds still remain within it).
  • The secret parameter must hash to the hashlock of the corresponding escrow.
  • The corresponding escrow's timelock must be in the future, according to the ledger's agreed time.

Functionality:

  • The escrow's status is set to equal 2.
  • The funds in this escrow are sent to the escrow's destinationId.
  • Triggers the unlocked event unlocked(escrowId, 2).

unlockCancel

Returns the funds to the creator of the escrow. This function is called for cancelling an escrow regardless on if the escrow is for fungible or non fungible tokens.

Input Variables:

- string escrowId*: a unique reference to the escrow to claim the funds back from.

e.g.:
unlockCancel("myUniqueCryptoKittiesEscrowId123")

Requirements:

  • The escrowId parameter must correspond to an escrow that is currently locked (i.e. it does exist and the funds still remain within it).
  • The corresponding escrow's timelock must not be in the future, according to the ledger's agreed time.

Functionality:

  • The escrow's status is set to equal 3.
  • The funds in this escrow are sent back to the escrow's creator.
  • Triggers the unlocked event unlocked(escrowId, 3).

updateTimeLock

Increases the timelock of an escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to increase the timelock of.
- unixtime newTimeLock*: the newly proposed timelock.

e.g.:
updateTimeLock("escrow12345", 1663858784)

Requirements:

  • The escrowId parameter must correspond to an escrow that is currently locked (i.e. it does exist and the funds still remain within it).
  • The sender of this transaction must equal the originId of the corresponding escrow.
  • The newTimeLock parameter must be greater than the corresponding escrow's current timelock.

Functionality:

  • Increase the timelock of the escrow to the proposed newTimeLock time.
  • Triggers the updated event updated("myUniqueEscrowId123", [timelock]).

getOriginId

Returns the origin of the funds of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the hashlock of.

Output Variables:

- accountId originId*: the origination of the funds of this escrow. 

e.g.:
getOriginId("myUniqueEscrowId123") -> 0x448706b99c9aa5aa972d8ebe096debeec146a97d

Functionality:

  • Will return the originId parameter of this escrow.
  • If there is no escrow corresponding to escrowId, then the DLT default empty accountId will be returned.

getHashLock

Returns the hashlock of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the hashlock of.

Output Variables:

- bytes hashlock*: the hash of the secret. 

e.g.:
getHashLock("myUniqueEscrowId123") -> "8C762F9491E5017B0934DE9E32C44459974876F8A2E369B4302738CAD7DBF2E4"

Functionality:

  • Will return the hashlock parameter of this escrow.
  • If there is no escrow corresponding to escrowId, then zero will be returned.

getTimeLock

Returns the timelock of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the timelock of.

Output Variables:

- unixtime timelock*: the time that the escrow expires (after which a withdrawal from the creator of the escrowed funds is possible).

e.g.
getTimeLock("myUniqueEscrowId123") -> 629644383

Functionality:

  • Will return the timelock parameter of this escrow.
  • If there is no escrow corresponding to escrowId, then zero will be returned.

getDestinationId

Returns the destinationId of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the destinationId of.

Output Variables:

- accountId destinationId*: a unique reference to the account who will receive the funds if they are unlocked before the expiry time.

e.g.:
getDestinationId("myUniqueEscrowId123") -> 0x4f6742badb049791cd9a37ea913f2bac38d01279

Functionality:

  • Will return the destinationId parameter of this escrow.
  • If there is no escrow corresponding to escrowId, then the DLT default empty accountId will be returned.

getAmount

Returns the amount of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the amount of.

Output Variables:

- integer amount*: the amount of the fungible tokens in the escrow. 

e.g.:
getAmount("myUniqueEscrowId123") -> 1000000000000000000

Functionality:

  • Will return the amount parameter of this escrow.
  • If there is no escrow corresponding to escrowId or if the escrow is for non-fungible tokens, then zero will be returned.

Note:

  • Even after the escrowId is unlocked, the escrow's amount parameter will be equal to the tokens that were locked in this escrow - for historical audit purposes.

getTokenIds

Returns the tokenIds of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the tokenIds of.

Output Variables:

- integer[] tokenIds*: a list of the non-fungible tokenIds in the escrow.

e.g.:
getTokenIds("myUniqueCryptoKittiesEscrowId123") -> [1,512]

Functionality:

  • Will return the tokenIds parameter of this escrow.
  • Will return the empty array if escrowId does not exist or if the escrow is for fungible tokens.
  • If there is no escrow corresponding to escrowId or if the escrow is for fungible tokens, then the empty array will be returned.

Note:

  • Even after the escrowId is unlocked, the escrow's tokenIds parameter will be equal to the tokenIds that were locked in this escrow - for historical audit purposes.

getTokenUnit

Returns the token unit of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the token unit of.

Output Variables:

- string unit*: the unit of this token.

e.g. getTokenUnit("myUniqueEscrowId123") -> "QNT"

Functionality:

  • Will return the unit relating to the token(s) in this escrow.
  • If there is no escrow corresponding to escrowId, then the empty string will be returned.

getTokenType

Returns the token type of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the token type of.

Output Variables:

- string type*: the type of this token.

e.g. getTokenType("myUniqueEscrowId123") -> "ERC20"

Functionality:

  • Will return the type relating to the token(s) in this escrow.
  • If there is no escrow corresponding to escrowId, then the empty string will be returned.

getTokenIssuer

Returns the token issuer of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the token issuer of.

Output Variables:

- accountId issuer*: unique reference to the issuer of this token (e.g. smart contract) that is being escrowed. It is not required for the native token type.

e.g. getTokenIssuer("myUniqueEscrowId123") -> 0x4a220e6096b25eadb88358cb44068a3248254675

Functionality:

  • Will return the issuer relating to the token(s) in this escrow.
  • If there is no escrow corresponding to escrowId or if the token type locked is the native token type, then the DLT default empty accountId will be returned.

getSecret

Returns the revealed secret of the hash time lock escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the status of.

Output Variables:

- string secret*: the secret that unlocked the escrow.

e.g. getSecret("myUniqueEscrowId123") -> "MyAwesomeSecret"

Functionality:

  • Will return the secret that unlocked the escrow.
  • If there is no escrow corresponding to escrowId or if the escrow has not been claimed yet, then the empty string will be returned.

getStatus

Returns the status of a particular escrow.

Input Variables:

- string escrowId*: a unique reference to the escrow to get the status of.

Output Variables:

- enum status: the current status of this escrow.

e.g.:
getStatus("myUniqueEscrowId123") -> 1 (i.e. locked)

Functionality:

  • Will return the escrow's status parameter, which corresponds to one of the options from HashTimeLockStatuses = {doesNotExist, locked, unlockedClaimed, unlockedCancelled}.
  • If there is no escrow corresponding to escrowId, then zero will be returned.

getHashAlgorithm

Returns the hash algorithm used by this escrow contract.

Output Variables:

- enum algorithm: the algorithm used for escrows in this smart contract.

e.g.:
getHashAlgorithm() -> sha256

Functionality:

  • Will return the hash algorithm used by this smart contract.

Notes:

  • There should be an agreed enum list for this

Events

Locked

MUST trigger when tokens are locked in the escrow upon creation.

Event Parameters:

- escrowId*: a unique reference to the escrow that has been created.
- boolean fungibleToken: were fungible tokens locked (true) or non-fungible tokens (false).

e.g.:
locked("myUniqueEscrowId123", true)

Reasoning:

  • This is to alert watchers of a new escrow and of what type it is.

Notes:

  • The rest of the escrow params can be read from the different get functions of the smart contract.

Unlocked

MUST trigger when tokens are unlocked from the escrow, either from a claim or a cancel.

Event Parameters:

- escrowId*: a unique reference to the escrow that has been unlocked.
- integer status: what status is the escrow now in (according to the enum defining the statuses)

e.g.
unlocked("myUniqueEscrowId123", 2)

Reasoning:

  • This is to alert watchers of a movement of funds of an escrow.

Notes:

  • The status parameter is an integer to allow for easy reuse of this event across many escrow types (e.g. later we can add partialClaim etc).
  • The rest of the escrow params can be read from the different get functions of the smart contract.

Updated

MUST trigger when the timelock of the escrow is increased.

Event Parameters:

- escrowId*: a unique reference to the escrow that has been updated.
- string[] params: which escrow parameters have just been changed

e.g.
updated("myUniqueEscrowId123", [timelock])

Reasoning:

  • This is to alert watchers of a change of an escrow not related to the funds that are currently in it.
  • The params parameter is a string array to allow for easy reuse of this event across many escrow types (e.g. now we can use this for timelock increase but later we can add amount increase etc).

Notes:

  • The rest of the escrow params can be read from the different get functions of the smart contract.

Rationale

This is described in the relevant sections above

Backwards Compatibility

This is the first QIP so N/A.

Test Cases

N/A.

Reference Implementation

None yet

Security Considerations

  • DestinationId is compulsory to avoid front running in DLTs where this is a possibility
  • The addToken function should be protected by permissions of the escrow contract owner, otherwise a malicious token contract could be added that could perform all types of attacks (e.g. reentrancy).
  • There is no deleteToken or replaceToken so that the contract remains in most ways trustless. Even though a particular user(s) are trusted to add tokens to this escrow, individual users can verify the tokens added and decided to only use the ones that they have also audited.