Skip to content

Latest commit

 

History

History
199 lines (164 loc) · 11.5 KB

File metadata and controls

199 lines (164 loc) · 11.5 KB
title Chained NFTs to put Files and Code On-Chain
author Marco Vasapollo (@marcovasapollo), Alessandro Mario Lagana Toschi (@alet89)
status Draft
type Standards Track
category ERC
created 2019-04-19
requires
721
165

Simple Summary

A simple way to put files and code into chained NFTs to improve the decentralization of modern Dapps and DAOs

Abstract

Put complex files On-Chain, surpassing the Ethereum Block's size limits via a combination of chained Non-Fungible Tokens, generated by splitting the hexadecimal code of a file different NFTs. Every Chained NFT is a part of a sequence based on the deployed file, saved by an array into the Smart Contract.

Motivation

Modern Dapps are using centralized servers or distributed systems like IPFS to store Images and the Front-End part. This is a simple point of failure because with this setup a dapp still needs a well-known organization to rule it, at the end of the day this setup makes dapps not totally censorship-resistant especially if we focussed on non-financial applications. As well as ENS will remove the needs of a censurable DNS, this EIP is designed to put small files directly On-Chain removing as much as possible the censurable part of future Dapps.

Specification

Every NFT token is like a normal ERC-721 Token, with some methods to mint and finalize chained bunches of data:

pragma solidity ^0.5.0;

contract IRobe /* is IERC721 */ {

    /// @dev Creates a new NFT Token with this specified payload of data.
    /// @return a unique id representing this single NFT
    function mint(bytes calldata payload) external returns(uint256);

    /// @dev Creates a new NFT Token with this specified payload of data,
    /// but it will be not possible to concatenate new NFTs to this one anymore.
    /// @return a unique id representing this single NFT
    function mintAndFinalize(bytes calldata payload) external returns(uint256);

    /// @dev Attaches a new NFT to an already-existing one to expand it.
    /// rootTokenId must represent the very first one id of the chain to be expanded
    /// @return a unique id representing this single NFT
    function mint(uint256 rootTokenId, bytes calldata payload) external returns(uint256);

    /// @dev Attaches a new NFT to an already-existing one to expand it,
    /// but it will be not possible to concatenate new NFTs to this one anymore.
    /// rootTokenId must represent the very first one id of the chain to be expanded
    /// @return a unique id representing this single NFT
    function mintAndFinalize(uint256 rootTokenId, bytes calldata payload) external returns(uint256);

    /// @dev Finalizing an NFT means that you cannot expand anymore the chain of concatenated
    /// NFTs this id represents
    function finalize(uint256 rootTokenId) external;

    /// @dev Returns true if this tokenId belongs to a finalized chain.
    /// False otherwise.
    function isFinalized(uint256 tokenId) external view returns(bool);

    /// @dev Returns all the tokenIds that composes the givend NFT
    function getChain(uint256 tokenId) external view returns(uint256[] memory);

    /// @dev Returns the root NFT of this tokenId
    function getRoot(uint256 tokenId) external view returns(uint256);

    ///@dev Returns the content payload of the specified NFT
    function getContent(uint256 tokenId) external view returns(bytes memory);

    ///@dev Returns the position this NFT has in the NFTs chain it belongs to
    function getPositionOf(uint256 tokenId) external view returns(uint256);

    ///@dev Returns the Id of the NFT at the specific position in the chain represented by the given rootTokenId
    function getTokenIdAt(uint256 rootTokenId, uint256 position) external view returns(uint256);

    ///@dev Syntactic sugar - Gives all the information of a specific NFT: 
    /// its position in the chain
    /// the address of the wallet that minted it
    /// its data payload
    function getCompleteInfo(uint256 tokenId) external view returns(uint256, address, bytes memory);

    ///@dev event raised everytime a new NFT is minted, 
    /// with the root of the NFTs chain, the new id already created and the address of the wallet that submitted the transaction.
    /// When the minted id is the root of the NFT chain, rootTokenId and newTokenId will have the same value
    event Mint(uint256 indexed rootTokenId, uint256 indexed newTokenId, address indexed sender);

    ///@dev event raised when a NFT chain is finalized and it will be not possible anymore to expand it
    event Finalize(uint256 indexed rootTokenId);
}

/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
///  Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface ERC721 /* is ERC165 */ {
    /// @dev This emits when ownership of any NFT changes by any mechanism.
    ///  This event emits when NFTs are created (`from` == 0) and destroyed
    ///  (`to` == 0). Exception: during contract creation, any number of NFTs
    ///  may be created and assigned without emitting Transfer. At the time of
    ///  any transfer, the approved address for that NFT (if any) is reset to none.
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);

    /// @dev This emits when the approved address for an NFT is changed or
    ///  reaffirmed. The zero address indicates there is no approved address.
    ///  When a Transfer event emits, this also indicates that the approved
    ///  address for that NFT (if any) is reset to none.
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);

    /// @dev This emits when an operator is enabled or disabled for an owner.
    ///  The operator can manage all NFTs of the owner.
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    /// @notice Count all NFTs assigned to an owner
    /// @dev NFTs assigned to the zero address are considered invalid, and this
    ///  function throws for queries about the zero address.
    /// @param _owner An address for whom to query the balance
    /// @return The number of NFTs owned by `_owner`, possibly zero
    function balanceOf(address _owner) external view returns (uint256);

    /// @notice Find the owner of an NFT
    /// @dev NFTs assigned to zero address are considered invalid, and queries
    ///  about them do throw.
    /// @param _tokenId The identifier for an NFT
    /// @return The address of the owner of the NFT
    function ownerOf(uint256 _tokenId) external view returns (address);

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT. When transfer is complete, this function
    ///  checks if `_to` is a smart contract (code size > 0). If so, it calls
    ///  `onERC721Received` on `_to` and throws if the return value is not
    ///  `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    /// @param data Additional data with no specified format, sent in call to `_to`
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;

    /// @notice Transfers the ownership of an NFT from one address to another address
    /// @dev This works identically to the other function with an extra data parameter,
    ///  except this function just sets data to "".
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
    ///  TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
    ///  THEY MAY BE PERMANENTLY LOST
    /// @dev Throws unless `msg.sender` is the current owner, an authorized
    ///  operator, or the approved address for this NFT. Throws if `_from` is
    ///  not the current owner. Throws if `_to` is the zero address. Throws if
    ///  `_tokenId` is not a valid NFT.
    /// @param _from The current owner of the NFT
    /// @param _to The new owner
    /// @param _tokenId The NFT to transfer
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;

    /// @notice Change or reaffirm the approved address for an NFT
    /// @dev The zero address indicates there is no approved address.
    ///  Throws unless `msg.sender` is the current NFT owner, or an authorized
    ///  operator of the current owner.
    /// @param _approved The new approved NFT controller
    /// @param _tokenId The NFT to approve
    function approve(address _approved, uint256 _tokenId) external payable;

    /// @notice Enable or disable approval for a third party ("operator") to manage
    ///  all of `msg.sender`'s assets
    /// @dev Emits the ApprovalForAll event. The contract MUST allow
    ///  multiple operators per owner.
    /// @param _operator Address to add to the set of authorized operators
    /// @param _approved True if the operator is approved, false to revoke approval
    function setApprovalForAll(address _operator, bool _approved) external;

    /// @notice Get the approved address for a single NFT
    /// @dev Throws if `_tokenId` is not a valid NFT.
    /// @param _tokenId The NFT to find the approved address for
    /// @return The approved address for this NFT, or the zero address if there is none
    function getApproved(uint256 _tokenId) external view returns (address);

    /// @notice Query if an address is an authorized operator for another address
    /// @param _owner The address that owns the NFTs
    /// @param _operator The address that acts on behalf of the owner
    /// @return True if `_operator` is an approved operator for `_owner`, false otherwise
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

interface ERC165 {
    /// @notice Query if a contract implements an interface
    /// @param interfaceID The interface identifier, as specified in ERC-165
    /// @dev Interface identification is specified in ERC-165. This function
    ///  uses less than 30,000 gas.
    /// @return `true` if the contract implements `interfaceID` and
    ///  `interfaceID` is not 0xffffffff, `false` otherwise
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

Rationale

The implications are massive, we're testing this EIP with the DFO project to create community-driven DAOs without need to a well know organization or a well-known team of developers to rule and developing it. DFOs are designed to be built by anonymous people without trust each other, using ENS, this EIP and a smart way to code Smart Contracts like microservices. DFO it's already working in its Alpha https://dfohub.com. Another interesting use of this EIP is about the ownership of art without trust entities, transacting every part of the history of the developing of digital goods, so trusting the history without known the creator. The most common use will be for the Front-End of Dapps, we're working on simplifying the usage of this EIP for every Dapp developer, you can test it here: https://robe.ninja.

Copyright

Copyright and related rights waived via CC0.