Skip to main content

Getting Started

Overview

This tutorial will guide you on how to set up a Machine using the mintBlue SDK. You will set up a new project directory, install the SDK, and create a small demo Machine tracking the plantation of trees to offset carbon emissions. Let's get green 🌲

Prerequisites

info

If you don't want to copy code, you can run the example explained in this tutorial by cloning the mintBlue SDK and running the example.

git clone https://gitlab.com/mintBlue/sdk.git
cd sdk
npm install
npm run build
cd examples/machine

Make sure to replace the sdkToken and project_id variables in the main.ts file with your own SDK Access Token and project ID. Then, run the example with the following command:

npm run example-cli

You can now interact with the example.

Step 1: Create your project directory

Open your terminal and create a directory called forest-app. After you create the project directory, navigate to the directory by running the following command:

mkdir forest-app && cd forest-app

Initialize a Node.js project in this new directory by running the following command:

npm init -y

Install the mintBlue SDK with NPM.

npm install @mintblue/sdk

Next, let's create two files. The first file main.ts holds the Machine logic while the second file forest.ts holds the application logic.

touch main.ts && touch forest.ts

And finally, let's add some skeleton code to our main.js file. Make sure to replace the placeholder <YOUR-SDK-TOKEN> with your SDK Access Token and <YOUR-PROJECT-ID> with your project ID.

main.ts
import { Machine, MemoryRecorder, MintblueReader, MintblueWriter } from "@mintblue/sdk";
import { Forest } from "./forest.js";

async function main() {
const sdkToken = "<YOUR-SDK-TOKEN>";
const project_id = "<YOUR-PROJECT-ID>";
}

main();

Step 2: Create the forest application logic

The App represents a class that contains the logic for your application interacting with a blockchain. Each App maintains a state and includes one or multiple functions that modify the state.

It's time to implement the business logic for our tree-planting application. Open your forest.js file, and import the App object from the mintBlue SDK to create our application logic.

forest.ts
import { App } from "@mintblue/sdk";

Now, we can add the Forest class that implements the SDK App class. It expects an AppState called state. The state object contains our planted trees and an estimation of the carbon we've offset.

forest.ts
import { App } from "@mintblue/sdk";

// The interface for the state of the Forest app.
interface ForestState {
trees: { location: string; info?: Record<string, any> }[];
estimatedCarbonOffset: number;
}

export class Forest implements App {
state: ForestState = {
trees: [],
estimatedCarbonOffset: 0,
};
}

The plantTrees function expects a location and the amount of trees we've planted. Notice the _ctx context argument, a mandatory argument containing the application context. If a public function modifies the state, it requires the _ctx argument as the first argument.

forest.ts
import { App } from "@mintblue/sdk";

// The interface for the state of the Forest app.
interface ForestState {
trees: { location: string; info?: Record<string, any> }[];
estimatedCarbonOffset: number;
}

export class Forest implements App {
state: ForestState = {
trees: [],
estimatedCarbonOffset: 0,
};

plantTrees(_ctx: Context, location: string, amount: number) {
for (let i = 0; i < amount; i++) {
this.state.trees.push({
location: location,
});
}
this.state.estimatedCarbonOffset = this._estimateCarbonOffset();
}

private _estimateCarbonOffset() {
return this.state.trees.length * 42;
}
}

When you plant a new tree, we automatically update the estimated carbon offset in the state with the result of a private function. This function doesn't require the _ctx because it doesn't directly modify the state.

Step 3: Create a new Forest Machine

The forest application logic is ready to create our forest Machine.

The Forest class has already been imported. We must create a new MemoryRecorder and our blockchain Reader and Writer. Besides that, we tell the machine to emit application events with the same name as our app function and send the latest snapshot.

main.ts
import { Machine, MemoryRecorder, MintblueReader, MintblueWriter } from "@mintblue/sdk";
import { Forest } from "./forest.js";

async function main() {
const sdkToken = "<YOUR-SDK-TOKEN>";
const project_id = "<YOUR-PROJECT-ID>";

const recorder = new MemoryRecorder();
const machine = new Machine(
Forest,
await MintblueReader.create(sdkToken, project_id),
await MintblueWriter.create(sdkToken, project_id),
recorder,
{ emitAppEvents: true }
);

const app = machine.getInstance();
}

main();

And finally, the getInstance() method lets us get an instance of our machine that we can use in the next step to interact with. The app object represents a proxied version of the application logic where our plantTrees() method is wrapped to write actions to the action storage automatically. In this example, the action storage is a blockchain, but it can also be an alternative storage solution like an S3 bucket.

info

When we call a function on the app object, like plantTrees(), it won't execute it. It will write the function name and arguments to the blockchain.

When the MintblueReader receives an event from the mintBlue platform, the plantTrees() function is executed, and the application state is updated.

📚 If you want to learn more about this software design pattern, it's called Command-Query Responsibility Segregation (CQRS) in Event Sourcing.

Step 4: Interact with the Forest App

Let's have some fun with our Forest application 🥳 We would like to add an event handler to listen for freshly planted trees, plant a new tree in Brussels, and start dispatching events from the Reader to update our application state.

Listen for "plantTrees" events

First, let's listen for plantTrees evens. You can add a new event listener to the machine object using the .on keyword and provide it with the function name you want to listen for. Make sure the function exists in your AppLogic, like our plantTrees function.

To verify our state has been updated, let's log the application state by accessing the state property of the app object.

main.ts
machine.on("plantTrees", (snapshot) => {
console.log("A tree was planted");
console.log(`Location: ${snapshot.action.args[0]}`);
console.log(app.state);
});

Plant three new trees in Brussels

Now, let's plant a new tree by calling our application instance. Remember, we are not executing the function in the AppLogic. We are sending the function name and arguments to the mintBlue platform to be recorded on the blockchain.

main.ts
console.log("Planting 🌲");
await app.plantTrees("Brussels", 3);
console.log(app.state);

Let's log the application state here. You'll find an empty state because the state is only updated when our Reader receives an event.

{ "trees": [], "estimatedCarbonOffset": 0 }

Therefore, let's start our Reader in the next step.

Start dispatching Reader events

Let's start dispatching events by calling the start() function on the machine object. It instructs the Reader to begin dispatching events, like our plantTrees events.

main.ts
machine.start();

When this function is called, we expect the event listener to print the text A tree was planted with its location and update the state by executing our plantTrees function. This will update the state. You should see the following state, including three planted trees in Brussels.

{
"trees": [
{ "location": "Brussels" },
{ "location": "Brussels" },
{ "location": "Brussels" }
],
"estimatedCarbonOffset": 126
}

Below, you can find the full code implementation.

Code Check ✅

import { Machine, MemoryRecorder, MintblueReader, MintblueWriter } from "@mintblue/sdk";
import { Forest } from "./forest.js";

async function main() {
const sdkToken = "<YOUR-SDK-TOKEN>";
const project_id = "<YOUR-PROJECT-ID>";

const recorder = new MemoryRecorder();
const machine = new Machine(
Forest,
await MintblueReader.create(sdkToken, project_id),
await MintblueWriter.create(sdkToken, project_id),
recorder,
{ emitAppEvents: true }
);

const app = machine.getInstance();

machine.on("plantTrees", (snapshot) => {
console.log("A tree was planted");
console.log(`Location: ${snapshot.action.args[0]}`);
console.log(app.state);
});

console.log("Planting 🌲");
await app.plantTrees("Brussels", 3);
console.log(app.state);

machine.start();
}

main();