Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC: Sustainable NFT Collections #752

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open

Conversation

gfLobo
Copy link

@gfLobo gfLobo commented Dec 4, 2024


eip: 7832
title: Sustainable NFT Collections
description: Economically sustainable NFTs with dynamic fees and donation-based engagement.
author: Gustavo Lobo (@gfLobo)
discussions-to: https://ethereum-magicians.org/t/erc-7832-sustainable-nft-collections/22201
status: Draft
type: Standards Track
category: ERC
created: 2024-12-04
requires: 721

Abstract

This EIP proposes a standard for creating economically sustainable NFT collections based on 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.

Motivation


NFT systems often face issues like inflationary supply and lack of mechanisms to incentivize meaningful engagement between creators and contributors. This EIP addresses these gaps by introducing:

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

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.


  1. Variables and Constants

    mintBaseFee

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

    creatorSignatureFee

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

    maxMintsPerUserInCycle

    Type: uint256
    Description:
    The maximum number of mints a user can perform in a cycle. Once the limit is exceeded, the user's minting count is reset.

    lastUpdateTimestamp

    Type: uint256
    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

    Type: uint256 (constant)
    Description:
    The time interval between contract terms updates. MUST be fixed to 30 days.

    CREATOR_ROLE

    Type: bytes32 (constant)
    Description:
    The role identifier for creators in the system.

    CONTRIBUTOR_ROLE

    Type: bytes32 (constant)
    Description:
    The role identifier for contributors in the system.

    mintsPerUserInCycle

    Type: mapping(address => uint256)
    Description:
    A mapping that tracks the number of mints a user has performed in the current cycle. It is used to enforce the maximum minting limit per user.


  2. Events

    CreatorTermsUpdated(uint256 mintBaseFee, uint256 creatorSignatureFee, uint256 maxMintsPerUserInCycle)

    Description:
    Emitted when the contract terms related to minting are updated by the DEFAULT_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.

  3. Modifiers

    onlyIfNotPaused

    Description:
    Ensures that the contract MUST NOT be paused when executing a function.

    onlyTokenOwner(uint256 tokenId)

    Description:
    Ensures that the caller is the owner of the specified token. This is checked by comparing the caller's address with the owner of the token identified by tokenId.

    updateCooldown

    Description:
    Ensures that the function can only be executed after the update interval has passed since the last contract term update.

    modifier updateCooldown() {
        require(
            block.timestamp >= lastUpdateTimestamp + UPDATE_INTERVAL, 
            "Updates not available until contract update interval is reached."
        );
        _;
    }

  4. Functions

    function safeMint(string memory uri)

    Type: Public and payable function
    Description:
    Allows the caller to mint a new token to their address with a provided 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]++;
    }

    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.
    • The modifier nonReentrant is REQUIRED to prevent reentrancy attacks.
    • The modifier onlyIfNotPaused is RECOMMENDED.

    mintFee

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

    function mintFee() public view returns (uint256) {
        if (hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) return 0;
        uint256 userMints = mintsPerUserInCycle[msg.sender];
        uint256 divisor = userMints == 0 ? 1 : Math.log2(userMints + 2);
        return mintBaseFee / divisor;
    }

    Requirements:

    • If the caller has the DEFAULT_ADMIN_ROLE, the fee can be OPTIONAL. Otherwise, the minting fee MUST be paid by the caller.

    donate(address creator)

    Type: Public and payable function
    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.caller MUST NOT be the same as the creator.
    • The donation amount MUST be greater than zero.

    Events:

    • MUST emit a DonationReceived event after the donation is processed.

    getCreatorSignature

    Type: Public and payable function
    Description:
    Allows a user to acquire a creator's signature by paying the required fee.

    function getCreatorSignature() public payable onlyIfNotPaused;

    Requirements:

    • It's RECOMMENDED to use the modifier onlyIfNotPaused to pause during emergencies.
    • The caller MUST pay the creator signature fee.
    • After the payment, the caller SHALL be granted the CREATOR_ROLE.
    • The modifier onlyIfNotPaused is RECOMMENDED.

    updateTerms(uint256 mintBaseFee, uint256 creatorSignatureFee, uint256 maxMintsPerUserInCycle)

    Type: Public function
    Description:
    Allows the admin to update the minting fee, creator signature fee, and the maximum mints per user in a cycle. The update MUST only be executed after the updateCooldown period has passed.

    Requirements:

    • Only the DEFAULT_ADMIN_ROLE MUST call this function.
    • The updateCooldown period SHALL be respected before another update can occur.

    Events:

    • MUST emit a CreatorTermsUpdated event after the contract terms are updated.

    withdraw(uint256 amount)

    Type: Public function
    Description:
    Allows the DEFAULT_ADMIN_ROLE to withdraw ETH from the contract.

    function withdraw(uint256 amount) public onlyRole(DEFAULT_ADMIN_ROLE) nonReentrant;

    Requirements:

    • Only the DEFAULT_ADMIN_ROLE MUST call this function.
    • The nonReentrant modifier is REQUIRED.

    burn(uint256 tokenId)

    Type: Public function
    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 according with onlyTokenOwner(tokenId) modifier.

    pause

    Type: Public function
    Description:
    Allows the DEFAULT_ADMIN_ROLE to pause the contract, disabling certain functions.

    Requirements:

    • Only the DEFAULT_ADMIN_ROLE SHOULD call this function to pause the contract.

    unpause

    Type: Public function
    Description:
    Allows the DEFAULT_ADMIN_ROLE to unpause the contract, re-enabling functionality.

    Requirements:

    • Only the DEFAULT_ADMIN_ROLE SHOULD call this function to unpause the contract.

Rationale

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

  1. 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 minting cycles, we ensure that users are incentivized to engage with the platform by receiving gradual discounts as they mint. Using a logarithmic reduction in minting fees ensures that the process is gradual, preventing market manipulation and maintaining scarcity over time.
  2. 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 721. Extensions like dynamic minting fees, donation systems are modular and do not impact existing NFT token functionalities.


Test Cases

  • Validate minting behavior, including fee calculations based on user mint history.
  • Ensure correct ETH transfers to creators and that the CONTRIBUTOR_ROLE is assigned properly.
  • Verify mint fee adjustments based on user activity.
  • Confirm that contract functions are correctly paused during an emergency.
  • Test access control, ensuring only authorized roles can mint, donate, or update the contract.
  • Verify that only the token owner can burn their token.

Reference Implementation

The following interface and constructor MUST be implemented.

pragma solidity ^0.8.20;


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

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

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

    // 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 DEFAULT_ADMIN_ROLE pause the contract
    function pause() external;

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

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

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

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

}
/// @notice Contract constructor
/// @param _tokenName The name of the token.
/// @param _tokenSymbol The symbol of the token.
/// @param _mintBaseFee The initial base fee for minting tokens.
/// @param _creatorSignatureFee The fee required to become a member.
/// @param _maxMintsPerUserInCycle Maximum of mints per user in cycle
constructor(
    string memory _tokenName,
    string memory _tokenSymbol,
    uint256 _mintBaseFee,
    uint256 _creatorSignatureFee,
    uint256 _maxMintsPerUserInCycle
)
    ERC721(_tokenName, _tokenSymbol)
{
    _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
    _grantRole(CREATOR_ROLE, msg.sender);
    _grantRole(CONTRIBUTOR_ROLE, msg.sender);

    updateTerms(
        _mintBaseFee,
        _creatorSignatureFee,
        _maxMintsPerUserInCycle
    );
}

Security Considerations

OpenZeppelin contracts such as ReentrancyGuard, Burnable, AccessControl, Pausable are RECOMMENDED for implementations.

  • Reentrancy Protection:
    The OpenZeppelin's ReentrancyGuard would offer protection against reentrancy attacks.
  • Access Management:
    The OpenZeppelin's AccessControl would help with the roles.
  • Paused State:
    The OpenZeppelin's Pausable would help Administrators to pause the contract during emergencies to prevent unwanted operations and mitigate risks during uncertain times.
  • Burning Security:
    The OpenZeppelin's Burnable would 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

Copyright and related rights waived via CC0.

@eip-review-bot
Copy link
Collaborator

eip-review-bot commented Dec 4, 2024

File ERCS/erc-7832.md

Requires 1 more reviewers from @g11tech, @SamWilsn, @xinbenlv

ERCS/erc-xxxx.md Outdated Show resolved Hide resolved
ERCS/erc-xxxx.md Outdated Show resolved Hide resolved
ERCS/erc-xxxx.md Outdated Show resolved Hide resolved
ERCS/erc-xxxx.md Outdated Show resolved Hide resolved
ERCS/erc-xxxx.md Outdated Show resolved Hide resolved
@github-actions github-actions bot added w-ci and removed w-ci labels Dec 5, 2024
@eip-review-bot eip-review-bot changed the title Add ERC: Sustainable Collections Add ERC: Sustainable NFT Collections Dec 5, 2024
@github-actions github-actions bot added w-ci and removed w-ci labels Dec 5, 2024
@github-actions github-actions bot removed the w-ci label Dec 11, 2024
@github-actions github-actions bot added the w-ci label Dec 11, 2024
@gfLobo gfLobo changed the title Add ERC: Sustainable NFT Collections Add ERC-7832: Sustainable NFT Collections Dec 13, 2024
@eip-review-bot eip-review-bot changed the title Add ERC-7832: Sustainable NFT Collections Add ERC: Sustainable NFT Collections Dec 13, 2024
@github-actions github-actions bot removed the w-ci label Dec 13, 2024
@github-actions github-actions bot added the w-ci label Dec 13, 2024
@github-actions github-actions bot removed the w-ci label Dec 14, 2024
Copy link

The commit 6d818df (as a parent of e158795) contains errors.
Please inspect the Run Summary for details.

@github-actions github-actions bot added the w-ci label Dec 15, 2024
@github-actions github-actions bot removed the w-ci label Dec 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants