Get example contract

We have prepared a very simple contract that contains only two files: config.yaml with protocol.proto


Define the contract name, version number, and several files that the contract depends on. Here we have only one data structure definition file.

name: create_product
version: 1
namespace: CoreTx
description: Create product that can be sold within the vending machine
  fg:t:create_product: ForgeAbi.CreateProductTx
  fg:x:product: ForgeAbi.Product
proto: protocol.proto


Define the data structures and new transaction types that will be used in the contract.

syntax = "proto3";
package forge_abi;

message CreateProductTx {
  Product product = 1;

message Product {
  string name = 1;
  string brand = 2;

The above code has been placed on GitHub, and the acquisition method is relatively simple:

git clone
cd forge-product-factory-contract

Prepare the compilation environment

TODO: There should be a few additional tools installed here

Compile contract file

Then execute directly forge contract:compile ./protocolAnd get the following output:

❯ forge contract:compile protocol
ℹ Contract meta: {"name":"create_product","version":1}

ℹ Generating elixir language support:
✔ Latest forge compiler version: v0.18.2
✔ forge-compiler info fetch success
ℹ Start download
ℹ Downloading forge-compiler |████████████████████████████████████████ 100% || 2.84/2.84 MB
✔ Elixir itx generated: .compiled/create_product/elixir/create_product/create_product.itx.json

ℹ Generating JavaScript language support:
✔ Protobuf js generated: .compiled/create_product/javascript/protocol_pb.js
✔ JSON spec generated: .compiled/create_product/javascript/protocol_spec.json
✔ type_urls json generated: .compiled/create_product/javascript/protocol_url.json
✔ JavaScript entry file generated: .compiled/create_product/javascript/index.js

If this is the first time forge contract:compile, Forge CLI will automatically download the contract compilation tool forge-compiler

current contract:compile Can produce can give Forge Elixir SDK with Forge JS SDK The files used also produce result files that can be used directly for deployment to chain nodes.

Deployment contract

If you want to execute the contract directly: forge protocol:deploy .compiled/create_product/elixir/create_product/create_product.itx.jsonAnd the result is as follows:

❯ forge contract:deploy .compiled/create_product/elixir/create_product/create_product.itx.json
ℹ Working on app-chain chain
ℹ Connect to grpc endpoint: tcp://
✔ Moderator checked success
ℹ Deploy contract to tcp://
Contract detail:
{ address: 'z2E3q3PUNCzMNvKi1RuT7KHJ7VnUGZMhjPQwF',
  name: 'create_product',
  version: 1,
  namespace: 'CoreTx',
  description: 'Create product that can be sold within the vending machine',
   [ { url: 'fg:t:create_product',
       module: 'ForgeAbi.CreateProductTx' },
     { url: 'fg:x:product', module: 'ForgeAbi.Product' } ],
   'syntax = "proto3";\npackage forge_abi;\n\nmessage CreateProductTx {\n  Product product = 1;\n}\n\nmessage Product {\n  string name = 1;\n  string brand = 2;\n}\n',
  pipeline: '',
  sourcesList: [],
   [ { checksum: [Uint8Array], binary: [Uint8Array] },
     { checksum: [Uint8Array], binary: [Uint8Array] } ],
  tagsList: [],
  data: undefined }
✔ Contract deploy success
ℹ Inspect tx with forge tx 6800DF1ED99D3A5B9F41C2F54A2644CC4F34A42DAF57B8889E6418553A9EF6F1

Contracts with the same name and the same version are not allowed to be deployed repeatedly, so if the contract code changes, you need to increment the version number, recompile, and then deploy.

Verify deployment results

carried out forge contract:ls | grep create_productIf you can see the results, the deployment is normal.

❯ forge contract:ls | grep create_product
│ create_product          │ z2E3q3PUNCzMNvKi1RuT7KHJ7VnUGZMhjPQwF  │ running       │ v1       │

Use compiled code

In wangshijun/forge-product-factory-contract Examples of how to use compiled code in the Forge JS SDK:

/* eslint-disable no-console */

const moment = require("moment");
const GraphqlClient = require("@arcblock/graphql-client");
const { toAssetAddress } = require("@arcblock/did-util");
const { fromTokenToUnit } = require("@arcblock/forge-util");
const { fromRandom, WalletType } = require("@arcblock/forge-wallet");

const endpoint = process.env.FORGE_API_HOST || ""; // testnet

const client = new GraphqlClient(`${endpoint}/api`);
const sleep = timeout => new Promise(resolve => setTimeout(resolve, timeout));

(async () => {
  try {
    const merchant = fromRandom();
    const customer = fromRandom();
    console.log({ merchant: merchant.toJSON(), customer: customer.toJSON() });

    // 1. declare merchant
    let res = await client.sendDeclareTx({
      tx: { itx: { moniker: "merchant" } },
      wallet: merchant
    console.log("merchant declare tx", res);
      "merchant view link",

    // 2. create vendor machine
    const machine = {
      moniker: `Vending_Machine_${merchant.toAddress()}`,
      readonly: false, // if we want to update the machine, we should set this to false
      transferrable: false,
      data: {
        type: "AssetFactory",
        value: {
          description: "Unlimited Vending Machine",
          limit: 99999,
          price: {
            value: Buffer.from(fromTokenToUnit(1).toBuffer())
          template: JSON.stringify({ name: "{{name}}", brand: "{{brand}}" }),
          allowedSpecArgs: ["name", "brand"],
          assetName: "Product",
          attributes: {
            transferrable: false
    const assetAddress = toAssetAddress(machine);
    machine.address = assetAddress;
    res = await client.sendCreateAssetTx({
      tx: { itx: machine },
      wallet: merchant
    console.log("factory template",;
      "view machine state",
    console.log("create machine tx", `${endpoint}/node/explorer/txs/${res}`);

    // wait for machine state consolidates
    await sleep(3000);

    // 3. read machine
    const { state } = await client.getAssetState({ address: assetAddress });
    console.log("machine state", state);

    // 4. declare customer
    res = await client.declare({ moniker: "customer", wallet: customer });
    console.log("customer declare tx", res);
      "customer view link",

    // 5. get some money
    res = await client.checkin({ wallet: customer });
    console.log("poke tx", res);
    console.log("poke link", `${endpoint}/node/explorer/txs/${res}`);
    await sleep(3000);

    // 6. create product and its address
    const product = {
      ttl: 0,
      readonly: true,
      transferrable: false,
      parent: machine.address,
      data: {
        type: "Product",
        value: { name: "Coca", brand: "Pepsi" }
    const goodAddress = toAssetAddress(product);
    console.log("product value",;

    // 7. acquire asset
    res = await client.sendAcquireAssetTx({
      tx: {
        itx: {
          to: machine.address,
          specs: [
              address: goodAddress,
              data: JSON.stringify(
      wallet: customer
    console.log("product spec", {
      address: goodAddress,
      data: JSON.stringify(
    console.log("product address", goodAddress);
    console.log("acquire tx", res);
    console.log("acquire link", `${endpoint}/node/explorer/txs/${res}`);
  } catch (err) {