Skip to content

Commit

Permalink
update: revised the specification interface and rationale
Browse files Browse the repository at this point in the history
Signed-off-by: MASDXI <sirawitt42@gmail.com>
  • Loading branch information
MASDXI committed Nov 29, 2024
1 parent b5d14c0 commit 5446318
Showing 1 changed file with 83 additions and 36 deletions.
119 changes: 83 additions & 36 deletions ERCS/erc-7818.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,40 @@ This extension facilitates the development of [ERC-20](./eip-20.md) standard com

## Specification

### Epoch Mechanism

**Epochs** represent a specific period or block range during which certain tokens are valid. They can be categorized into two types

- **block-based** Defined by a specific number of blocks (e.g., 1000 `blocks`).
- **time-based** Defined by a specific duration in seconds (e.g., 1000 `seconds`).

Tokens linked to an `epoch` remain valid as long as the `epoch` is active. Once the specified number of `blocks` or the duration in `seconds` has passed, the `epoch` expires, and any tokens associated with it are considered expired.

### Balance Look Back Over Epochs

To retrieve the usable balance, tokens are checked from the **current epoch** against a **past epoch** (which can be any **_n_** epochs back). The past epoch can be set to any value **n**, allowing flexibility in tracking and summing tokens that are still valid from previous epochs, up to **n** epochs back.

The usable balance is the sum of tokens valid between the **current epoch** and the **past epoch**, ensuring that only non-expired tokens are considered.

#### Example Scenario

| **epoch** | **balance** |
| --------- | ----------- |
| 1 | 100 |
| 2 | 150 |
| 3 | 200 |

- **Current Epoch**: 3
- **Past Epoch**: 1 epoch back
- **Usable Balance**: 350

Tokens from **Epoch 2** and **Epoch 3** are valid. The same logic applies for any **_n_** epochs back, where the usable balance includes tokens from the current epoch and all prior valid epochs.

#### Flexibility

- Past epochs can be any **_n_** epochs back, allowing for flexible balance tracking.
- Only tokens that are valid between the **current epoch** and the **past epoch** are included in the usable balance.

The keywords “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Compatible implementations MUST inherit from [ERC-20](./eip-20.md)'s interface and **MUST** have all the following functions and all function behavior **MUST** meet the specification.
Expand All @@ -37,61 +71,71 @@ Compatible implementations MUST inherit from [ERC-20](./eip-20.md)'s interface a
pragma solidity >=0.8.0 <0.9.0;
/**
* @title ERC-7818: Expirable ERC20
* @dev Interface for creating expirable ERC20 tokens.
* @title ERC-7818 interface
* @dev Interface for adding expirable functionality to ERC20 tokens.
*/
import "./IERC20.sol";
interface IERC7818 is IERC20 {
/**
* @dev Enum represents the types of `epoch` that can be used.
* @notice The implementing contract may use one of these types to define how the `epoch` is measured.
*/
enum EPOCH_TYPE {
BLOCKS_BASED, // measured in the number of blocks (e.g., 1000 blocks)
TIME_BASED // measured in seconds (UNIX time) (e.g., 1000 seconds)
}
/**
* @dev Retrieves the balance of a specific `epoch` owned by an account.
* @param account The address of the account.
* @param epoch "MAY" represents an epoch, round, or period.
* @param epoch The `epoch for which the balance is checked.
* @return uint256 The balance of the specified `epoch`.
* @notice `epoch` "MUST" represent a unique identifier, and its meaning "SHOULD"
* align with how contract maintain the `epoch` in the implementing contract.
* @notice "MUST" return 0 if the specified `epoch` is expired.
*/
function balanceOf(
function balanceOfAtEpoch(
address account,
uint256 epoch
) external view returns (uint256);
/**
* @dev Retrieves the current epoch of the contract.
* @return uint256 The current epoch of the token contract,
* often used for determining active/expired states.
* @dev Retrieves the latest epoch currently tracked by the contract.
* @return uint256 The latest epoch of the contract.
*/
function epoch() external view returns (uint256);
function currentEpoch() external view returns (uint256);
/**
* @dev Retrieves the duration a token remains valid.
* @return uint256 The validity duration.
* @notice `duration` "MUST" specify the token's validity period.
* The implementing contract "SHOULD" clearly document,
* whether the unit is blocks or time in seconds.
* @dev Retrieves the duration of a single epoch.
* @return uint256 The duration of a single epoch.
* @notice The unit of the epoch length is determined by the `validityPeriodType()` function.
*/
function duration() external view returns (uint256);
function epochLength() external view returns (uint256);
/**
* @dev Returns the type of the epoch.
* @return EPOCH_TYPE Enum value indicating the unit of epoch.
*/
function epochType() external view returns (EPOCH_TYPE);
/**
* @dev Checks whether a specific `epoch` is expired.
* @param epoch "MAY" represents an epoch, round, or period.
* @param epoch The `epoch` to check.
* @return bool True if the token is expired, false otherwise.
* @notice Implementing contracts "MUST" define the logic for determining expiration,
* typically by comparing the current `epoch()` with the given `epoch`.
* @notice Implementing contracts "MUST" define and document the logic for determining expiration,
* typically by comparing the latest epoch with the given `epoch` value,
* based on the `EPOCH_TYPE` measurement (e.g., block count or time duration).
*/
function expired(uint256 epoch) external view returns (bool);
function isEpochExpired(uint256 epoch) external view returns (bool);
/**
* @dev Transfers a specific `epoch` and value to a recipient.
* @param to The recipient address.
* @param epoch "MAY" represents an epoch, round, or period.
* @param epoch The `epoch` for the transfer.
* @param value The amount to transfer.
* @return bool True if the transfer succeeded, false or reverted if give `epoch` it's expired.
* @notice The transfer "MUST" revert if the token `epoch` is expired.
* @return bool True if the transfer succeeded, otherwise false.
*/
function transfer(
function transferAtEpoch(
address to,
uint256 epoch,
uint256 value
Expand All @@ -101,12 +145,11 @@ interface IERC7818 is IERC20 {
* @dev Transfers a specific `epoch` and value from one account to another.
* @param from The sender's address.
* @param to The recipient's address.
* @param epoch "MAY" represents an epoch, round, or period.
* @param epoch The `epoch` for the transfer.
* @param value The amount to transfer.
* @return bool True if the transfer succeeded, false or reverted if give `epoch` it's expired.
* @notice The transfer "MUST" revert if the token `epoch` is expired.
* @return bool True if the transfer succeeded, otherwise false.
*/
function transferFrom(
function transferFromAtEpoch(
address from,
address to,
uint256 epoch,
Expand All @@ -117,20 +160,24 @@ interface IERC7818 is IERC20 {

### Behavior specification

- `balanceOf` **MUST** return the total balance of tokens held by an account that are still valid (i.e., have not expired). This includes any tokens associated with specific periods, epochs, or other identifiers, provided they remain within their validity duration. Expired tokens **MUST NOT** be included in the returned balance, ensuring that only actively usable tokens are reflected in the result.
- `balanceOf` **MUST** return the total balance of tokens held by an account that are still valid (i.e., have not expired). This includes any tokens associated with specific epochs, provided they remain within their validity duration. Expired tokens **MUST NOT** be included in the returned balance, ensuring that only actively usable tokens are reflected in the result.
- `balanceOfEpoch` returns the balance of tokens held by an account at the specified `epoch`, If the specified epoch is expired, this function **MUST** return `0`.
For example, if `epoch` 5 has expired, calling `balanceOfByEpoch(address, 5)` **MUST** return `0` even if there were tokens previously held in that epoch."
- `transfer` and `transferFrom` **MUST** exclusively transfer tokens that remain non-expired at the time of the transaction. Attempting to transfer expired tokens **MUST** revert the transaction or return false. Additionally, implementations **MAY** include logic to prioritize the automatic transfer of tokens closest to expiration, ensuring that the earliest expiring tokens are used first, provided they meet the non-expired condition.
- `transferByEpoch` and `transferFromByEpoch` **MUST** transfer the specified number of tokens held by an account at the specified epoch to the recipient, If the epoch has expired, the transaction **MUST** `revert` or return `false`
- `totalSupply` **SHOULD** be set to `0` or `type(uint256).max` due to the challenges of tracking only valid (non-expired) tokens.
- The implementation **MAY** use a standardized custom error revert message, such as `ERC7818TransferredExpiredToken` or `ERC7818TransferredExpiredToken(address sender, uint256 epoch)`, to clearly indicate that the operation failed due to attempting to transfer expired tokens.
- The implementation **MAY** use a standardized revert message, such as `ERC7818TransferredExpiredToken` or `ERC7818TransferredExpiredToken(address sender, uint256 epoch)`, to clearly indicate that the operation failed due to attempting to transfer expired tokens.

## Rationale
### Additional Potential Useful Function
These **OPTIONAL** functions provide additional functionality that might be useful depending on the specific use case.

The rationale for developing an expirable [ERC-20](./eip-20.md) token extension is based on several key requirements that ensure its practicality and adaptability for various applications to
- `epochInfo` returns both the start and end of the specified epoch.
- `remainingDurationBeforeEpochChange` returns the remaining time or blocks before the epoch change happens, based on the epoch type.

### Compatibility with the existing [ERC-20](./eip-20.md) standard.
The extension should integrate smoothly with the [ERC-20](./eip-20.md) interface, This ensures compatibility with existing token ecosystems and third-party tools like wallets and blockchain explorers.
## Rationale

### Flexible interface for various implementation.
The smart contract should be extensible, allowing businesses to tailor the expiration functionality to their specific needs like expiry in bulk or each token independent expire, whether it’s dynamic reward systems or time-sensitive applications.
Although the term `epoch` is an abstract concept, it leaves room for various implementations. For example, epochs can support more granular tracking of tokens within each epoch, allowing for greater control over when tokens are valid or expired on-chain. Alternatively, epochs can support bulk expiration, where all tokens within the same epoch expire simultaneously. This flexibility enables different methods of tracking token expiration, depending on the specific needs of the use case.
`epoch` also introduces a "lazy" way to simplify token expiration tracking in a flexible and gas-efficient manner. Instead of continuously updating the expiration state with `write` operations by the user or additional services, the current epoch can be calculated using a `read` operation.

## Backwards Compatibility

Expand Down

0 comments on commit 5446318

Please sign in to comment.