Skip to content

Commit 311bb15

Browse files
authored
Merge 0843dc8 into 3c9bd6f
2 parents 3c9bd6f + 0843dc8 commit 311bb15

File tree

3 files changed

+271
-0
lines changed

3 files changed

+271
-0
lines changed

ERCS/erc-7891.md

+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
eip: 7891
3+
title: Splitting and Merging of NFTs
4+
description: Interface for hierarchical NFTs, enabling splitting a single NFT and merging multiple NFTs
5+
author: Nitin Bhagat (@nitin312) <bhagatnitin312@gmail.com>, JongWook Bae <bae@cwnu.ac.kr>, Su-Hyun Lee <sleepl@changwon.ac.kr>
6+
discussions-to: https://ethereum-magicians.org/t/eip-7891-hierarchical-nfts-with-splitting-and-merging/22986
7+
status: Draft
8+
type: Standards Track
9+
category: ERC
10+
created: 2025-02-15
11+
requires: 721, 6150
12+
---
13+
14+
## Abstract
15+
16+
This standard extends [EIP-721](./eip-721.md) and [EIP-6150](./eip-6150.md). This introduces a structured parent-child relationship between NFTs, allowing an NFT to be fractionally split into multiple child NFTs and merged back into a single entity. It provides interfaces to retrieve an NFT's parent, children, and hierarchical status, ensuring flexible ownership management. This standard is particularly useful for applications in fractional ownership, asset distribution, and composable digital assets, opening new possibilities in fields like real estate, gaming, and decentralized finance.
17+
18+
## Motivation
19+
20+
This EIP introduces hierarchical NFTs with splitting and merging capabilities, allowing assets to be dynamically restructured. This proposal is crucial for fractional ownership, gaming assets, and financial instruments, where assets need to be split or merged.
21+
22+
1. **Splitting**: One of the key limitations of [EIP-6150](./eip-6150.md) is its rigid hierarchy, where NFTs are permanently assigned to a parent without the ability to restructure ownership. In many real-world scenarios, assets need to be split into smaller, independent units. This EIP introduces a standardized way to split an NFT into multiple child NFTs, enabling dynamic asset management. For example, in financial markets, a share NFT can be split into multiple fractional share NFTs, allowing investors to own and trade smaller portions of a share.
23+
24+
2. **Merging**: Just as assets need to be split, there are scenarios where multiple NFTs should be combined into a single entity. The proposed EIP enables a merging mechanism, allowing child NFTs to be consolidated into a single parent NFT, allowing asset management and transactions. For instance, in finance, fractional share NFTs can be merged back into a full share NFT, enabling seamless ownership consolidation. This is particularly useful for investors who gradually accumulate fractions of a stock and later want to own a full share.
25+
26+
3. **Share Distribution**: This EIP introduces ownership share management, allowing NFTs to track and distribute fractional ownership among multiple stakeholders. This solves fractional ownership tracking within parent-child NFT structures. This also allows dynamic adjustments of ownership based on splitting and merging actions. For example, a real estate NFT representing a building can have multiple owners with different share percentages. When the NFT is split, the new NFTs retain a proportion of the original ownership share. When merged, the system redistributes the shares accordingly. This Enables multi-party ownership in digital assets.
27+
28+
### How the proposed EIP Improves Over Existing Standards
29+
30+
| Feature | [EIP-721](./eip-721.md) | [EIP-1155](./eip-1155.md) | [EIP-6150](./eip-6150.md) | EIP (Proposed) |
31+
|--------------------------|---------|---------|---------|------------------|
32+
| Unique NFTs |||||
33+
| Fungible & Non-Fungible |||||
34+
| Hierarchical Structure |||||
35+
| Parent-Child Relationship |||||
36+
| NFT Splitting |||||
37+
| NFT Merging |||||
38+
| Fractional Ownership |||||
39+
| Ownership Redistribution |||||
40+
41+
42+
## Specification
43+
44+
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.
45+
46+
Every compliant contract MUST implement this proposal, [EIP-721](./eip-721), [EIP-165](./eip-165), and [ERC-6150](./eip-6150)
47+
48+
```solidity
49+
pragma solidity ^0.8.0;
50+
51+
// Note: the ERC-165 identifier for this interface is 0x43cb816b.
52+
interface IERC7891 /* is IERC6150, IERC721, IERC165 */ {
53+
/**
54+
* @notice Emitted when a child token is minted under a parent with an assigned share.
55+
* @param parentId The ID of the parent token
56+
* @param childId The ID of the newly minted child token
57+
* @param share Share percentage assigned to the child token
58+
*/
59+
event Split(uint256 indexed parentId, uint256 indexed childId, uint8 share);
60+
61+
/**
62+
* @notice Emitted when multiple child tokens are merged into a new token.
63+
* @param newTokenId The ID of the newly minted merged token
64+
* @param mergedTokenIds Array of token IDs that were merged
65+
*/
66+
event Merged(uint256 indexed newTokenId, uint256[] mergedTokenIds);
67+
/**
68+
* @notice Mints a new root-level parent NFT.
69+
* @param _tokenURI URI string pointing to token metadata
70+
* @return tokenId The ID of the newly minted parent token
71+
*/
72+
function mintParent(string memory _tokenURI) external payable returns (uint256 tokenId);
73+
74+
/**
75+
* @notice Mints a child NFT under a given parent with a specific share allocation.
76+
* @param parentId ID of the parent token
77+
* @param _share Share percentage assigned to the child token
78+
* @return tokenId The ID of the newly minted child token
79+
*/
80+
function mintSplit(uint256 parentId, uint8 _share) external payable returns (uint256 tokenId);
81+
82+
/**
83+
* @notice Merges multiple child NFTs into a new token under the same parent.
84+
* @param parentId ID of the parent token
85+
* @param _tokenIds Array of child token IDs to be merged
86+
* @return newTokenId The ID of the newly minted merged token
87+
*/
88+
function mintMerge(uint256 parentId, uint256[] memory _tokenIds) external payable returns (uint256 newTokenId);
89+
90+
/**
91+
* @notice Transfers share ownership from one NFT to another.
92+
* @param to Token ID receiving the share
93+
* @param from Token ID sending the share
94+
* @param _share Share percentage to transfer
95+
*/
96+
function sharePass(uint256 to, uint256 from, uint8 _share) external;
97+
98+
/**
99+
* @notice Burns an NFT and transfers its share back to the parent NFT.
100+
* @param tokenId The ID of the token to burn
101+
*/
102+
function burn (uint256 tokenId) external;
103+
}
104+
```
105+
106+
## Rationale
107+
108+
This EIP builds upon [ERC-721](./eip-721) and [ERC-6150](./eip-6150) to introduce a structured mechanism for share-based hierarchical NFTs, enabling splitting, merging, and fractional ownership directly within the token standard. The proposal reuses [ERC-6150](./eip-6150)'s parent-child architecture to preserve compatibility and reduce implementation complexity. Share management is embedded natively through internal mappings, allowing each token to track its fractional ownership independently without relying on external protocols. Functions like `mintSplit` and `mintMerge` are designed to reflect real-world asset behaviors, clearly distinguishing between asset decomposition and consolidation. The `sharePass` function facilitates redistribution of shares between tokens without requiring minting or burning, offering an efficient internal transfer mechanism. A `burn` function is included to allow share return to the parent on destruction, aligning with ownership. Overall, the interface is purposefully minimal and intuitive, designed for extensibility while maintaining gas efficiency and semantic clarity.
109+
110+
## Backwards Compatibility
111+
112+
The proposed EIP extends [EIP-721](./eip-721.md) and [EIP-6150](./eip-6150.md), making it backward compatible.
113+
114+
## Reference Implementation
115+
116+
Implementation: [EIP-7891](../../assets/eip-7891/ERC-7891.sol).
117+
118+
## Security Considerations
119+
120+
No security considerations were found.
121+
122+
## Copyright
123+
124+
Copyright and related rights waived via [CC0](../LICENSE.md).
125+

assets/eip-7891/ERC7891.sol

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
4+
import "./ERC6150.sol";
5+
import "./IERC7891.sol";
6+
import "@openzeppelin/contracts/utils/Counters.sol";
7+
8+
contract ERC7891 is ERC6150, IERC7891 {
9+
using Counters for Counters.Counter;
10+
Counters.Counter private _tokenIds;
11+
12+
mapping(uint256 => string) private _tokenURIs;
13+
mapping(uint256 => uint8) public share;
14+
15+
constructor() ERC6150("ERC7891", "NFT") {}
16+
17+
function mintParent(string memory tokenURI) external returns (uint256) {
18+
_tokenIds.increment();
19+
uint256 tokenId = _tokenIds.current();
20+
_safeMintWithParent(msg.sender, 0, tokenId);
21+
share[tokenId] = 100;
22+
_tokenURIs[tokenId] = tokenURI;
23+
return tokenId;
24+
}
25+
26+
function mintSplit(uint256 parentId, uint8 _share) external returns (uint256) {
27+
require(share[parentId] >= _share, "Insufficient parent share");
28+
_tokenIds.increment();
29+
uint256 childId = _tokenIds.current();
30+
_safeMintWithParent(msg.sender, parentId, childId);
31+
sharePass(parentId, childId, _share);
32+
_tokenURIs[childId] = _tokenURIs[parentId];
33+
emit Split(parentId, childId, _share);
34+
return childId;
35+
}
36+
37+
function mintMerge(uint256 parentId, uint256[] memory tokenIds) external returns (uint256) {
38+
uint8 totalShare = 0;
39+
for (uint256 i = 0; i < tokenIds.length; i++) {
40+
require(parentOf(tokenIds[i]) == parentId, "Not a child of the same parent");
41+
totalShare += share[tokenIds[i]];
42+
_burn(tokenIds[i]);
43+
}
44+
_tokenIds.increment();
45+
uint256 newParentId = _tokenIds.current();
46+
_safeMintWithParent(msg.sender, parentId, newParentId);
47+
share[newParentId] = totalShare;
48+
emit Merged(newParentId, tokenIds);
49+
return newParentId;
50+
}
51+
52+
function sharePass(uint256 from, uint256 to, uint8 _share) public {
53+
share[from] += _share;
54+
share[to] -= _share ;
55+
}
56+
57+
function burn(uint256 _tid) public {
58+
uint256 pid = parentOf(_tid);
59+
if (pid == 0) share[_tid] = 0 ;
60+
else sharePass( pid, _tid, share[_tid]);
61+
62+
_safeBurn(_tid);
63+
64+
}
65+
66+
/// @inheritdoc IERC165
67+
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, IERC165) returns (bool) {
68+
return
69+
interfaceId == type(IERC7891).interfaceId || super.supportsInterface(interfaceId);
70+
}
71+
72+
function getInterfaceID() external pure returns (bytes4) {
73+
return type(IERC7891).interfaceId;
74+
}
75+
76+
}
77+
78+
79+
80+
81+
82+

assets/eip-7891/IERC7891.sol

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.0;
3+
import "./IERC6150.sol";
4+
5+
/**
6+
* @title IERC7891: Hierarchical NFTs with Splitting, Merging, and Share Management
7+
* @dev This interface extends ERC-6150 for share-based hierarchical NFTs.
8+
* It includes mechanisms for minting parent and child NFTs, merging, share transfer, and burning.
9+
* Note: The ERC-165 identifier for this interface is 0x43cb816b
10+
*/
11+
interface IERC7891 is IERC6150 {
12+
13+
/**
14+
* @notice Emitted when a child token is minted under a parent with an assigned share.
15+
* @param parentId The ID of the parent token
16+
* @param childId The ID of the newly minted child token
17+
* @param share Share percentage assigned to the child token
18+
*/
19+
event Split(uint256 indexed parentId, uint256 indexed childId, uint8 share);
20+
21+
/**
22+
* @notice Emitted when multiple child tokens are merged into a new token.
23+
* @param newTokenId The ID of the newly minted merged token
24+
* @param mergedTokenIds Array of token IDs that were merged
25+
*/
26+
event Merged(uint256 indexed newTokenId, uint256[] mergedTokenIds);
27+
28+
/**
29+
* @notice Mints a new root-level parent NFT.
30+
* @param _tokenURI URI string pointing to token metadata
31+
* @return tokenId The ID of the newly minted parent token
32+
*/
33+
function mintParent(string memory _tokenURI) external payable returns (uint256 tokenId);
34+
35+
/**
36+
* @notice Mints a child NFT under a given parent with a specific share allocation.
37+
* @param parentId ID of the parent token
38+
* @param _share Share percentage assigned to the child token
39+
* @return tokenId The ID of the newly minted child token
40+
*/
41+
function mintSplit(uint256 parentId, uint8 _share) external payable returns (uint256 tokenId);
42+
43+
/**
44+
* @notice Merges multiple child NFTs into a new token under the same parent.
45+
* @param parentId ID of the parent token
46+
* @param _tokenIds Array of child token IDs to be merged
47+
* @return newTokenId The ID of the newly minted merged token
48+
*/
49+
function mintMerge(uint256 parentId, uint256[] memory _tokenIds) external payable returns (uint256 newTokenId);
50+
51+
/**
52+
* @notice Transfers share ownership from one NFT to another.
53+
* @param to Token ID receiving the share
54+
* @param from Token ID sending the share
55+
* @param _share Share percentage to transfer
56+
*/
57+
function sharePass(uint256 to, uint256 from, uint8 _share) external;
58+
59+
/**
60+
* @notice Burns an NFT and transfers its share back to the parent NFT.
61+
* @param tokenId The ID of the token to burn
62+
*/
63+
function burn(uint256 tokenId) external;
64+
}

0 commit comments

Comments
 (0)