Skip to main content

Rell integration

In the bootstrap project, the rell/src directory gets configured to be the Rell project's root module. rell/src/lib/ft3 directory contains the FT3 module.

You can use the rell/src/module.rell file that already has some template code that could serve as an entry point for your dapp, although you can choose any module structure you like.

Defining dapp_account entity

You can use the account entity that's provided by .lib.ft3.account module for account management. But your dapp might need to record more information about the user, like username, email address, etc. We can add an entity to record that information.

An example of dapp's account could be as follows:

import acc: .lib.ft3.account;

entity dapp_account {
key account: acc.account;
key username: text;

In this user model, there is no public key or user ID. acc.account provides these details. acc.account has an id property that uniquely identifies an account, and access gets controlled by acc.account_auth_descriptor that includes the user's public key.

The underlying structure of acc.account and acc.account_auth_descriptor is available in Account Management.

After you define the dapp_account entity, the next step is to define the operation used to create an instance of dapp_account:

operation create_dapp_account(
username: text,
user_auth: acc.auth_descriptor
) {
val account_id = acc.create_account_with_auth(user_auth);
create dapp_account (
acc.account @ { account_id }

query get_dapp_accounts() {
return dapp_account @? {} ( id =, first_name = .first_name, last_name = .last_name, age = .age);

Restart the node for changes to take effect. Because you've changed the database structure, you need to add the -W option to delete the database and add a new dapp_account table:

postchain/bin/ <dapp_name> -W

Make sure to update your client's Blockchain RID config with the newly generated blockchainRID.

On the client side, you can use ft3-lib (or postchain-client) as follows:

import DirectoryService from "./lib/directory-service";
import { util } from "postchain-client";
import { blockchainRID } from "../configs/constants";

import {
} from "ft3-lib";

const keyPair = util.makeKeyPair();
const user = new User(
new SingleSignatureAuthDescriptor(keyPair.pubKey, [

const blockchain = await Blockchain.initialize(
new DirectoryService()
const session = blockchain.newSession(user);

op("create_dapp_account", "John", "Doe", 30, user.authDescriptor)

You can use the get_dapp_accounts query to verify whether the create_dapp_account operation ran successfully:

const rest = pcl.restClient.createRestClient(nodeApiUrl, blockchainRID, 5);

const gtx = pcl.gtxClient.createClient(
Buffer.from(blockchainRID, "hex"),

const allDappAccoounts = await gtx.query("get_dapp_accounts");


This section reviews some of the built-in utilities that FT3 modules provide.

It would be best to remember that the built-in operations and queries all have a matching interface in the JavaScript library.

Account module

import acc: .lib.ft3.account;


function create_account_with_auth (auth_descriptor): byte_array

Create a new FT3 account using the provided ft3.account.auth_descriptor

  • auth_descriptor: The auth_descriptor used to create this account.
  • return: (equal to auth_descriptor.hash())
function auth_and_log(account_id: byte_array, auth_descriptor_id: byte_array, required_flags: list<text>): account

Authorize given auth_descriptor for required authorization flags, and apply the rate limiter constraints configured in config.template.xml. This is the default authorization mechanism for operations.

  • account_id: id of the account
  • auth_descriptor_id: is equal to auth_descriptor.hash()
  • required_flags: list of required authorization flags (see Account Management)
  • return: the account instance
function require_auth (account, descriptor_id: byte_array, required_flags: list<text>)

Authorizes given auth_descriptor, but doesn't apply rate limiter constraints.

function _add_auth_descriptor (account, auth_descriptor)
function _delete_auth_descriptor(auth_descriptor: account_auth_descriptor)
function _delete_all_auth_descriptors_exclude(account, auth_descriptor_id: byte_array)

Utilities for managing auth_descriptors.


operation delete_auth_descriptor (account_id: byte_array, auth_descriptor_id: byte_array, delete_descriptor_id: byte_array)

operation delete_all_auth_descriptors_exclude(account_id: byte_array, auth_descriptor_id: byte_array)

operation add_auth_descriptor (account_id: byte_array, auth_id: byte_array, new_desc: acc.auth_descriptor)


query get_account_auth_descriptors(id: byte_array)

query get_account_by_id(id: byte_array)

query get_account_by_auth_descriptor(auth_descriptor)

query get_accounts_by_participant_id(id: byte_array)

query get_accounts_by_auth_descriptor_id(descriptor_id: byte_array)

Core module

import core: .lib.ft3.core;


function register_asset (name, issuing_chain_rid: byte_array): asset

Register a new asset on the chain.

function _get_asset_balances(account_id: byte_array): list<(id:byte_array,name:text,amount:integer,chain_id:byte_array)>

Get asset balance of an account.

function ensure_balance(acc.account, asset): balance

Get account's balance of an asset, or create one if it doesn't exist.

struct xfer_input {
account_id: byte_array;
asset_id: byte_array;
auth_descriptor_id: byte_array;
amount: integer;
extra: map<text, gtv>;

struct xfer_output {
account_id: byte_array;
asset_id: byte_array;
amount: integer;
extra: map<text, gtv>;

function _transfer (inputs: list<xfer_input>, outputs: list<xfer_output>)

Perform an asset transfer from accounts described in xfer_input to accounts in xfer_output.

If xfer_output.extra map contains a reg_auth_desc key, then the value is auth_descriptor to create a new account (meaning you can create a new account and then transfer the asset to it immediately in one transaction).


operation transfer (inputs: list<ft3.xfer_input>, outputs: list<ft3.xfer_output>)


query get_asset_balances(account_id: byte_array)

query get_asset_balance(account_id: byte_array, asset_id: byte_array)

query get_asset_by_name(name)

query get_asset_by_id(asset_id: byte_array)

query get_all_assets()

query get_payment_history(account_id: byte_array, after_block: integer)