Building and Deploying EOS Contracts using EOSFactory

The purpose of this tutorial is to demonstrate how you can use EOSFactory to execute the simplest development cycle: create a new contract, edit the code, build the contract, deploy it and interact with it.


Run Python CLI in VSC

Open a bash terminal and run Python CLI:


Once in the Python shell, import the EOSFactory library:

from eosfactory.eosf import *

Create a new contract from template

To create a new contract from a pre-defined template all you need is a name for the contract and the name of the template, for example:

contract = ContractBuilder(project_from_template("foo_bar", template="01_hello_world"))

NOTE: Do not use spaces in contract names. What is allowed are letters, numbers, underscores _, dots . and dashes -. Regarding the second parameter, as of now there are three templates to choose from (i.e. 01_hello_world, 02_eosio_token and 03_tic_tac_toe), with more coming in the future. This parameter is optional, the default value is 01_hello_world.

Create reference to an existing contract

The above command creates a new folder and inside it a new smart-contract file is placed. However, if you want to access an existing smart-contract, use the following syntax and specifying the entire path, for example:

contract = ContractBuilder("/mnt/d/Workspaces/EOS/contracts/foo_bar")

Or use just the folder name, if the contract is located in the workspace you explicitly defined when installing EOSFactory:

contract = ContractBuilder("foo_bar")

In a similar way, you can access demo contracts shipped with EOSFactory, for example:

contract = ContractBuilder("01_hello_world")

Edit the source code

To check the directory where the contract’s files are located:


Locate the folder containing the new contract (if you’re not sure where it is, use the output produced by the contract.path method) and edit the foo_bar.cpp file in your favorite text editor by commenting out line 18, i.e. require_auth( user ):

#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

#define DEBUG

#include "logger.hpp"
#include ""

using namespace eosio;

class hello : public eosio::contract {
    using contract::contract;

    void hi( account_name user ) {
      logger_info( "debug user name: ", name{user} );
      //require_auth( user );
      print( "Hello, ", name{user} );

EOSIO_ABI( hello, (hi) )

Build the new contract

You can generate ABI and the web assembly code separately:


Or you can generate both at the same time:

Deploy the contract

First, start the testnet and initialize the workspace:


Then create an account which will be holding the contract:

create_account("host", master)

Next, let’s redefine the contract, so that it’s associated with the above account and thus becomes deployable:

contract = Contract(host, contract.path())

Or you can use the name of the contract’s folder (provided it’s located in your smart-contract workspace):

contract = Contract(host, "foo_bar")

Or you can use the entire path to the contract’s folder:

contract = Contract(host, "/mnt/d/Workspaces/EOS/contracts/foo_bar")

Next, we can deploy the contract:


NOTE: In a similar way you could deploy a demo contract supplied by EOSFactory, for example:

contract = Contract(host, "01_hello_world")

Or a demo contract supplied by EOSIO, for example:

contract = Contract(host, "eosio.token")

Test the contract

First, let’s create a couple of testing accounts:

create_account("alice", master)
create_account("carol", master)

You can play with the contract by sending it actions with different arguments:

contract.push_action("hi", {"user":alice}, permission=alice)
contract.push_action("hi", {"user":carol}, permission=carol)
contract.push_action("hi", {"user":alice}, permission=carol)
contract.push_action("hi", {"user":carol}, permission=alice)

NOTE: The push_action method takes three parameters:

  • the name of the action, e.g. "hi",
  • the data required by the action, e.g. {"user":alice},
  • the permissions required by the action, e.g. alice.

Regarding permissions, EOSFactory offers several options:

contract.push_action("hi", {"user":alice}, alice)
contract.push_action("hi", {"user":alice}, permission=alice)
contract.push_action("hi", {"user":alice}, permission=(alice, Permission.ACTIVE))
contract.push_action("hi", {"user":alice}, permission=[(alice, Permission.ACTIVE), (carol, Permission.OWNER)])

All the above variations should work, as the contract allows anyone to authorize the hi action.

NOTE: Among other things, the console displays a debugger output, for example:

INFO debug user name: alice @ 12:26:05[17](hi)

This is the result of a logger_info clause in the foo_bar.cpp file (line 17):

logger_info( "debug user name: ", name{user} );

Modify the code, re-compile & re-deploy

And now let’s modify the hi method by uncommenting line 18, so that that contract authenticates the user before further execution:

void hi( account_name user ) {
  logger_info( "debug user name: ", name{user} );
  require_auth( user );
  print( "Hello, ", name{user} );

Re-compile the contract:

And re-deploy the contract:


Now, if we attempt to mismatch the user and the authority, the contract will throw an error:

contract.push_action("hi", {"user":alice}, permission=carol)
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!

But if we use the appropriate authority, there should no error:

contract.push_action("hi", {"user":alice}, permission=alice)

Clean up

When your are done your contract, you might want to delete it from your workspace:


NOTE: The above command removes the entire folder.

To stop the testnet:


To exit Python CLI:


Alternatively, use the ctrl-D shortcut.