From cc3ec2920a4d1fcd92e51485b64533fc61bd77e5 Mon Sep 17 00:00:00 2001 From: hiddenintheworld Date: Thu, 1 Jun 2023 10:24:35 +0800 Subject: [PATCH 1/6] Add EIP-8192 --- EIPS/eip-8192.md | 213 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 EIPS/eip-8192.md diff --git a/EIPS/eip-8192.md b/EIPS/eip-8192.md new file mode 100644 index 0000000000000..4936195acf985 --- /dev/null +++ b/EIPS/eip-8192.md @@ -0,0 +1,213 @@ +--- +eip: 8192 +title: NFT Dynamic Ownership +description: An innovative extension to NFT token that introduces dynamic ownership and nesting capabilities. +author: hiddenintheworld.eth (@hiddenintheworld) +discussions-to: https://ethereum-magicians.org/t/erc-8192-nft-dynamic-ownership/14516 +status: Draft +type: Standards Track +category: ERC +created: 2023-06-01 +requires: 721 +--- + +## Abstract + +A standard interface for non-fungible tokens (NFTs) with dynamic ownership, which extends the capabilities of the original `ERC-721` standard by enabling NFTs to be owned by either addresses or other NFTs. The proposed `ERC-721D` standard introduces dynamic ownership in the world of NFTs. Instead of a token being owned solely by an address, as it is in the `ERC-721` standard, tokens following the `ERC-721D` standard can be owned by either an address or another token. This opens up new possibilities and adds an extra layer of complexity and opportunity in the NFT space. This EIP outlines the rules and functions needed to support this dynamic ownership model while maintaining compatibility with `ERC-721` standards. + +## Motivation + +Non-fungible tokens (NFTs) have paved the way for unique digital assets. However, they are inherently restricted by their static ownership. `ERC-721D` aims to innovate the concept of NFT ownership by allowing tokens to have dynamic ownership chains. This could unlock an entirely new dimension for tokenized digital assets and decentralized applications (dApps). + +## Specification + + + +### Overview + +`ERC-721D` is a standard interface for NFTs with dynamic ownership. It provides essential functionalities to manage, transfer, and track the ownership of tokens. It is an extension of the `ERC-721` standard. + +### Data Structures + +The `ERC-721D` standard introduces a new data structure, **```Ownership```**. Each token has an **```Ownership```** associated with it that consists of the **```ownerAddress```** and the **```tokenId```**. The **```ownerAddress```** is the address of the token owner, which can be an EOA or a contract address. If the owner is another NFT, then tokenId represents the ID of the owner token. + +### Functions + +The `ERC-721D` standard defines a set of functions for interacting with tokens. It includes existing functions from the `ERC-721` standard, like **```balanceOf```** and **```ownerOf```**, with necessary modifications to support dynamic ownership. It also introduces new functions like **```setOwnership```** to manage dynamic ownership. The **```mint```** and **```burn```** functions have been overridden to account for changes in the balance of dynamic owners. The **```_transfer```** function has been updated to handle transfers involving dynamic owners. + +### Implementation + +Below is the full implementation: + + +```solidity +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; + +/* + +The ERC721D standard is an advanced and dynamic implementation of the ERC721 token standard. +This innovative contract takes the non-fungible token (NFT) concept a step further by introducing dynamic ownership. +In conventional NFTs, a token can only be owned by an address. +However, in the ERC721D standard, ownership can be dynamic, meaning an NFT can be owned by either an address or another NFT. +This introduces a new layer of complexity and opportunity in the NFT space. + +*/ +contract ERC721D is ERC721, Ownable { + + // The Ownership structure represents the owner of the token + struct Ownership { + address ownerAddress; // The address of the owner + uint256 tokenId; // The token Id of the owner if the owner is an NFT + } + + // Mapping from token ID to Ownership + mapping(uint256 => Ownership) private _owners; + + // Mapping from owner address to token balance + mapping(address => uint256) private _balances; + + constructor() ERC721("ERC721D", "ERC721D") {} + + // Mint new token + // `to` is the address that will own the minted token + // `tokenId` is the identifier for the new token + function mint(address to, uint256 tokenId) public onlyOwner { + _mint(to, tokenId); + _owners[tokenId] = Ownership(to, 0); + _balances[to] += 1; + } + + // Burn token + // `tokenId` is the identifier for the token + function burn(uint256 tokenId) public { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721D: caller is not owner nor approved"); + + Ownership memory oldOwnership = _owners[tokenId]; + if (oldOwnership.ownerAddress != address(0)) { + // Decrease the balance of the old owner + _balances[oldOwnership.ownerAddress] -= 1; + } + + // Set token ownership to the zero address (burning the token) + _owners[tokenId] = Ownership(address(0), 0); + _burn(tokenId); + } + + // Transfer Nested Ownership of a token + // `tokenId` is the identifier for the token + // `newOwnerAddress` is the address of the new owner + // `newTokenId` is the token Id of the new owner if the owner is an NFT + function transferNestedOwnership(uint256 tokenId, address newOwnerAddress, uint256 newTokenId) public { + require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721D: caller is not owner nor approved"); + Ownership memory oldOwnership = _owners[tokenId]; + + // First time ownership, balance increases + // Ownership is changing, adjust the balances + if (oldOwnership.ownerAddress == address(0) || oldOwnership.ownerAddress != newOwnerAddress) { + address oldOwner = oldOwnership.ownerAddress; + _balances[oldOwner] -= 1; + _balances[newOwnerAddress] += 1; + } + // Else: The token is being re-assigned to a different token but the same owner, do not change the balance. + + _owners[tokenId] = Ownership(newOwnerAddress, newTokenId); + } + + // Overrides the 'ownerOf' function from the ERC721 standard. + // Returns the current owner of the token identified by `tokenId`. + // It navigates through potential layers of ownership, making it suitable for dynamic token structures. + function ownerOf(uint256 tokenId) public view override(ERC721) returns (address) { + address currentOwnerAddress = _owners[tokenId].ownerAddress; + uint256 currentTokenId = _owners[tokenId].tokenId; + + // This loop will go through the ownership layers of the token. + // It stops if the owner address is zero (no owner), or if there's an error calling the ownerOf function on the owner contract, + // or if the returned owner is the same as the current owner (end of ownership chain). + while (currentOwnerAddress != address(0)) { + bytes memory payload = abi.encodeWithSignature("ownerOf(uint256)", currentTokenId); + (bool success, bytes memory result) = currentOwnerAddress.staticcall(payload); + if (!success || result.length == 0) { + break; + } + + address newOwnerAddress = abi.decode(result, (address)); + if (newOwnerAddress != currentOwnerAddress) { + currentOwnerAddress = newOwnerAddress; + currentTokenId = _owners[currentTokenId].tokenId; + } else { + break; + } + } + + // Return the final owner in the chain + return currentOwnerAddress; + } + + // This internal function is used to implement the transfer of tokens, following the ERC721 standard but allowing dynamic token ownership. + // It transfers the `tokenId` token from the `from` address to the `to` address. + function _transfer(address from, address to, uint256 tokenId) internal virtual override { + require(ownerOf(tokenId) == from, "ERC721D: transfer of token that is not owned"); + Ownership memory oldOwnership = _owners[tokenId]; + + _approve(address(0), tokenId); + _owners[tokenId] = Ownership(to, 0); + + if (oldOwnership.ownerAddress == address(0)) { + // The token is being owned for the first time, increase the balance of the new owner + _balances[to] += 1; + } else if (oldOwnership.ownerAddress != to) { + // The token is changing owner, adjust the balances + address oldOwner = oldOwnership.ownerAddress; + _balances[oldOwner] -= 1; + _balances[to] += 1; + } + + emit Transfer(from, to, tokenId); + } + + // An internal function that checks if a `spender` is an approved operator or the owner of a token. + // Returns true if the `spender` is an approved operator or the owner of the `tokenId` token. + // The function follows the ERC721 standard requirements. + function _isApprovedOrOwner(address spender, uint256 tokenId) internal view override returns (bool) { + require(_exists(tokenId), "ERC721D: operator query for nonexistent token"); + address owner = ownerOf(tokenId); + return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); + } + + // Overrides the `balanceOf` function from the ERC721 standard. + // Returns the balance (number of owned tokens) of the `owner` address. + // It checks for the zero address and returns the balance from the internal _balances mapping. + function balanceOf(address owner) public view override(ERC721) returns (uint256) { + require(owner != address(0), "ERC721D: balance query for the zero address"); + return _balances[owner]; + } + + // This function returns the ownership details of the `tokenId` token. + // Returns a struct with the owner's address and the token id of the token owned by the returned token (if any). + function owners(uint256 tokenId) public view returns (Ownership memory) { + return _owners[tokenId]; + } + +} +``` + +## Rationale + +The `ERC-721D` standard seeks to expand the potential of NFTs by introducing dynamic ownership. This innovation could open up new use cases in the fields of digital assets, dApps, digital identity, and more. As the digital economy evolves, the need for complex and dynamic relationships between tokens will become increasingly relevant, and the `ERC-721D` standard addresses this need. + +## Backwards Compatibility + +`ERC-721D` is fully backward compatible with the `ERC-721` standard. It extends the `ERC-721` standard by adding dynamic ownership while maintaining all existing functionalities. Any existing `ERC-721` token can be upgraded to an `ERC-721D` token while retaining its original capabilities. + +## Security Considerations + +As with any smart contract standard, security considerations are paramount. Implementers of `ERC-721D` should ensure that they have robust systems in place for managing the Ownership structure and that transfers, minting, and burning of tokens are handled securely. It's crucial to thoroughly test and audit any contracts that implement `ERC-721D` to avoid potential security risks. Moreover, dealing with dynamic ownership presents additional complexities, which require extra caution while implementing this standard. + +## Copyright + +Copyright and related rights waived via CC0. \ No newline at end of file From 55e40b4cf1d5405ad04fb18ae3b47316d82b4436 Mon Sep 17 00:00:00 2001 From: hiddenintheworld Date: Thu, 1 Jun 2023 10:26:47 +0800 Subject: [PATCH 2/6] Add EIP-8192 --- EIPS/eip-8192.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-8192.md b/EIPS/eip-8192.md index 4936195acf985..cbb114144c916 100644 --- a/EIPS/eip-8192.md +++ b/EIPS/eip-8192.md @@ -210,4 +210,4 @@ As with any smart contract standard, security considerations are paramount. Impl ## Copyright -Copyright and related rights waived via CC0. \ No newline at end of file +Copyright and related rights waived via CC0. From 10197818f647ea646a1cb30d6df30190d00dc316 Mon Sep 17 00:00:00 2001 From: hiddenintheworld <92841362+hiddenintheworld@users.noreply.github.com> Date: Fri, 9 Jun 2023 09:50:52 +0800 Subject: [PATCH 3/6] Update EIPS/eip-8192.md Co-authored-by: Andrew B Coathup <28278242+abcoathup@users.noreply.github.com> --- EIPS/eip-8192.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-8192.md b/EIPS/eip-8192.md index cbb114144c916..50dd29b3d7b31 100644 --- a/EIPS/eip-8192.md +++ b/EIPS/eip-8192.md @@ -1,5 +1,5 @@ --- -eip: 8192 +eip: 7110 title: NFT Dynamic Ownership description: An innovative extension to NFT token that introduces dynamic ownership and nesting capabilities. author: hiddenintheworld.eth (@hiddenintheworld) From 02b5787261be1c4d752ffbb50963a6e6fce2d40f Mon Sep 17 00:00:00 2001 From: hiddenintheworld <92841362+hiddenintheworld@users.noreply.github.com> Date: Fri, 9 Jun 2023 09:52:21 +0800 Subject: [PATCH 4/6] Rename eip-8192.md to eip-7110.md --- EIPS/{eip-8192.md => eip-7110.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EIPS/{eip-8192.md => eip-7110.md} (100%) diff --git a/EIPS/eip-8192.md b/EIPS/eip-7110.md similarity index 100% rename from EIPS/eip-8192.md rename to EIPS/eip-7110.md From 0ee87855794c26b7ef285e16d25f5634ea1d3ebe Mon Sep 17 00:00:00 2001 From: hiddenintheworld <92841362+hiddenintheworld@users.noreply.github.com> Date: Fri, 9 Jun 2023 09:58:50 +0800 Subject: [PATCH 5/6] Update eip-7110.md --- EIPS/eip-7110.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7110.md b/EIPS/eip-7110.md index 50dd29b3d7b31..37dcdfc74367a 100644 --- a/EIPS/eip-7110.md +++ b/EIPS/eip-7110.md @@ -3,7 +3,7 @@ eip: 7110 title: NFT Dynamic Ownership description: An innovative extension to NFT token that introduces dynamic ownership and nesting capabilities. author: hiddenintheworld.eth (@hiddenintheworld) -discussions-to: https://ethereum-magicians.org/t/erc-8192-nft-dynamic-ownership/14516 +discussions-to: https://ethereum-magicians.org/t/erc-7110-erc-721-dynamic-ownership/14516 status: Draft type: Standards Track category: ERC From 6c01dbc15ba0e5192d88c45c826e3749182e094b Mon Sep 17 00:00:00 2001 From: hiddenintheworld <92841362+hiddenintheworld@users.noreply.github.com> Date: Sat, 10 Jun 2023 20:46:21 +0800 Subject: [PATCH 6/6] Update eip-7110.md --- EIPS/eip-7110.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EIPS/eip-7110.md b/EIPS/eip-7110.md index 37dcdfc74367a..99db9daf92633 100644 --- a/EIPS/eip-7110.md +++ b/EIPS/eip-7110.md @@ -3,7 +3,7 @@ eip: 7110 title: NFT Dynamic Ownership description: An innovative extension to NFT token that introduces dynamic ownership and nesting capabilities. author: hiddenintheworld.eth (@hiddenintheworld) -discussions-to: https://ethereum-magicians.org/t/erc-7110-erc-721-dynamic-ownership/14516 +discussions-to: https://ethereum-magicians.org/t/erc-7110-nft-dynamic-ownership/14516 status: Draft type: Standards Track category: ERC