Let’s create some tokens

Sneha Damle
Corda
Published in
10 min readAug 5, 2019

Just read about the second Libra hearing with the US Congress. As fascinating as it sounds the detail is more complicated. While Libra has the privilege of leveraging Facebook’s large user base, people will need a way to invest in various assets. The community of people using cryptocurrencies will expand quickly as Libra is launched, and this market also needs a solution for representing real-world, non-currency assets.

The Corda TokenSDK can help create those solutions with digital tokenization!

Let’s get to the basics of Corda s Token SDK.

The pains of buying or selling real estate are well-known. Limited accessibility to sources, non-transparency, crooked brokers. It’s a tough task.

However, the Corda TokenSDK allows for the tokenization of assets like property, making it easier to buy or sell property. Let’s examine an example.

Tokens in Corda

The underlying “thing” can either be fixed (which will be unique and not change with time), or it can be evolvable. It can exist off ledger (something more physical like house, commodities, cryptocurrencies like BTC) or it can exist on ledger , where the underlying thing is issued on ledger (equity issued directly on ledger).

Corda’s TokenSDK model is flexible enough to accommodate any type of “underlying thing” in question and helps tokenize it easily. It has a set of interfaces/ flows/ abstract classes, analogous to ERC 20 / ERC 721 standards for fungible and non-fungible tokens.

Everything in Corda is an agreement. Tokens represent an agreement between an issuer and an owner, it's an asset from the holder’s perspective and liability from the issuer’s perspective. Its confers rights to the holder to claim the underlying asset against the issuer. An exception to this could be a cryptocurrency with anonymous miners. Hence it is very important to fully understand the classification of the “underlying thing” under consideration before moving onto tokenization.

Let's first start by defining our house type and then we will move on to creating tokens.

Define the token type, in our case the type will be House

The valuation of my property will certainly change, in fact, it should and I should get a better price after a few years.

I will define a HouseTokenType, and since my house valuation can change, I am going to extend the EvolvableTokenType state which is a LinearState.

Representing House type as a Linear State

It makes sense for my house evolvable token type to implement Linear State so I can track changes like valuation to my house via the linear id.

How will I get notified if someone tries to change the price of my house?

It also makes sense for me to add myself as a maintainer so that I should be notified of the updates to my house state on the ledger. By default, the participant list in the state is the maintainers list specified in the EvolvableTokenType.

I can create this house on the ledger or ask some custodian/reference data maintainer to do so. The issuer is not the owner in all cases. I will define my house rules in HouseTokenTypeContract which is a typical Corda Contract.

The issuer might not be in the list of participants every time, there could be scenarios when the issuer will just issue the tokens and is not interested/or should not be allowed to see the state updates.

Note: The HouseTokenType is just a type and has nothing to do with the amount of tokens.

@BelongsToContract(HouseTokenTypeContract.class)
public class HouseTokenType extends EvolvableTokenType {

private final BigDecimal valuation;// this is expected to change
private final Party issuer;// I will add myself to this
private final Party owner;// As of I will be the owner
private final UniqueIdentifier linearId;// this will be my unique id representing my house
private final int fractionDigits;//The number of fractional digits allowable for this token type. I am going to set this to zero allowing whole integer amount.

Create House token type on the ledger

Once I have defined my house token type I can call CreateEvolvableTokens which is a built-in flow which can be called via RPC or while unit testing.

HouseTokenType houseType = new HouseTokenType (valuation, getOurIdentity(), new UniqueIdentifier(), 0);

I will create my house token type instance and wrap it in a transaction state specifying the notary.

TransactionState transactionState = new TransactionState (houseType, notary);

Next, it must be passed to the CreateEvolvableTokens flow.

subFlow(new CreateEvolvableTokens(transactionState));

Here’s the complete code block:

@Override
@Suspendable
public SignedTransaction call() throws FlowException {
Party notary = getServiceHub(). getNetworkMapCache(). getNotaryIdentities(). get(0);

HouseTokenType houseType = new HouseTokenType (valuation, getOurIdentity(), new UniqueIdentifier(), 0);

TransactionState transactionState = new TransactionState (houseType, notary);

return (SignedTransaction) subFlow(new CreateEvolvableTokens(transactionState));
}

To sum it up, CreateEvolvableToken flow takes our house token type, establishes sessions with counter-parties mentioned in the participants list, signs the transaction, collects signatures from all the participants, gets it signed by the notary and finally commits our house state type on the ledger. As we have our token type ready we can now create some tokens representing our house.

Create tokens based off my house token type on the ledger

Fungible tokens will help me create more than one token. Each token represents a specific value. I can then embed my house type state class into my fungible token. It helps establish the fact that the token is being created for house type.

Fungible Tokens helps me divide my house into multiple tokens, each token representing a specific value. Many investors can then invest in these individual tokens of smaller amounts. This will raise the liquidity level of my house. I can also use non-fungible token which will be a single unique token to represent my house. I can then embed my house state into either fungible or non-fungible tokens.

But what if I embed my house state in a token and my house valuation changes?

My tokens will still refer to the old valuation and I might get less price at the time of redemption. I definitely don’t want this.

TokenPointer helps to solve this problem. Instead of embedding the state I will embed the pointer instead, which will always point to the latest version of my house. If there is a fixed asset which is not evolvable I wouldn’t mind embedding it directly into my token, as its value is not going to change for its lifetime.

Grab the created house on the ledger by querying the vault, and call the toPointer method to get the pointer pointing to it.

TokenPointer tokenPointer = tokenType.toPointer(tokenType.getClass());

Token Pointer is a type of token, and serves as a proxy to the EvolvableTokenType.

As discussed above, a token is always an asset for the current holder and a liability of the issuer. It is an agreement between the holder and the original issuer. So we need an issuer field in every token definition.

IssuedTokenType issuedTokenType = new IssuedTokenType(getOurIdentity(), tokenPointer);

Fungible and non-fungible tokens ultimately represent a contract state with the participant list being that of the holder, which is the current default behaviour. This behaviour can be overridden if you want to add any auditor/observer to the list.

Creating Non-Fungible Tokens

Let's take the created issuedTokenType which specifies our house and issuer. Let's also grab the current holder whom the token should be assigned after creating.

NonFungibleToken nonFungibleToken = new NonFungibleToken (issuedTokenType, holder, new UniqueIdentifier(),null);

Create Fungible Tokens

To create a fungible token I will also have to specify how many tokens should be created of house type. I will just wrap the issuedTokenType into an Amount object specifying the quantity and nominal display unit size of a single token.

Amount<IssuedTokenType> amount = new Amount(100L, BigDecimal.ZERO, issuedTokenType);FungibleToken fungibleToken  = new FungibleToken(amount, holder, null);

Fungible tokens can be split and merged. This doesn't mean if my neighbour also issues a house using my HouseTokenType class, fungible tokens representing his house and my house can be merged. The credit risk associated with his house is completely different than mine and hence our house values are also different. So unless the issuer is the same we cannot merge the tokens of a particular token type.

Once I have my fungible or non-fungible tokens I can call the built-in IssueTokens flow to issue the tokens onto the ledger.

Here’s the complete code:

@Override
@Suspendable
public SignedTransaction call() throws FlowException {
//using id of my house to grab the house from db.
// you can use any custom criteria depending on your requirements
UUID uuid = UUID.fromString(id);
//construct the query criteria using uuid
QueryCriteria queryCriteria = new QueryCriteria. LinearStateQueryCriteria(null, ImmutableList.of(uuid), null, Vault.StateStatus.UNCONSUMED, null);
// grab the house off the ledger
StateAndRef<HouseTokenType> stateAndRef = getServiceHub().getVaultService().queryBy (HouseTokenType.class, queryCriteria).getStates().get(0);

HouseTokenType tokenType= stateAndRef.getState() .getData();
//get the pointer pointer to the house
TokenPointer tokenPointer = tokenType.toPointer (tokenType.getClass());
//assign the issuer to the house type who will be issuing the tokens
IssuedTokenType issuedTokenType = new IssuedTokenType(getOurIdentity(), tokenPointer);
//mention the current holder also
NonFungibleToken token = new NonFungibleToken(issuedTokenType, holder, new UniqueIdentifier(),null);
//call the built in IssueTokens flow to issue tokens on the ledger
return (SignedTransaction) subFlow(new IssueTokens(ImmutableList.of(token)));
}

To sum it up, I retrieved the created house type from the vault, got a pointer to it, added the issuer, holder and called the built-in IssueTokens flow which initiates sessions with counterparties, signs the transaction, collects signatures from all the counterparties, gets the signature from the notary and finally calls FinalityFlow to commit the tokens on the ledger.

Assign/Move house token to another party

I can now move the created tokens to another party by calling the built-in MoveNonFungibleTokens Flow/MoveFungibleTokensFlow by specifying the party who will now be the current holder of the token.

PartyAndToken partyAndToken = new PartyAndToken(recipient, token);
return (SignedTransaction) subFlow(new MoveNonFungibleTokens(partyAndToken))

You can only move the tokens if you are the current holder of the tokens. In the case of fungible tokens, Corda uses the Token Selection Algorithm which is similar to the Coin Selection Algorithm from the finance module. The default behaviour is to query all the owned tokens from the vault, order them by StateRef and then query for a required token amount. This is not the best and most efficient approach and will be modified eventually by in-memory token selection. Of course, this can be customised by passing in a custom query.

To sum it up, the move tokens flow establishes sessions with the participants, uses token selection to retrieve available tokens (only RELEVANT_TOKENS are used see below for relevancy status), initiator signs the transaction, collects signatures from all participants (from relevant parties), gets the signature from notary and then commits the transaction onto the ledger.

Redeem Token

Let’s say you want to get out of the deal. You redeem your investment, possibly with some additional interest and transfer your stake/ownership token in the house to the original issuer. The issuer can then mark the token as consumed.

I can use the RedeemFungibleTokens/RedeemNonFungibleTokens to redeem the tokens by specifying the issuer.

subFlow(new RedeemNonFungibleTokens(token, issuer));

To sum it up, the redeem flow after establishing sessions with the participants uses token selection to get the spendable relevant tokens, signs the transaction, collects the signatures from all participants, gets the signature from the notary, and eventually calls the finality flow which destroys (exit of token) the tokens off the ledger.

Note: In the token-sdk we always assume that issuers will be responsible for redemption for all the available token classes. The exception to this are token classes based on native cryptocurrencies created on Corda, which cannot be redeemed.

Points to note

  1. Token-sdk is released as a part of corda version 4. Token sdk is a library which can be imported into your cordapp. It contains 3 modules : token-contracts , token-flows , tokens-money
  2. Referring to the underlying “thing” as asset sounds misleading as the thing can be an asset for one Party and can be a liability for another Party. As such, there is an assumption that all fungible things are ownable. This is not always true. Hence the token-sdk doesn’t use FungibleAsset and OwnableState and to add more flexibility, FungibleState , a new state has been added.
  3. As mentioned in the corda white paper, data distribution groups/clubs are currently planned for future releases. Once they are available then reference state owners will be able to distribute updates to subscribers more easily(eg few properties of the house change how do we rely upon this information to the token holders)
  4. By default till corda version 3, the node stored only the “relevant” states in its vault. Corda 4 introduced the concept of relevancy status. Token Pointer maintains a Linear Pointer to the asset type. When a transaction is constructed in Corda 4, transaction builder automatically adds the state pointed by the linear pointer as a reference state. This helps to establish a many to one relationship and many states can refer to a single state. This state now will also get added to your state irrespective of you being a participant in the reference state or not. If you are not on the reference states participant list, this state is irrelevant for you, and you should not be able to spend it. Hence now you can have many irrelevant states also in your vault. Token selection algorithm only makes use of relevant states to get the available tokens for spending. So it always makes sense to keep the relevancy status in mind going forward. (When migrating from Corda 3 to 4 there are additional steps required to handle this column.)

We can say the state is “relevant” when

The state is Ownable and the node is the owner of the state

The participants list contains a public key which is known to the node. i.e. it is the nodes current public key or is one of the public key generated previously as a part of public-private key pair.

Links

Full Code is available here

Token SDK on Github here

Todd McDonald s blog about the Emergence of Enterprise tokens

Cais Manai explaining Tokenized art.

Thanks to Roger Willis and The Corda Team

Thanks for reading — Sneha Damle, Developer Evangelist (R3).

--

--