Write states

This tutorial guides you through writing the two states you need in your CorDapp: AppleStamp and BasketofApples. You will be creating these states in the contracts/src/main/java/com/applestamp/states/ directory in this tutorial. Refer to the TemplateState.java file in this directory to see a template state.

After you have completed this tutorial, you will know how to create and implement states in a CorDapp.

Before you start building states, read Key concepts: States.

The easiest way to write any CorDapp is to start from a template. This ensures that you have the correct files to begin building.

  1. Navigate to the Kotlin and Java template repositories and decide which you’d like to clone:
  1. Open a terminal window in the directory where you want to download the CorDapp template.

  2. Run the following command:

git clone https://github.com/corda/cordapp-template-kotlin.git
git clone https://github.com/corda/cordapp-template-java.git
  1. After you have cloned the repository you wish to use, navigate to the correct subdirectory:

    cd cordapp-template-kotlin
    
    cd cordapp-template-java
    
  2. After you clone the CorDapp template, open the cordapp-template-kotlin or cordapp-template-java in IntelliJ IDEA. If you don’t know how to open a CorDapp in IntelliJ, see the documentation on Running a sample CorDapp.

  3. Rename the package to tutorial. This changes all instances of the template package in the project to tutorial. In the drop-down menu that appears, select Rename module.

First create the AppleStamp state. This state is the voucher issued to customers.

  1. Right-click the states folder, select New > Java Class and create a file called AppleStamp.

  2. Open the file.

The first thing you should do when writing a state is add the @BelongsToContract annotation. This annotation establishes the relationship between a state and a contract. Without this, your state does not know which contract is used to verify it.

  1. If you’ve copied in the template state, change the TemplateContract.class to AppleStampContract.class.

  2. Add the annotation @BelongsToContract(AppleStampContract.class) to your state.

This what your code should look like so far:

@BelongsToContract(AppleStampContract.class)

When naming your CorDapp files, it’s best practice to match your contract and state names. In this case the state is called AppleStamp, so the contract is called AppleStampContract. Follow this naming convention when you write an original CorDapp to avoid confusion.

The next line of code you add defines the type of ContractState you implement with the AppleStamp class. Add this line to ensure that Corda recognizes the AppleStamp as a state.

In this case, use a LinearState to tie the AppleStamp to a LinearID.

Add the public class AppleStamp implementing a LinearState.

This is what your code should look like now:

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

Next, add the private variables for the stamp description (stampDesc), the issuer of the stamp (issuer), and the current owner of the stamp (holder).

After adding these variables, your code should look like this:

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

    //Private Variables
    private String stampDesc; //For example: "One stamp can be exchanged for a basket of Gala apples."
    private Party issuer; //The person who issued the stamp.
    private Party holder; //The person who currently owns the stamp.
  }
  1. All LinearStates must have a variable for the state’s linear ID. Add this variable under the private variables:
private UniqueIdentifier linearID;
  1. All Corda states must include a parameter to indicate the parties that store the states. Add this parameter below the LinearStare variable:
private List<AbstractParty> participants;

After adding these sections, your code should look like this:

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

    //Private Variables
    private String stampDesc; //For example: "One stamp can exchange for a basket of HoneyCrispy Apple"
    private Party issuer; //The person who issued the stamp
    private Party holder; //The person who currently owns the stamp

    //LinearState required variable.
    private UniqueIdentifier linearID;

    //Parameter required by all Corda states to indicate storing parties
    private List<AbstractParty> participants;

  }

Add a constructor to initialize the objects in the AppleStamp state.

If you’re using IntelliJ, you can generate the constructor with a shortcut.

  1. On macOS, press Command + N.

    On Windows, press Alt + Insert.

  2. Select Constructors in the Generate menu.

  3. Select all the constructors that appear and click OK.

  4. Add the @ConstructorForDeserialization annotation before the constructor to ensure that all variables appear.

    This annotation:

    • Indicates which constructor is used for serialization when there are multiple constructors in a state class.
    • Is usually annotated at the constructor that has the most parameters fields.

After adding the constructor, your code should look like this:

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

    //Private Variables
    private String stampDesc; //For example: "One stamp can exchange for a basket of HoneyCrispy Apple"
    private Party issuer; //The person who issued the stamp
    private Party holder; //The person who currently owns the stamp

    //LinearState required variable.
    private UniqueIdentifier linearID;

    //ALL Corda States must have this parameter to indicate storing parties.
    private List<AbstractParty> participants;

    //Constructor Tips: Command + N in IntelliJ can auto generate constructor.
    @ConstructorForDeserialization
    public AppleStamp(String stampDesc, Party issuer, Party holder, UniqueIdentifier linearID) {
        this.stampDesc = stampDesc;
        this.issuer = issuer;
        this.holder = holder;
        this.linearID = linearID;
        this.participants = new ArrayList<AbstractParty>();
        this.participants.add(issuer);
        this.participants.add(holder);
    }

To access a private variable outside of its class in Java, you must use a getter. If you do not use getters, your Corda node cannot pick up the variables.

Add a getter for each variable. After you’ve added the getters, your code should look like this:

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

    //Private Variables
    private String stampDesc; //For example: "One stamp can exchange for a basket of HoneyCrispy Apple"
    private Party issuer; //The person who issued the stamp
    private Party holder; //The person who currently owns the stamp

    //LinearState required variable.
    private UniqueIdentifier linearID;

    //ALL Corda State required parameter to indicate storing parties
    private List<AbstractParty> participants;

    //Constructor Tips: Command + N in IntelliJ can auto generate constructor.
    @ConstructorForDeserialization
    public AppleStamp(String stampDesc, Party issuer, Party holder, UniqueIdentifier linearID) {
        this.stampDesc = stampDesc;
        this.issuer = issuer;
        this.holder = holder;
        this.linearID = linearID;
        this.participants = new ArrayList<AbstractParty>();
        this.participants.add(issuer);
        this.participants.add(holder);
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return this.participants;
    }

    @NotNull
    @Override
    public UniqueIdentifier getLinearId() {
        return this.linearID;
    }

    //Getters
    public String getStampDesc() {
        return stampDesc;
    }

    public Party getIssuer() {
        return issuer;
    }

    public Party getHolder() {
        return holder;
    }

}

If you’re using IntelliJ or another IDE, the IDE automatically adds the imports you need.

IntelliJ indicates that an import is missing with red text. To add the import:

  1. Click the red text. A message appears: “Unresolvable reference: {name of the missing input}”.

  2. On macOS, press Option + Enter to automatically import that variable.

    On Windows, press Alt + Enter to automatically import that variable.

  3. Repeat this process with all missing imports.

Once you have added all imports, your code should look like this:

package com.tutorial.states;

import com.tutorial.contracts.AppleStampContract;
import net.corda.core.contracts.BelongsToContract;
import net.corda.core.contracts.LinearState;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
import net.corda.core.serialization.ConstructorForDeserialization;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

@BelongsToContract(AppleStampContract.class)
public class AppleStamp implements LinearState {

    //Private Variables
    private String stampDesc; //For example: "One stamp can exchange for a basket of HoneyCrispy Apple"
    private Party issuer; //The person who issued the stamp
    private Party holder; //The person who currently owns the stamp

    //LinearState required variable.
    private UniqueIdentifier linearID;

    //ALL Corda State required parameter to indicate storing parties
    private List<AbstractParty> participants;

    //Constructor Tips: Command + N in IntelliJ can auto generate constructor.
    @ConstructorForDeserialization
    public AppleStamp(String stampDesc, Party issuer, Party holder, UniqueIdentifier linearID) {
        this.stampDesc = stampDesc;
        this.issuer = issuer;
        this.holder = holder;
        this.linearID = linearID;
        this.participants = new ArrayList<AbstractParty>();
        this.participants.add(issuer);
        this.participants.add(holder);
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return this.participants;
    }

    @NotNull
    @Override
    public UniqueIdentifier getLinearId() {
        return this.linearID;
    }

    //Getters
    public String getStampDesc() {
        return stampDesc;
    }

    public Party getIssuer() {
        return issuer;
    }

    public Party getHolder() {
        return holder;
    }

}

The BasketOfApples state is the basket of apples that Farmer Bob self-issues to prepare the apples for Peter. Now that you’ve written your first state, try writing the BasketOfApples state using the following information.

Private variables:

  • description - The brand or type of apple. Use type String.
  • farm - The origin of the apples. Use type Party.
  • owner - The person exchanging the basket of apples for the voucher (Farmer Bob). Use type Party.
  • weight - The weight of the basket of apples. Use type int.

The BasketOfApples state is involved in two transactions. In the first transaction, Farmer Bob self-issues the BasketOfApples. The Farm party then fills both the owner and farm fields of the transaction. You could compact the transaction to carry only these parameters: public BasketOfApples(String description, Party farm, int weight) {}

If you are writing in Java, when you have multiple constructors in one state class, you must annotate which constructor is the base for serialization. This constructor will most likely carry all relevant information for the state. For example, the constructor public BasketOfApples(String description, Party farm, int weight) {}), does not have an owner field. You must create another constructor that has all fields, and annotate this constructor with @ConstructorForDeserialization.

Once you’ve written the BasketOfApples state, check your code against the sample below. Your code should look something like this:

package com.tutorial.states;

import com.tutorial.contracts.BasketOfApplesContract;
import net.corda.core.contracts.BelongsToContract;
import net.corda.core.contracts.ContractState;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
import net.corda.core.serialization.ConstructorForDeserialization;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

@BelongsToContract(BasketOfApplesContract.class)
public class BasketOfApples implements ContractState {

    //Private Variables
    private String description; //Brand or type
    private Party farm; //Origin of the apple
    private Party owner; //The person who exchange the basket of apple with the stamp.
    private int weight;

    //ALL Corda State required parameter to indicate storing parties
    private List<AbstractParty> participants;

    //Constructors
    //Basket of Apple creation. Only farm name is stored.
    public BasketOfApples(String description, Party farm, int weight) {
        this.description = description;
        this.farm = farm;
        this.owner=farm;
        this.weight = weight;
        this.participants = new ArrayList<AbstractParty>();
        this.participants.add(farm);
    }

    //Constructor for object creation during transaction
    @ConstructorForDeserialization
    public BasketOfApples(String description, Party farm, Party owner, int weight) {
        this.description = description;
        this.farm = farm;
        this.owner = owner;
        this.weight = weight;
        this.participants = new ArrayList<AbstractParty>();
        this.participants.add(farm);
        this.participants.add(owner);
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return participants;
    }

    //getters
    public String getDescription() {
        return description;
    }

    public Party getFarm() {
        return farm;
    }

    public Party getOwner() {
        return owner;
    }

    public int getWeight() {
        return weight;
    }

    public BasketOfApples changeOwner(Party buyer){
        BasketOfApples newOwnerState = new BasketOfApples(this.description,this.farm,buyer,this.weight);
        return newOwnerState;
    }

}

Follow the Write the contracts tutorial to continue on this learning path.

Was this page helpful?

Thanks for your feedback!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.

We're sorry this page wasn't helpful. Let us know how we can make it better!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Create an issue

Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.