153 lines
6.9 KiB
Markdown
153 lines
6.9 KiB
Markdown
|
---
|
|||
|
eip: 1132
|
|||
|
title: Extending ERC20 with token locking capability
|
|||
|
author: nitika-goel <nitika@govblocks.io>
|
|||
|
type: Standards Track
|
|||
|
category: ERC
|
|||
|
status: Stagnant
|
|||
|
created: 2018-06-03
|
|||
|
discussions-to: https://github.com/ethereum/EIPs/issues/1132
|
|||
|
---
|
|||
|
|
|||
|
## Simple Summary
|
|||
|
|
|||
|
An extension to the ERC20 standard with methods for time-locking of tokens within a contract.
|
|||
|
|
|||
|
## Abstract
|
|||
|
|
|||
|
This proposal provides basic functionality to time-lock tokens within an ERC20 smart contract for multiple utilities without the need of transferring tokens to an external escrow smart contract. It also allows fetching balance of locked and transferable tokens.
|
|||
|
|
|||
|
Time-locking can also be achieved via staking (#900), but that requires transfer of tokens to an escrow contract / stake manager, resulting in the following six concerns:
|
|||
|
|
|||
|
1. additional trust on escrow contract / stake manager
|
|||
|
2. additional approval process for token transfer
|
|||
|
3. increased ops costs due to gas requirements in transfers
|
|||
|
4. tough user experience as the user needs to claim the amount back from external escrows
|
|||
|
5. inability for the user to track their true token balance / token activity
|
|||
|
6. inability for the user to utilize their locked tokens within the token ecosystem.
|
|||
|
|
|||
|
## Motivation
|
|||
|
|
|||
|
dApps often require tokens to be time-locked against transfers for letting members 1) adhere to vesting schedules and 2) show skin in the game to comply with the underlying business process. I realized this need while building Nexus Mutual and GovBlocks.
|
|||
|
|
|||
|
In [Nexus Mutual](https://nexusmutual.io), claim assessors are required to lock their tokens before passing a vote for claims assessment. This is important as it ensures assessors’ skin in the game. The need here was that once a claim assessor locks his tokens for ‘n’ days, he should be able to cast multiple votes during that period of ‘n’ days, which is not feasible with staking mechanism. There are other scenarios like skills/identity verification or participation in gamified token curated registries where time-locked tokens are required as well.
|
|||
|
|
|||
|
In [GovBlocks](https://govblocks.io), I wanted to allow dApps to lock member tokens for governance, while still allowing members to use those locked tokens for other activities within the dApp business. This is also the case with DGX governance model where they’ve proposed quarterly token locking for participation in governance activities of DGX.
|
|||
|
|
|||
|
In addition to locking functionality, I have proposed a `Lock()` and `Unlock()` event, just like the `Transfer()` event , to track token lock and unlock status. From token holder’s perspective, it gets tough to manage token holdings if certain tokens are transferred to another account for locking, because whenever `balanceOf()` queries are triggered on token holder’s account – the result does not include locked tokens. A `totalBalanceOf()` function intends to solve this problem.
|
|||
|
|
|||
|
The intention with this proposal is to enhance the ERC20 standard with token-locking capability so that dApps can time-lock tokens of the members without having to transfer tokens to an escrow / stake manager and at the same time allow members to use the locked tokens for multiple utilities.
|
|||
|
|
|||
|
## Specification
|
|||
|
|
|||
|
I’ve extended the ERC20 interface with the following enhancements:
|
|||
|
|
|||
|
### Locking of tokens
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Locks a specified amount of tokens against an address,
|
|||
|
* for a specified reason and time
|
|||
|
* @param _reason The reason to lock tokens
|
|||
|
* @param _amount Number of tokens to be locked
|
|||
|
* @param _time Lock time in seconds
|
|||
|
*/
|
|||
|
function lock(bytes32 _reason, uint256 _amount, uint256 _time) public returns (bool)
|
|||
|
```
|
|||
|
|
|||
|
### Fetching number of tokens locked under each utility
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Returns tokens locked for a specified address for a
|
|||
|
* specified reason
|
|||
|
*
|
|||
|
* @param _of The address whose tokens are locked
|
|||
|
* @param _reason The reason to query the lock tokens for
|
|||
|
*/
|
|||
|
tokensLocked(address _of, bytes32 _reason) view returns (uint256 amount)
|
|||
|
```
|
|||
|
|
|||
|
### Fetching number of tokens locked under each utility at a future timestamp
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Returns tokens locked for a specified address for a
|
|||
|
* specified reason at a specific time
|
|||
|
*
|
|||
|
* @param _of The address whose tokens are locked
|
|||
|
* @param _reason The reason to query the lock tokens for
|
|||
|
* @param _time The timestamp to query the lock tokens for
|
|||
|
*/
|
|||
|
function tokensLockedAtTime(address _of, bytes32 _reason, uint256 _time) public view returns (uint256 amount)
|
|||
|
```
|
|||
|
|
|||
|
### Fetching number of tokens held by an address
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev @dev Returns total tokens held by an address (locked + transferable)
|
|||
|
* @param _of The address to query the total balance of
|
|||
|
*/
|
|||
|
function totalBalanceOf(address _of) view returns (uint256 amount)
|
|||
|
```
|
|||
|
|
|||
|
### Extending lock period
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Extends lock for a specified reason and time
|
|||
|
* @param _reason The reason to lock tokens
|
|||
|
* @param _time Lock extension time in seconds
|
|||
|
*/
|
|||
|
function extendLock(bytes32 _reason, uint256 _time) public returns (bool)
|
|||
|
```
|
|||
|
|
|||
|
### Increasing number of tokens locked
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Increase number of tokens locked for a specified reason
|
|||
|
* @param _reason The reason to lock tokens
|
|||
|
* @param _amount Number of tokens to be increased
|
|||
|
*/
|
|||
|
function increaseLockAmount(bytes32 _reason, uint256 _amount) public returns (bool)
|
|||
|
```
|
|||
|
### Fetching number of unlockable tokens under each utility
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Returns unlockable tokens for a specified address for a specified reason
|
|||
|
* @param _of The address to query the the unlockable token count of
|
|||
|
* @param _reason The reason to query the unlockable tokens for
|
|||
|
*/
|
|||
|
function tokensUnlockable(address _of, bytes32 _reason) public view returns (uint256 amount)
|
|||
|
```
|
|||
|
### Fetching number of unlockable tokens
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Gets the unlockable tokens of a specified address
|
|||
|
* @param _of The address to query the the unlockable token count of
|
|||
|
*/
|
|||
|
function getUnlockableTokens(address _of) public view returns (uint256 unlockableTokens)
|
|||
|
```
|
|||
|
### Unlocking tokens
|
|||
|
```solidity
|
|||
|
/**
|
|||
|
* @dev Unlocks the unlockable tokens of a specified address
|
|||
|
* @param _of Address of user, claiming back unlockable tokens
|
|||
|
*/
|
|||
|
function unlock(address _of) public returns (uint256 unlockableTokens)
|
|||
|
```
|
|||
|
|
|||
|
### Lock event recorded in the token contract
|
|||
|
`event Locked(address indexed _of, uint256 indexed _reason, uint256 _amount, uint256 _validity)`
|
|||
|
|
|||
|
### Unlock event recorded in the token contract
|
|||
|
`event Unlocked(address indexed _of, uint256 indexed _reason, uint256 _amount)`
|
|||
|
|
|||
|
## Test Cases
|
|||
|
|
|||
|
Test cases are available at [https://github.com/nitika-goel/lockable-token](https://github.com/nitika-goel/lockable-token).
|
|||
|
|
|||
|
## Implementation
|
|||
|
|
|||
|
- Complete implementation available at https://github.com/nitika-goel/lockable-token
|
|||
|
- [GovBlocks](https://govblocks.io) Project specific implementation available at https://github.com/somish/govblocks-protocol/blob/Locking/contracts/GBTStandardToken.sol
|
|||
|
|
|||
|
## Copyright
|
|||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|