Discussion thread for: EIP-4671: Non-Tradable Tokens Standard by omaraflak · Pull Request #4671 · ethereum/EIPs · GitHub
Abstract
NTTs represent inherently personal possessions (material or immaterial), such as university diplomas, online training certificates, government issued documents (national id, driving licence, visa, wedding, etc.), badges, labels, and so on.
As the name implies, NTTs are not made to be traded or sold. They don’t have monetary value. They only serve as a proof of possession.
Motivation
US, 2017, MIT published 111 diplomas on a blockchain. France, 2018, Carrefour multinational retail corporation used blockchain technology to certify the provenance of its chickens. South Korea, 2019, the state published 1 million driving licences on a blockchain-powered platform.
Each of them made their own smart contracts, with different implementations. We think diplomas, food labels, or driving licences are just a subset of a more general type of tokens: non-tradable tokens. Tokens that represent certificates or labels that were granted to you by some authority.
By providing a common interface for this type of tokens, we allow more applications to be developed and we position blockchain technology as a standard gateway for verification of personal possessions.
Specification
A single NTT contract, is seen as representing one type of badge by one authority. For instance, one NTT contract for MIT diplomas, one NTT contract for the state driving licences, and so on…
- An address might possess multiple tokens, which are indexed.
- An authority who delivers a certificate should be in position to invalidate it. Think of driving licences or weddings. However, it cannot delete your token.
- The issuer of a token might be someone else than the contract creator.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/utils/introspection/IERC165.sol";
interface INTT is IERC165 {
/// @notice Count all tokens assigned to an owner
/// @param owner Address for whom to query the balance
/// @return Number of tokens owned by `owner`
function balanceOf(address owner) external view returns (uint256);
/// @notice Check if a token hasn't been invalidated
/// @param owner Address for whom to check the token validity
/// @param index Index of the token
/// @return True if the token is valid, False otherwise
function isValid(address owner, uint256 index) external view returns (bool);
/// @notice Get the issuer of a token
/// @param owner Address for whom to check the token issuer
/// @param owner Index of the token
/// @return Address of the issuer
function issuerOf(address owner, uint256 index) external view returns (address);
}
Extensions
Metadata
An interface allowing to add metadata linked to each token, as in ERC721.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface INTTMetadata {
/// @return Descriptive name of the tokens in this contract
function name() external view returns (string memory);
/// @return An abbreviated name of the tokens in this contract
function symbol() external view returns (string memory);
/// @notice URI to query to get the token's metadata
/// @param owner Address of the token's owner
/// @param index Index of the token
/// @return URI for the token
function tokenURI(address owner, uint256 index) external view returns (string memory);
}
Delegation
An interface to standardize delegation rights of token minting.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface INTTDelegate {
/// @notice Grant one-time minting right to `operator` for `owner`
/// An allowed operator can call the function to transfer rights.
/// @param operator Address allowed to mint a token
/// @param owner Address for whom `operator` is allowed to mint a token
function delegate(address operator, address owner) external;
/// @notice Grant one-time minting right to a list of `operators` for a corresponding list of `owners`
/// An allowed operator can call the function to transfer rights.
/// @param operators Addresses allowed to mint
/// @param owners Addresses for whom `operators` are allowed to mint a token
function delegateBatch(address[] memory operators, address[] memory owners) external;
/// @notice Mint a token. Caller must have the right to mint for the owner.
/// @param owner Address for whom the token is minted
function mint(address owner) external;
/// @notice Mint tokens to multiple addresses. Caller must have the right to mint for all owners.
/// @param owners Addresses for whom the tokens are minted
function mintBatch(address[] memory owners) external;
}
Implementation
The implementation is a bit long. You’ll find it in the PR.
NTT for EIP ?
As a first NTT, why not create the EIP Creator Badge ? An NTT created by the Ethereum foundation, and attributed to EIP-standard creators ?
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./NTT.sol";
contract EIPCreatorBadge is NTT {
constructor() NTT("EIP Creator Badge", "EIP") {}
function giveThatManABadge(address owner) external {
require(_isCreator(), "You must be the contract creator");
_mint(owner);
}
function _baseURI() internal pure override returns (string memory) {
return "https://eips.ethereum.org/ntt/";
}
}
Any thoughts or comments are greatly appreciated!