eAuction – Creating a sample auction house CorDapp from scratch! (Part1)

May 04, 2020

By Ashutosh Meher, Developer Evangelist at R3

Since the time blockchain and DLTs became popular, their use cases have been envisioned in multiple different areas. As we see more and more of them coming to life, one such interesting area is auctions. Let’s try to implement an e-auction application using the Corda blockchain platform.

New to Corda? A great way to start with Corda is to take a look at one of our online bootcamp webinars. The recording for one of them is available here: https://www.youtube.com/watch?v=tVE1rKbFA3g&t=6349s

You may also consider joining us for one of our in-person or live virtual bootcamps. Keep an eye on the link below to know what events are coming up: https://corda.net/blockchain-bootcamp/

Scope

Before we jump into code, let’s take some time to define the scope of the prototype we are building. So, what do we want our auction app to do?

  • The first thing that is quite evident is we need something to put on auction. So we should be able to both issue an asset on the ledger, and then auction that asset.
  • We should be able to create an auction and the auction should be visible to everyone in the network. Corda’s privacy model doesn’t currently allow this, so we will see how to tackle that shortly.
  • The Auction should be able to accept bids from the network participants.
  • The auction should have a deadline at which it should stop accepting bids, so we need a way to schedule an action at a pre-determined time to end the auction.
  • The auction should finally be settled, i.e. the highest bidder should be able to pay the highest bid amount to receive the auctioned item.
  • And finally, we should be able to remove finished auctions.

That would conclude an auction. And, it should be good enough for our prototype.

Here are a few snapshots of the final prototype we are aiming at.

Implementation

Contract State

Let’s think about our data model. What is it that we want to store on the ledger? I think storing the asset and the auction information should be good enough.

You might consider storing all the bids data as well, but for simplicity’s shake, I am just interested in the highest bid and bidder, which I would directly store in the auction.

The “Asset” ContractState

The first class we are implementing is Asset. It’s a simple ContractState with a title, description and an imageUrl.

public class Asset implements ContractState {
    private final String title;
    private final String description;
    private final String imageUrl;
}

Let’s make our lives a little easier – we don’t want to deal with fungible assets now. So let’s make it a LinearState, which means our asset can’t be split and merged. It’s a single unique asset.

Implementing LinearState would require you to introduce a linearId which remains consistent across state evolution.

public class Asset implements LinearState {
    // title, desc and imageUrl
    private final UniqueIdentifier linearId;
    @Override
    public UniqueIdentifier getLinearId() {
        return linearId;
    }   
}

What else? Let’s also make it OwnableState, so that it has a single owner at any point in time.

Learn more about ContractState hierarchy here: https://docs.https://corda.net/docs/corda-os/4.4/api-states.html

Now, this is a bit more involved than just adding a new owner property. We also need to override a method called withNewOwner() . This method should be called from a flow when there is a need to transfer ownership. So, we expect this method to return the updated state and the command for transfer action. This is wrapped in a CommandAndState object. As you can easily guess, this updated state becomes the output of the transaction and the command becomes the command of the transaction being built to perform the ownership transfer.

public class Asset implements OwnableState, LinearState {
   // title, desc, imageUrl and linearId
   private final AbstractParty owner;
   @Override
   public AbstractParty getOwner() {
      return owner;
   }
   @Override
   public CommandAndState withNewOwner(
                          @NotNull AbstractParty newOwner) {
     return new CommandAndState(
            new AssetContract.Commands.TransferAsset(),
            new Asset(this.getLinearId(), this.getTitle(),
              this.getDescription(), this.getImageUrl(), newOwner      
         ));
   }
}

I guess that’s the most important part. Here’s how our final Asset class should look:

The “AuctionState” ContractState

Now that we have an Asset let’s take a look at the Auction.

StatePointer

The first thing that we want to concentrate on is linking this Auction to an Asset. For that, we are going to use StatePointer. It allows you to point to a ContractState without directly including it in the ContractState.

private final LinearPointer<Asset> auctionItem;

Learn more about StatePointer in my previous blog here: https://medium.com/corda/linking-corda-states-using-statepointer-16e24e5e602.

You could also take a look at the documentation here: https://docs.https://corda.net/docs/corda-os/4.0/design/linear-pointer/design.html

The second most important thing to consider here is the scope we agreed upon to be able to end the auction at a predetermined time. Corda allows you to schedule events using SchedulableState. So let’s take advantage of that feature and schedule an event to end the action. For that to happen we need to implement the SchedulableState interface.

public class AuctionState implements SchedulableState

Learn more about Event Scheduling in the documentation here: https://docs.https://corda.net/docs/corda-os/4.4/event-scheduling.html

But how do we schedule an action to end the auction? What we are trying to achieve is to stop accepting bids, so to indicate whether the auction is active or inactive (ended) let’s introduce a variable in our AuctionState.

private final Boolean active;

What we need to do in order to end the auction is to update this variable to false. But when do we update it? Well, at a particular time, for which we need a new variable, so let’s introduce bidEndTime.

private final Instant bidEndTime;

Now, obviously it requires a transaction. You can’t directly update a variable in a ContractState, since its immutable. So, we need to trigger a flow that would execute that particular transaction. Luckily for us, as we implement the SchedulableState we are also required to override a method called nextScheduledActivity() which could be used to trigger a flow at a particular instant. This method returns a SechduledActivity which is used by the scheduler to schedule the execution of a flow at a particular time. In our case, we schedule the EndAuctionFlow.Initiator .

@Override
public ScheduledActivity nextScheduledActivity(
               StateRef thisStateRef,
               FlowLogicRefFactory flowLogicRefFactory) {
    FlowLogicRef flowLogicRef = flowLogicRefFactory.create(
            "net.corda.samples.flows.EndAuctionFlow$Initiator",     
             auctionId
    );
    return new ScheduledActivity(flowLogicRef, this.bidEndTime);
}

The rest is simple, just add a few more variables to store some information like the base bid price, a unique auction id, the participants (bidders), etc.

Here’s how the final AuctionState would look like:

Contracts

We have the data model in place now. Let’s take a look at the business validations.

There’s a lot of things we are doing here like issue and transfer of assets, issue, bid, end, settlement and exit of auctions. Now that’s a lot of work, so I would just concentrate on some of the important bits while ignoring the more trivial ones.

VerifyBids

This is where we check if the auction is accepting bids and a few more important validations which are pretty self-explanatory as shown below:

AuctionState inputState = (AuctionState)tx.getInput(0);
AuctionState outputState = (AuctionState) tx.getOutput(0);
if(!inputState.getActive()) 
    throw new IllegalArgumentException("Auction has Ended");

if(outputState.getHighestBid().getQuantity() <     
                 inputState.getBasePrice().getQuantity())
    throw new IllegalArgumentException(
          "Bid Price should be greater than base price");

if(inputState.getHighestBid() != null &&
        outputState.getHighestBid().getQuantity() <= 
                   inputState.getHighestBid().getQuantity())
    throw new IllegalArgumentException(
         "Bid Price should be greater than previous highest bid");

VerifyEndAuction

When the auction ends we want to make sure that auctioneer signs the transaction.

Command command = tx.getCommand(0);
if(!command.getSigners().contains(((AuctionState)tx.getOutput(0))
              .getAuctioneer().getOwningKey()))
    throw new IllegalArgumentException(
                    "Auctioneer Signature Required");

VerifySettlement

This is where we verify the settlement of the auction, We want to make sure both the auctioneer and the auction winner are signing the transaction. And also good to check that the auction has ended.

Command command = tx.getCommand(0);
AuctionState auctionState = (AuctionState) tx.getInput(0);

if(auctionState.getActive())
    throw new IllegalArgumentException("Auction is Active");

if(!(command.getSigners().contains(auctionState.getAuctioneer()
     .getOwningKey())) && (auctionState.getWinner()!=null &&   
     command.getSigners().contains(auctionState.getWinner()
     .getOwningKey())))
    throw new IllegalArgumentException(
                   "Auctioneer and Winner must Sign");

VerifyExit

Finally, this is where the auction is all settled or ended without receiving any bids and we want to consume the auction and exit the state.

  • We need to make sure that the auction is inactive
  • If the auction has got bids, both the auctioneer and the winner must sign the transaction and also, the auction must be settled.
  • If the auction didn’t receive any bids, we just need to have the auctioneer’s signature.
Command command = tx.getCommand(0);
AuctionState auctionState = (AuctionState) tx.getInput(0);
Asset asset = (Asset) tx.getReferenceInput(0);

if(auctionState.getActive())
    throw new IllegalArgumentException("Auction is Active");

if(auctionState.getWinner() != null) {
    if (!(command.getSigners().contains(auctionState.getAuctioneer()
    .getOwningKey())) && (auctionState.getWinner() != null &&  
    command.getSigners().contains(auctionState.getWinner()
    .getOwningKey())))
        throw new IllegalArgumentException(
                    "Auctioneer and Winner must Sign");

    if (!(asset.getOwner().getOwningKey().equals(auctionState
    .getWinner().getOwningKey())))
        throw new IllegalArgumentException(
                    "Auction not settled yet");
}else{
    if (!(command.getSigners().contains(auctionState.getAuctioneer()
    .getOwningKey())))
        throw new IllegalArgumentException("Auctioneer must Sign");
}

Take look at the final AuctionContract here:

https://github.com/corda/samples/blob/release-V4/auction-cordapp/contracts/src/main/java/net/corda/samples/contracts/AuctionContract.java

That’s all for this post. We have successfully completed the State and Contracts part of our Auction CorDapp. Want to continue to build the Auction CorDapp?

Find the Part-2 of this blog post here , where I discuss how to implement the flows. The final completed source code is available below.

Source Code

The source code discussed in the post can be found below. It contains the completed Cordapp with the states, contracts and flows all implemented. It also has a UI and a client implemented to play around. Check it out!

corda/samples

What’s next?

Thanks for reading through, hopefully, you like this post. To learn how to implement the flows of the Auction CorDapp checkout the Part-2 of this blog post here.

Want to learn more and connect with other CorDapp developers? You may consider joining us in our public slack channel. Learn more about Corda at https://corda.net. Official documents can be found at docs.https://corda.net.

— Ashutosh Meher 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.

Follow Ashutosh on Twitter: @iashutoshmeher, LinkedIn: @iashutoshmeher

 

Share: