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: ERC-721 Ownership Shares Extension #266

Merged
merged 42 commits into from
Apr 16, 2024
Merged
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8136d4a
add erc: data access control
Feb 17, 2024
2d60f55
update
bizliaoyuan Feb 17, 2024
975547d
Update erc-7626.md
bizliaoyuan Feb 17, 2024
d23d89e
Update erc-7626.md
chenly Feb 17, 2024
1eac0a2
Update erc-7626.md
chenly Feb 17, 2024
6ce9942
Ownable
bizliaoyuan Feb 18, 2024
0836e71
Update erc-7626.md
chenly Feb 18, 2024
c407c14
change title
Feb 19, 2024
20c7af4
Update ERCS/erc-7626.md
chenly Feb 19, 2024
9c811ea
Update ERCS/erc-7626.md
chenly Feb 19, 2024
22de7f7
rename eip #
Feb 19, 2024
9af4272
rename
Feb 19, 2024
5a3589b
Update erc-7628.md
chenly Feb 20, 2024
1647e89
Update erc-7628.md
chenly Feb 20, 2024
77dfbdf
ERC-721 Balance Extension
bizliaoyuan Feb 23, 2024
8ca460b
update
bizliaoyuan Feb 23, 2024
92cf780
update
bizliaoyuan Feb 23, 2024
db3d4f9
Error: error[preamble-requires-ref-description]: proposals mentioned …
bizliaoyuan Feb 23, 2024
6437253
Error: error[preamble-uint-requires]: preamble header `requires` item…
bizliaoyuan Feb 23, 2024
2020a6d
Update erc-7628.md
bizliaoyuan Feb 23, 2024
c720423
Update erc-7628.md
bizliaoyuan Feb 23, 2024
02281a1
add IERC721
bizliaoyuan Feb 23, 2024
d566d27
Update erc-7628.md
chenly Feb 24, 2024
3b856e4
update
bizliaoyuan Feb 24, 2024
9885664
Merge branch 'erc-7626' of https://github.com/chenly/ERCs into erc-7626
bizliaoyuan Feb 24, 2024
672f523
update Security Considerations
bizliaoyuan Feb 24, 2024
adcf37e
Merge branch 'master' into erc-7626
chenly Feb 24, 2024
5d8c76e
Add Reference Implementation
bizliaoyuan Feb 26, 2024
0134f59
check allowance
bizliaoyuan Feb 26, 2024
5c47ee6
update
bizliaoyuan Feb 26, 2024
48cbcfd
update
bizliaoyuan Feb 26, 2024
18220ef
Update erc-7628.md
chenly Feb 28, 2024
132ed56
Correct some text formatting
Mar 1, 2024
fae628f
change Rationale
bizliaoyuan Mar 2, 2024
50367fa
new version
bizliaoyuan Mar 6, 2024
1c0dafa
change link
bizliaoyuan Mar 6, 2024
63c4333
update
bizliaoyuan Mar 6, 2024
d68a4bb
update
bizliaoyuan Mar 6, 2024
f9f4677
Update erc-7628.md
bizliaoyuan Mar 7, 2024
f93384f
Update erc-7628.md
bizliaoyuan Mar 7, 2024
a3961f4
Update erc-7628.md
bizliaoyuan Mar 7, 2024
a8f5aa5
rm SharesAdded
bizliaoyuan Mar 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 195 additions & 0 deletions ERCS/erc-7628.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
---
eip: 7628
title: ERC-721 Ownership Shares Extension
description: Introduces ownership shares to ERC-721 tokens, allowing for queryable, transferable, and approvable fractional ownership.
author: Chen Liaoyuan (@chenly) <cly@kip.pro>
discussions-to: https://ethereum-magicians.org/t/erc-7628-erc-721-ownership-shares-extension/18744
status: Draft
type: Standards Track
category: ERC
created: 2024-02-20
requires: 721
---

## Abstract

The proposal introduces an attribute of ownership and profit share quantities for each token under an NFT. This attribute signifies a stake in the ownership and profit rights associated with the NFT's specific privileges, enabling the querying, transferring, and approval of these shares, thereby making the shares represented by each token applicable in a broader range of use cases.

## Motivation

At times, when we wish to distribute dividends or assign rights to tokens of an NFT based on their share of ownership, it becomes necessary to equip each token with an attribute indicating the number of ownership shares. While [ERC-1155](./eip-1155.md) allows for the representation of ownership stakes through the balance of a token held by a wallet address, it sacrifices the uniqueness of each token. Conversely, [ERC-721](./eip-721.md) maintains the uniqueness of each token but lacks an attribute to signify the share of ownership rights, and its metadata does not allow for the free transfer of these share quantities by the token owner. This extension seeks to merge the features of [ERC-1155](./eip-1155.md) and [ERC-721](./eip-721.md), enabling holders of each share to possess characteristics akin to those of a token owner, thus bridging the gap between share representation and token uniqueness.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174.

Implementers of this extension **MUST** have all of the following functions:

```solidity
pragma solidity ^0.8.0;

interface IERC7628 /* is IERC721 */ {
/// @notice Returns the number of decimal places used for ownership shares.
/// @return The number of decimal places for ownership shares.
function shareDecimals() external view returns (uint8);

/// @notice Returns the total sum of ownership shares in existence for all tokens.
/// @return The total sum of ownership shares.
function totalShares() external view returns (uint256);

/// @notice Returns the ownership share of the specified token.
/// @param tokenId The identifier of the token.
/// @return The ownership share of the token.
function shareOf(uint256 tokenId) external view returns (uint256);

/// @notice Returns the share allowance granted to the specified spender by the owner for the specified token.
/// @param tokenId The identifier of the token.
/// @param spender The address of the spender.
/// @return The share allowance granted to the spender.
function shareAllowance(uint256 tokenId, address spender) external view returns (uint256);

/// @notice Approves the specified address to spend a specified amount of shares on behalf of the caller.
/// @param tokenId The identifier of the token.
/// @param spender The address of the spender.
/// @param shares The amount of shares to approve.
function approveShare(uint256 tokenId, address spender, uint256 shares) external;

/// @notice Transfers ownership shares from one token to another.
/// @param fromTokenId The identifier of the sender token.
/// @param toTokenId The identifier of the recipient token.
/// @param shares The amount of shares to transfer.
function transferShares(uint256 fromTokenId, uint256 toTokenId, uint256 shares) external;

/// @notice Transfers ownership shares from one token to another address (resulting in a new token or increased shares at the recipient address).
/// @param fromTokenId The identifier of the sender token.
/// @param to The address of the recipient.
/// @param shares The amount of shares to transfer.
function transferSharesToAddress(uint256 fromTokenId, address to, uint256 shares) external;

/// @notice Adds a specified amount of shares to a token, only callable by the contract owner.
/// @param tokenId The identifier of the token.
/// @param shares The amount of shares to add.
function addSharesToToken(uint256 tokenId, uint256 shares) external;

/// @notice Emitted when ownership shares are transferred from one token to another.
/// @param fromTokenId The identifier of the sender token.
/// @param toTokenId The identifier of the recipient token.
/// @param amount The amount of shares transferred.
event SharesTransfered(uint256 indexed fromTokenId, uint256 indexed toTokenId, uint256 amount);

/// @notice Emitted when an approval is granted for a spender to spend shares on behalf of an owner.
/// @param tokenId The token identifier.
/// @param spender The address of the spender.
/// @param amount The amount of shares approved.
event SharesApproved(uint256 indexed tokenId, address indexed spender, uint256 amount);
}
```

## Rationale

#### Share Issuance to a Token

Issuing additional shares to a token allows for flexible management of ownership stakes in digital assets, catering to the evolving needs of stakeholders. It ensures transparency and security in modifying ownership structures directly on the blockchain, facilitating scenarios like profit sharing or investment adjustments.

#### Transferring Shares to an Address

Enabling shares to be transferred to an address enhances NFT liquidity and accessibility by allowing fractional ownership. This feature supports diverse use cases like fractional sales or collateralization, making NFTs more adaptable and inclusive for a broader audience.

## Backwards Compatibility

This standard is fully [ERC-721](./eip-721.md) compatible.

## Reference Implementation

```solidity
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract ERC7628 is IERC7628, ERC721, Ownable, ReentrancyGuard {
mapping(uint256 => uint256) private _shareBalances;
mapping(uint256 => mapping(address => uint256)) private _shareAllowances;
uint256 private _totalShares;
uint256 private _nextTokenId;

constructor(address initialOwner)
ERC721("MyToken", "MTK")
Ownable(initialOwner)
{}

function addSharesToToken(uint256 tokenId, uint256 shares) public override onlyOwner {
require(tokenId > 0, "ERC7628: tokenId cannot be zero");
_shareBalances[tokenId] += shares;
_totalShares += shares;
emit SharesTransfered(0, tokenId, shares);
}

function shareDecimals() external pure override returns (uint8) {
return 18;
}

function totalShares() external view override returns (uint256) {
return _totalShares;
}

function shareOf(uint256 tokenId) external view override returns (uint256) {
return _shareBalances[tokenId];
}

function shareAllowance(uint256 tokenId, address spender) external view override returns (uint256) {
return _shareAllowances[tokenId][spender];
}

function approveShare(uint256 tokenId, address spender, uint256 shares) external override {
require(spender != ownerOf(tokenId), "ERC7628: approval to current owner");
require(msg.sender == ownerOf(tokenId), "ERC7628: approve caller is not owner");

_shareAllowances[tokenId][spender] = shares;
emit SharesApproved(tokenId, spender, shares);
}

function transferShares(uint256 fromTokenId, uint256 toTokenId, uint256 shares) external override nonReentrant {
require(_shareBalances[fromTokenId] >= shares, "ERC7628: insufficient shares for transfer");
require(_isApprovedOrOwner(msg.sender, fromTokenId), "ERC7628: transfer caller is not owner nor approved");

_shareBalances[fromTokenId] -= shares;
_shareBalances[toTokenId] += shares;
emit SharesTransfered(fromTokenId, toTokenId, shares);
}

function transferSharesToAddress(uint256 fromTokenId, address to, uint256 shares) external override nonReentrant {
require(_shareBalances[fromTokenId] >= shares, "ERC7628: insufficient shares for transfer");
require(_isApprovedOrOwner(msg.sender, fromTokenId), "ERC7628: transfer caller is not owner nor approved");

_nextTokenId++;
_safeMint(to, _nextTokenId);
_shareBalances[_nextTokenId] = shares;
emit SharesTransfered(fromTokenId, _nextTokenId, shares);
}

// Helper function to check if an address is the owner or approved
function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) {
return (spender == ownerOf(tokenId) || getApproved(tokenId) == spender || isApprovedForAll(ownerOf(tokenId), spender));
}
}
```

## Security Considerations

#### Clear Approvals on Transfer
When transferring token ownership, it is crucial to clear all existing approvals. This precaution prevents previously authorized parties from retaining access after the token has changed hands.

#### Prevent Reentrancy
Implementations must guard against reentrancy attacks. This involves ensuring that functions altering balances or ownership are secure against such vulnerabilities, particularly during share transfers.

#### Validate IDs and Addresses
Verifying the legitimacy of token IDs and wallet addresses in all operations is essential. This step helps avoid errors and ensures that tokens and their associated shares are handled correctly.

#### Manage Shares on Ownership Change
Proper management of share quantities is vital during a token ownership transfer. It's important to ensure that shares are accurately accounted for and transferred alongside the token to maintain the integrity of ownership stakes.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
Loading