Skip to main content

How to implement a custom writer?

Summary

If you want more control over the writing part of the mintBlue Machine, you can implement a custom MintblueWriter. In this tutorial, you'll learn which methods you need to implement to build a successful custom Writer.

Prerequisites

We recommend you complete the Getting Started section before implementing a custom Writer.

Step 1: Implementing required methods

A Writer class expects only three methods to be implemented. The fetchWriteStatus() function is optional.

interface Writer {
    /** Writes action to the storage. */
    write(action: ActionWithoutId): Promise<void>;

    /** Handles errors or rejections that occur during the write process. */
    handleError(error: any): void;

    /**
     * (Optional) Fetches the confirmation or status of the written data.
     * Useful for ensuring data integrity or completion.
     */
    fetchWriteStatus?(dataId: string): Promise<string>;
}
  • static async create(sdkToken, project_id)
  • async write(action)
  • handleError(error)

Below is the skeleton for your class. The constructor expects an initialized mintBlue client object and your project ID.

import { Mintblue, Action } from "@mintblue/sdk";

export class CustomMintblueWriter {
    private mintblue: Mintblue;
    private project_id: string;

    constructor(mintblue: Mintblue, project_id: string) {
        this.mintblue = mintblue;
        this.project_id = project_id;
    }

    /**
     * Factory method to create an instance of the CustomMintblueWriter.
     * Initializes the mintBlue SDK and returns a new writer instance.
     */
    static async create(sdkToken: string, project_id: string): Promise<CustomMintblueWriter> {}

    /**
     * Writes the given action as a transaction to the mintBlue platform.
     */
    async write(action: Action): Promise<void> {}

    /**
     * Handles and logs errors from the write operations.
     */
    handleError(error: any): void {
        console.log("MintblueWriter Error " + error);
    }
}

Implementation of the create() method is optional. However, it is recommended to define this method to facilitate the instantiation of the class. Direct instantiation via the constructor is not possible for a mintblue object due to the requirement of asynchronous logic.

Step 2: Adding the create() logic

The create() function is a static method to create an instance of your CustomMintblueWriter. Make sure to create a new instance of your class name. It uses an instantiated mintBlue client object and your project ID.

/**
 * Factory method to create an instance of the CustomMintblueWriter.
 * Initializes the mintBlue SDK and returns a new writer instance.
 */
static async create(sdkToken: string, project_id: string): Promise<CustomMintblueWriter> {
  const mintblue = await Mintblue.create({ token: sdkToken });
  return new CustomMintblueWriter(mintblue, project_id);
}

Step 3: Adding the write() logic

The write logic sends an action to the mintBlue platform, writing it to storage. An action always contains the function name f and function arguments args. You must use the createTransaction function to send the data to the mintBlue platform.

The createTransaction function expects an object with outputs. First of all, you need to send a data output. The value property expects the function name f and function arguments args. However, you can modify this object and add optional data like a time property.

async write(action: Action): Promise<void> {
  if (!this.mintblue) {
    throw new Error('mintBlue SDK not initialized yet');
  }

  try {
    await this.mintblue.createTransaction({
      project_id: this.project_id,
      outputs: [
        {
          type: 'data',
          value: { f: action.f, args: action.args, time: Date.now() } as any,
          encrypt: true,
          sign: true
        }
      ]
    });
  } catch (e) {
    this.handleError(e);
  }
}

It's also possible to send multiple data outputs, like an additional data output, or a micropayment or file output.

Here's an example with an additional data output.

async write(action: Action): Promise<void> {
  if (!this.mintblue) {
    throw new Error('mintBlue SDK not initialized yet');
  }

  try {
    await this.mintblue.createTransaction({
      project_id: this.project_id,
      outputs: [
        {
          type: 'data',
          value: { f: action.f, args: action.args } as any,
          encrypt: true,
          sign: true,
        },
        {
          type: 'data',
          value: { time: Date.now() } as any,
          encrypt: false,
          sign: false
        }
      ]
    });
  } catch (e) {
    this.handleError(e);
  }
}

How do you use a custom Writer?

Finally, let's use your CustomMintblueWriter. Ensure you import your Writer and initialize it like in the Getting Started tutorial.

import { CustomMintblueWriter } from "./customWriter.js";

const recorder = new MemoryRecorder();
const writer = await CustomMintblueWriter.create(sdkToken, project_id);

const machine = new Machine(SupplyChain, await MintblueReader.create(sdkToken, project_id), writer, recorder);

Verify the transaction

Additionally, you can verify if your custom Writer works as expected by inspecting your transactions in the mintBlue project overview. Navigate to your project's transactions tab, and you can verify the transaction and its data.

transactions

Click the "View" button to inspect the transaction details. You'll find a similar output with one or multiple data outputs like the JSON data below.

[
    {
        "type": "data",
        "value": {
            "f": "plantTrees",
            "args": ["Belgium", 1]
        },
        "encrypt": true,
        "sign": true
    },
    {
        "type": "data",
        "value": {
            "time": 1698404885242
        },
        "encrypt": false,
        "sign": false
    },
    {
        "type": "payment",
        "to": "12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX",
        "satoshis": 24669
    }
]

Note: mintBlue adds a payment output as the last input to store the data on the chain.

Below, you can find the full code implementation.

Code Check ✅

import { Mintblue, Action } from "@mintblue/sdk";

export class CustomMintblueWriter {
    private mintblue: Mintblue;
    private project_id: string;

    constructor(mintblue: Mintblue, project_id: string) {
        this.mintblue = mintblue;
        this.project_id = project_id;
    }

    /**
     * Factory method to create an instance of the CustomMintblueWriter.
     * Initializes the mintBlue SDK and returns a new writer instance.
     *
     * @param sdkToken - The SDK Access Token for authentication with mintBlue.
     * @param project_id - The project identifier in mintBlue where the data will be written.
     * @returns A promise that resolves to a new CustomMintblueWriter instance.
     */
    static async create(sdkToken: string, project_id: string): Promise<CustomMintblueWriter> {
        const mintblue = await Mintblue.create({ token: sdkToken });
        return new CustomMintblueWriter(mintblue, project_id);
    }

    /**
     * Writes the given action as a transaction to the mintBlue platform.
     *
     * @param action - The action to write.
     * @throws Will throw an error if the mintBlue SDK has not been initialized.
     * @returns A promise that resolves when the write operation completes or rejects on error.
     */
    async write(action: Action): Promise<void> {
        if (!this.mintblue) {
            throw new Error("mintBlue SDK not initialized yet");
        }

        try {
            await this.mintblue.createTransaction({
                project_id: this.project_id,
                outputs: [
                    {
                        type: "data",
                        value: { f: action.f, args: action.args, time: Date.now() } as any,
                        encrypt: true,
                        sign: true
                    },
                    {
                        type: "data",
                        value: { time: Date.now() } as any,
                        encrypt: false,
                        sign: false
                    }
                ]
            });
        } catch (e) {
            this.handleError(e);
        }
    }

    /**
     * Handles and logs errors from the write operations.
     *
     * @param error - The error to handle and log.
     */
    handleError(error: any): void {
        console.log("MintblueWriter Error " + error);
    }
}