EOS Coin Handler

Module contents

EOS Coin Handler

This python module is a Coin Handler for Privex’s CryptoToken Converter, designed to handle all required functionality for both receiving and sending tokens on the EOS network.

It will automatically handle any payments.models.Coin which has it’s type set to eos

To use this handler, you must first create the base coin with symbol EOS:

Coin Name:  EOS
Symbol:     EOS
Our Account: (username of account used for sending/receiving native EOS token)
Custom JSON: {"contract": "eosio.token"}

To change the RPC node from the admin panel, simply set the host/port/username/password on the EOS Coin:

# Below are the defaults used if you don't configure the EOS coin:
Host: eos.greymass.com
Port: 443
User: (leave blank)
Pass: (leave blank)
Custom JSON: {"ssl": True}

Coin Settings (Custom JSON settings)

Tip

You can override the defaults for all EOS coins by setting the settings_json for a coin with the symbol EOS.

All Coin’s handled by the EOS handler will inherit the EOS coin’s custom JSON settings, which can be overrided via the individual coin’s settings_json.

You can set the following JSON keys inside of a Coin’s “settings_json” field to adjust settings such as the “contract account” for the token, whether or not to use SSL with the RPC node, as well as the precision (DP) of the coin, if it’s different from the default of 4 decimal places.

Coin Key Description
endpoint (str) The base URI to query against, e.g. /eos_rpc/
ssl (bool) Whether or not to use SSL (https). Boolean true or false
contract (str) The contract account for this token, e.g. eosio.token or steemenginex
precision (int) The precision (decimal places) of this coin (defaults to 4)
load_method (str) Either actions to use v1/history, or pvx to use Privex EOS History
history_url (str) (if load_method is pvx) Privex history URL, e.g. https://eos-history.privex.io

Copyright:

+===================================================+
|                 © 2019 Privex Inc.                |
|               https://www.privex.io               |
+===================================================+
|                                                   |
|        CryptoToken Converter                      |
|                                                   |
|        Core Developer(s):                         |
|                                                   |
|          (+)  Chris (@someguy123) [Privex]        |
|                                                   |
+===================================================+
payments.coin_handlers.EOS.reload()[source]

Reload’s the provides property for the loader and manager from the DB.

By default, since new tokens are constantly being created for EOS, our classes can provide for any models.Coin by scanning for coins with the type eos. This saves us from hard coding specific coin symbols.

Submodules

EOSLoader module

class payments.coin_handlers.EOS.EOSLoader.EOSLoader(symbols)[source]

Bases: payments.coin_handlers.base.BaseLoader.BaseLoader, payments.coin_handlers.EOS.EOSMixin.EOSMixin

clean_txs(account, symbol, contract, transactions: Iterable[dict]) → Generator[dict, None, None][source]

Filters a given Iterable of dict’s containing raw EOS “actions”:

  • Finds only incoming transfer transactions from accounts that are not us (account)
  • Filters transactions by both symbol and verifies they’re from the contract account
  • Outputs valid incoming TXs in the standardised Deposit format.
Parameters:
  • account (str) – The account which should be receiving symbol
  • symbol (str) – The coin symbol to search for, e.g. “EOS”
  • contract (str) – The EOS contract account to filter by, e.g. “eosio.token”
  • transactions (Iterable[dict]) – An iterable list/generator of EOS actions as dict’s
Return Generator cleaned_txs:
 

A generator yielding valid Deposit TXs as dict’s

get_actions(account: str, count=100) → List[dict][source]

Loads EOS transactions for a given account, and caches them per account to avoid constant queries.

Parameters:
  • account – The EOS account to load transactions for
  • count – Amount of transactions to load
Return list transactions:
 

A list of EOS transactions as dict’s

list_txs(batch=100) → Generator[dict, None, None][source]

Get transactions for all coins in self.coins where the ‘to’ field matches coin.our_account If load() hasn’t been ran already, it will automatically call self.load()

Parameters:batch – Amount of transactions to load per batch
Returns:Generator yielding dict’s that conform to models.Deposit
load(tx_count=1000)[source]

Prepares the loader by disabling any symbols / coin objects that don’t have an our_account set, or don’t have a contract set in either models.Coin.settings_json or default_contracts

Parameters:tx_count – Amount of transactions to load per account, most recent first
Returns:None
provides
pvx_clean_txs(account, symbol, contract, transactions: Iterable[dict]) → Generator[dict, None, None][source]

Filters a given Iterable of dict’s containing EOS “actions” from Privex history API

  • Finds only incoming transfer transactions from accounts that are not us (account)
  • Filters transactions by both symbol and verifies they’re from the contract account
  • Outputs valid incoming TXs in the standardised Deposit format.
Parameters:
  • account (str) – The account which should be receiving symbol
  • symbol (str) – The coin symbol to search for, e.g. “EOS”
  • contract (str) – The EOS contract account to filter by, e.g. “eosio.token”
  • transactions (Iterable[dict]) – An iterable list/generator of EOS actions as dict’s
Return Generator cleaned_txs:
 

A generator yielding valid Deposit TXs as dict’s

pvx_get_actions(account: str, count=100, symbol=None, contract=None, history_url=None) → List[dict][source]

Loads EOS transactions for a given account, and caches them per account to avoid constant queries.

Parameters:
  • account – The EOS account to load transactions for
  • count – Amount of transactions to load
Return list transactions:
 

A list of EOS transactions as dict’s

EOSManager module

class payments.coin_handlers.EOS.EOSManager.EOSManager(symbol: str)[source]

Bases: payments.coin_handlers.base.BaseManager.BaseManager, payments.coin_handlers.EOS.EOSMixin.EOSMixin

address_valid(*addresses) → bool[source]

Check if one or more account usernames exist on the EOS network.

Example:

>>> if not self.address_valid('someguy12333', 'steemenginex'):
...     print('The EOS account "someguy12333" and/or "steemenginex" does not exist. ')
Parameters:addresses (str) – One or more EOS usernames to verify the existence of
Return bool account_exists:
 True if all of the given accounts in addresses exist on the EOS network.
Return bool account_exists:
 False if at least one account in addresses does not exist on EOS.
address_valid_ex(*addresses)[source]

Check if one or more account usernames exist on the EOS network. Throws an exception if any do not exist.

A slightly different version of address_valid() which raises AccountNotFound with the username that failed the test, instead of simply returning True / False.

Parameters:addresses (str) – One or more EOS usernames to verify the existence of
Raises:AccountNotFound – When one of the accounts in addresses does not exist.
balance(address: str = None, memo: str = None, memo_case: bool = False) → decimal.Decimal[source]

Return the balance of self.symbol for our “wallet”, or a given address/account, optionally filtered by memo

Parameters:
  • address – The address or account to get the balance for. If None, return our total wallet (or default account) balance.
  • memo – If not None (and coin supports memos), return the total balance of a given memo
  • memo_case – Whether or not to total memo’s case sensitive, or not. False = case-insensitive memo
Raises:

AccountNotFound – The requested account/address doesn’t exist

Return Decimal:

Decimal() balance of address/account, optionally balance (total received) of a given memo

build_tx(tx_type, contract, sender, tx_args: dict, key_types=None, broadcast: bool = True) → dict[source]

Crafts an EOS transaction using the various arguments, signs it using the stored private key for sender, then broadcasts it (if broadcast is True) and returns the result.

Example:

>>> args = {"from": "someguy12333", "to": "steemenginex", "quantity": "1.000 EOS", "memo": ""}
>>> res = self.build_tx('transfer', 'eosio.token', 'someguy12333', args)
>>> print(res['transaction_id'])
dc9ece0dfb8da0b92068e23bdc22c971e0bc713d31ffc1b7552a861197b0d23e
Parameters:
  • tx_type (str) – The type of transaction, e.g. “transfer” or “issue”
  • contract (str) – The contract username to execute against, e.g. ‘eosio.token’
  • sender (str) – The account name that will be signing the transaction, will auto lookup it’s private key
  • tx_args (dict) – A dictionary of transaction arguments to add to the payload data
  • key_types (list) – (optional) Which types of key can be used for this TX? e.g. [‘owner’, ‘active’]
  • broadcast (bool) – (default: True) If true, broadcasts the TX after signing. Otherwise returns just the signed TX and does not broadcast it to the network.
Return dict tfr:
 

The results of the transaction. Includes information about the broadcast if it was sent.

can_issue = True
get_deposit() → tuple[source]
Return tuple:If the coin uses addresses, this method should return a tuple of (‘address’, coin_address)
Return tuple:If the coin uses accounts/memos, this method should return a tuple (‘account’, receiving_account) The memo will automatically be generated by the calling function.
classmethod get_privkey(from_account: str, key_types: list = None) → Tuple[str, str][source]

Find the EOS models.CryptoKeyPair in the database for a given account from_account , decrypt the private key, then returns a tuple containing (key_type:str, priv_key:str,)

If no matching key pair could be found, will raise an AuthorityMissing exception.

Example:

>>> key_type, priv_key = EOSManager.get_privkey('steemenginex', key_types=['active'])
>>> print(key_type)
active
>>> print(priv_key)  # The below private key was randomly generated for this pydoc block, is isn't a real key.
5KK4oSvg9n5NxiAK9CXRd7zhbARpx8oxh15miPTXW8htGbYQPKD
Parameters:
  • from_account (str) – The EOS account to find a private key for
  • key_types (list) – (optional) A list() of key types to search for. Default: [‘active’, ‘owner’]
Raises:
  • AuthorityMissing – No key pair could be found for the given from_account
  • EncryptKeyMissing – CTC admin did not set ENCRYPT_KEY in their .env, or it is invalid
  • EncryptionError – Something went wrong while decrypting the private key (maybe ENCRYPT_KEY is invalid)
Return tuple k:

A tuple containing the key type (active/owner etc.) and the private key.

issue(amount: decimal.Decimal, address: str, memo: str = None, trigger_data=None)[source]

Issue (create/print) tokens to a given address/account, optionally specifying a memo if supported

Parameters:
  • amount (Decimal) – Amount of tokens to issue, as a Decimal()
  • address – Address or account to issue the tokens to
  • memo – Memo to issue tokens with (if supported)
  • trigger_data (dict) – Metadata related to this issue transaction (e.g. the deposit that triggered this)
Raises:
  • IssuerKeyError – Cannot issue because we don’t have authority to (missing key etc.)
  • IssueNotSupported – Class does not support issuing, or requested symbol cannot be issued.
  • AccountNotFound – The requested account/address doesn’t exist
Return dict:

Result Information

Format:

dict {
    txid:str - Transaction ID - None if not known,
    coin:str - Symbol that was sent,
    amount:Decimal - The amount that was sent (after fees),
    fee:Decimal    - TX Fee that was taken from the amount,
    from:str       - The account/address the coins were issued from.
                     If it's not possible to determine easily, set this to None.
    send_type:str  - Should be statically set to "issue"
}
provides
send(amount, address, from_address=None, memo=None, trigger_data=None) → dict[source]

Send a given amount of EOS (or a token on EOS) from from_address to address with the memo memo.

Only amount and address are mandatory.

Parameters:
  • amount (Decimal) – Amount of coins/tokens to send, as a Decimal()
  • address (str) – Destination EOS account to send the coins/tokens to
  • memo (str) – Memo to send coins/tokens with (default: “”)
  • from_address (str) – EOS Account to send from (default: uses Coin.our_account)
Raises:
Return dict:

Result Information

Format:

dict {
  txid:str       - Transaction ID - None if not known,
  coin:str       - Symbol that was sent,
  amount:Decimal - The amount that was sent (after fees),
  fee:Decimal    - TX Fee that was taken from the amount (static Decimal(0) for EOS)
  from:str       - The account the coins were sent from.
  send_type:str  - Statically set to "send"
}
send_or_issue(amount, address, memo=None, trigger_data=None) → dict[source]

Attempt to send an amount to an address/account, if not enough balance, attempt to issue it instead. You may override this method if needed.

Parameters:
  • amount (Decimal) – Amount of coins/tokens to send/issue, as a Decimal()
  • address – Address or account to send/issue the coins/tokens to
  • memo – Memo to send/issue coins/tokens with (if supported)
  • trigger_data (dict) – Metadata related to this issue transaction (e.g. the deposit that triggered this)
Raises:
  • IssuerKeyError – Cannot issue because we don’t have authority to (missing key etc.)
  • IssueNotSupported – Class does not support issuing, or requested symbol cannot be issued.
  • AccountNotFound – The requested account/address doesn’t exist
Return dict:

Result Information

Format:

dict {
  txid:str       - Transaction ID - None if not known,
  coin:str       - Symbol that was sent,
  amount:Decimal - The amount that was sent (after fees),
  fee:Decimal    - TX Fee that was taken from the amount,
  from:str       - The account(s)/address(es) the coins were sent from. if more than one, comma separated.
                   If it's not possible to determine easily, set this to None.
  send_type:str  - Should be set to "send" if the coins were sent, or "issue" if the coins were issued.
}
validate_amount(amount: Union[decimal.Decimal, float, str], from_account: str = None) → decimal.Decimal[source]

Validates a user specified EOS token amount by:

  • if amount is a float, we round it down to a 4 DP string
  • we then pass the amount to Decimal so we can perform more precise calculations
  • checks that the amount is at least 0.0001 (minimum amount of EOS that can be sent)
  • if from_account is specified, will raise NotEnoughBalance if we don’t have enough balance to cover the TX.

Example:

>>> amount = self.validate_amount(1.23)
>>> amount
Decimal('1.23')
Parameters:
  • amount (Decimal) – The amount of EOS (or token) to be sent, ideally as Decimal (but works with float/str)
  • from_account (str) – (optional) If specified, check that from_account has enough balance for this TX.
Raises:
  • ArithmeticError – When the amount is lower than the lowest amount allowed by the token’s precision
  • NotEnoughBalance – The account from_account does not have enough balance to send this amount.
  • TokenNotFoundfrom_account does not have a listed balance of self.symbol
Return Decimal amount:
 

The amount after sanitization, converted to a Decimal

EOSMixin module

Copyright:

+===================================================+
|                 © 2019 Privex Inc.                |
|               https://www.privex.io               |
+===================================================+
|                                                   |
|        CryptoToken Converter                      |
|                                                   |
|        Core Developer(s):                         |
|                                                   |
|          (+)  Chris (@someguy123) [Privex]        |
|                                                   |
+===================================================+
class payments.coin_handlers.EOS.EOSMixin.EOSMixin[source]

Bases: payments.coin_handlers.base.SettingsMixin.SettingsMixin

EOSMixin - A child class of SettingsMixin that is used by both EOSLoader and EOSManager for shared functionality.

Main features:

- Access the EOS shared instance via :py:attr:`.eos`
- Get the general ``EOS`` symbol coin settings via :py:attr:`.eos_settings`
- Access individual token settings (e.g. contract) via ``self.settings[symbol]``
- Helper method :py:meth:`.get_contract` - get contract via DB, or fall back to :py:attr:`.default_contracts`
- Automatically sets setting defaults, such as the RPC node (using Greymass node over SSL)

Copyright:

+===================================================+
|                 © 2019 Privex Inc.                |
|               https://www.privex.io               |
+===================================================+
|                                                   |
|        CryptoToken Converter                      |
|                                                   |
|        Core Developer(s):                         |
|                                                   |
|          (+)  Chris (@someguy123) [Privex]        |
|                                                   |
+===================================================+
all_coins

Ensures that the coin ‘EOS’ always has it’s settings loaded by base.SettingsMixin by overriding this method all_coins to inject the coin EOS if it’s not our symbol.

Return dict coins:
 A dict<str,Coin> of supported coins, mapped by symbol
chain = 'eos'

This controls the name of the chain and is used for logging, cache keys etc. It may be converted to upper case for logging, and lower case for cache keys. Forks of EOS may override this when sub-classing EOSMixin to adjust logging, cache keys etc.

chain_coin = 'EOS'

Forks of EOS may override this when sub-classing EOSMixin to change the native coin symbol of the network

chain_type = 'eos'

Used for looking up ‘coin_type=xxx’ Forks of EOS should override this to match the coin_type they use for provides generation

current_rpc = None

Contains the current EOS API node as a string

default_contracts = {'EOS': 'eosio.token'}

To make it easier to add common tokens on the EOS network, the loader/manager will fallback to this map between symbols and contracts.

This means that you don’t have to set the contract in the custom JSON for popular tokens in this list, such as the native EOS token (which uses the contract account eosio.token).

eos

Returns an instance of Cleos and caches it in the attribute _eos after creation

eos_settings

Since EOS deals with tokens under one network, this is a helper property to quickly get the base EOS settings

Return dict settings:
 A map of setting keys to their values
get_contract(symbol: str) → str[source]

Attempt to find the contract account for a given token symbol, searches the database Coin objects first using settings - if not found, falls back to default_contracts

Example usage:

>>> contract_acc = self.get_contract('EOS')
>>> print(contract_acc)
 eosio.token
Parameters:

symbol (str) – The token symbol to find the contract for, e.g. EOS

Raises:
Return str contract_acc:
 

The contract username as a string, e.g. eosio.token

provides = ['EOS']

This attribute is automatically generated by scanning for models.Coin s with the type eos. This saves us from hard coding specific coin symbols. See __init__.py for populating code.

replace_eos(**conn) → eospy.cleos.Cleos[source]

Destroy the EOS Cleos instance at _eos and re-create it with the modified connection settings conn

Also returns the EOS instance for convenience.

Only need to specify settings you want to override.

Example:

>>> eos = self.replace_eos(host='example.com', port=80, ssl=False)
>>> eos.get_account('someguy123')
Parameters:conn – Connection settings. Keys: endpoint, ssl, host, port, username, password
Return Cleos eos:
 A Cleos instance with the modified connection settings.
setting_defaults = {'endpoint': '/', 'history_url': 'https://eos-history.privex.io', 'host': 'eos.greymass.com', 'load_method': 'actions', 'password': None, 'port': 443, 'precision': 4, 'ssl': True, 'telos': False, 'username': None}

Default settings to use if any required values are empty, e.g. default to Greymass’s RPC node

load_method can be either pvx for Privex EOS History API, or actions to use v1/history from the RPC node.

settings

Get all settings, mapped by coin symbol (each coin symbol dict contains custom json settings merged)

Return dict settings:
 A dictionary mapping coin symbols to settings
url

Creates a URL from the host settings on the EOS coin