Skip to content

Creating Custom HyperBEAM Devices

You can create your own HyperBEAM devices with Erlang, Rust or C++, and test them using WAO JS SDK.

You should write unit tests in device's own language such as Erlang with eunit.

Minimum Viable Device

You can add an arbitrary device in the HyperBEAM/src directory.

-module(dev_foo).
-export([ info/3 ]).
-include_lib("eunit/include/eunit.hrl").
-include("include/hb.hrl").
 
info(Msg, _, Opts) ->
    {ok, hb_ao:set(Msg, #{ <<"version">> => <<"1.0">> }, Opts)}.

Then add your device to the preloaded_devices list in HyperBEAM/src/hb_ops.

preloaded_devices => [
  #{<<"name">> => <<"ans104@1.0">>, <<"module">> => dev_codec_ans104},
  #{<<"name">> => <<"compute@1.0">>, <<"module">> => dev_cu},
  ...
  #{<<"name">> => <<"foo@1.0">>, <<"module">> => dev_foo}
],

Now you can execute the functions using the WAO HB class.

import assert from "assert"
import { describe, it, before, after, beforeEach } from "node:test"
import { wait, toAddr, Server } from "wao/test"
import { HyperBEAM, HB } from "wao"
import { resolve } from "path"
import { readFileSync } from "fs"
 
const cwd = "../../HyperBEAM" // HyperBEAM directory
const wallet = ".wallet.json" // operator wallet relative to cwd
const port = 10001
const gateway = 4000
const url = `http://localhost:${port}`
 
describe("Hyperbeam Legacynet", function () {
  let hbeam, hb, jwk, server
  before(async () => {
    server = new Server({ port: gateway, log: true, hb_url: url })
    hbeam = new HyperBEAM({ cwd, wallet, port, gateway })
    jwk = JSON.parse(
      readFileSync(resolve(import.meta.dirname, cwd, wallet), "utf8")
    )
    await wait(5000)
  })
  beforeEach(async () => {
    hb = await new HB({ url }).init(jwk)
  })
  after(async () => hbeam.kill())
 
  it("should query a custome device", async () => {
    const res = await hb.text(hb.path("foo", "info"))
  })
})

Execution Device for AO Process

To create an execution device for AO process, you need to implement at least init/3, normalize/3, compute/3 and snapshot/3.

-module(dev_foo).
-export([ compute/3, init/3, snapshot/3, normalize/3 ]).
-include_lib("eunit/include/eunit.hrl").
-include("include/hb.hrl").
 
compute(Msg1, Msg2, Opts) ->
    case hb_ao:get([<<"body">>,<<"Action">>], Msg2, Opts) of
	Other ->
	    {ok, hb_ao:set( Msg1, #{  }, Opts )}
    end.
 
init(Msg, Msg2, Opts) -> 
    {ok, hb_ao:set(Msg, #{ }, Opts)}.
 
snapshot(Msg, _Msg2, _Opts) -> {ok, Msg}.
 
normalize(Msg, _Msg2, _Opts) -> {ok, Msg}.

Now you can spawn a process by specifying execution-device.

import assert from "assert"
import { describe, it, before, after, beforeEach } from "node:test"
import { wait, toAddr, Server } from "wao/test"
import { HyperBEAM, HB } from "wao"
import { resolve } from "path"
import { readFileSync } from "fs"
 
const cwd = "../../HyperBEAM" // HyperBEAM directory
const wallet = ".wallet.json" // operator wallet relative to cwd
const port = 10001
const gateway = 4000
const url = `http://localhost:${port}`
 
describe("Hyperbeam Legacynet", function () {
  let hbeam, hb, jwk, server
  before(async () => {
    server = new Server({ port: gateway, log: true, hb_url: url })
    hbeam = new HyperBEAM({ cwd, wallet, port, gateway })
    jwk = JSON.parse(
      readFileSync(resolve(import.meta.dirname, cwd, wallet), "utf8")
    )
    await wait(5000)
  })
  beforeEach(async () => {
    hb = await new HB({ url }).init(jwk)
  })
  after(async () => hbeam.kill())
 
  it("should query a custome device", async () => {
    const { pid } = await hb.spawn({"excecution-device": "foo@1.0"})
    const { slot, res } = await hb.message({ pid })
  })
})