The concept of “soulbound” tokens has been making the rounds, by way of a great article Vitalik wrote recently. This has of course kicked off discussion of implementing the concept as an ERC token smart contract standard on Ethereum. We’ve gotten the question “should soulbound tokens be added to Scrypto?”
Short answer: Already done.
Slightly longer answer: Soulbound tokens using Scrypto were possible even before we had heard of them, because our asset-oriented model makes them just a matter of 1 line of configuration.
And soulbound tokens the asset-oriented way are even more flexible and powerful.
What’s a Soulbound Token?
Vitalik’s article on the subject is well worth a read - it nicely summarizes the concept and why it’s valuable for a number of use cases. But briefly, the idea is that sometimes you want a token to be non-transferable (or transferable only under certain conditions). The term “soulbound” is taken from equipment in World of Warcraft that, once picked up, cannot be given to anybody else, ensuring that only the person that earned it in the first place can own it.
Basically, sometimes you want to issue a token to a particular person and know that it will stay associated with that person, rather than get sent around or sold. As Vitalik points out, this would be great for NFTs that represent items in games, that prove you attended an event, or generally represent some aspect of your identity such as governance/DAO voting tokens.
Vitalik’s article goes on to consider some different ways that you might implement a token smart contract that would restrict the transferability of ownership (and remember that in the EVM model, token “ownership” is a matter of entries in a list managed by the smart contract).
Of course assets using Scrypto on Radix work a different way – they’re features of the platform itself, not smart contracts. So does this mean that soulbound tokens aren’t possible on Radix? Just the opposite.
How do I make a Soulbound Token with Scrypto?
Soulbound tokens immediately feel useful to us because they’re just the digital asset version of a familiar concept in real world assets. We often encounter things like tickets, licenses, passes, certificates, and other things where we understand that it’s only valid for the original recipient. Another way of saying it is that restricted transferability is an inherent intuitive behavior of these kinds of assets.
Definition of intuitive asset behavior is exactly how tokens, NFTs, and any other type of asset is done with Scrypto. With Scrypto, we model all kinds of assets as resources.
Fortunately, there are only a few behaviors that make intuitive sense for anything we would ever want to consider an asset: They can be created and destroyed (according to some rules), they can be moved from place to place (according to some rules), and that’s about it.
We match that behavior with Scrypto resources: they can be minted or burned, they are always stored in vaults (which are always held by components – like a smart contract or an account), and they move between vaults via temporary containers called buckets.
We can describe this set of behaviors as a state machine which you can diagram out like this:
The arrows in the diagram are “state transitions” for a given resource over its lifetime. We can create a huge variety of intuitive “physical” resource behaviors just by defining the rules of how (or if) the resource can take each of those state transitions.
For example, the simplest “asset type” you might imagine is just a pile of rocks. You can’t create or destroy rocks. If you own a rock, you can give it to whoever you want. Nobody can take your rock without your permission. The behavior of rock assets can be described by the rules of resource state transactions: Nobody is allowed to mint or burn it, and only you (the current owner) are allowed to withdraw it from your vault and put it in a bucket to be given to somebody else.
Maybe you see where we’re going now.
With Scrypto, a soulbound token just means setting the state transitions in/out of vaults to be restricted in some way. Maybe taking the resource out of a vault is impossible for anyone to do, or maybe it’s only allowed with approval from some third party person/system/smart contract/DAO.
And Scrypto lets you specify exactly this without writing the logic in custom smart contract code. Instead, you just request a new resource from the platform and specify a few “access control rules” that correspond to our state transitions.
So creating a simple soulbound token with Scrypto just means adding this line when requesting the resource creation:
.restrict_withdraw( rule!(deny_all), LOCKED )
Once the token is put in a vault the first time, the platform guarantees that nobody can ever withdraw it again (and the LOCKED means that nobody can change this restriction later). No need for a special smart contract, no new ERC standard for the community to debate, and no chance of unexpected behavior if your implementation isn’t perfect.
What about more complex soulbound tokens?
If we continue going through Vitalik’s use case examples for soulbound tokens, there are more complex things we might want to do than to totally restrict transfer forever.
What if a person legitimately wants/needs to change accounts? What if the token does need to be transferable, but only when approved by some issuing authority (which is doing some kind of proof of humanity)?
We can think of even more: What if we want a token that starts soulbound, but later can be released if certain conditions are met. What if we want a token that starts freely transferable but becomes locked to an account at some point? What if the token should only be transferable if a fee has been paid?
In the EVM model on Ethereum (and other smart contract platforms), all of these things are possible – but they each require custom implementation of smart contract logic, and standards that have to wind their way through a lengthy and uncertain proposal process.
In the Radix Engine model using Scrypto, all of the above are just simple rules we can set on the token when creating it.
How? We use a powerful concept called badges. A badge is just a resource that is used for authorization. The holder of a badge can produce a proof of that badge and present it in a transaction. And the access control rules on resources (and components!) can check for those proofs. So if we look at this line again…
.restrict_withdraw( rule!(deny_all), LOCKED )
… instead of making the rule “deny_all”, and instead of specifying that rule as LOCKED, we could put in requirements that a certain badge be present to be able to withdraw the tokens, or change the rule.
And those badges can be held by anyone or anything that we want “in the loop” whenever somebody wishes to perform that action. Want a soulbound token that can only be transferred if a DAO has voted to approve it? Just make the rule a single badge and give that badge to the DAO (component) smart contract. Now we can use the transaction manifest to request a proof of the badge from the DAO, and perform the withdrawal and transfer of the soulbound token in a single atomically composed transaction.
We’ve separated the application-specific logic (I want a DAO that has special rules for when it wants to allow transfer of the soulbound token) from the core universal asset behavior (withdrawing this token from a vault needs special approval). This makes the asset-oriented approach easier to use, more predictable in behavior – and even more powerful than the smart contract approach because it’s all still working with a universal resource that everything else on the network already understands, rather than a new smart contract standard.
We love working through use cases like soulbound tokens because it usually just highlights another cool pattern for using Radix’s asset-oriented model.