BasicNanoClient

nanohelp.wallet.WalletManager

Business logic to abstract the raw nano node RPC logic.

This class is responsible for creating wallets, adding accounts to wallets, and making transactions.

Source code in nanohelp/wallet.py
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
class WalletManager:
    """Business logic to abstract the raw nano node RPC logic.

    This class is responsible for creating wallets, adding accounts to wallets,
    and making transactions.
    """
    def __init__(
            self: Self,
            secret_manager: SecretManager,
            node_address: str = "http://127.0.0.1:17076") -> None:
        """Initialize the wallet manager.

        Params:
            - node_address: the address of the node to connect to.
                Defaults to the local node at port 17076.

        """
        self.client = BasicNanoClient(node_address)
        LOG.debug("Google Application Credentials: %s", os.environ['GOOGLE_APPLICATION_CREDENTIALS'])
        self.secret_manager = secret_manager

    def create_wallet(self: Self, project: str, name: str) -> Optional[Tuple[str, str]]:
        """Generate a new private key and create a new wallet.

        Params:
            - project: the project to store the secret in
            - name: the name of the wallet to create

        Returns: a tuple containing wallet_id and account_address
        """
        try:
            wallet_id = self.client.wallet_create(
                self.secret_manager.generate_and_store_private_key(project, name)
            )['wallet']
            account_address = self.client.accounts_create(wallet_id)['accounts'][0]
        except Exception as e:
            LOG.error(f"Failed to create wallet: {e}")
            LOG.exception(e)
            return None

        return wallet_id, account_address

    def add_account_to_wallet(self: Self, wallet_id: str) -> Optional[str]:
        """Add a new account to an existing wallet.

        Params:
            - wallet_id: the wallet id to add the account to
        """
        try:
            account_address = self.client.accounts_create(wallet_id)['accounts'][0]
        except Exception as e:
            LOG.error(f"Failed to add account to wallet {wallet_id}: {e}")
            LOG.exception(e)
            return None

        return account_address

    def make_transaction(
            self: Self,
            source_wallet: str,
            source_account: str,
            destination_account: str,
            amount: int,
            private_key: str,
            retries: int = 3) -> Optional[str]:
        """Make a transaction.

        Params:
            - source_wallet: the wallet id of the source account
            - source_account: the address of the source account
            - destination_account: the address of the destination account
            - amount: the amount of Nano to be sent
            - private_key: the private key of the source account
            - retries: the number of times to retry the transaction in case of failure  # noqa: E501

        Returns: the transaction block
        """
        if retries <= 0:
            raise ValueError("Transaction failed after multiple retries")

        try:
            account_list = self.client.account_list(source_wallet)['accounts']
            if source_account not in account_list:
                raise ValueError(f"Account {source_account} doesn't belong to wallet {source_wallet}")  # noqa: E501

            # Check if the account has enough balance
            balance = self.client.account_info(source_account)['balance']
            if int(balance) < amount:
                raise ValueError(f"Account {source_account} has insufficient balance")  # noqa: E501

            # Make the transaction
            transaction = self.client.send(
                source_wallet,
                source_account,
                destination_account,
                amount,
                private_key
            )
            LOG.info(f"Transaction successful: {transaction}")
            block = transaction.get('block')
            LOG.info(f"Block: {block}")
            return block

        except Exception as e:
            LOG.error(f"Transaction failed: {e}")
            LOG.exception(e)
            # TODO: redo retry to use decorator
            return self.make_transaction(
                source_wallet,
                source_account,
                destination_account,
                amount,
                private_key,
                retries - 1
            )

__init__(secret_manager, node_address='http://127.0.0.1:17076')

Initialize the wallet manager.

Parameters:
  • - (node_address) –

    the address of the node to connect to. Defaults to the local node at port 17076.

nanohelp/wallet.py
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def __init__(
        self: Self,
        secret_manager: SecretManager,
        node_address: str = "http://127.0.0.1:17076") -> None:
    """Initialize the wallet manager.

    Params:
        - node_address: the address of the node to connect to.
            Defaults to the local node at port 17076.

    """
    self.client = BasicNanoClient(node_address)
    LOG.debug("Google Application Credentials: %s", os.environ['GOOGLE_APPLICATION_CREDENTIALS'])
    self.secret_manager = secret_manager

create_wallet(project, name)

Generate a new private key and create a new wallet.

Parameters:
  • - (project) –

    the project to store the secret in

  • - (name) –

    the name of the wallet to create

nanohelp/wallet.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def create_wallet(self: Self, project: str, name: str) -> Optional[Tuple[str, str]]:
    """Generate a new private key and create a new wallet.

    Params:
        - project: the project to store the secret in
        - name: the name of the wallet to create

    Returns: a tuple containing wallet_id and account_address
    """
    try:
        wallet_id = self.client.wallet_create(
            self.secret_manager.generate_and_store_private_key(project, name)
        )['wallet']
        account_address = self.client.accounts_create(wallet_id)['accounts'][0]
    except Exception as e:
        LOG.error(f"Failed to create wallet: {e}")
        LOG.exception(e)
        return None

    return wallet_id, account_address

add_account_to_wallet(wallet_id)

Add a new account to an existing wallet.

Parameters:
  • - (wallet_id) –

    the wallet id to add the account to

nanohelp/wallet.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def add_account_to_wallet(self: Self, wallet_id: str) -> Optional[str]:
    """Add a new account to an existing wallet.

    Params:
        - wallet_id: the wallet id to add the account to
    """
    try:
        account_address = self.client.accounts_create(wallet_id)['accounts'][0]
    except Exception as e:
        LOG.error(f"Failed to add account to wallet {wallet_id}: {e}")
        LOG.exception(e)
        return None

    return account_address

make_transaction(source_wallet, source_account, destination_account, amount, private_key, retries=3)

Make a transaction.

Parameters:
  • - (source_wallet) –

    the wallet id of the source account

  • - (source_account) –

    the address of the source account

  • - (destination_account) –

    the address of the destination account

  • - (amount) –

    the amount of Nano to be sent

  • - (private_key) –

    the private key of the source account

  • - (retries) –

    the number of times to retry the transaction in case of failure # noqa: E501

nanohelp/wallet.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def make_transaction(
        self: Self,
        source_wallet: str,
        source_account: str,
        destination_account: str,
        amount: int,
        private_key: str,
        retries: int = 3) -> Optional[str]:
    """Make a transaction.

    Params:
        - source_wallet: the wallet id of the source account
        - source_account: the address of the source account
        - destination_account: the address of the destination account
        - amount: the amount of Nano to be sent
        - private_key: the private key of the source account
        - retries: the number of times to retry the transaction in case of failure  # noqa: E501

    Returns: the transaction block
    """
    if retries <= 0:
        raise ValueError("Transaction failed after multiple retries")

    try:
        account_list = self.client.account_list(source_wallet)['accounts']
        if source_account not in account_list:
            raise ValueError(f"Account {source_account} doesn't belong to wallet {source_wallet}")  # noqa: E501

        # Check if the account has enough balance
        balance = self.client.account_info(source_account)['balance']
        if int(balance) < amount:
            raise ValueError(f"Account {source_account} has insufficient balance")  # noqa: E501

        # Make the transaction
        transaction = self.client.send(
            source_wallet,
            source_account,
            destination_account,
            amount,
            private_key
        )
        LOG.info(f"Transaction successful: {transaction}")
        block = transaction.get('block')
        LOG.info(f"Block: {block}")
        return block

    except Exception as e:
        LOG.error(f"Transaction failed: {e}")
        LOG.exception(e)
        # TODO: redo retry to use decorator
        return self.make_transaction(
            source_wallet,
            source_account,
            destination_account,
            amount,
            private_key,
            retries - 1
        )