Skip to main content

User Token Partition: Object Oriented Design

Alphabill offers a flexible framework for defining custom token types, creating tokens of these types, and transacting with such tokens.

Fungible and Non-Fungible Tokens

Each token type in Alphabill is designated as either fungible or non-fungible. Fungible tokens and non-fungible tokens (NFTs) are represented differently in the state tree: fungible tokens have an amount (64-bit unsigned integer) and can be split and joined. NFTs, on the other hand, might contain a URI and arbitrary binary data which is possible to update.

The most important property of a fungible token is the amount or value that it represents. A fungible token can be split into several smaller tokens of the same type so that the sum of the values of the resulting tokens is equal to the value of the original token. Conversely, several fungible tokens of one type can be joined into one larger token of the same type so that the value of the resulting single token is equal to the sum of the values of the input tokens. In both cases, the source tokens are consumed in the process (deleted from the state tree), to ensure that neither of these operations create or destroy value and don't consume memory.

Non-fungible tokens, in contrast, have distinct identities and, in general, even two tokens of the same type are not interchangeable, even though they may share some characteristics. While currently the most well-known use of non-fungible tokens is collectible items of digital artwork, we expect this to be a relatively niche application in the longer term and most future use cases to revolve around representing various permissions, rights, and credentials instead.

Predicates

Predicates are functions that return a single TRUE or FALSE. They are used to check if an input meets some condition. For example, isDigit(c) might be a predicate function that returns TRUE if its input character c is a digit, and FALSE otherwise.

Predicates are used in the User Token Partition to enable the customization and programmability of tokens, similar to Bitcoin locking and unlocking scripts. There are many types of predicates used in Alphabill and their use together with object-oriented inheritance enables a rich ecosystem of tokens to be developed.

Owner Predicate

In general, ownership of each token in Alphabill is controlled by an owner predicate. The predicate is a function that receives as inputs a transaction order and the current state of the unit that the transaction targets. The function returns a decision whether the transaction should be accepted or rejected. In most cases, the condition is that the transaction order must have a signature that verifies with a public key embedded in the predicate. If a transaction order is deemed valid by the predicate, it is allowed to execute. Executing a "transfer" transaction means just replacing the current owner predicate with a new one. Often, the new predicate is a similar function but contains a different public key—with the effect that a different private key must be used to sign the next transaction order affecting the token; since the old owner can no longer produce valid transaction orders, this indeed means that the control of the token has been handed over to the new owner.

Simplified example of an owner predicate being updated through a transaction order.

The above figure shows a transaction order (on the left) and the state of a token before and after the transaction order is executed. The validator does the necessary checks (such as making sure the token ID corresponds to an existing token of the correct type for the transaction), then the "proof for old predicate" is computed to make sure it satisfies the current owner predicate. If so, then the owner predicate is updated.

Token Types

Each user-defined token belongs to a token type. The type determines several properties common to all tokens of this type. Token types are also represented as first-class entities in the User Token Partition, that is, each token type is allocated space in the state tree. Anyone can define a token type but unlike regular tokens, they cannot be transferred. Instead, a token type defines several predicates that affect the tokens of this type.

Simplified illustration of a non-fungible token type

Mint Predicate: Enforcing Restrictions on Minting

The mint predicate must be satisfied to create new tokens of this type. A typical example is that the minting order may be required to be signed using a specific private key—which would limit the issuance of new tokens of this type to the owner of the key. This condition is freely programmable and can enforce many other kinds of restrictions. For example, the creator of the token type may require royalties for the use of the type; this can be enforced by having the minting condition check for proof of payment. Another example is limiting minting to a certain set of authorized parties; this can be enforced by having the mint predicate require proof of membership in the corresponding list, which can itself be implemented and managed as a special user-defined token.

Inherited Owner Predicate: Enforcing Restrictions on Transfers

A second predicate in a token type is the inherited owner predicate of all tokens of the type. With this predicate defined, any transfer order with any token of the type will have to satisfy both the owner predicate on the token itself and the inherited owner predicate from the token type.

Inherited owner predicates are immutable and not replaced by transaction orders.

The crucial difference between the two is that the transfer transaction replaces the owner predicate on the token itself, but the inherited owner predicate is immutable and can therefore be used to define restrictions that the owners of the token cannot remove. A very simple example is making tokens non-transferable by setting the inherited owner predicate to a function that always returns false; a possible use for such tokens is representing any nontransferable credentials such as driving licenses or academic degrees. Another possible use case for the inherited owner predicate is implementing mandatory royalty payments to the original author of a digital artwork whenever the work is sold from one owner to the next.

Inherited Subtype Predicate: Enforcing Restrictions through Inheritance

Another predicate in a token type is the inherited subtype predicate. When a subtype is defined, the tokens of the subtype inherit the properties of their parent types along the inheritance chain. As an example, many jurisdictions limit certain financial products to accredited investors only in order to prevent less experienced parties from taking undue risks.

Some bonds fall into this category and (as a rather simplified example) this can be modeled by having a "generic bond" token type from which a "restricted bond" type is derived, where the "generic bond" type defines inherited owner conditions that apply to investors buying any bonds (such as the buyer having passed the KYC checks) and the "restricted bond" type adds the condition that the buyer has to be an accredited investor. Transfers of tokens of the "generic bond" type need to satisfy in addition to the tokens' owner predicate the predicates for transfer of the "generic bond" type. Transfers of the "restricted bond" type need to satisfy the same predicates as the "generic bond" type and in addition the predicates for the "restricted bond" type.

Examples of token type inheritance

In the spirit of object-oriented modeling and design in general software engineering, it is possible to inherit many levels deep and define a rich ontology of token types.

Data Update Predicate: Implementing State Machines

Each non-fungible token has a "data" field. The contents and interpretation of this field are entirely user defined. The data is mutable, subject to satisfying an additional predicate called the data update predicate. This works like the other predicates: a user sends a transaction order which supplies the new data that should replace the old data. Both the old and the new data are supplied as arguments to the predicate and if the predicate returns TRUE, then the token's data is changed by the transaction.

Simplified example of an NFT owner updating the NFT data

While the data is mutable, the data update predicate is not. It is defined when the token is created and cannot be modified after that. Additionally, non-fungible token types can define clauses that are inherited into the data update predicates of all tokens of their types. This allows application developers to implement various kinds of state machines, where the data update predicates specify which state transitions are allowed and which are not, and those rules cannot be overridden by the token owners.

Putting together the features of inheritance and controlled mutability allows developers to create a rich ecosystem of useful tokens that can be used to represent real world processes and use cases. Tokens can be directly used for the exchange of digital rights, goods, and services, and they can be programmed to follow business processes in an automated decentralized manner.