Soulbound tokens are non-transferable digital assets. The core concept is storing on-chain data directly relating to a specific address. This is nothing new but there is potential for a ERC SBT contract to provide a industry standard for how we manage user data.
Ethereum gas costs make meaningful data storage in relation to users accounts expensive and slow. For developers it’s like using the worlds worse database, well second worse after MySQL perhaps. The impending migration to Ethereum 2.0 over the next year will implement sharding which will open up capacity. This combined with the adoption of layer 2 blockchains such as optimistic and zk rollups means it’s going to become a lot faster and a lot cheaper to use Solidity as a user database.
Soulbound tokens shot to light when Vitalik’s co-authored whitepaper was released at:
The paper doesn’t read like one of Vitalik’s own works (I can understand most of it) and it goes through possible use cases and privacy concerns of SBT’s.
Fundamentally Soulbound tokens are just non-transferable tokens which store data such as balances, urls, hashes, encrypted strings on a public blockchain. Where as an ERC20 or NFT token can be transferred between users SBT’s are static and cannot be moved between accounts.
There is no current ERC standard for SBT’s so this is my interpretation:
The token contract allows the operator to associate an address with a structured data set. It also allows 3rd parties to use the contract to create profile data sets with the same structure. Built on the principle right that users have the ability to delete their data.
function mint(address _soul, Soul memory _soulData) external;
Function called by the operator (contract owner by default) of the contract to mint a new token. The new token associates the Soul data structure with a users wallet address
function burn(address _soul) external;
Allows a user to destroy any data stored about their wallet. Effectively burning their soul, which doesn’t sound great admittedly.
function update(address _soul, Soul memory _soulData) external;
Operator can update the data at any point but does need to provide a complete data set and can’t currently increment a single integer within it for example without additional code.
function hasSoul(address _soul) external view returns (bool);
Checks if a sole has been minted and exists for a specific wallet address. Returns false prior to mint and true after.
function getSoul(address _soul) external view returns (Soul memory);
Public view function to receive all the Soul data for a specific address. No privacy here at all on a public blockchain.
function setProfile(address _soul, Soul memory _soulData) external;
A profile is a 3rd party data structure which is mapped to msg.sender and an address provided. This allows anyone to store data in the same data structure including the individual user.
function getProfile(address _profiler, address _soul) external view returns (Soul memory);
Public view function which returns the soul data for a set user relative to the profiler which is the address that called setProfile.
function listProfiles(address _soul) external view returns (address memory);
We store the profiles set up for each address in an array so they can be burnt later. listProfiles provides this list of data profiles for a user. This would also be useful for things like course levels where a separate profile certificate could be minted for each completed stage of the course.
function hasProfile(address _profiler, address _soul) external view returns (bool);
Returns true or false if a profile exists for a specific address and profiler address.
function removeProfile(address _profiler, address _soul) external;
User has the right to remove any profiles attached to their account.
event Mint(address _soul);
event Burn(address _soul);
event Update(address _soul);
event SetProfile(address _profiler, address _soul);
event RemoveProfile(address _profiler, address _soul);