Alert Source Discuss
⚠️ Draft Standards Track: ERC

ERC-7832: Sustainable collaborative NFT collections

Economically sustainable collaborative NFT collections with dynamic fees and donation-based engagement.

Authors Gustavo Lobo (@gflobo)
Created 2024-12-04
Discussion Link https://ethereum-magicians.org/t/erc-7832-sustainable-nft-collections/22201
Requires EIP-721

Abstract

This EIP proposes a standard for creating economically sustainable NFT governance for collections built on collaborative models based on ERC-721. It introduces dynamic minting fees, role-based access control, and a donation-based engagement model to enhance creator-community interactions. These mechanisms aim to balance scarcity, incentivize meaningful participation, and ensure sustainable growth for both creators and contributors.

The model defines “economically sustainable” as tokens whose minting value, creator subscription fees, and token quantity within each progressive discount cycle can only be adjusted once every 30 days from the last update by an ADMIN user. These mechanisms prevent excessive administrative modifications that could disrupt market stability, ensuring consistent price discovery and maintaining participant confidence. By aligning incentives and fostering predictability, the model creates a robust framework for engagement and value creation.

Motivation

As the NFT market matures, one of the recurring challenges faced by both creators and users is the inflationary nature of supply and the lack of effective mechanisms to engage the community meaningfully. NFT collections built on collaborative models require governance systems that empower all stakeholders—creators, contributors, and collectors—while also maintaining long-term economic sustainability. The introduction of this proposal aims to solve these issues by fostering a more dynamic, flexible, and transparent system for NFT collections. This EIP addresses these gaps by introducing:

  • Role-Based Access: Empowering creators while ensuring transparent governance by admins.
  • Dynamic Minting Fees: To align token costs with user activity and ownership.
  • Donation-Based Engagement: Encouraging contributions to creators.

Specification

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.

The following interface MUST be implemented.

// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.20;


interface IERC7832 is IERC721 {
    
    // Events
    event CreatorTermsUpdated(
        uint256 mintBaseFee,
        uint256 creatorSignatureFee,
        uint256 maxMintsPerUserInCycle);

    event DonationReceived(address from, address to, uint256 amount);

    // Function to get the current token ID
    function currentTokenId() external view returns (uint256);

    // Function to get the current mint base fee
    function mintBaseFee() external view returns (uint256);
    
    // Function to get the creator signature fee
    function creatorSignatureFee() external view returns (uint256);
    
    // Function to get the maximum mints a user can perform during his progressive discount cycle per mint
    function maxMintsPerUserInCycle() external view returns (uint256);

    // Function to get the last update timestamp
    function lastUpdateTimestamp() external view returns (uint256);

    // Function to get the update interval constant (30 days)
    function UPDATE_INTERVAL() external pure returns (uint256);

    // Function to get the CREATOR_ROLE
    function getCreatorSignature() external payable;

    // Function to get the CONTRIBUTOR_ROLE identifier
    function CONTRIBUTOR_ROLE() external pure returns (bytes32);

    // Function to get CREATOR_ROLE identifier
    function CREATOR_ROLE() external pure returns (bytes32);

    // Function to get ADMIN_ROLE identifier
    function ADMIN_ROLE() external pure returns (bytes32);

    // Function to check the number of mints a user has performed in their current cycle of progressive discounts
    function mintsPerUserInCycle(address user) external view returns (uint256);

    // Allow users to donate ETH to a specific creator in the system.
    function donate(address creator) external payable;

    // Allow users to check their mint fee
    function mintFee() external view returns (uint256);

    // Allow token owners to burn their tokens
    function burn(uint256 tokenId) external;

    // Allow ADMIN_ROLE pause the contract
    function pause() external;

    // Allow ADMIN_ROLE unpause the contract
    function unpause() external;

    // Allow CREATOR_ROLE to mint
    function safeMint(string memory uri) external payable;

    // Allow ADMIN_ROLE to update contract terms
    function updateTerms(
        uint256 mintBaseFee,
        uint256 creatorSignatureFee,
        uint256 maxMintsPerUserInCycle
    ) external;

    // Allow ADMIN_ROLE to withdraw funds from the contract
    function withdraw(uint256 amount) external;

}

currentTokenId()

Description:

Tracks the current token ID in the system. Can also be used to determine how many tokens have been minted in the system.

mintBaseFee()

Description:
The base fee for minting a token, paid by the user to create a new token.

creatorSignatureFee()

Description:
The fee required for a user to acquire a creator’s signature, allowing them to become a creator in the system.

maxMintsPerUserInCycle()

Description:
The maximum number of mints a user can perform during their current cycle of progressive discounts. Once the limit is exceeded, the user’s minting count is reset to zero.

lastUpdateTimestamp()

Description:
Timestamp of the last time the contract terms were updated (e.g., minting fees and creator signature fees). It is used to determine when the contract’s terms can be updated again.

UPDATE_INTERVAL()

Description:
The time interval between contract terms updates. MUST be fixed to 30 days.

ADMIN_ROLE()

Description:
The role identifier for admins in the system.

Requirements:

  • MUST be assigned inside the constructor to the msg.sender.

CREATOR_ROLE()

Description:
The role identifier for creators in the system.

Requirements:

  • MUST be assigned inside the constructor to the msg.sender.

CONTRIBUTOR_ROLE()

Description:
The role identifier for contributors in the system.

mintsPerUserInCycle(address user)

Description:
Tracks the number of mints a user has performed in their current cycle of progressive discounts. It is used to enforce the maximum minting limit per user.

CreatorTermsUpdated(uint256 mintBaseFee, uint256 creatorSignatureFee, uint256 maxMintsPerUserInCycle)

Description:
Emitted when the contract terms related to minting are updated by the ADMIN_ROLE.

DonationReceived(address from, address to, uint256 amount)

Description:
Emitted when a user donates ETH to a creator. This event tracks the details of the donation, including the donor’s address, the recipient’s address, and the donation amount.

Parameters:

  • from: The address of the user making the donation.
  • to: The address of the creator receiving the donation.
  • amount: The amount of ETH donated.

safeMint(string memory uri)

Description:
Allows the caller to mint a new token to their address with a provided URI.

Requirements:

  • The caller MUST have the CREATOR_ROLE.
  • The user MUST pay the minting fee, which is dynamic based on their previous minting activity.
  • If the minting limit is exceeded, the user’s mint count SHALL be reset to zero.
  • Is RECOMMENDED to require that the contract is not paused before using this function.

mintFee()

Description:
Calculates and returns the current minting fee that the caller MUST pay, based on the number of mints performed during his current discount per mint cycle. Is RECOMMENDED that the fee use a logarithmic reduction to adjust the fee smoothly.

Formula: ```math \text{mintFee} = \begin{cases} 0, & \text{if msg.sender has the ADMIN_ROLE} \ \frac{\text{mintBaseFee}}{\log_x(\text{userMints})}, & \text{if } \text{userMints} > 1 \ \frac{\text{mintBaseFee}}{1}, & \text{if } \text{userMints} <= 1 \end{cases}

> Note: Please note that the returned logarithm is always rounded to integers due to the characteristics of Solidity with floating-point numbers.


**Requirements**:  
- The minting fee **MUST** be paid by the caller.  

#### `donate(address creator)`
**Description**:  
Allows users to donate ETH to a creator, helping fund their activities. After making a donation, the donor **SHALL** receive the **CONTRIBUTOR_ROLE**.

**Requirements**:  
- The provided address **MUST** be a valid creator (having the **CREATOR_ROLE**).  
- The `msg.sender` **MUST NOT** be the same as the `creator`.  
- The donation amount **MUST** be greater than zero.
- **MUST** emit a `DonationReceived` event after the donation is processed.

#### `getCreatorSignature()`
**Description**:  
Allows a user to acquire a creator's signature by paying the required fee.

**Requirements**:  
- The caller **MUST** pay the creator signature fee.  
- After the payment, the caller **SHALL** be granted the **CREATOR_ROLE**.
- Is **RECOMMENDED** to require that the contract is not paused before using this function.

#### `updateTerms(uint256 mintBaseFee, uint256 creatorSignatureFee, uint256 maxMintsPerUserInCycle)`
**Description**:  
Allows the admin to update the minting fee, creator signature fee, and the maximum mints per user in a cycle of progressive discounts.

**Requirements**:  
- Only the `ADMIN_ROLE` **MUST** call this function.  
- **MUST** be called in the contract constructor as the first update of the contract terms.
- The update interval period **SHALL** be respected before another update can occur.
- **MUST** emit a `CreatorTermsUpdated` event after the contract terms are updated.

#### `withdraw(uint256 amount)`
**Description**:  
Allows the `ADMIN_ROLE` to withdraw ETH from the contract.

**Requirements**:  
- Only the `ADMIN_ROLE` **MUST** call this function.  

#### `burn(uint256 tokenId)`
**Description**:  
Allows the owner of a token to burn (destroy) the token specified by `tokenId`.

**Requirements**:  
- The caller **MUST** be the owner of the token.

#### `pause()`
**Description**:  
Allows the `ADMIN_ROLE` to pause the contract, disabling certain functions.

**Requirements**:  
- Only the `ADMIN_ROLE` **SHOULD** call this function to pause the contract.

#### `unpause()`
**Description**:  
Allows the `ADMIN_ROLE` to unpause the contract, re-enabling functionality.

**Requirements**:  
- Only the `ADMIN_ROLE` **SHOULD** call this function to unpause the contract.

## Rationale  

Below are the key considerations and justifications for the design choices:

1. **Access Control**
   - **Problem**: In collaborative NFT systems, it is essential to ensure that critical contract functions are executed only by authorized users to prevent misuse or manipulation. Without proper access control, there is a risk that anyone could modify key parameters, such as minting fees, terms, or contract pauses, which could lead to instability or unfair advantages for certain users.
   - **Solution**: By introducing role-based access control, this standard ensures that only trusted actors can perform sensitive actions. Admins are responsible for updating contract terms, pausing or unpausing the contract, and withdrawing funds, while creators can manage their own collections and get donations. This prevents arbitrary changes that might harm market stability or erode community trust.

2. **Dynamic Minting Fees**  
   - **Problem**: Fixed minting fees often lead to hoarding and disproportionate ownership, limiting equitable access to NFTs.  
   - **Solution**: By dynamically adjusting minting fees based on user activity within defined <u>minting cycles</u>, we ensure that users are incentivized to engage with the platform by receiving <u>gradual discounts as they mint</u>. Using a logarithmic reduction in minting fees ensures that the process is gradual, preventing market manipulation and maintaining scarcity over time.

3. **Donation-Based Engagement**  
   - **Problem**: Creators often lack sustainable models for fostering community engagement and receiving contributions.  
   - **Solution**: The donation system provides a transparent way for contributors to support their favorite creators directly. This can be used to attribute benefits in future trades, for example. This encourages deeper engagement and strengthens the relationship between creators and their communities.



### Backwards Compatibility  

This EIP is fully compatible with ERC-721. Extensions like dynamic minting fees, donation systems are modular and do not impact existing NFT token functionalities.



### Reference Implementation 

#### `mintFee()`
```solidity
function mintFee() public view returns (uint256) {
    if (hasRole(ADMIN_ROLE, msg.sender)) return 0;
    uint256 userMints = mintsPerUserInCycle(msg.sender);
    uint256 divisor = userMints <= 1 ? 1 : Math.log2(userMints);
    return mintBaseFee / divisor;
}

safeMint(string memory uri)

function safeMint(string memory uri)
public
payable
override 
onlyIfNotPaused
nonReentrant
onlyRole(CREATOR_ROLE)
{
    bool userMintsExceeded = mintsPerUserInCycle(msg.sender) + 1 > maxMintsPerUserInCycle;

    require(msg.value >= mintFee(), "Not enough ETH!");

    uint256 tokenId = currentTokenId++;
    _safeMint(msg.sender, tokenId);
    _setTokenURI(tokenId, uri);

    if(userMintsExceeded){
        mintsPerUserInCycle(msg.sender) = 0;
    }
    mintsPerUserInCycle(msg.sender)++;
}

Security Considerations

  • Reentrancy Protection:
    Is RECOMMENDED to make sure the functions safeMint, withdraw and donate are protected against reentrancy attacks.
  • Paused State:
    The Administrators MUST be able to pause the contract during emergencies to prevent unwanted operations and mitigate risks during uncertain times.
  • Burning Security: Ensure that only the owner of a token can burn it, reducing the risk of malicious contracts or unauthorized users destroying tokens belonging to others. The burn behavior is restricted to the ownership function, enhancing security by preventing accidental or abusive token destruction.

Copyright and related rights waived via CC0.

Citation

Please cite this document as:

Gustavo Lobo (@gflobo), "ERC-7832: Sustainable collaborative NFT collections [DRAFT]," Ethereum Improvement Proposals, no. 7832, December 2024. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7832.