Loyalty program on Quorum

This tutorial will guide you through creating a simple loyalty program contract and deploying it on your Quorum network.

The loyalty program does the following:

  • Joins each account that calls the contract.
  • Lets each account check their loyalty program balance.

This tutorial uses Truffle as Truffle officially supports Quorum.

The contract and the Truffle configuration are in the GitHub repository.

Prerequisites

Overview

To get from zero to a deployed Quorum network with the running contract, do the following:

  1. With Chainstack, create a Consortium project.
  2. With Chainstack, deploy a Quorum network.
  3. With Chainstack, access your Quorum node credentials.
  4. With Truffle, create and compile the contract.
  5. With Truffle, deploy the contract to your local development network.
  6. With Truffle, test the contract.
  7. With Truffle, deploy the contract to your Quorum network running with Chainstack.

Step-by-step

Create a Consortium project

See Create a project.

Deploy a Quorum network

See Deploy a consortium network.

Get your Quorum node access and credentials

See View node access and credentials.

Create and compile the contract

  1. On your machine, create a directory for the contract. Initialize Truffle in the directory:
truffle init

This will generate the Truffle boilerplate structure:

.
├── contracts
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
└── truffle-config.js
  1. Go to the contracts directory. Create a loyaltyProgram.sol file in the directory.
pragma solidity ^0.5.8;

contract loyaltyProgram {
    mapping (address => uint) private balances;
    address public owner;

    constructor() public payable {
        /* Set the owner to the creator of this contract */
        owner = msg.sender;
    }

    /// Join a customer with the loyalty program
    function join() public returns (uint){
        address user = msg.sender;
        return user.balance;
    }

    /// Reads balance of the account requesting
    function balance() public view returns (uint) {
        return balances[msg.sender];
    }
}
  • join joins the address with the program.
  • balance returns the loyalty program balance associated with the address.
  • msg.sender is the address that calls the function.
  1. Create 2_deploy_contracts.js in the migrations directory.
var loyaltyProgram = artifacts.require("./loyaltyProgram.sol");

module.exports = function(deployer) {
  deployer.deploy(loyaltyProgram);
};

This will create the loyaltyProgram.sol contract deployment instructions for Truffle.

  1. Compile the contract.

Run:

truffle compile

This will compile the contract and put it in your build/contracts directory in the .json format.

Deploy the contract to your local development network

  1. Start the development network on your machine:
truffle develop
  1. Without exiting the Truffle console, deploy the contract to the local development network:
truffle(develop)>  migrate

This will deploy the contract to the development network as specified in truffle-config.js.

Test the contract

  1. Navigate to the test directory.
  2. Create a loyaltyProgramTest.js file:
var loyaltyProgram = artifacts.require("./loyaltyProgram.sol");

contract("loyaltyProgram", function(accounts) {
  const account = accounts[0]

  it("should join the address with the program", async () => {
    const program = await loyaltyProgram.deployed();

    await program.join({from: account});
  });
});
  1. Start the local development network:
truffle develop
  1. Without exiting the Truffle console, run the test:
truffle(develop)> test

The test run output should be Passing.

Deploy the contract to your Quorum network

  1. Install HDWalletProvider.

HDWalletProvider is Truffle's separate npm package used to sign transactions.

Run:

npm install @truffle/hdwallet-provider
  1. Edit truffle-config.js to add:
  • HDWalletProvider
  • Your Quorum network running with Chainstack
const HDWalletProvider = require("@truffle/hdwallet-provider");
const mnemonic = "word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15";

module.exports = {
  networks: {
    development: {
        host: "127.0.0.1",
        port: 9545,
        network_id: "5777"
    },
    quorum: {
        provider: () => new HDWalletProvider(mnemonic, "RPC_ENDPOINT"),
        network_id: "*",
        gasPrice: 0,
        gas: 4500000,
        type: "quorum"
    }
   }
};

where

  • quorum — any network name that you will pass to the truffle migrate --network command.
  • HDWalletProvider — Truffle's custom provider to sign transactions.
  • mnemonic — your mnemonic that generates your accounts. You can also generate a mnemonic online with Mnemonic Code Converter. Make sure you generate a 15 word mnemonic.
  • RPC_ENDPOINT — your Quorum node RPC endpoint. The format is https://user-name:pass-word-pass-word-pass-word@nd-123-456-789.p2pify.com. See View node access and credentials.
  • network_id — your Quorum network ID. Available under Access and credentials > Network ID. You can set it to * for any.
  • gasPrice — the setting must be 0 for the Quorum network.
  • gas — the setting must be the default 4500000 for the Quorum network.
  • type — the setting must be quorum to instruct Truffle for the Quorum network deployment.

Example:

const HDWalletProvider = require("@truffle/hdwallet-provider");
const mnemonic = "word1 word2 word3 word4 word5 word6 word7 word8 word9 word10 word11 word12 word13 word14 word15";

module.exports = {
  networks: {
    development: {
        host: "127.0.0.1",
        port: 9545,
        network_id: "5777"
    },
    quorum: {
        provider: () => new HDWalletProvider(mnemonic, "https://user-name:pass-word-pass-word-pass-word@nd-123-456-789.p2pify.com"),
        network_id: "*",
        gasPrice: 0,
        gas: 4500000,
        type: "quorum"
    }
   }
};
  1. Run:
truffle migrate --network quorum

This will engage 2_deploy_contracts.js and deploy the loyaltyProgram.sol contract to your Quorum network as specified in truffle-config.js.

You can view the deployed contract and the contract address on the Chainstack platform by navigating to your Quorum project > Explorer > Contracts.

Interact with the contract

The following contract interaction example is done with Quorum Geth. For Quorum Geth installation instructions, see Tools.

Connect to a node in your Quorum network

Run:

./geth attach RPC_ENDPOINT

where

  • RPC_ENDPOINT — your Quorum node RPC endpoint with the RPC username and RPC password. The format is https://user-name:pass-word-pass-word-pass-word@nd-123-456-789.p2pify.com.

Example:

./geth attach https://user-name:pass-word-pass-word-pass-word@nd-123-456-789.p2pify.com

This will put you in the Geth console interactive mode.

Set the ABI variable for the contract

Truffle creates the contract's ABI when you run truffle compile and saves it to your project's /build/contracts directory in .json format. Navigate to the directory and get the ABI.

Run:

> var abi = JSON.parse('CONTRACT_ABI')

where

  • CONTRACT_ABI — your contract's ABI in one line.

ABI for loyaltyProgram.sol:

> var abi = JSON.parse('[{"constant":true,"inputs":[],"name": "owner","outputs":[{"name":"","type":"address"}],"payable": false,"stateMutability":"view","type":"function"},{"inputs":[],"payable": true,"stateMutability": "payable","type": "constructor"},{"constant":false,"inputs":[],"name": "join","outputs":[{"name":"","type": "uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs": [],"name":"balance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]')'

Set the contract address

Set the address of the deployed contract.

Run:

> eth.contract(abi).at('CONTRACT_ADDRESS')

where

  • CONTRACT_ADDRESS — the address of the deployed contract. Get the address by navigating on the Chainstack platform to your Quorum project > Explorer > Contracts.

Example:

> eth.contract(abi).at('0x1bF2345B6789BcC1234567aE89cedFE1Ef2E34B5')

Set a variable to the contract at the address

Run:

> var CONTRACT_NAME = eth.contract(abi).at('CONTRACT_ADDRESS')

where

  • CONTRACT_NAME — any name you want to call the contract.
  • CONTRACT_ADDRESS — the address of the deployed contract. Get the address by navigating on the Chainstack platform to your Quorum project > Explorer > Contracts.

Example:

> var loyaltyProgram = eth.contract(abi).at('0x1bF2345B6789BcC1234567aE89cedFE1Ef2E34B5')

Set the default Quorum address to interact with the contract

Run:

> eth.defaultAccount = "QUORUM_ADDRESS"

where

  • QUORUM_ADDRESS — an address created with one of the node deployments. Available on the Chainstack platform under Access and credentials > Default wallet address.

Example:

> eth.defaultAccount = "0x12d34fe5f67ff89f1c23456c78d9123df45cb67a"

Call the contract

As the loyaltyProgram.sol contract has the join function, call join:

> loyaltyProgram.join()

This will create a transaction and give you the transaction ID.

Now check the balance:

> loyaltyProgram.balance()

This will display the balance:

> 0