During my trips to deliver Corda bootcamps, I received a lot of requests for an advanced code sample about TokenSDK, which drove me to create this code sample.
This blog explains my process of creating this sample CorDapp. Although I will not cover all the basic Corda concepts, I will explain some critical lines.
If you haven’t run through the sample yet, feel free to download and run through the suggested example from the Github project here.
The sample has six significant flows. They simulate how a stock is issued, transferred and eventually, how the dividend is paid off.
Going through the flows
1. Issuing a stock
To issue a new token type, I created StockState with the use of CreateEvolvableToken flow. The StockState carries the information about the stock like the stock name, symbol, announcing dividends, etc. You can extend it to add more info such as the price and yearly report.
When a stock is issued in real life, regulators like SEC or FINRA should be notified. In Corda, we treat these regulators as observers which record the change of states without having to sign the transaction. In the following line, observers will record the creation of the StockState.
`subFlow(new CreateEvolvableTokens(stockState, observers));`
In TokenSDK, most of the build-in flows have an observer parameter which developers do not have to take care of themselves, which is very powerful.
There are not many tricks hidden in MoveStock. You can find that moving a token from one party to another is just as easy as with that one line. We may not merely transfer the stock to others but instead exchange for some other assets — let’s say money. We will get back to this later.
subFlow(new MoveFungibleTokens(amount, recipient));
This flow looks straight forward in code, but the underlying concept is a bit complicated.
To announce a dividend, I need to update the dividend and the dates attributes of StockState. Notably, as the nature of EvolvableToken, UpdateEvolvableTokenFlow updates the token independently to the token holders. That implies shareholders will not be aware of dividend announced in AnnounceDividend flow.
Arguably, I could inform the shareholders, just like I did for observers. But if we think about having thousands of shareholders, this could generate heavy traffic to the network at one time. In addition, not every shareholder would like to know about each update.
4. GetStockUpdate is necessary
As a result, I need to write a flow to claim the dividend. However, if I try running ClaimDividendReceivable in the shareholder node after executing AnnounceDividend, by the design of Corda, there is an error about including a historical state. This is because the shareholder is still holding a consumed state and building the transaction with it.
Therefore, I wrote a flow for shareholders to get stock updates. Same as real-life cases, the shareholder originates the update request to the company. Using the Corda built-in flows, SendTransactionFlow and ReceiveTransactionFlow, I can easily exchange transactions between two nodes.
subFlow(new SendTransactionFlow(holderSession, stx));
Stockholder is not a participant!
Beware that the shareholder is not involved in any of these transactions. At first, I was not aware of this and no transactions recorded into the vault. To force the node recording this transaction, we can use the StatesToRecord.ALL_VISIBLE for the stateToRecord parameter. The shareholder will store this change event even if the party has not taken any part in the transaction.
SignedTransaction stx = subFlow(new ReceiveTransactionFlow(session, true, StatesToRecord ALL_VISIBLE));
5. ClaimDividendRecivable — most complicated
The purpose of this flow is to simulate what happens on the ex-dividend date. Imagine on the ex-dividend date, the amount of stock held by shareholders will be recorded such that dividend is issued based on this record.
Therefore, my designed goal of ClaimDividendRecivable flow is to create a Dividend state. The shareholder can later exchange for some asset on the pay date with this Dividend state.
What I want is to build the transaction on the company at the start. But the company does not have the balance of the stockholder. So, I created a ClaimNotification class that carries the stockholder’s stock balance and other information and sends it to the company node. This is a common way to encapsulate data between nodes.
(Discussion: I made the stockholder become the initiator instead of the company. It may make more sense for the company to trigger it. But, that would create another round trip making this sample too complicated.)
1 Token = 1 Cent (smallest quantity of the currency)
Then, all I needed to do is to calculate the dividend and create a Dividend state, which is simple, I thought. Later I realized that calculating the yields and dividends is actually difficult and that took me half a day to figure it out!
The problem was that I had not dealt with the fractional digits of the price properly. Taking USD as an example, one token is equivalent to a cent but not a dollar. It is fundamental to the Corda token concept, and I missed it.
To calculate the value of the stock, we need to multiply the stock price with the fractional digit of the currency as the power of 10.
BigDecimal stockValue = stockPrice.multiply(BigDecimal.valueOf(Math.pow(10.0, currency.getDefaultFractionDigits())))
6. Finishing off with PayDividend
So we have created the Dividend state. When payday arrives, we need to transform them into fiat currencies which will be done by PayDividend flow.
For the transaction to be built, apart from the dividend state, we need a set of fungible token states to represent the company paying the stockholders. Instead of querying the stored token balance, doing the math and creating the output state, we could use the TokenSDK helper functions to assist us.
The move token combo
Let me introduce the TokenSelection.generateMove() method. It generates a pair of input and output states. Under the hood, the TokenSelection object queries the required token state with an efficient coin selection algorithm. I have created a TempTokenSelectionFactory just to instantiate the TokenSelection as it works better in Kotlin currently.
Now we have prepared all the states, we can instantiate a TransactionBuilder object, as usual. Here comes the trick — we can use the MoveTokensUtilitiesKt.addMoveTokens() method to put the pair of I/O states into the transaction builder directly.
Tada! With the use of the combo, TokenSelection.generateMove() and MoveTokensUtilitiesKt.addMoveTokens(), we can move tokens with just a few lines.
Creating this CorDapp is very challenging as I do not have much financial knowledge. But TokenSDK has made token exchange a lot easier. Most of redundant code and special handling are abstracted away. Especially the move tokens combo which could apply to many situations.
Thanks for reading this far and I hope you enjoyed it. I will add new features to this CorDapp from time to time. Stay tuned.
If you have any thoughts, please feel find me, Wayne Lam, in this public slack channel.
-This post was authored by Wayne Lam, Developer Relations at R3.