Source code for litewax.abigen

import os
import requests

file_start = """from __future__ import annotations
import eospy.cleos
import eospy.keys
import pytz
import datetime as dt
from typing import Tuple, Any

class Action:
    \"\"\"
    Action class for calling actions. 
    In :meth:`result` contains the result of the action.

    :param contract: Contract object
    :type contract: object
    :param action: Action name
    :type action: str
    :param args: Action arguments
    :type args: dict

    \"\"\"

    __slots__ = ("__contract", "__action", "__args", "__result")

    def __init__(self, contract: {name}, action: str, args: dict):
        self.__contract = contract
        self.__action = action
        self.__args = args

        self.__result = self()
    
    @property
    def contract(self) -> {name}:
        \"\"\"
        Contract object

        :return:
        \"\"\"
        return self.__contract

    @property
    def action(self) -> str:
        \"\"\"
        Action name

        :return:
        \"\"\"
        return self.__action

    @property
    def args(self) -> dict:
        \"\"\"
        Action arguments

        :return:
        \"\"\"
        return self.__args

    @property
    def result(self) -> dict:
        \"\"\"
        Result of the action

        :return: 
        \"\"\"

        return self.__result

    def __str__(self):
        return f"[{self.contract.permission}] {self.contract.actor} > {name}::{self.action}({self.args})"
    
    def __repr__(self):
        return self.__str__()

    def __call__(self):
        return self.contract.call(self.action, self.args)

class {name}:
    \"\"\"
    {name} contract object

    :param actor: Account name
    :type actor: str
    :param permission: Permission name
    :type permission: str
    :param node: The node to use (default: https://wax.greymass.com)
    :type node: str

    \"\"\"
    __slots__ = ("__wax", "__actor", "__permission")

    def __init__(self, actor: str="", permission: str="active", node: str="https://wax.greymass.com"):
        self.__wax = eospy.cleos.Cleos(url=node, version='v1')
        self.__actor = actor
        self.__permission = permission

    @property
    def actor(self) -> str:
        \"\"\"
        Account name

        :return:
        \"\"\"
        return self.__actor
    
    @actor.setter
    def actor(self, actor: str) -> None:
        self.__actor = actor

    @property
    def permission(self) -> str:
        \"\"\"
        Permission name

        :return:
        \"\"\"

        return self.__permission

    @permission.setter
    def permission(self, permission: str) -> None:
        self.__permission = permission

    @property
    def wax(self) -> eospy.cleos.Cleos:
        \"\"\"
        Cleos instance

        :return:
        \"\"\"

        return self.__wax

    def __str__(self):
        return f"{name}(actor={self.actor}, permission={self.permission}, node={self.wax.url})"

    def set_actor(self, actor: str):
        self.actor = actor

    def generatePayload(self, account: str, name: str) -> dict:
        if self.actor == "":
            raise ValueError("actor is not set")
        return {
            "account": account,
            "name": name,
            "authorization": [{
                "actor": self.actor,
                "permission": self.permission,
            }],
        }

    def return_payload(self, payload, args) -> dict:
        # if args is empty, return payload
        if not args:
            payload['data'] = ''
            return payload
            
        data = self.wax.abi_json_to_bin(
            payload['account'], 
            payload['name'], 
            args
        )
        payload['data'] = data['binargs']
        return payload

    # ACTIONS
    def call(self, action: str, args: dict) -> dict:
        \"\"\"
        ## CALL ANY ACTION
        - Parametrs:
            - action: str
            - args: dict

        - Returns:
            - dict
        \"\"\"

        base = self.generatePayload("{normal_name}", action)

        return self.return_payload(base, args)

"""

file_action = """
    def {action}(self, {args}) -> dict:
        \"\"\"
        ## ACTION: {name}.{action}
        - Parametrs:
            {description}

        - Returns:
            - dict
        \"\"\"

        {action}_args = {genargs}
        return Action(self, "{action}", {action}_args)

"""

file_final = """    # ACTIONS END

    def push_actions(self, private_keys: Any, *actions) -> Tuple[dict, bool]:
        trx = {
            "actions": [a.result for a in list(actions)]
        }
            
        trx['expiration'] = str(
            (dt.datetime.utcnow() + dt.timedelta(seconds=60)).replace(tzinfo=pytz.UTC))

        if isinstance(private_keys, str):
            private_keys = eospy.keys.EOSKey(private_keys)

        elif isinstance(private_keys, list):
            private_keys = [eospy.keys.EOSKey(i) for i in private_keys]

        resp = self.wax.push_transaction(trx, private_keys, broadcast=True)
        return resp, True

    def create_trx(self, private_key: str, **actions) -> Tuple[dict, dict]:
        ac = []
        for k, v in actions.items():
            ac.append(eval(f'self.{k}({", ".join({v})})'))
        return self.push_actions(private_key, ac)

if __name__ == '__main__':
    contract = {name}(actor="")
    contract.push_actions(
        "PRIVATE_KEY",
        contract.transfer(
            _from="",
            to="",
            memo="",
            symbol=""
        )
    )

"""

TYPES = {
    'name': 'str',
    'asset': 'str',
    'uint64': 'int',
    'uint32': 'int',
    'uint16': 'int',
    'uint8': 'int',
    'int64': 'int',
    'int32': 'int',
    'int16': 'int',
    'int8': 'int',
    'float64': 'float',
    'float32': 'float',
    'float128': 'float',
    'bool': 'bool',
    'string': 'str',
    'public_key': 'str',
    'signature': 'str'
}

[docs]def check_ban(text): banwords = { "from": "_from", "for": "_for" } if text not in banwords.keys(): return text else: return banwords.get(text)
[docs]class abigen(): """ abigen class for generating python classes from abi to interact with contracts :param node: wax node url :return: :class:`abigen` class """ __slots__ = ('__node') def __init__(self, node: str="https://wax.greymass.com"): self.__node = node @property def node(self) -> str: """ Node URL """ return self.__node
[docs] def gen(self, name: str) -> str: """ Generate python class from abi :param name: contract name :type name: str :return: content of generated file :rtype: str """ actions = self.get_abi(name) out = file_start for action in actions: if action['name'].isupper(): # it's a table continue for action in actions: amtext = file_action if action['name'].isupper(): # it's a table continue if action.get('fields', {}): formargs = [] for x in action.get('fields', {}): formargs.append(f"{check_ban(x['name'])}: {TYPES.get(x['type'], 'str') if '[]' not in x['type'] else 'list'}") args = ', '.join(formargs) desc = '\n '.join([f"- {x['name']}: {x['type']}" for x in action.get('fields', {})]) else: args= '' desc = '' action_args = "{\n" + str(',\n'.join([f" \"{x['name']}\": {check_ban(x['name'])}" for x in action.get('fields', {})])) + "\n }" for text, target in [ ['{action}', action['name']], ['{description}', desc], ['{args}', args], ['{name}', name], ['{genargs}', action_args] ]: amtext = amtext.replace(text, target) out += amtext out += file_final out = out.replace('{normal_name}', name) out = out.replace('{name}', name.replace('.', '_')) if not os.path.exists('contracts'): os.makedirs('contracts') with open('./contracts/' + name.replace('.', '_') + '.py', "w", encoding='utf-8') as f: f.write(out) return out
[docs] def get_abi(self, account_name: str) -> dict: """ Get contract abi from node :param account_name: contract name :type account_name: str :return: abi :rtype: dict """ r = requests.post(f"{self.node}/v1/chain/get_abi", json={"account_name": account_name}).json() return r['abi']['structs']
[docs] def get_tx_info(self, tx: str) -> dict: """ # Depricated Get transaction info from node :param tx: transaction id :type tx: str :return: transaction info :rtype: dict """ return requests.post(f"{self.node}/v1/history/get_transaction", json={"id": tx, "block_num_hint": 0}).json()
#if __name__ == "__main__": # app = abigen() # app.gen("res.pink")