ethereum / EIPs

The Ethereum Improvement Proposal repository
https://eips.ethereum.org/
Creative Commons Zero v1.0 Universal
12.87k stars 5.27k forks source link

ERC1238: Non-transferrable Non-Fungible Tokens (NTT) #1238

Closed nicola closed 2 years ago

nicola commented 6 years ago
eip: 1238
title: Non-transferrable Non-Fungible Tokens (badges)
author: Nicola Greco (and future others)
status: WIP
category: ERC
created: 2018-07-20

Non-transferable tokens

Simple Summary

A badge is a token that once assigned it cannot be transferred. Badges can be accumulated through time and put at stake. Simply speaking, badges are statements about a public key, they can be quantitative (e.g. reputation, experience) or qualitative (badges, titles).

Abstract

The Non-transferrable token standard defines a set of standard APIs allowing the identification of statements (called badges) attributed to a public key, such that different dapps and smart contract can use to filter users or to provide user with different badges different experiences. More importantly, this standard defines a way for which users can put their badges at stake. Badges cannot be transferred but can be lost (after staking) or can expire.

Motivation

The inspiration for designing badges comes from a conversation with Andrew Miller. The idea is to have a token that once assigned it cannot be transferred (like reputation) and that it can be used by websites, or contracts to make me perform some actions. For example, if a user accumulates paper submissions at conferences, then they can use their paper badges to request grants. It's important that they can never share these badges.

This is the equivalent of a variety of other use cases

Requirements for specification

Next steps

There are two main steps here:

However, I will have very little time to actually bring this to life. It would be awesome if any contributor here feels strong and empowered to take over this standard and make this their own. I started this while thinking on a fun project for academia (namely called research coin), maybe someone wants to help there!

Arachnid commented 6 years ago

I like the idea of badges, but I think it's a mistake to tie them to a single address. Over time people will have multiple accounts, and making them non-transferrable will discourage people from switching to a more secure account (eg, from a software wallet to a hardware one).

nicola commented 6 years ago

I guess you can always ask the issuer to re-issue a new badge, or maybe you can only transfer a badge if the issuer agrees on the transfering. The problem is with decentralized issuing, this might be an issue!

adibas03 commented 6 years ago

I think a way to escape the address restriction is to utilize the proxy implementation #121 or identity from #725 such that the proxy/identity has the badge and such the owner can have access with any of the owned addresses

chiro-hiro commented 6 years ago

@nicola will the reputation be decayed overtime?

The same thing happens in real world :)

Arachnid commented 6 years ago

@adibas03 That demonstrates how pointless preventing transfers would be - it's easily evaded, and so just makes life more difficult for users.

nicola commented 6 years ago

@chiro-hiro it would be up to you, how you implement it

@Arachnid I agree and disagree, the identity would be an ID where you can decide the set of keys that own that ID, you can't transfer badges across IDs.. For example, when playing in a game, you only have one ID per player and you only have some experience. In other words, I believe there is something useful here.

note: re-issuing is still a valid option for some usecases

adibas03 commented 6 years ago

@Arachnid I see your point about the non-transferable attribute. Similar to game credits, which are not natively transferrable, but the accounts with the credits and repuation can be sold. So, the proxy/Identity accounts can be transferred in a way. @nicola I think @Arachnid has a point here. Also, could the badges be an example of a claim as described in #735

MicahZoltu commented 6 years ago

There is a big difference between transferring individual badges and transferring all badges associated with a key. I think there is noue in allowing users to transfer all of their badges to a new key, while also preventing individual badges from being transferred away.

One can think of the set of badges as identifying a user, and you cannot split the user into parts and have it be the same user, but you can transfer a user to a new private key.

vongohren commented 6 years ago

What differences do you see between badges and claims? Ref: https://medium.com/uport/erc1056-erc780-an-open-identity-and-claims-protocol-for-ethereum-aef7207bc744

Both #735 and uPort uses the wording of claims.

nicola commented 6 years ago

735 looks very similar, if not identical to that, I will reach out to them.

PhABC commented 6 years ago

I worked on some "badge" token stuff to quantify participation events for fun and also opted for "transfer all or none scheme" as @MicahZoltu proposes, which I think is good enough for the intent here.

jcksncllwy commented 6 years ago

@vongohren A significant difference between #735 claims and #1238 badges is information ownership. In #735 the Claim Holder owns any claims made about themselves. The problem with this is that there is no way for a Claim Issuer to revoke or alter a claim once it has been issued. While #735 does specify a removeClaim method, a malicious implementation could simply ignore that method call, because they own the claim.

Imagine that SafeEmploy™, a background checking company, issues a claim about Timmy. The claim states that Timmy has never been convicted of any felonies. Timmy makes some bad decisions, and now that claim is no longer true. SafeEmploy™ executes removeClaim, but Timmy's #735 contract just ignores it, because Timmy wants to stay employed (and is crypto-clever).

1238 badges do not have this problem. Ownership of a badge/claim is entirely determined by the contract issuing the badges, not the one receiving them. The issuer is free to remove or change those badges as they see fit.

conejoninja commented 6 years ago

@jcksncllwy In #725 (along with #735) The claim issuer could remove the key (from their own identity) that signed the claim to revoke it.

OFRBG commented 6 years ago

Reputation system are likely to be abused. Cutting the explanation and straight to the example:

A major org around Ethereum decides to screen users. The screening process may only be performed by said org, since they set the rules for their own DApp. Being the first mover with a household name.

Since Ethereum doesn't have truly private methods and attributes, other DApp groups can easily read the badges other users have. If the major org with first mover advantage creates its own version of crypto-TSA, other groups will simply save time and resources, piggybacking on what's available.

If the problem isn't already blatantly obvious, think about the reputation bottleneck this causes: the major org or orgs that work as a crypto-TSA become gatekeepers for the whole system. We know from history that some things we do aren't the best, but they stuck because they were the most comfortable. When going from a reputation-less system into a reputation-based system, whatever comes first is most likely to stay.

On a trustless network every address should be no different from any other. I think a measured reputation system goes against what crypto should be. In other words, creating an issued-reputation ERC is opening Pandora's Box.

cbruguera commented 5 years ago

I think this is an interesting idea. It's definitely related to the concept of claims, but I can see an interoperability advantage in defining the behavior of these "claims" in a contract specific to the claim "type" (i.e. badge), instead of residing in the claim holder or issuer contracts (similar to how ERC20 and ERC721 work).

With regard to not limiting badge ownership to a single address, that's where we start getting into identity territory, which is not a trivial one. I came across this proposal recently, perhaps you guys might be interested in having a look at it and it would be good to know your feedback on the matter.

One question about defining this as a standard: should badge issuers be able to arbitrarily revoke the badge? Or would this stay outside the scope of a standard and be more a matter of particular implementation?

adibas03 commented 5 years ago

@OFRBG You are right about the danger of Reputation, but as you mentioned, the implementation plays a big role.All or null transfer-ability as suggested by @MicahZoltu still seems the best possible option. Also, the two main preferable implementation I believe are (I am using claim t refer to both claims #735 and badges #1238),

jahowle commented 5 years ago

Full Disclosure: I work at www.uport.me

@nicola I think the most important thought in your original posting is this

Off-chain tokens: This specification should work also for tokens that are not issued on chain (say by a third party signer) and can be presented on chain only when necessary (it's understandable that this is an edge case and could fall off the spec, but it's an interesting one!)

Why put reputation claims on-chain at all?

My assumption is that it reduces the friction between the party consuming the reputation and the party with the reputation, but that can potentially be solved at the UX/application level through easy selective, progressive, and active disclosure.

I worry about the privacy of users in any implementation of an on-chain claims registry.

I'd like to hear your's or anyone's argument for on-chain claims over off-chain claims.

snaketh4x0r commented 5 years ago

nice eip @nicola I just got some free time from my busy college so I decided to contribute to decentralize web. so here is example code with non transferable functionality implemented.

Badge is NON transferable token issued by owner to a particular identity.

badges are issued only once to new identities. to issue new badges new contracts have to be deployed. there can be a new function introduced to remove a identity from transfer restriction so it can be issued more badge tokens(which is of no use) but then it will have drawbacks as described in second way further below.

There were two ways to implement it,

first by preventing transfer for each identity which uses lot of gas as each identity is stored on blockchain. secondly by freezing transfer function of contract,its gas efficient but it had few drawbacks consider owner unfreezed transfer to issue new badge token for particular new identity,during this time any existing token holder could transfer token,onlyOwner modifier on transfer is of no use as owner would be able to transfer a token between identities hence owner can be bribed by token holders to transfer their tokens to another token holder but we have to prevent that. so first way is best way plus owner can set his address as target and so no one will be able transfer any token in future not even owner hence no new token issued.

owner here can be single address or multi-sig address contract controlled by single individual or group->centralized DAO->decentralized public by anyone->truly decentralized

Repution system is not implemented as secure,safe reputation system on chain is not proposed yet,all current iterations of reputation system on chain are exploitable,defining it into a standard will be like making a bug a standard.

off chain tokens can be created thanks to create2 but it would be better to use it in universal login. so a proxy identity contract will be created for a user to which owner will issue a badge or non transferable token and it will be controlled by set of allowed keys.

I disagree with @Arachnid that Non transfer function of token will discourage people from switching to more secure wallet as a solution like universal login can be used and so there will be no need to switch wallets as token will be issued to secure smart contract wallet controlled by keys selected by user.

Applications

1.can be used to issue badges to identity 2.can be used for voting tokens as votes between user cannot be transferred among other user but sent to contract for voting. 3.to give Credentials to identities like driver license or educational certificate from academic institutions,identity holding them won't be able to transfer their credentials like license or certificates to others. 4.still to be discovered.....

->code starts here


// ERC Token Standard #20 Interface extended to Non transferable token(Badge) standard
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
// ----------------------------------------------------------------------------
/*
contract ERC20Interface {
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function freezeAccount(address target,bool freeze) public returns (bool success);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
    event NonTransferable(address target, bool frozen);

*/

pragma solidity ^0.5.0;

// ----------------------------------------------------------------------------
// 'FIXED' 'Example Fixed Supply Non Transferable Token' token contract
//
// Symbol      : FIXED
// Name        : Example Fixed Supply Non Transferable Token
// Total supply: 1,000,000.000000000000000000
// Decimals    : 18
//
// Enjoy.
//
// ERC-20 standard taken from (c) BokkyPooBah / Bok Consulting Pty Ltd 2018. The MIT Licence.
//thanks for your contribution to community
//extended for non transferable token by Snaketh4xor
// ----------------------------------------------------------------------------

// ----------------------------------------------------------------------------
// Safe maths
// ----------------------------------------------------------------------------
library SafeMath {
    function add(uint a, uint b) internal pure returns (uint c) {
        c = a + b;
        require(c >= a);
    }
    function sub(uint a, uint b) internal pure returns (uint c) {
        require(b <= a);
        c = a - b;
    }
    function mul(uint a, uint b) internal pure returns (uint c) {
        c = a * b;
        require(a == 0 || c / a == b);
    }
    function div(uint a, uint b) internal pure returns (uint c) {
        require(b > 0);
        c = a / b;
    }
}

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
// ----------------------------------------------------------------------------
contract ERC20Interface {
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

// ----------------------------------------------------------------------------
// Contract function to receive approval and execute function in one call
//
// Borrowed from MiniMeToken
// ----------------------------------------------------------------------------
contract ApproveAndCallFallBack {
    function receiveApproval(address from, uint256 tokens, address token, bytes memory data) public;
}

// ----------------------------------------------------------------------------
// Owned contract
// ----------------------------------------------------------------------------
contract Owned {
    address public owner;
    address public newOwner;

    event OwnershipTransferred(address indexed _from, address indexed _to);

    constructor() public {
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function transferOwnership(address _newOwner) public onlyOwner {
        newOwner = _newOwner;
    }
    function acceptOwnership() public {
        require(msg.sender == newOwner);
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
        newOwner = address(0);
    }
}

// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and a
// fixed supply extended to Non transferable token 
// ----------------------------------------------------------------------------
contract FixedSupplyToken is ERC20Interface, Owned {
    using SafeMath for uint;

    string public symbol;
    string public  name;
    uint8 public decimals;
    uint _totalSupply;

    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;
    mapping(address => bool) public frozenAccount;

    event NonTransferable(address target, bool frozen);

    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    constructor() public {
        symbol = "FIXED";
        name = "Example Fixed Supply Token";
        decimals = 18;
        _totalSupply = 1000000 * 10**uint(decimals);
        balances[owner] = _totalSupply;
        emit Transfer(address(0), owner, _totalSupply);
    }

    // ------------------------------------------------------------------------
    // Total supply
    // ------------------------------------------------------------------------
    function totalSupply() public view returns (uint) {
        return _totalSupply.sub(balances[address(0)]);
    }

    // ------------------------------------------------------------------------
    // Get the token balance for account `tokenOwner`
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public view returns (uint balance) {
        return balances[tokenOwner];
    }

    //stops transfer of tokens for identity.
    function freezeAccount(address target, bool freeze) onlyOwner public returns (bool success) {
        frozenAccount[target] = freeze;
        emit NonTransferable(target, freeze);
        return true;
    }
    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to `to` account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) onlyOwner public returns (bool success) {
        require(!frozenAccount[to]);
        require(!frozenAccount[msg.sender]);
        balances[msg.sender] = balances[msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);
        freezeAccount(to, true);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Token owner can approve for `spender` to transferFrom(...) `tokens`
    // from the token owner's account
    //
    // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
    // recommends that there are no checks for the approval double-spend attack
    // as this should be implemented in user interfaces
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) onlyOwner public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Transfer `tokens` from the `from` account to the `to` account
    //
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the `from` account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) onlyOwner public returns (bool success) {
        require(!frozenAccount[to]);
        require(!frozenAccount[from]);
        balances[from] = balances[from].sub(tokens);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);
        freezeAccount(to, true);
        emit Transfer(from, to, tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public view returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }

    // ------------------------------------------------------------------------
    // Token owner can approve for `spender` to transferFrom(...) `tokens`
    // from the token owner's account. The `spender` contract function
    // `receiveApproval(...)` is then executed
    // ------------------------------------------------------------------------
    function approveAndCall(address spender, uint tokens, bytes memory data) onlyOwner public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, address(this), data);
        return true;
    }

    // ------------------------------------------------------------------------
    // Don't accept ETH
    // ------------------------------------------------------------------------
    function () external payable {
        revert();
    }

    // ------------------------------------------------------------------------
    // Owner can transfer out any accidentally sent ERC20 tokens
    // ------------------------------------------------------------------------
    function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyOwner returns (bool success) {
        return ERC20Interface(tokenAddress).transfer(owner, tokens);
    }
}
winnal commented 5 years ago

I was thinking of a use-case for a non-transferable token for the purposes of a founders token, where tokens are bound to the user account, and the user can use the tokens for voting, private access to the platform (like beta testing) and spending/burning to redeem a benefit, but cannot trade or transfer the token, which would make a founders token sale (FTO) exempt from securities regulation as the token cannot be traded, yet would enable there to be a development fund through a token sale.

The concept comes from many traditional industries that sell a product before it’s been developed, like a founder’s or limited edition and preorder. They are able to do this because these sales cannot be transferred and is essentially a purchase rather than an investment. And in the case of non-transferable tokens with utility, you can effectively conduct a sale of tokens as an exclusive feature or benefits that cannot be traded therefore not considered an investment.

Creating such a token will have tremendous potential for ICOs (designated as a FTO) to be conducted for development funding that won’t be deemed a security. It could completely change the way startups get seed funding without the limitations of an investment deal.

xuhcc commented 4 years ago

I think that it is sufficient to provide a single method that returns boolean value indicating transferrability of a token. All other details should be left to implementers. Such interface will be compatible with ERC20, ERC721, ERC1155 and other token standards.

Here's an example:

contract ERC1238 is ERC165 {

    bool internal _transfersEnabled;

    /*
     *     bytes4(keccak256('transfersEnabled()')) == 0xbef97c87
     */
    bytes4 private constant _INTERFACE_ID_ERC1238 = 0xbef97c87;

    constructor () public {
        _registerInterface(_INTERFACE_ID_ERC1238);
    }

    function transfersEnabled() external view returns (bool) {
        return _transfersEnabled;
    }
}

The name trasfersEnabled is borrowed from MiniMe token contract. It is also used by Aragon to designate non-transerrable reputation and membership tokens in DAOs.

Transferrability of ERC1238 token can change depending on some condition, so it allows complex logic like the "all or none" scheme described earlier in this thread.

rosspeili commented 4 years ago

I am not sure how viable this contract can be, but having in mind potential future use-cases, I assume it should be abuse-proof. For example, We create a biometric web3 ID by completing a DNA sequencing, we upload this data to a bioinformatics related blockchain like Zenome, ARNA, or Encrypgen. The respective DLT platform could mint NTTs based on a KYC procedure that includes the genomic data. This NTT could be used as a web3 ID, and it can also be used to retrieve a wallet in case of loss of all recovery methods including private key and seed phrase. Eg. I lost my web3 wallet that has a biometric NTT, I perform a new test or request my test from the lab, or blockchain that created the initial contract using real-time KYC. Proving our new or retrieved DNA test, we could retrieve our wallet, again, considering the ERC is secure and truly nontransferable. That for, if once I mint my own biometric ID and manage to transfer it to your wallet, I could easily create a second test to claim access to your wallet that will basically indicate it bears my DNA. NTT must be obsolete. It's nice to have NTTs with expiration data, but biometric IDs could be only one time mint, one time transfer, nonfungible, and not transferable tokens. That's one of the most exciting ERCs and its a shame DeFi dudes are not paying attention.

Genobank commented 4 years ago

@rosspeili please review our peer reviewed article. Happy to collaborate.

"Privacy Laws, Genomic data and Non-fungible-tokens" https://jbba.scholasticahq.com/article/13164.pdf

Genobank commented 4 years ago

@rosspeili please review our peer reviewed article. Happy to collaborate.

"Privacy Laws, Genomic data and Non-fungible-tokens" https://jbba.scholasticahq.com/article/13164.pdf

fulldecent commented 3 years ago

If anybody is interested in an implementation of the concepts discussed in this thread, please see our publication:

https://genobank.io/biosample-permission-token-with-non-fungible-tokens


Here we are creating tokens that allow the creator to edit them at any time.

All tokens are created as ERC-721s and are namespaced to the account owner. i.e. bits 1...160 are the address of msg.sender and the remaining 96 bits are chosen by the token creator.

Effectively this allows anybody to create badges/tokens and they are not transferred.


And in other words, it is creating a public data structure:

mapping (OWNER address => mapping (uint96 => (TOKEN METADATA, ADDRESS)))

ra-phael commented 3 years ago

Partly inspired by this issue, @arcalinea and I are working on a reputation service that uses non-transferrable, non-fungible tokens as on-chain proofs of reputation. The token, which we call a “badge”, gets issued to an address that a user wants to link to a centralized service (i.e. Twitter, Github), through our third-party reputation verification service.

We are still playing with this concept, and have created a contract issuing these badges to the Kovan testnet. Any feedback is welcome!

At first, we drafted a version that is ERC-721 compliant, but that adds unnecessary overhead to the simple concept of a badge, which does not need approve and transfer functions, or a tokenURI field with associated metadata. The badge contract we have deployed has different requirements. These are the basic things we believe a badge contract implementation needs to have:

If this type of token becomes useful for applications, it would be great to standardize non-transferrable, non-fungible tokens.

Some contract code is below. The version below does not have a token struct with id and timestamp - doing so makes querying for the time the badge was created easier, but costs more in gas. An alternate version with timestamps can be found here.

Version without timestamp:

interface IBadge {
    // @dev Emitted when `tokenId` token is minted to `to`, an address.
    event Minted(
        address indexed to,
        bytes32 indexed tokenId,
        uint256 timestamp
    );

    // @dev Emitted when `tokenId` token is burned.
    event Burned(
        address indexed owner,
        bytes32 indexed tokenId,
        uint256 timestamp
    );

    // @dev Returns the badge's name
    function name() external view returns (string memory);

    // @dev Returns the badge's symbol.
    function symbol() external view returns (string memory);

    // @dev Returns the ID of the token owned by `owner`, if it owns one, and 0 otherwise
    function tokenOf(address owner) external view returns (bytes32);

    // @dev Returns the owner of the `tokenId` token.
    function ownerOf(bytes32 tokenId) external view returns (address);
}

contract Badge is IBadge {
    // Badge's name
    string private _name;

    // Badge's symbol
    string private _symbol;

    // Mapping from token ID to owner's address
    mapping(bytes32 => address) private _owners;

    // Mapping from owner's address to token ID
    mapping(address => bytes32) private _tokens;

    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    // Returns the badge's name
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    // Returns the badge's symbol
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    // Returns the token ID owned by `owner`, if it exists, and 0 otherwise
    function tokenOf(address owner)
        public
        view
        virtual
        override
        returns (bytes32)
    {
        require(owner != address(0), "Invalid owner at zero address");

        return _tokens[owner];
    }

    // Returns the owner of a given token ID, reverts if the token does not exist
    function ownerOf(bytes32 tokenId)
        public
        view
        virtual
        override
        returns (address)
    {
        require(tokenId != 0, "Invalid tokenId value");

        address owner = _owners[tokenId];

        require(owner != address(0), "Invalid owner at zero address");

        return owner;
    }

    // Checks if a token ID exists
    function _exists(bytes32 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    // @dev Mints `tokenId` and transfers it to `to`.
    function _mint(address to, bytes32 tokenId) internal virtual {
        require(to != address(0), "Invalid owner at zero address");
        require(tokenId != 0, "Token ID cannot be zero");
        require(!_exists(tokenId), "Token already minted");
        require(tokenOf(to) == 0, "Owner already has a token");

        _tokens[to] = tokenId;
        _owners[tokenId] = to;

        emit Minted(to, tokenId, block.timestamp);
    }

    // @dev Burns `tokenId`.
    function _burn(bytes32 tokenId) internal virtual {
        address owner = Badge.ownerOf(tokenId);

        delete _tokens[owner];
        delete _owners[tokenId];

        emit Burned(owner, tokenId, block.timestamp);
    }
}
cryptoregtech commented 3 years ago

It is standard still live?

htadashi commented 3 years ago

With the recent NFT boom, I think this standard could make a comeback. Besides @ra-phael use case, here is another recent example:

https://blog.questbook.app/2021/05/29/introducing-the-decentralized-learning-economy-whitepaper-draft/#:~:text=We%20introduce%20a%20new%20class%20of%20NFT%20called%20NFT-Ls.%20These%20are%20Non%20Fungible%20Tokens%20of%20Learning.%20These%20differ%20from%20the%20standard%20NFTs%20in%20the%20sense%20that%20they%20are%20non%20tradable.%20Once%20you%20earned%20it%2C%20it%20is%20yours%20forever.

mark-smartseal commented 2 years ago

I like the idea of badges, but I think it's a mistake to tie them to a single address. Over time people will have multiple accounts, and making them non-transferrable will discourage people from switching to a more secure account (eg, from a software wallet to a hardware one).

I think they should be tied to a single address. The actions of each address represent a persona which is a subset of a person's identity. Whatever actions you decide to do with that wallet (buying, selling, borrowing, etc) can be rated by another wallet. So if you build a persona with a single wallet, you can now use the social credit that has been issued to this wallet through NTTs to do trusted actions like borrowing money or selling physical assets through an online marketplace.

If you switch wallets, you have to build up these NTTs on your new wallet. That's a feature. Banks and governments can issue NTTs against your wallet that represents your identity. So you still may be able to prove that these badges belong to you as long as a trusted third-party determines that both wallets belong to you.

sandys commented 2 years ago

Goldfinch is already doing this. It has issued non-transferable NFT via off-chain verification.

https://medium.com/goldfinch-fi/introducing-unique-identity-uid-the-first-nft-for-identity-830a89207509

vongohren commented 2 years ago
Screenshot 2021-11-10 at 13 40 25

This was the feedback on such an ID as goldfinch, from the SSI community, who do identity thourougly and using blockchain in a feasable manner

mark-smartseal commented 2 years ago
Screenshot 2021-11-10 at 13 40 25

This was the feedback on such an ID as goldfinch, from the SSI community, who do identity thourougly and using blockchain in a feasable manner

Would love to learn more about their objections. Seems to me that what Goldfinch is doing really works and isn't crazy as long as the tokens are filtered correctly on the application layer and some kind of standard metadata format is used. How does the SSI community do KYC/identity, reputation, credit, credentials, badges, etc.. for the persona associated with a wallet?

vongohren commented 2 years ago

I have sent you a message, but for the rest of the thread. Seek info here: https://identity.foundation/. That will lead you to: https://www.w3.org/TR/did-core/ && https://www.w3.org/TR/vc-data-model/

VC datamodel is credentials with a signature. That again can be reputation, credit, credentials, bagdes and so on. It can also be KYC credentials. Its just a standardized credential data model that is being embraced by some of the biggest players such as Microsoft, Square and so on. It is a way to let data flow on the internet without third party intermdiarries, and the DID is what allows for cryptography to work efficiently.

mark-smartseal commented 2 years ago

I have sent you a message, but for the rest of the thread. Seek info here: https://identity.foundation/. That will lead you to: https://www.w3.org/TR/did-core/ && https://www.w3.org/TR/vc-data-model/

VC datamodel is credentials with a signature. That again can be reputation, credit, credentials, bagdes and so on. It can also be KYC credentials. Its just a standardized credential data model that is being embraced by some of the biggest players such as Microsoft, Square and so on. It is a way to let data flow on the internet without third party intermdiarries, and the DID is what allows for cryptography to work efficiently.

Makes a lot of sense. A VC is a clean, gas-free way to issue a "badge". However, the revocation of that credential isn't really possible unless another separate signed message supersedes and "revokes" the previous one... which can be awkward to implement in some applications. The main benefit of issuing a VC as a token is that the VC is stored on-chain and it's easy to revoke. Revoking is important when a wallet becomes compromised. The benefit of keeping it on-chain and connected to a contract is that you don't need identity hubs. If an application is issuing positive and negative VCs against a wallet, it's better to have them connected to a single contract so they can be aggregated to form a score. If you're doing this on an identity hub then you need to trust that the hub is reporting ALL of the VCs (positive and negative) issued against that wallet in order to create an accurate aggregate score.

vongohren commented 2 years ago

A VC is so much more than "bagdes". You have to think broader. A VC is primarily just a datamodel that is cryptographical verifiable and semantically proovable. That is powerful.

Revocation is very much possible with simple mechanisms such as blockchain CLRs. As you can see here: https://www.w3.org/TR/vc-data-model/#example-a-holder-presenting-a-verifiable-credential-that-was-passed-to-it-by-the-subject. If you use CLR(https://en.wikipedia.org/wiki/Certificate_revocation_list) style revocation you also keep privacy intact.

You shall NEVER store credentials on the chain, that is a big privacy breach! The owner of a credential shall have this stored in an encrypted manner and present when needed.

You have to think physical credentials to really grasp the value here. And all the same time keeping it secure and private, and dont allow for correlation by bad actors.

If you get this then you will be seeing the power of the current standards

nikhsha commented 2 years ago

This seems to have broader applicability for use cases such as loyalty cards. The issuer of loyalty points often doesn't want those points to be traded - they want to reward actual loyal customers by incentivising them back to their business rather than by paying them in pseudo-cash. So a balance of loyalty points that stick to a particular wallet and can only be augmented or spent with the issuer's permission seems like a pretty useful addition to the NFTs-as-loyalty-card concepts that are being kicked around.

crjones commented 2 years ago

Interesting post that seems relevant to this conversation:

https://vitalik.ca/general/2022/01/26/soulbound.html

ra-phael commented 2 years ago

From the example use cases presented in the description of this EIP, one thing becomes clear:

Badges can’t be bought, they must be earned.

Badges are markers of recognition and claims about a specific identity. If someone is simply able to buy a badge, the whole reputation system falls apart. One shouldn’t wonder “Has this account really gained all these reputation points / karma from this DAO or they just bought them from members?”. Additionally, if an entity wants to give special access of a service to a list of people (through accounts they control) via tokens, these should not be transferrable; otherwise a completely different set of accounts might get in.

As opposed to standards like ERC721, this standard, which makes transferring tokens from one account to another impossible, would provide a strong signal about badge ownership upon which reputation systems can be built.

One of the main criticisms against badges is the fact that making token transfers impossible is pointless since someone can just sell their account with the badges assigned to it.

It’s still work in progress but we are exploring some proposals for this EIP, starting with a naive implementation heavily inspired from ERC1155 and would love to get some feedback on it.

As mentioned in the requirements, badges can represent a unique item or a quantity, so the idea is to stay agnostic to the fungibility of badges while taking advantage of the same features brought by ERC1155, mainly managing multiple “token collections” by deploying just one contract and batch operations to mint both fungible and non-fungible tokens at once.

Here’s the interface: https://github.com/violetprotocol/ERC1238-token/blob/main/contracts/ERC1238/IERC1238.sol

Here’s an implementation: https://github.com/violetprotocol/ERC1238-token/blob/main/contracts/ERC1238/ERC1238.sol

There’s also an alternative interface and implementation using a struct to represent a tokenCollection which is a set of either fungible or non-fungible tokens: https://github.com/violetprotocol/ERC1238-token/blob/variant-with-struct/contracts/ERC1238/ERC1238.sol

The repository also contains some proposals as extensions for:

For expiration, there can be some logic in the implementer’s smart contract to burn tokens after some time has passed or burning can be externally triggered by the issuer for example.

waterdrops0 commented 2 years ago

https://github.com/curelycue/Nouns-Non-Transferables-Factory https://discourse.nouns.wtf/t/proposal-nouns-non-transferables-factory/551?u=waterdrops

NounsDAO proposal - exploring badge tokens use-cases. ⌐◨-◨

clacladev commented 2 years ago

Maybe I am naive, but in a world where we will have high TPS rollups and high data availability (cheap transactions and storage), we may want to start to store on-chain actions we do on a future web3 social app. So we can create an app with right-to-leave.

For example a tweet or a comment on a piece of content in a web3 social app, should be soul-bound to the author. Why not using a Non Transferrable NFT to represent that? Let's say we have a web3 Instagram. A new photo entry should be represented as an NFT (multimedia on IPFS), while the comments and likes should be represented as Non Transferrable NFT.

This use case has different considerations and requirements compared to a badge or an earned certificate. A comment is not earned, but it's created.

Therefore, should't the Non Transferable NFT standard be a very simple and neutral standard?

jaygopalan commented 2 years ago

Hey @ra-phael, thanks for working on this.

  • This is a valid point but maybe we could keep this standard as much simple as possible and consider this to be outside of its scope. As a result, ERC1238 would provide the guarantee that a badge can’t be transferred from an address to another, but wouldn’t claim to provide a binding between a person/entity and an address. Such continuity could be verified off-chain by services that want to leverage badges.

I think this is actually better. Different off-chain use cases will approach binding between the entity and the address in different ways based on tradeoffs between privacy and security (like @ccarnino said some items should be soulbound some transferrable). But all of those use cases will rely on the basic ERC1238 functionality that badges can't be transferred between addresses.

In most use cases, verifying entities in the real world probably becomes more important as the entity address accumulates more badges but err on privacy when getting started.

  • “Semi-fungible” tokens: Non-transferable NFTs with a shared baseId embedded in their id. For example this lets you create certificates for a course where each certificate has a unique id but one is able to lookup if an account holds a certificate for this course.

What do you see as different use cases for escrow vs burnable approach to staking?

vongohren commented 2 years ago

This seems to have broader applicability for use cases such as loyalty cards. The issuer of loyalty points often doesn't want those points to be traded - they want to reward actual loyal customers by incentivising them back to their business rather than by paying them in pseudo-cash. So a balance of loyalty points that stick to a particular wallet and can only be augmented or spent with the issuer's permission seems like a pretty useful addition to the NFTs-as-loyalty-card concepts that are being kicked around.

How about Drivers License that is important to keep private, prescriptions from the doctor, account verification from the bank and so on.

Il stop pushing this VC mindset here because it seems that this thread wants to solve this with the possibility of on chain reputability, while I believe that too much on chain data can harm us in a bad way.

On chain is a great trust anchor, but need to architect it in a way that it does not leak data

clacladev commented 2 years ago

With this standard I would focus on the core issue of "limiting minting and transferability”. Then private non transferrable NFTs, could be a future standard.

From what I read in this thread, there are different use cases that can be covered:


 
We can call this new types of tokens in a lot of different ways, depending on the different combinations of needs. Let’s call them Badge Tokens for now, so they can be a flexible and composable concept.


A Badge Token standard will have an interface that answers the following questions:


IMO, by having an interface which replies to these questions, we can achieve a lot of different use cases, while being easily understandable.

rosspeili commented 2 years ago

NTTs could be also used as ban cards or restrictive flags. Eg. send an expiring NTT card to an actor with bad behavior in a virtual domain. When he tries to log in next time to the domain, eg. cryptovoxels, he is instantly transferred to the "jail" section, or just sees a black screen for the time he holds this NTT that he also can't get rid of.

Now, a little bit of imagination, and this can get us to a nightmare worst than black mirror and modern china combined, where citizen wallets or web3 IDs would get NTTs that restrict them from enter specific public spaces, museums, universities, or even travel, yet, I don't think there's a way to go around this.

Thoughts? happy to philosophize on future use-cases on tw or dc: @rosspeili @rosspeili#0100

Max-Levitskiy commented 2 years ago

@rosspeili when we develop technologies, we can't create a guarantee it won't be used for some negative stuff. Kitchen knives or sweat floors in the bathroom kill a lot of people. :) If we have some use case, which people need they will implement it. They will write a smart contract, use other blockchain or just create an off-chain solution. So, it's not a philosophical question, but a question about standardization, which is needed for better Ethereum adoption.

ra-phael commented 2 years ago

What do you see as different use cases for escrow vs burnable approach to staking?

Currently in the repo, ERC1238Holdable introduces a distinction between which address owns some tokens and which address holds them (could be a smart contract where there are held in escrow). An address can only burn tokens up to the amount they hold. ERC1238Stakable is a bit more radical as it lets a token owner grant some "burn" allowances to some addresses which then have the ability to give them up. One use case would be taking a loan and using your badge tokens as collateral but there are probably others. I think both approaches could be used. The first one is easier to reason about when burning tokens but it doesn't let an issuer burn/revoke any token that they minted (which can be a requirement in some cases). With the second one the question is what happens to allowances when tokens are burnt? Either they could be decreased proportionally or left untouched.

@vongohren I totally agree with you. Even though diplomas, national ids, etc... are brought up as use cases for badges, I don't view this standard as an invitation to store personally identifiable information publicly on-chain and strongly advise against it, because there's no way to undo it. VCs have this advantage that they can be stored privately but are more difficult to use in smart contracts than on-chain tokens.

A Badge Token standard will have an interface that answers the following questions:


  • can it only be minted by the authority (contract owner)?

@ccarnino I would leave that out from the standard; just like how ERC20 or ERC721 implementations provide internal functions and let developers define their own authorization logic for minting (could be the contract owner, a DAO, a multi-sig, a pre-approved list of addresses...).

  • NFTs transferrable only to other wallets I own

How would you make sure they're owned by the same person? I think it's simpler to not allow transfers between addresses altogether and let issuers burn + re-issue to another address if need be.

nicola commented 2 years ago

Leaving this as a note based on conversations that I had in the past few days.

Things that need to be addressed:

zhongeric commented 2 years ago

In practice, I think that buying an externally owner account for its badges is not that attractive. In the case of a Web 2 account being sold, the first thing the buyer does is changing the email and password associated to it. However the private key associated with an EOA can’t be changed obviously.

@ra-phael This parallel you drew to web 2 was really great, and I think we should expand on this further. I also do not think NTTs should offer any guarantee of user-address matching, but rather leave it up to services to corroborate them with their own off-chain data to verify a user.

On the topic of transferability (especially between wallets of the same user), looking to web2 implementations of this might be our best bet. For example, if Alice completes an IT certification course under the email A, for short, a certificate is now tied to that email address in the issuer's database. Now, if Alice decides to switch to a more secure email, B, she would have a few options for getting her hard earned certificate to be owned by her new account:

  1. Request a new certificate from the issuer, proving she previously received it using her account under A. This would lead to a burning of the certificate on account A. The issuer can use any off-chain PII they collected to verify that Alice is the owner of both accounts (kyc, etc.)
  2. Stop using her old account under A and redo the course to earn another certificate under B.

Alice expects that she will be able to transfer her previous certificates to her new account, but understands that she has to prove her identity beforehand to the issuer. If she doesn't want to, option 2 is less ideal but works. How can we correlate this with NTTs?

A proposal that implements the following would be interesting to explore:

Assuming that "account flipping" is a non-issue due to the immutability of private keys, users can still transfer NTTs between their accounts by verifying ownership with its issuer. Issuers are also incentivized to prevent against sybil attacks since they could severely weaken the credibility of their issued NTTs (ex. allowing users to print other people's concert tickets). They can also choose to implement varying levels of verification depending on their needs since the NTTs do not establish any standard (ownership can mean having control of the original email, or it can use Government IDs). Users can choose to share as much as they want with issuers, and they can remove unwanted NTTs from their account (malicious airdrops, attempts to dox, etc.).

Curious to hear thoughts!

csuwildcat commented 2 years ago

Are folks here aware of Decentralized Identifiers and Verifiable Credentials? I ask because those are existing international standards that already provide this functionality. You can use these tools today via countless implementations and libraries available in all major languages across all platforms. The neat part is they are faster, cheaper, and more efficient for these use cases.

vongohren commented 2 years ago

@csuwildcat thank for stepping in and showing support for that, I have been linking to that world pretty heavily in my comments above, but its difficult to look out of the ethereum world

wwwmaster1 commented 2 years ago

There is some great thought here, and in the DID/VC world that can be leveraged as @vongohren has been saying, but I believe there is an assumption that credentials must come from an authority - but that's not how it works IRL. Credentials come from individuals (I tell you I went to "Harvard") but the verification comes from an authority (Harvard's Records). There is obviously incentive for me to lie and there may be no cost to do so, but anyone should be able to validate it with some effort (a bit of gas), and it may not be worth ruining my reputation to do so.

The problem I see with DID and SSI solutions these days is that there is always some third party that runs the show, or requires a special wallet, so it's not really decentralized and trustless. Why can't I create my own identity credentials (true or imaginary) and store them (or identifiers to IPFS files) in my metamask wallet, and allow them to be verified (or not) by some authority (like SSL). In fact, some of that data may even be encrypted (private medical records), and require the certification from authority in order to be decrypted.