Relayer Interface Architecture

Each chain is defined by a chain interface and contract interface, which are defined by the following abstract base classes:

class BaseChainInterface(abc.ABC):
    """
    Base class for all chain interfaces
    Governs transaction retrieval and creation
    """

    @abc.abstractmethod
    def sign_and_send_transaction(self, tx):
        """
        Given a raw transaction, signs it and sends it to the chain
        Args:
            tx: the raw transaction to be sent to the chain
        """
        pass

    @abc.abstractmethod
    def get_transactions(self, address, height=None):
        """
            Retrieves all transactions from the chain that fit interface-dependent filters
        """
        pass

    @abc.abstractmethod
    def get_last_block(self):
        """
            Retrieves the current block height of the chain
        """
        pass


class BaseContractInterface(abc.ABC):
    """
    Base class for all contract interfaces
    Governs contract interaction, execution, and event parsing.
    """
    address = None

    @abc.abstractmethod
    def call_function(self, function_name, *args):
        """
        Given a function in a contract, and the arguments to that function,
        calls it on chain
        Args:
            function_name: the name of the contract function to call
            *args: the (potentially many) arguments to pass to that function
        """
        pass

    @abc.abstractmethod
    def parse_event_from_txn(self, event_name, txn) -> List[Task]:
        """
        Given a transaction, outputs all the events of a particular name
        that were emitted in that transaction
        Args:
            event_name: the event to look for
            txn: the transaction to parse
        Returns: a list of Tasks corresponding to the events
        """
        pass

Any chain that implements these python interfaces can connect to the relayer, by modifying the config generation function, the current implementation of which is given below:

def generate_eth_config(config_dict, provider=None):
    """
    Converts a config dict into a tuple of (chain_interface, contract_interface, event_name, function_name)
    for ethereum
    Args:
        config_dict: a dict containing contract address, contract schema, and wallet address
        provider: an optional API client

    Returns: the relevant tuple of chain, contract, event, and function

    """
    priv_key = bytes.fromhex(os.environ['ethereum-private-key'])
    address = config_dict['wallet_address']
    contract_address = config_dict['contract_address']
    contract_schema = config_dict['contract_schema']
    event_name = 'logNewTask'
    function_name = 'postExecution'
    initialized_chain = EthInterface(private_key=priv_key, address=address, provider=provider)
    initialized_contract = EthContract(interface=initialized_chain, address=contract_address,
                                       abi=contract_schema)
    eth_tuple = (initialized_chain, initialized_contract, event_name, function_name)
    return eth_tuple


def generate_scrt_config(config_dict, provider=None):
    """
        Converts a config dict into a tuple of (chain_interface, contract_interface, event_name, function_name)
        for secret
        Args:
            config_dict: a dict containing contract address, contract schema, and wallet address
            provider: an optional API client

        Returns: the relevant tuple of chain, contract, event, and function

    """
    priv_key = bytes.fromhex(os.environ['secret-private-key'])
    address = config_dict['wallet_address']
    contract_address = config_dict['contract_address']
    with open(f'{Path(__file__).parent.absolute()}/secret_abi.json') as f:
        contract_schema = f.read()
    event_name = 'wasm'
    function_name = list(json.loads(contract_schema).keys())[0]
    initialized_chain = SCRTInterface(private_key=priv_key, address=address, provider=provider)
    initialized_contract = SCRTContract(interface=initialized_chain, address=contract_address,
                                        abi=contract_schema)
    scrt_tuple = (initialized_chain, initialized_contract, event_name, function_name)
    return scrt_tuple


def generate_full_config(config_file, provider_pair=None):
    """
    Takes in a yaml filepath and generates a config dict for eth and scrt relays
    Args:
        config_file: the path to the relevant config file
        provider_pair: a pair of scrt and eth providers, optional

    Returns:
            a dict mapping scrt and eth to their respective configs

    """
    with open(config_file) as f:
        config_dict = safe_load(f)
    if provider_pair is None:
        provider_eth, provider_scrt = None, None
    else:
        provider_eth, provider_scrt = provider_pair
    eth_config = generate_eth_config(config_dict['ethereum'], provider=provider_eth)
    scrt_config = generate_scrt_config(config_dict['secret'], provider=provider_scrt)
    keys_dict = {'secret': {'verification': config_dict['secret']['contract_eth_address'],
                            'encryption': config_dict['secret']['contract_encryption_key']}}
    return {'ethereum': eth_config, 'secret': scrt_config}, keys_dict

Last updated