With the new quarterly release of the Token SDK comes a welcome change for the Java developer in the form of a refined APIs and two new token builders. This post will highlight some of these changes and give examples of how it’s easier than ever to work with digital assets on Corda.
Code snippets and examples are provided from the spaceships sample project available here.
Before we begin, let’s quickly recap the process of issuing tokens on Corda. There are two types of issuances — fungible and non-fungible. A fungible token is one that can be split and merged, for example; a fiat currency like a Peso or US dollar, or a share in a stock. A non-fungible token is something that is indivisible and issued, moved, or redeemed in whole. This often is useful for representing things which are unique in the real world — such as a house or a bike (click here for a full recap of Token SDK and free Corda training).
Each type of token is composed of building blocks allowing the developer to be flexible and have the most representative power when expressing an on-ledger digital asset.
A fungible token is a composition of:
A non-fungible token is a composition of:
Each of these components is an object which must be defined and instantiated to create your final token before issuing. It’s quite simple once you get the hang of it but admittedly, it used to be a bit cumbersome.
Here is an example of a fully built fungible token on the original Token SDK:
TokenType myToken = new TokenType("My Token", 2);IssuedTokenType issuedMyToken = new IssuedTokenType(issuingParty, myToken);Amount<IssuedTokenType> amountMyIssuedToken = new Amount<>(100, issuedMyToken);FungibleToken fungibleMyToken = new FungibleToken(amountMyIssuedToken, holder);
… now things just got a WHOLE lot easier.
Money and Currency Utilities
One of the most common TokenTypes developers will be creating on Corda are those representing real-world ISO standard fiat currencies. Some people might not be aware that the Corda Token SDK includes utility classes for rapidly instantiating these tokens. [Source code]
The MoneyUtilities
class retrieves correctly defined TokenTypes and Amounts of TokenTypes for major fiat and digital currencies; it makes use of the FiatCurrency
and DigitalCurrency
definitions.
import com.r3.corda.lib.tokens.money.MoneyUtilities;// Instantiating currencies and amountsTokenType usdTokenType = MoneyUtilities.getUSD();TokenType rippleTokenType = MoneyUtilities.getXRP();Amount<TokenType> amountAUD = MoneyUtilities.getAUD(100.22);
You can see a basic demonstration of how these standard currencies can be fed into token issuances here.
Handling Amounts in Java
You’ll notice from the previous code-block that we were able to quickly generate an instance of Amount<TokenType> with a single line call to MoneyUtilities, but what about amounts of our own custom
TokenTypes, or for a dynamic TokenType? For this we have access to AmountUtilities
.
In its simplest form you can quickly return amounts of any TokenType.
import com.r3.corda.lib.tokens.contracts.utilities.AmountUtilities;// Creating an amount of customTokenTypedouble amount = 102.00;
TokenType customTokenType = new TokenType("Bob Token", 2);Amount<TokenType> myAmount = AmountUtilities.amount(newValue, customTokenType);
Take a look at a more advanced example where an Amount of one TokenType is converted to an Amount of another using fixed exchange rates. [Source code]
/**
* exchangeCurrency calculates an amount back in targetCurrency that is fair exchange with an itemCurrency
* This allows a buyer to know how much of his held currency to give to a seller to satisfy the price.
* @param amount
* @param targetCurrency
* @return
*/
static Amount<TokenType> exchangeCurrency(Amount<TokenType> amount, TokenType targetCurrency) {
int itemCurrFractionDigits = amount.getToken().getFractionDigits();
double newValue = (amount.getQuantity()/Math.pow(10,itemCurrFractionDigits)) * (rates.get(amount.getToken().getTokenIdentifier()) / rates.get(targetCurrency.getTokenIdentifier()));
return AmountUtilities.amount(newValue, targetCurrency);
}
Beyond just creating Amounts, the AmountUtilities
has functions for summation on Iterables. Got a bunch of amounts that you want to add up? No problem.
List<Amount<TokenTypes>> myAmounts = ...;Amount<TokenType> mySum = AmountUtilities.sumTokensOrThrow(myAmounts);
Java Token Builders
Alright. With some basic currency TokenTypes under our belt. Let’s put on our hard-hats and start playing with the biggest change for Java developers this release cycle — the new FungibleTokenBuilder
and NonFungibleTokenBuilder
classes!
Remember how we described fully formed tokens as compositions (see above diagrams)? Constructing was a multi-stage process. In Kotlin, since the original release of the Token SDK, you have been able to leverage the language’s in-fix notation to rapidly build tokens with syntax such as: val myFungibleToken = 10.GBP issuedBy issuingParty heldBy holder
, which is great. But there was nothing analogous in Java… until now.
The TokenBuilder classes replicate this single call instantiation using Java’s familiar builder paradigm.
Let’s build a FungibleToken
:
Party issuingParty = ...;
Party holder = ...;FungibleToken token = new FungibleTokenBuilder()
.ofTokenType(MoneyUtilities.getGBP())
.withAmount(10)
.issuedBy(issuingParty)
.heldBy(holder)
.buildFungibleToken();
Let’s build a NonFungibleToken
:
Party manufacturer = ...;
Party owner = ...;NonFungibleToken token = new NonFungibleTokenBuilder()
.ofTokenType(new BicycleTokenType())
.issuedBy(manufacturer)
.heldBy(owner)
.buildNonFungibleToken();
Now this is SO much more legible and concise! Additionally, the builders will check that your composition is correct so you never have to worry about missing a stage in your Token creation.
See a more detailed usage of the builders in the spaceships reference sample where they are used tokenize fungible and non-fungible space craft. [Source code]
More Flexibility in Your Flows
It’s not just building tokens that is now easier in Java — it’s also working with them through your flows. From building a transaction, to querying and manipulating assets; here’s an introduction to some of the tools available.
NotaryUtilities
: Notaries are fundamental to Corda workflows (if you need a refresher — click here). The Token SDK includes a utility that provides functions for algorithmically selecting notaries for your transaction. If you are just starting with Corda, you may be accustomed to calling something like getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
this grabs the first available notary on the network. The NotaryUtilities
class allows you to define ‘preferred’ notaries. It defaults to the first notary — but you can override to select statically, algorithmically, or randomly. (Note: getting creative with notary selection should only be done at issuance. Generally throughout the lifecycle of an asset it should be bound to the same notary — so for subsequent operations, pull the notary off the input-state).
Querying your Tokens — What’s on Ledger?
Corda has established patterns for querying the vault (aka ledger) — click here to refresh. So all the basics apply, BUT tokens are so standardized and defined so that there are utilities to give you the information you need quickly and effortlessly in Java through QueryUtilities
.
As seen above — when called in a flow through the perspective of your node, you can rapidly retrieve Fungible or NonFungible states, Amounts, and QueryCriteria objects (to build more complex queryBy operations). It’s simple and painless. Taken from our spaceship sample, the below code-snippet sends to a counter-party the Amount of SpaceShipTokenType held, and their value (which is a property). [Source code]
@Suspendable
@Override
public Void call() throws FlowException {
// receive request for value of the given shipId
String shipId = counterpartySession.receive(String.class).unwrap(it -> it);
UUID shipUUID = UUID.fromString(shipId);
SpaceshipTokenType spaceshipTokenType = FlowHelpers.uuidToSpaceShipTokenType(getServiceHub().getVaultService(), shipUUID);
Amount<TokenType> amountOfSpaceShipTokens = QueryUtilities.tokenBalance(getServiceHub().getVaultService(), spaceshipTokenType.toPointer());
counterpartySession.send(new Pair<>(amountOfSpaceShipTokens, spaceshipTokenType.getValue()));
return null;
}
Token Selection and Building Transactions
The Token SDK base is programmed in Java’s sister language Kotlin. This has many advantages and the two, are 100% interoperable. Using early versions of the SDK you would have realized how the provided flows make it a joy to issue, move, or redeem tokens. For instance, I could send Ashutosh some of my tokens with a single line subFlow(new MoveFungibleTokens(new PartyAndAmount<>(ashutosh, amount)));
wow!
But as you got into more complex transactions (i.e. manually adding elements to a Transaction builder), you may have been frustrated by the fact that many default parameters were not overloaded.
tokenSelection.generateMove(getRunId().getUuid(), Collections.singletonList(sendingPartyAndAmount), getOurIdentity(), null);
— that’s a mouth-full, and why are we passing in a null?
Things are simpler now as all constructors and function signatures are now overloaded. Lets try that again,
tokenSelection.generateMove(Collections.singletonList(sendingPartyAndAmount), getOurIdentity());
You still have access to all the powerful optional parameters — if you choose!
Ready to dive in? Check out a FULL ATOMIC (single transaction) delivery-vs-payment exchange in our spaceship example. [Source code]
Advanced Queries, Made Simple
Okay okay, so we all agree it’s VERY easy to use the new Java APIs to create tokens, do basic operations and queries, etc. But what else can we do? How about use lambdas for precisely targeted, custom query operations that allows you to go beyond the standard provided queries… Enter the TokenQueryBy
class.
Here’s the basic format. You instantiate a TokenQueryBy, throw in your own custom predicate, only limited to your imagination; pass it to your token Selector — and 💥.
TokenQueryBy tokenQueryBy = new TokenQueryBy(
issuer,
it -> {...condition...}
);
Selector selector = new DatabaseTokenSelection(getServiceHub());
return selector.selectTokens(totalAmountHeld, tokenQueryBy);
In our spaceship sample we have a simple and creative use of this pattern where our utility function returns all fungible tokens with a fractional amount — it’s a ‘loose change finder’. Using this as an example you can build any type of predicate you desire. [Source code]
What’s Next?
This is just a sampling of some of the new API changes available with the latest release of the Token SDK. There’s a lot of functionality and positive changes you’ll stumble on as you develop.
Since its initial release, some of the biggest companies in the world have integrated Corda tokens into their CorDapps. It’s one of the most effective ways to standardize your representation of digital assets. So keep building and inventing and stay tuned for future releases. In the meantime, check out the brand new free Corda training platform. 💪
Want to learn more about building awesome blockchain applications on Corda? Be sure to visit corda.net, check out our community page to learn how to connect with other Corda developers, and sign up for one of our newsletters for the latest updates.
— Anthony Nixon is a Developer Evangelist at R3, an enterprise blockchain software firm working with a global ecosystem of more than 350 participants across multiple industries from both the private and public sectors to develop on Corda, its open-source blockchain platform, and Corda Enterprise, a commercial version of Corda for enterprise usage.