eospyo Tutorial

Detroit Ledger Technologies
7 min readAug 29, 2022

--

So, you’ve read about EOS, you’re already a python developer, and you now want to connect your old favorite technology (Python) to your new favorite technology (EOS). You’ve seen a few options, and there are currently several to choose from, but we believe EOSPYO is the simplest and most direct way to connect python to eosio. If this is you and you’re curious, please read on.

What are the other options and how do they compare

  • pyeoskit :
    They have a great project. It used to be heavily based on UUOSKit, so I’ll focus on just one of them.
  • UUOSKit:
    It seems they found a clever way to use libuuoskit binaries using cython.
    That is a good idea since we need to maintain only one codebase and they may get the benefits from a pre-compiled code.
    But we are quite concerned by the use of an external wallet (in this case, a file on the disk) that would behave like a global state. Also, it is a problem if you're to use non-resilient or read-only file systems (like some containers or function-as-a-service computing).
  • eospy:
    Their project is similar to ours, but seems to have slowed development significantly.
  • µEOSIO:
    We can’t state how thankful we are for this project and the contributors as EOSPYO is based on this and we use some of this code in EOSPYO.
  • Our api, however, is very different. Also we’re using nodeos rpc calls to serialize the data.

Summary, why eospyo is different

We believe it’s our easy to conceptualize api, it’s easy to parallelize, and it works well with some cloud tools, and it allows keys to exist on disk separate from the code files.

Getting Started

Requirements

  1. Python 3.8+ For simplicity, this tutorial is assuming you’ve already gotten Python 3.8+ installed and running. If not, please follow this link
  2. Pip or Poetry is encouraged To install Pip follow this link. To install Poetry follow this link.
  3. Either docker or keys for an eosio testnet
    This link includes various containerized docker instances Alternatively, EOS testnet connections can be found here WAX testnet connections and account creation can be found here Note: if you want to contribute to eospyo development, you will have to setup docker to run tests through pytest.
  4. A contract to interact with ? The contracts relevant to the examples will be linked. However, basic system contracts such as eosio.token are available on all testnets referenced in this document.

Installation

You’ll need to identify a path to your new virtual environment; you can read more about venv (virtual environment) here if you are not already familiar. python3 -m venv /path/to/new/virtual/environment source /path/to/new/virtual/environment/bin/activate pip install eospyo or poetry add eospyo Note: when developing for a real production application, since eospyo api still unstable, we recommend that the library version is fixed. For instance: `pip install eospyo=0.6.0’

Core concepts and ideology

EOSPYO is written and designed to act primarily like a database driver for chain interactions. This model fits well considering an eosio blockchain as a public database wherein the various contracts are effectively stored procedures with only access to other data in active rows.

The eosio core code and smart contracts are written in C++17 and contract actions have their data serialized in a native C++ format as C++ is a strongly typed language. This requires that all interactions with the chain also have their data fully serialized to be compatible with the native C++ code. To provide a faster response during execution, all serialization errors are attempted to be caught at the time of serialization rather than at the time of blockchain execution. The resulting responses from eosio contract calls are also serialized. Currently, EOSPYO deserializes select parts of the response. However, the next major release will include complete deserialization of the response for maximum ability to introspect the results.

Additionally, all eosio actions require actions to be signed. As such keys are required to be stored on disk or in memory and accessible to eospyo. For our examples, the keys are in the code, but this is not the recommended action for a vast number of security implications.

Additional useful tools

Block explorers

https://bloks.io has most eosio chains and their respective testnets available to select from the upper left logo. However, for completeness here are links directly to EOS and WAX main and test nets.

EOS testnet EOS Main net

Wax testnet Wax Main net

EOSIO tooling

Cleos is the command line access to eosio and is available for the posix compliant command line interpreter of your choice. This tool also provides additional options for account creation allowing for additional automation of testing.

EOSIO Reading

CC32D9’s Amazing Smart Contract Developer’s Handbook which contains explanations of how eosio works, how nodeos works, lifecycle of a transaction and a great deal more.

Examples with explanations

WAX System Token Transfer Example

The complete example is included at the bottom if you wish to simply copy/paste.

Requirements

  1. Create two accounts here Save your keys for both accounts as you will require them to complete the exercises EOSIO Naming conventions for account creation can be found here An abbreviated (and sufficient for purposes of this tutorial) are inline with the linked tool.
  2. Add tokens to appropriate accounts at the same location or there are instructions for making this a RESTful call if you have the desire/need to automate the said process for automated testing.

Example break down line by line

"""Transfer some WAX to a receiver account."""
import eospyo

Standard Doc description and importing our library

data = [
eospyo.Data(name="from", value=eospyo.types.Name("me.wam")),
eospyo.Data(name="to", value=eospyo.types.Name("receiver")),
eospyo.Data(
name="quantity",
value=eospyo.types.Asset("55.00000000 WAX"),
),
eospyo.Data(
name="memo",
value=eospyo.types.String("Trying Eospyo"),
),
]

Here we are serializing the required smart contract fields details of which can be found here. As a contract action has multiple fields (labeled as Data types in eospyo), a Data list must be defined with all the required Data of a specified action, and each Data instance must have a name and a value with a specified type (this tells eospyo how to serialize each Data instance).

It is important to note that the types here are aligned with the types as specified by the appropriate contract. A full list of types will be appended to this doc. However, for readability we will list the types being used here and their usage.

  • Name is an account name (string) has to follow the EOSIO account naming specifications (listed in the Data types section).
  • Asset is the type of token as defined in the chain appropriate eosio.token contract.
  • String is a classic UTF8 compliant string. However, we currently do not support multi-byte UTF8 characters (such as special characters, emojis, etc) due to unpredictable serialization, this is in the pipeline for future fixes.
auth = eospyo.Authorization(actor="me.wam", permission="active")

Authorization tells EOSPYO who the actor is i.e. the account name and which key to attempt to use (see here for details on EOSIO key management and the ability to limit key actions) for now use the account you created earlier and populated with tokens as ‘actor’ and “active” as permission type. Though this would be a good way to test proper creation of action specific keys also.

action = eospyo.Action(
account="eosio.token",
name="transfer",
data=data,
authorization=[auth],
)

Action takes the pieces we’ve already built (data and auth) and names the contract account “account” ‘eosio.token’ as previously noted and names the action we wish to execute and packages this into an object that is ready to be serialized completely.

raw_transaction = eospyo.Transaction(actions=[action])

Transaction returns the complete action ready to be connected to a network and executed. Note that a raw_transaction can contain more than one action. The reason Transaction can contain multiple actions is because all actions are atomic, so if you passed in multiple actions, either all are executed or none are ever executed. This is useful for actions that are tied closely together. For example, a transfer (currency) action and a transfer asset action (neither action should execute if the other failed).

net = eospyo.WaxTestnet()
linked_transaction = raw_transaction.link(net=net)

EOSPYO contains a number of predefined EOSIO networks with appropriate setup provided. Extending this is beyond the scope of this tutorial. This step instantiates WAX test net and links our transaction to this network.

key = "a_very_secret_key"
signed_transaction = linked_transaction.sign(key=key)

Note: Key must be replaced with the value of the private key that was generated with account creation. As previously mentioned this should not go into code that will be shared or posted elsewhere. It would be better placed in an appropriate file using the tools of the users choice (see config, secrets and various other tools) [¹]

resp = signed_transaction.send()

This line executes the transaction on the network (finally!) and captures the response.

Complete examples

All complete examples can be found in the github repo

Appendix

Network Names and URLs as defined

WaxTestnet: https://testnet.waxsweden.org/

WaxMainnet: https://facings.waxpub.net

EosMainnet: https://api.eossweden.org

KylinTestnet: https://kylin.eossweden.org

Jungle3Testnet: https://jungle3.eossweden.org

TelosMainnet: https://telos.caleos.io/

TelosTestnet: https://testnet.telos.caleos.io

ProtonMainnet: https://proton.cryptolions.io

ProtonTestnet: https://testnet.protonchain.com

UosMainnet: https://uos.eosusa.news

FioMainnet: https://fio.cryptolions.io

Local: http://127.0.0.1:8888

Data types

The following Data types are currently implemented

UnixTimeStamp

Standard epoch

String

This works for all single byte UTF8 Characters currently

Asset

serializes an amount (can be a float value) and currency name together uses Symbol type to serialize precision and name of currency, uses Uint64 type to serialize amount amount and name are separated by one space example: 50.100000 WAX

Symbol

serializes a precision and currency name together precision is used to indicate how many decimals there are in an Asset type amount precision and name are separated by a comma example: 1,WAX

Bytes

Basic bytes value, used mainly for serialization internally Example: b’\x00'

Array

An Array of other types, value has parameters type and values: type must be defined as eospyo type, and values areis a list of those types. Example parameters: type_ = eospyo.types.Name, values = [“name1”, “name2”]

Name

EOSIO Account name. Read for exact conventions here

Int8

Integer between -128 and 128

Uint8

Unsigned integer between 0 and 256

Uint16

Unsigned integer between 0 and 65536 (2¹⁶)

Uint32

Unsigned integer between 0 and 4294967296 (2³²)

Uint64

Unsigned integer between 0 and 18446744073709551616 (2⁶⁴) This one is most commonly used in contracts since it has the largest range.

Varuint32

Variable Length Unsigned Integer. This provides more efficient serialization of 32-bit unsigned int

EOSPYO Classes and Methods

Exceptions

ConnectionError

This is the only custom exception and is triggered when a connection to the blockchain fails

ValidationError

This is the pydantic error and helps to make sure that all data is correctly serialized.

[¹]: Options include

--

--

Detroit Ledger Technologies

A benevolent block producer crew based in Detroit, MI building value on blockchain networks. Planting new seeds of economic opportunity.