forked from DecentralizedClimateFoundation/DCIPs
98 lines
5.2 KiB
Markdown
98 lines
5.2 KiB
Markdown
|
---
|
||
|
eip: 5114
|
||
|
title: Soulbound Badge
|
||
|
description: A token that is attached to a "soul" at mint time and cannot be transferred after that.
|
||
|
author: Micah Zoltu (@MicahZoltu)
|
||
|
discussions-to: https://ethereum-magicians.org/t/eip-5114-soulbound-token/9417
|
||
|
status: Review
|
||
|
type: Standards Track
|
||
|
category: ERC
|
||
|
created: 2022-05-30
|
||
|
---
|
||
|
|
||
|
|
||
|
## Abstract
|
||
|
|
||
|
A soulbound badge is a token that, when minted, is bound to another Non-Fungible Token (NFT), and cannot be transferred/moved after that.
|
||
|
|
||
|
|
||
|
## Specification
|
||
|
|
||
|
```solidity
|
||
|
interface IERC5114 {
|
||
|
// fired anytime a new instance of this badge is minted
|
||
|
// this event **MUST NOT** be fired twice for the same `badgeId`
|
||
|
event Mint(uint256 indexed badgeId, address indexed nftAddress, uint256 indexed nftTokenId);
|
||
|
|
||
|
// returns the NFT that this badge is bound to.
|
||
|
// this function **MUST** throw if the badge hasn't been minted yet
|
||
|
// this function **MUST** always return the same result every time it is called after it has been minted
|
||
|
// this function **MUST** return the same value as found in the original `Mint` event for the badge
|
||
|
function ownerOf(uint256 badgeId) external view returns (address nftAddress, uint256 nftTokenId);
|
||
|
|
||
|
// returns a URI with details about this badge collection
|
||
|
// the metadata returned by this is merged with the metadata return by `badgeUri(uint256)`
|
||
|
// the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://)
|
||
|
// the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://)
|
||
|
// data from `badgeUri` takes precedence over data returned by this method
|
||
|
// any external links referenced by the content at `collectionUri` also **MUST** follow all of the above rules
|
||
|
function collectionUri() external pure returns (string collectionUri);
|
||
|
|
||
|
// returns a censorship resistant URI with details about this badge instance
|
||
|
// the collectionUri **MUST** be immutable (e.g., ipfs:// and not http://)
|
||
|
// the collectionUri **MUST** be content addressable (e.g., ipfs:// and not http://)
|
||
|
// data from this takes precedence over data returned by `collectionUri`
|
||
|
// any external links referenced by the content at `badgeUri` also **MUST** follow all of the above rules
|
||
|
function badgeUri(uint256 badgeId) external view returns (string badgeUri);
|
||
|
|
||
|
// returns a string that indicates the format of the `badgeUri` and `collectionUri` results (e.g., 'EIP-ABCD' or 'soulbound-schema-version-4')
|
||
|
function metadataFormat() external pure returns (string format);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Implementers of this standard **SHOULD** also depend on a standard for interface detection so callers can easily find out if a given contract implements this interface.
|
||
|
|
||
|
|
||
|
## Rationale
|
||
|
|
||
|
### Immutability
|
||
|
|
||
|
By requiring that badges can never move, we both guarantee non-separability and non-mergeability among collections of soulbound badges that are bound to a single NFT while simultaneously allowing users to aggressively cache results.
|
||
|
|
||
|
### Content Addressable URIs Required
|
||
|
|
||
|
Soulbound badges are meant to be permanent badges/indicators attached to a persona.
|
||
|
This means that not only can the user not transfer ownership, but the minter also cannot withdraw/transfer/change ownership as well.
|
||
|
This includes mutating or removing any remote content as a means of censoring or manipulating specific users.
|
||
|
|
||
|
### No Specification for `badgeUri` Data Format
|
||
|
|
||
|
The format of the data pointed to by `collectionUri()` and `badgeUri(uint256)`, and how to merge them, is intentionally left out of this standard in favor of separate standards that can be iterated on in the future.
|
||
|
The immutability constraints are the only thing defined by this to ensure that the spirit of this badge is maintained, regardless of the specifics of the data format.
|
||
|
The `metadataFormat` function can be used to inform a caller what type/format/version of data they should expect at the URIs, so the caller can parse the data directly without first having to deduce its format via inspection.
|
||
|
|
||
|
|
||
|
## Backwards Compatibility
|
||
|
|
||
|
This is a new token type and is not meant to be backward compatible with any existing tokens other than existing viable souls (any asset that can be identified by `[address,id]`).
|
||
|
|
||
|
|
||
|
## Security Considerations
|
||
|
|
||
|
Users of badges that claim to implement this EIP must be diligent in verifying they actually do.
|
||
|
A badge author can create a badge that, upon initial probing of the API surface, may appear to follow the rules when in reality it doesn't.
|
||
|
For example, the contract could allow transfers via some mechanism and simply not utilize them initially.
|
||
|
|
||
|
It should also be made clear that soulbound badges are not bound to a human, they are bound to a persona.
|
||
|
A persona is any actor (which could be a group of humans) that collects multiple soulbound badges over time to build up a collection of badges.
|
||
|
This persona may transfer to another human, or to another group of humans, and anyone interacting with a persona should not assume that there is a single permanent human behind that persona.
|
||
|
|
||
|
It is possible for a soulbound badge to be bound to another soulbound badge.
|
||
|
In theory, if all badges in the chain are created at the same time they could form a loop.
|
||
|
Software that tries to walk such a chain should take care to have an exit strategy if a loop is detected.
|
||
|
|
||
|
|
||
|
## Copyright
|
||
|
|
||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|