Create Publication

We are looking for publications that demonstrate building dApps or smart contracts!
See the full list of Gitcoin bounties that are eligible for rewards.

Tutorial Thumbnail
Intermediate · 30 minutes

Algo Builder Tutorial Part 5: Algob Console

This is the fifth tutorial from the Algo Builder series:

In this tutorial we present how to use algob console to quickly and easily interact with Algorand Standard Assets and Smart Contracts.

Requirements

  • Brief knowledge about Blockchain and Algorand.
  • Brief introduction to algob
  • Brief knowledge about assets, accounts, transactions and signatures.

Background

Algo Builder fills a gap in the Algorand ecosystem to create and manage projects. Our goal is to make shipping Algorand applications simple, efficient, and scalable. Think about it as a Truffle suite for Algorand. For more info, check our articles in the developer portal.

Algob Console Introduction

Sometimes it’s nice to work with your contracts interactively for testing and debugging purposes, getting network config, paths, or for executing transactions by hand.

algob provides you an easy way to do this via an interactive console, with your contracts available and ready to use.

Usage

NOTE: Make sure you have installed algob and configured algob in your project. For creating and setting up a new project, click here.

  • To open console session run algob console in your project root.
  • To select network add --network networkName to the command.(eg. algob console --network localhost)
  • To exit algob console type .exit, ctrl + D or ctrl + C (twice).
  • To clear REPL console, use ctrl + L.
  • To enter multi-line mode type: .break.

After opening console, you should get the following:
image

Globals

Following globals are available in an algob console REPL:

  • deployer : algob deployer in run mode. User can access checkpoints, get logic signature, transferAlgos and all other functions supported by algob deployer.
  • algodClient : algosdk.Algodv2- an instance of algorand driver based on the current network (default if --network flag is not passed).
  • algosdk : User can access algosdk package functions using this object. (eg. algosdk.encodeAddress(..))
  • algob : all algob exported functions (eg. algob.mkAccounts(..), algob.balanceOf(..) etc)

Example Walkthrough

In the next section, for demonstration purposes, we will be using examples/asa project where user will be able to setup scripts and accounts, transfer algo’s (in microalgos) & ASA between accounts, interact with stateless smart contracts (contract account and delegation signature mode) using algob console.

Steps

Setup

In the examples/asa directory:
1. Follow the README file
2. deploy assets and smart contracts using algob deploy

This will deploy your assets (gold and tesla in this case) and store asc1 logic signature in checkpoint for delegated approval mode. Also, let’s initialize some of the accounts which we will use in further transactions.

Open a console session and initialize master account using deployer object

config file:  ../algob.config-template.js
★  Welcome to algob console ★
Try typing: config
algob> masterAccount = deployer.accountsByName.get("master-account")
{
  name: 'master-account',
  addr: 'WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE',
  sk: Uint8Array(64) [
     81, 210,  16, 184, 214, 254, 152, 138, 107, 191,  12,
    188, 175, 162,  72, 134,  82, 233, 249,  40,  97, 197,
    132,  81, 113,  16, 244,  19, 200, 221, 193, 155, 181,
    176, 219, 238, 106, 134,  42,  72, 146, 178, 183, 161,
      7, 238,  87, 145,  92,  34, 110,  74, 164, 136, 138,
    167, 248,   1, 168, 242,   0,  77,  71, 104
  ]
}

Similarly, initialize few more accounts (check algob.config.js for more details about accounts used in ASA template)

let goldOwner = deployer.accountsByName.get("alice");
let john = deployer.accountsByName.get("john");
let bob = deployer.accountsByName.get("bob");

You can also retreive asset information from checkpoint. Eg.

algob> deployer.asa
Map(1) {
  'gold' => {
    creator: 'EDXG4GGBEHFLNX6A7FGT3F6Z3TQGIU6WVVJNOXGYLVNTLWDOCEJJ35LWJY',
    txId: 'QBKYATG6Y7BS5A5NXE6OYRZ5B5TY22EOJC2UEK25GFMW5S7WVEVA',
    assetIndex: 25,
    confirmedRound: 3446,
    assetDef: {
      total: 5912599999515,
      decimals: 0,
      defaultFrozen: false,
      unitName: 'GLD',
      url: 'url',
      metadataHash: '12312442142141241244444411111133',
      note: 'note',
      noteb64: 'noteb64',
      manager: 'WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE',
      reserve: '2ILRL5YU3FZ4JDQZQVXEZUYKEWF7IEIGRRCPCMI36VKSGDMAS6FHSBXZDQ',
      freeze: 'WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE',
      clawback: 'WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE',
      optInAccNames: [Array]
    }
  }
}
algob> 

Let us now execute some transactions. We will use algob.executeTransaction for executing transactions on the Algorand Network.

Transfer Algos

Here, we will transfer 1 Algo from masterAccount to john. Code can be found in /scrips/transfer/master-fund-john.js.

Transaction params looks like :-

algob> rtypes = algob.runtime.types
algob> algoTransferParams = {
...   type: rtypes.TransactionType.TransferAlgo,
...   sign: rtypes.SignType.SecretKey,
...   fromAccount: masterAccount,
...   toAccountAddr: john.addr,
...   amountMicroAlgos: 1e6,
...   payFlags: { note: 'ALGO PAID' }
... };
{
  type: 0,
  sign: 0,
  fromAccount: {
    name: 'master-account',
    addr: 'WWYNX3TKQYVEREVSW6QQP3SXSFOCE3SKUSEIVJ7YAGUPEACNI5UGI4DZCE',
    sk: Uint8Array(64) [
       81, 210,  16, 184, 214, 254, 152, 138, 107, 191,  12,
      188, 175, 162,  72, 134,  82, 233, 249,  40,  97, 197,
      132,  81, 113,  16, 244,  19, 200, 221, 193, 155, 181,
      176, 219, 238, 106, 134,  42,  72, 146, 178, 183, 161,
        7, 238,  87, 145,  92,  34, 110,  74, 164, 136, 138,
      167, 248,   1, 168, 242,   0,  77,  71, 104
    ]
  },
  toAccountAddr: '2UBZKFR6RCZL7R24ZG327VKPTPJUPFM6WTG7PJG2ZJLU234F5RGXFLTAKA',
  amountMicroAlgos: 1000000,
  payFlags: { note: 'ALGO PAID' }
}

Executing the above transaction looks like:

algob> await algob.executeTransaction(deployer, algoTransferParams);
{
  'confirmed-round': 3727,
  'pool-error': '',
  'receiver-rewards': 184,
  'sender-rewards': 3997582,
  txn: {
    sig: Uint8Array(64) [
       57, 175,  29, 200,  41, 197,  17, 105,  16,   6, 207,
      212, 234,  17,  11, 212, 103, 128, 253,  57,  65, 153,
       47,  59, 220, 182, 226, 212,  35,  72, 248, 103, 123,
       76,   1, 223, 193, 196, 117, 231, 147,  45,  80,  53,
      104, 133, 229, 135, 144, 198,  21, 238,  73, 253, 177,
      135, 114, 142,  42,  36,  42,  97, 130,  11
    ],
    txn: {
      amt: 1000000,
      fee: 257000,
      fv: 3725,
      gen: 'private-v1',
      gh: [Uint8Array],
      lv: 4725,
      note: [Uint8Array],
      rcv: [Uint8Array],
      snd: [Uint8Array],
      type: 'pay'
    }
  }
}

Transfer Assets

We will transfer a single unit of gold ASA (which we deployed during the setup) from goldOwner to john. Relevant code can be found in /scrips/transfer/gold-to-john.js.

Let’s use .editor mode of REPL to write & execute multiple lines of code at once:-

algob> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
const rtypes = algob.runtime.types;
const gold = deployer.asa.get('gold'); // asa info from checkpoint
const goldOwner = deployer.accountsByName.get('alice');
const john = deployer.accountsByName.get('john');
algob.executeTransaction(deployer, {
  type: rtypes.TransactionType.TransferAsset,
  sign: rtypes.SignType.SecretKey,
  fromAccount: goldOwner,
  toAccountAddr: john.addr,
  amount: 1,
  assetID: gold.assetIndex,
  payFlags: {}
});

After transferring ASA, you can also check the balance (asset holding) of john using algob.balanceOf(..)

algob> await algob.balanceOf(deployer, john.addr, gold.assetIndex);
Asset Holding Info: {
  amount: 3,
  'asset-id': 25,
  creator: 'EDXG4GGBEHFLNX6A7FGT3F6Z3TQGIU6WVVJNOXGYLVNTLWDOCEJJ35LWJY',
  'is-frozen': false
}

Similar example can be found in /scrips/transfer/tesla-to-john.js (tesla ASA).

Transfer Algos according to ASC logic (Contract Account)

Here we will transfer some algos from a stateless smart contract (/assets/teal/2-gold-contract-asc.teal) to john.
+ We will first load the logic signature (using deployer.loadLogic(<file_name>.teal) and get it’s address(lsig.address()).
+ This address will be the sender(contract account mode) and receiver will be john.
+ Finally, we will transfer some algos using algob.executeTransaction(..) function. Transaction will pass/fail according to asc logic.

algob> lsig = await deployer.loadLogic("2-gold-contract-asc.teal");
LogicSig {
  tag: Uint8Array(7) [
     80, 114, 111,
    103, 114,  97,
    109
  ],
  logic: [
     2, 32,  4,  1,  4, 100, 144, 78, 49, 16,
    34, 18, 49, 16, 35,  18,  17, 49,  8, 36,
    14, 16, 49, 18, 36,  14,  16, 49, 32, 50,
     3, 18, 16, 49,  9,  50,   3, 18, 16, 49,
     1, 37, 14, 16
  ],
  args: [],
  sig: undefined,
  msig: undefined
}
algob> sender = lsig.address();
'QLDZKYJNITRHIL36SGNWJG2GXTMKO2WMRKELUT3C3OFPXKU47FUFCF6OEU'

The contract ensures that amount in microalgos must be <=100, otherwise the transaction will be rejected.

Transaction Pass:

algob> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
// Transactions for Transaction for ALGO - Contract : '2-gold-contract-asc.teal'  (Contract Mode)
// sender is contract account
const algoTxParam = {
  type: rtypes.TransactionType.TransferAlgo,
  sign: rtypes.SignType.LogicSignature,
  fromAccountAddr: lsig.address(),
  toAccountAddr: john.addr,
  amountMicroAlgos: 20n, // amt < 100
  lsig: lsig,
  payFlags: { totalFee: 1000 }
};

// Transaction PASS - As according to .teal logic, amount should be <= 100
algob.executeTransaction(deployer, algoTxParam);

{
  'confirmed-round': 4418,
  'pool-error': '',
  'receiver-rewards': 93,
  'sender-rewards': 1,
  txn: {
    lsig: { l: [Uint8Array] },
    txn: {
      amt: 20,
      fee: 1000,
      fv: 4416,
      gen: 'private-v1',
      gh: [Uint8Array],
      lv: 5416,
      rcv: [Uint8Array],
      snd: [Uint8Array],
      type: 'pay'
    }
  }
}

Transaction fail:

algob> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
const invalidTxnParams = {
  type: rtypes.TransactionType.TransferAlgo,
  sign: rtypes.SignType.LogicSignature,
  fromAccountAddr: lsig.address(),
  toAccountAddr: john.addr,
  amountMicroAlgos: 200, // amt > 100
  lsig: lsig,
  payFlags: { totalFee: 1000 }
};

// Transaction FAIL - Gets rejected by logic - As according to .teal logic, amount should be <= 100
algob.executeTransaction(deployer, invalidTxnParams);

// rejected by logic
Error: Bad Request
    at Request.callback (/home/ratik/Scale-it/algo-builder/node_modules/superagent/src/node/index.js:879:15)
    at fn (/home/ratik/Scale-it/algo-builder/node_modules/superagent/src/node/index.js:1130:18)
    at IncomingMessage.<anonymous> (/home/ratik/Scale-it/algo-builder/node_modules/superagent/src/node/parsers/json.js:19:7)
...

Code can be found in /scripts/transfer/gold-contract-sc.js

Transfer Assets according to ASC (Delegated Approval)

Here, we will first transfer some Algorand Standard Assets(ASA) from goldOwner (delegating authority in this case) to john according to asc /assets/4-gold-asa.teal.
goldOwner is the delegating authority here, as during deployment (algob deploy) the smart contract’s logic signature was signed by this account (check /scripts/2-gold-asc.js).

Logic signature (stored in checkpoint) is retreived using deployer.getDelegatedLsig('<file_name>.teal').
Assets are transferred using algob.executeTransaction({ type: TransactionType.TransferAsset, ...}).

Retreive lsig & assetId from checkpoint:

algob> lsigGoldOwner = deployer.getDelegatedLsig('4-gold-asa.teal');
LogicSig {
  tag: [
     80, 114, 111,
    103, 114,  97,
    109
  ],
  logic: [
     2,  32,   5,   1,   0,   4, 232,   7, 144,  78,  38,   1,
    32,  32, 238, 110,  24, 193,  33, 202, 182, 223, 192, 249,
    77,  61, 151, 217, 220, 224, 100,  83, 214, 173,  82, 215,
    92, 216,  93,  91,  53, 216, 110,  17,  18,  50,   4,  34,
    18,  49,  22,  35,  18,  16,  49,  18,  35,  18,  16,  49,
    16,  36,  18,  49,   0,  40,  18,  16,  49,  18,  37,  14,
    16,  17,  49,  16,  36,  18,  49,  32,  50,   3,  18,  16,
    49,   9,  50,   3,  18,  16,  49,   1,  33,   4,  14,  16,
    16
  ],
  args: [],
  sig: Uint8Array(64) [
     91, 218, 167,  24,  16, 245,  64, 176, 167, 113, 206,
    221,  37, 222, 184, 149,  22, 193, 197, 144,  50,  10,
    135, 207, 224,  74, 210, 194, 107,  66, 184, 151, 223,
    231, 203, 197, 255, 193, 187, 106, 184, 190, 204, 229,
     79, 143,  63, 255, 193,  56,  22,  46,  23, 200, 253,
     43, 126,  92, 137,  50,  26, 141, 222,  13
  ],
  msig: null
}
algob> assetID =  deployer.asa.get("gold").assetIndex;
25
algob> 

Here, the smart contract rejects a transaction if Asset Amount > 1000. Transactions for passing & failing scenario are shown below :-

algob> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
let validParams = {
  type: rtypes.TransactionType.TransferAsset,
  sign: rtypes.SignType.LogicSignature,
  fromAccountAddr: goldOwner.addr,
  toAccountAddr: john.addr,
  amount: 500,
  assetID: 'gold', // passing asa name is also supported
  lsig: lsigGoldOwner,
  payFlags: { totalFee: 1000 }
};

// Transaction PASS
algob.executeTransaction(deployer, validParams);

{
  'confirmed-round': 4628,
  'pool-error': '',
   ...
}

algob> .editor
// Entering editor mode (Ctrl+D to finish, Ctrl+C to cancel)
validParams.amount = 1500;

// Transaction FAIL
algob.executeTransaction(deployer, validParams);

Error: Bad Request
    at Request.callback (/home/ratik/Scale-it/algo-builder/node_modules/superagent/src/node/index.js:879:15)
    at fn (/home/ratik/Scale-it/algo-builder/node_modules/superagent/src/node/index.js:1130:18)
...

Code can be found in /scripts/transfer/gold-delegated-lsig.js

All smart contracts used in the above demonstration can be found in /assets folder and scripts in /scripts/transfer folder.