/
Open in Online IDE

Introduction to Libraries

Thou shalt not reinvent the wheel


Reading Time: 6 min

In the previous module, you created a token --a widely re-useable pattern. It was a fungible token, to be more precise. Congratulations.

The concept of a token is in fact so universal that R3 decided to create an SDK that can create all sorts of tokens. You will discover it in more detail in the next chapter. On a more general level, there are many existing patterns and classes that have already been implemented. Reusing them can not only save you time, but also help you agree on a vocabulary to use when discussing or interacting with others. Here, you will discover some of these concepts, non-exhaustively. It is always good to explore the docs or the code to look for well-solved implementations that are close to what you want.

After several chapters on the Tokens SDK, in the same module, you will learn about another library, Accounts, and discover Visual Studio Code, and how you might want to use it as your primary IDE.

Reusable classes for the previous exercise

For the state

When you created your token, ostensibly for an air-mile token type, did you use any of the following existing classes?

  • Amount to assist you in handling quantities, conversions and operations.
  • Issued to loosely couple an issuer and a type.
  • interface OwnableState which would make your air-mile easily identifiable to others as one with an owner.
  • interface FungibleState would let you identify something with the property that all instances are materially equivalent, so only the amount / quantity is important.

The previous exercise example did not use any of those either, but here is an example of what could have been:

public class TrialToken<T> implements FungibleState<Issued<T>>, OwnableState {

    @NotNull
    private final Amount<Issued<T>> amount;
    @NotNull
    private final AbstractParty owner;

    public TrialToken(
            @NotNull final Amount<Issued<T>> amount,
            @NotNull final AbstractParty owner) {
        this.amount = amount;
        this.owner = owner;
    }

    @NotNull
    @Override
    public Amount<Issued<T>> getAmount() {
        return amount;
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return Collections.singletonList(owner);
    }

    @NotNull
    @Override
    public AbstractParty getOwner() {
        return owner;
    }

    @NotNull
    public AbstractParty getIssuer() {
        return amount.getToken().getIssuer().getParty();
    }

    @NotNull
    public T getProduct() {
        return amount.getToken().getProduct();
    }

    @NotNull
    @Override
    public CommandAndState withNewOwner(@NotNull final AbstractParty newOwner) {
        throw new NotImplementedException("We need a contract command");
    }
}

Note the usage of Amount<Issued<T>> amount to couple the quantity and the issuer. Remember this coupling as you will see it again. In fact, even this part:

implements FungibleState<Issued<T>>, OwnableState

already exists as interface FungibleAsset<T>. It's amusing that even in TrialToken above, where existing classes were wisely reused, it just so happens that it nonetheless reinvented FungibleAsset. Let's make use of this class in our QuickAirMileToken:

public class QuickAirMileToken implements FungibleAsset<QuickAirMileToken.AirMile> {

    // The underlying product.
    public static class AirMile {}

    @NotNull
    private final Party issuer;
    @NotNull
    private final Amount<Issued<AirMile>> amount;
    @NotNull
    private final AbstractParty owner;

    public QuickAirMileToken(
            @NotNull final Party issuer,
            @NotNull final Amount<Issued<AirMile>> amount,
            @NotNull final AbstractParty owner) {
        this.issuer = issuer;
        this.amount = amount;
        this.owner = owner;
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return Collections.singletonList(owner);
    }

    @NotNull
    @Override
    public Amount<Issued<AirMile>> getAmount() {
        return amount;
    }

    @NotNull
    @Override
    public AbstractParty getOwner() {
        return owner;
    }

    @NotNull
    @Override
    public Collection<PublicKey> getExitKeys() {
        return Arrays.asList(issuer.getOwningKey(), owner.getOwningKey());
    }

    @NotNull
    @Override
    public QuickAirMileToken withNewOwnerAndAmount(@NotNull Amount<Issued<AirMile>> newAmount, @NotNull AbstractParty newOwner) {
        return new QuickAirMileToken(issuer, newAmount, newOwner);
    }

    @NotNull
    @Override
    public CommandAndState withNewOwner(@NotNull AbstractParty newOwner) {
        throw new NotImplementedException("Needs contract command");
    }
}

This implementation is not added to the course project folder because of a bug that is on track to be fixed in Java. The curious reader can try in Kotlin.

What about LinearState, that you saw in IOUState? No, it's not conducive to a fungible air-mile token because the idea of a LinearState is that there should be only one unconsumed state by a given id. There can be no plan of tracking individual tokens because that would be, by definition, non-fungible.

For the contract

If you poke around, you can see that there is an OnLedgerAsset contract:

abstract class OnLedgerAsset<T : Any, out C : CommandData, S : FungibleAsset<T>> : Contract

that seems to achieve what your TokenContract achieved. Poring through its code is more involved than the quick examples above, but you can recognize helper functions in its Companion object:

However, it is in the demo corda-finance-contracts CorDapp. R3 advises against using it in production, but this is learning, so add it to your "contracts" build.gradle:

dependencies {
    ...
    cordapp "$corda_release_group:corda-finance-contracts:$corda_release_version"
}

You can create your contract QuickAirMileContract now:

public class QuickAirMileContract extends OnLedgerAsset<
        QuickAirMileToken.AirMile,
        QuickAirMileContract.Commands,
        QuickAirMileToken> {

    @NotNull
    @Override
    public TransactionState<QuickAirMileToken> deriveState(
            @NotNull final TransactionState<? extends QuickAirMileToken> txState,
            @NotNull final Amount<Issued<QuickAirMileToken.AirMile>> amount,
            @NotNull final AbstractParty owner) {
        return new TransactionState<>(
                txState.getData().withNewOwnerAndAmount(amount, owner),
                txState.getContract(),
                txState.getNotary(),
                txState.getEncumbrance(),
                txState.getConstraint());
    }

    @NotNull
    @Override
    public Collection<CommandWithParties<Commands>> extractCommands(
            @NotNull final Collection<? extends CommandWithParties<? extends CommandData>> commands) {
        //noinspection unchecked
        return commands.stream()
                .filter(it -> it.getValue() instanceof Commands)
                .map(it -> (CommandWithParties<Commands>) it)
                .collect(Collectors.toList());
    }

    @NotNull
    @Override
    public CommandData generateExitCommand(@NotNull Amount<Issued<QuickAirMileToken.AirMile>> amount) {
        return new Commands.Redeem();
    }

    @NotNull
    @Override
    public MoveCommand generateMoveCommand() {
        return new Commands.Move();
    }

    @Override
    public void verify(@NotNull LedgerTransaction tx) throws IllegalArgumentException {
        throw new NotImplementedException("That's a lot of work");
    }

    public interface Commands extends CommandData {
        @SuppressWarnings("unused")
        class Issue implements Commands {
        }

        class Move implements Commands, MoveCommand {
            @Nullable
            @Override
            public Class<? extends Contract> getContract() {
                return QuickAirMileContract.class;
            }
        }

        class Redeem implements Commands {
        }
    }
}

But let's stop here, because implementing verify is still as much work as the exercise of the previous module.

If you are inclined to digress a bit, have a look at the example Cash.State implementation of OnLedgerAsset.

Conclusion

Despair not, you did your own TokenState and that was a great learning experience. Still, in the future, it is always a good idea to see if there is not already a few classes that will assist you in improving the interoperability of your CorDapp with existing CorDapps.

Let's stay on the topic of tokens. You are going to master this subject and discover the Tokens SDK library in the next chapter.

Discuss on Slack
Rate this Page
Would you like to add a message?
Submit
Thank you for your Feedback!