Use the Tokens SDK
- Reading time: 5 mins
- Discuss on Slack
Introduction
In the previous chapter, you discovered the core concepts of the Tokens SDK. In this chapter you will learn to use the SDK by way of example. The code samples in this chapter are used in place in a dedicated unit test file, UsdTokenCourseExercise
, and 3 flows to avoid any doubt about the environment in which the samples can be used.
The SDK comes with flows to Issue
, Move
, and Redeem
tokens. Those flows come in fungible, non-fungible, confidential, inlined, and initiating versions. Review the details here and then delve into the exercise in the next chapter.
You are going to advance through 4 overall steps per this list:
- Include the SDK in your project.
Issue
100$ to Alice.Move
50$ from Alice to Bob.- Have Bob
Redeem
25$.
Let’s start:
Include the SDK in your project
-
Add the release group and version inside the project root
build.gradle
, in theext
configuration block:tokens_release_version = '1.1' tokens_release_group = 'com.r3.corda.lib.tokens' confidential_id_release_version = '1.0' confidential_id_release_group = 'com.r3.corda.lib.ci'
If you are working with the course’s repository, you may want to update the
constants.properties
file instead:tokensReleaseVersion=1.1 ...
And update your
build.gradle
in010-empty-project
in the same fashion as the other parameters are collected:tokens_release_version = constants.getProperty("tokensReleaseVersion") ...
-
Add the repositories inside the root
build.gradle
:repositories { maven { url 'https://software.r3.com/artifactory/corda-lib' } maven { url 'https://software.r3.com/artifactory/corda-lib-dev' } }
-
Add the necessary
dependencies
in the respective modules'build.gradle
files. Only addtokens-money
if you plan on usingFiatCurrency
andDigitalCurrency
token type definitions:// For contracts cordapp "$tokens_release_group:tokens-contracts:$tokens_release_version" cordapp "$tokens_release_group:tokens-money:$tokens_release_version" // Optional // For workflows cordapp "$tokens_release_group:tokens-workflows:$tokens_release_version" cordapp "$tokens_release_group:tokens-selection:$tokens_release_version" cordapp "$confidential_id_release_group:ci-workflows:$confidential_id_release_version"
For the next steps, the code examples shown below are actually rootless. When you go to the repo itself, you will see that they are split into 3 parts:
- A somewhat generic flow.
- A convenience function that calls the flow with specific values.
- A test function that confirms it got it right.
Let the US mint issue
Now, on the US Mint’s node, within a flow’s call
function, Issue
$100 to Alice:
// Prepare what we are talking about.
final TokenType usdTokenType = new TokenType("USD", 2);
// Identify the issuer.
if (!getOurIdentity().getName().equals(CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US"))) {
throw new FlowException("We are not the US Mint");
}
final IssuedTokenType usMintUSD = new IssuedTokenType(getOurIdentity(), usdTokenType);
// Who is going to own the output, and how much?
// Create a 100$ token that can be split and merged.
final Amount<IssuedTokenType> oneHundredUSD = AmountUtilitiesKt.amount(100L, usMintUSD);
final Party alice = getServiceHub().getNetworkMapCache().getPeerByLegalName(
CordaX500Name.parse("O=Alice, L=New York, C=US"));
final FungibleToken usdToken = new FungibleToken(oneHundredUSD, alice, null);
// Issue the token to Alice.
final SignedTransaction issueTx = subFlow(new IssueTokens(
Collections.singletonList(usdToken), // Output instances
Collections.emptyList())); // Observers
With its [@Test mintIssues100ToAlice
]https://github.com/corda/corda-training-code/blob/master/030-tokens-sdk/workflows/src/test/java/com/template/usd/UsdTokenCourseExercise.java#L46-L51). By the way, the tokens-money
module (which comes with the SDK), has 2 predefined token types: FiatCurrency
and DigitalCurrency
, so you can do something like this:
// US Dollar token type.
final TokenType usdTokenType = FiatCurrency.Companion.getInstance("USD");
// Ripple token type.
final TokenType rippleTokenType = DigitalCurrency.Companion.getInstance("XRP");
// Other predefined digital currency types are: BTC, ETH, DOGE.
Let Alice move some to Bob
Then, on Alice’s node, Move
$50 from Alice to Bob:
// Move 50$ that were issued by the US Mint and held by Alice to Bob.
// Prepare what we are talking about.
final TokenType usdTokenType = FiatCurrency.Companion.getInstance("USD");
final Party usMint = getServiceHub().getNetworkMapCache().getPeerByLegalName(
CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US"));
if (usMint == null) throw new FlowException("No US Mint found");
// Who is going to own the output, and how much?
final Party bob = getServiceHub().getNetworkMapCache().getPeerByLegalName(
CordaX500Name.parse("O=Bob, L=New York, C=US"));
final Amount<TokenType> fiftyUSD = AmountUtilitiesKt.amount(50L, usdTokenType);
final PartyAndAmount<TokenType> fiftyUSDForBob = new PartyAndAmount<>(bob, fiftyUSD);
// Describe how to find those $ held by Alice.
final QueryCriteria issuedByUSMint = QueryUtilitiesKt.tokenAmountWithIssuerCriteria(usdTokenType, usMint);
final QueryCriteria heldByAlice = QueryUtilitiesKt.heldTokenAmountCriteria(usdTokenType, alice);
// Do the move
final SignedTransaction moveTx = subFlow(new MoveFungibleTokens(
Collections.singletonList(fiftyUSDForBob), // Output instances
Collections.emptyList(), // Observers
issuedByUSMint.and(heldByAlice), // Criteria to find the inputs
alice)); // change holder
With its @Test bobGot50Dollars
. Note how the last parameter is explicit: changeHolder = alice
. Why did it not rely on the default value of getOurIdentity()
?
- It is best to not leave anything to chance.
- When you are introduced to the Accounts Library, this default will get messy.
So better keep this as a habit.
For the avoidance of doubt, the change holder is Alice. In this case, change is meant as in “Do you have change on a $20 bill?” It is not meant as the verb to change hands. What Bob receives is expressed in the Collections.singletonList(fiftyUSDForBob), // Output instances
part.
Let Bob redeem some
Then, on Bob’s node, let Bob Redeem
$25:
// Prepare what we are talking about.
final TokenType usdTokenType = FiatCurrency.Companion.getInstance("USD");
final Party usMint = getServiceHub().getNetworkMapCache().getPeerByLegalName(
CordaX500Name.parse("O=US Mint, L=Washington D.C., C=US"));
if (usMint == null) throw new FlowException("No US Mint found");
// Describe how to find those $ held by Me.
final QueryCriteria heldByBob = QueryUtilitiesKt.heldTokenAmountCriteria(usdTokenType, bob);
final Amount<TokenType> twentyFiveUSD = AmountUtilitiesKt.amount(25L, usdTokenType);
// Do the redeem
final SignedTransaction redeemTx = subFlow(new RedeemFungibleTokens(
twentyFiveUSD, // How much to redeem
usMint, // issuer
Collections.emptyList(), // Observers
heldByBob, // Criteria to find the inputs
bob)); // change holder
With its @Test bobGot25DollarsLeft
. Note that this time it only created a QueryCriteria heldByBob
and did not need to create a QueryCriteria issuedByUSMint
as this is done automatically for us in the detail of the implementation.
Conclusion
You have seen how to include the SDK into your project, and how to do the 3 basic operations that your more complex flows would do as part of a CorDapp.