Skip to content

Commit 91826bb

Browse files
Merge pull request #20 from ElrondNetwork/development
Testnet fixes, typing improvements, add-ons for Legolas
2 parents 3f9ea3b + 027e549 commit 91826bb

54 files changed

Lines changed: 1083 additions & 341 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,13 @@
22
dist
33
__pycache__
44
*.egg-info
5+
*.py[cod]
6+
*$py.class
7+
.pytest_cache/
8+
erdpy/tests/testdata-out
9+
10+
# mypy
11+
.mypy_cache/
12+
13+
# Tags generated by ctags
14+
tags

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,8 @@ build-erdpy: clean
66

77
publish-erdpy: build-erdpy
88
twine upload dist/*
9+
10+
test:
11+
python3 -m unittest discover -s erdpy/tests
12+
pytest ./erdpy/tests/test_testnet.py -s
13+
source ./erdpy/tests/test_cli_all.sh && testAll || return 1

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,11 @@ Elrond Python Command Line Tools and SDK for interacting with the Elrond Network
44
## Documentation
55
[docs.elrond.com](https://docs.elrond.com/sdk-and-tools/erdpy/erdpy/)
66

7-
[pkg.go.dev](https://pkg.go.dev/github.com/ElrondNetwork/elrond-sdk/erdgo)
8-
97
## CLI
10-
[CLI](CLI.md)
8+
[CLI](erdpy/CLI.md)
119

1210
## Distribution
1311
[erdpy-up](https://docs.elrond.com/sdk-and-tools/erdpy/installing-erdpy/) and [PyPi](https://pypi.org/project/erdpy/#history)
1412

1513
## CHANGELOG
16-
[CHANGELOG](CHANGELOG.md)
14+
[CHANGELOG](erdpy/CHANGELOG.md)

erdpy/CLI.md

Lines changed: 104 additions & 24 deletions
Large diffs are not rendered by default.

erdpy/accounts.py

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
import os
33
from binascii import unhexlify
44
from os import path
5-
from typing import Any, Union
5+
from typing import Any, Optional
66

77
from erdpy import constants, errors, utils
8+
from erdpy.errors import LedgerError
89
from erdpy.interfaces import IAccount, IAddress
910
from erdpy.wallet import bech32, generate_pair, pem
1011
from erdpy.wallet.keyfile import get_password, load_from_key_file
@@ -44,13 +45,20 @@ def get_all(self):
4445

4546

4647
class Account(IAccount):
47-
def __init__(self, address: Any = None, pem_file: Union[str, None] = None, pem_index: int = 0, key_file: str = "", pass_file: str = ""):
48+
def __init__(self,
49+
address: Any = None,
50+
pem_file: Optional[str] = None,
51+
pem_index: int = 0,
52+
key_file: str = "",
53+
pass_file: str = "",
54+
ledger: bool = False):
4855
self.address = Address(address)
4956
self.pem_file = pem_file
5057
self.pem_index = int(pem_index)
5158
self.nonce: int = 0
59+
self.ledger = ledger
5260

53-
if pem_file:
61+
if self.pem_file:
5462
seed, pubkey = pem.parse(self.pem_file, self.pem_index)
5563
self.private_key_seed = seed.hex()
5664
self.address = Address(pubkey)
@@ -66,6 +74,8 @@ def sync_nonce(self, proxy: Any):
6674
logger.info(f"Account.sync_nonce() done: {self.nonce}")
6775

6876
def get_seed(self) -> bytes:
77+
if self.ledger:
78+
raise LedgerError("cannot get seed from a Ledger account")
6979
return unhexlify(self.private_key_seed)
7080

7181

@@ -74,9 +84,10 @@ class Address(IAddress):
7484
PUBKEY_LENGTH = 32
7585
PUBKEY_STRING_LENGTH = PUBKEY_LENGTH * 2 # hex-encoded
7686
BECH32_LENGTH = 62
87+
_value_hex: str
7788

7889
def __init__(self, value):
79-
self._value_hex = None
90+
self._value_hex = ''
8091

8192
if not value:
8293
return
@@ -102,7 +113,9 @@ def hex(self) -> str:
102113
def bech32(self) -> str:
103114
self._assert_validity()
104115
pubkey = self.pubkey()
105-
return bech32.bech32_encode(self.HRP, bech32.convertbits(pubkey, 8, 5))
116+
b32 = bech32.bech32_encode(self.HRP, bech32.convertbits(pubkey, 8, 5))
117+
assert isinstance(b32, str)
118+
return b32
106119

107120
def pubkey(self):
108121
self._assert_validity()
@@ -113,7 +126,7 @@ def is_contract_address(self):
113126
return self.hex().startswith(constants.SC_HEX_PUBKEY_PREFIX)
114127

115128
def _assert_validity(self):
116-
if self._value_hex is None:
129+
if self._value_hex == '':
117130
raise errors.EmptyAddressError()
118131

119132
def __repr__(self):

erdpy/cli.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import erdpy.cli_data
1414
import erdpy.cli_deps
1515
import erdpy.cli_dispatcher
16+
import erdpy.cli_ledger
1617
import erdpy.cli_network
1718
import erdpy.cli_testnet
1819
import erdpy.cli_transactions
@@ -85,6 +86,7 @@ def setup_parser():
8586
commands.append(erdpy.cli_transactions.setup_parser(subparsers))
8687
commands.append(erdpy.cli_validators.setup_parser(subparsers))
8788
commands.append(erdpy.cli_accounts.setup_parser(subparsers))
89+
commands.append(erdpy.cli_ledger.setup_parser(subparsers))
8890
commands.append(erdpy.cli_wallet.setup_parser(subparsers))
8991
commands.append(erdpy.cli_network.setup_parser(subparsers))
9092
commands.append(erdpy.cli_cost.setup_parser(subparsers))

erdpy/cli_contracts.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
from typing import Any
44

5+
from pathlib import Path
6+
57
from erdpy import cli_shared, errors, projects, utils
68
from erdpy.accounts import Account, Address
79
from erdpy.contracts import CodeMetadata, SmartContract
@@ -110,8 +112,10 @@ def _add_project_arg(sub: Any):
110112

111113
def _add_project_or_bytecode_arg(sub: Any):
112114
group = sub.add_mutually_exclusive_group(required=True)
113-
group.add_argument("--project", default=os.getcwd(), help="🗀 the project directory (default: current directory)")
114-
group.add_argument("--bytecode", help="the WASM file")
115+
group.add_argument("--project", default=os.getcwd(),
116+
help="🗀 the project directory (default: current directory)")
117+
group.add_argument("--bytecode", type=str,
118+
help="the file containing the WASM bytecode")
115119

116120

117121
def _add_contract_arg(sub: Any):
@@ -217,8 +221,8 @@ def dump_tx_and_result(tx: Any, result: Any, args: Any):
217221

218222

219223
def _prepare_contract(args: Any) -> SmartContract:
220-
if args.bytecode:
221-
bytecode = utils.read_file(args.bytecode, binary=True).hex()
224+
if len(args.bytecode):
225+
bytecode = utils.read_binary_file(Path(args.bytecode)).hex()
222226
else:
223227
project = load_project(args.project)
224228
bytecode = project.get_bytecode()

erdpy/cli_ledger.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from erdpy.ledger.ledger_app_handler import ElrondLedgerApp
2+
from erdpy import cli_shared
3+
import logging
4+
from typing import Any
5+
6+
logger = logging.getLogger("cli.ledger")
7+
8+
9+
def setup_parser(subparsers: Any) -> Any:
10+
parser = cli_shared.add_group_subparser(subparsers, "ledger", "Get Ledger App addresses and version")
11+
subparsers = parser.add_subparsers()
12+
13+
sub = cli_shared.add_command_subparser(subparsers, "ledger", "addresses", "Get the addresses within Ledger")
14+
sub.add_argument("--num-addresses", required=False, type=int, default=10, help="The number of addresses to fetch")
15+
sub.set_defaults(func=print_addresses)
16+
17+
sub = cli_shared.add_command_subparser(subparsers, "ledger", "version", "Get the version of the Elrond App for Ledger")
18+
sub.set_defaults(func=print_version)
19+
20+
return subparsers
21+
22+
23+
def print_addresses(args):
24+
ledger_app = ElrondLedgerApp()
25+
for i in range(args.num_addresses):
26+
address = ledger_app.get_address(0, i)
27+
print('account index = %d | address index = %d | address: %s' % (0, i, address))
28+
ledger_app.close()
29+
30+
31+
def print_version(args):
32+
ledger_app = ElrondLedgerApp()
33+
print("Elrond App version: " + ledger_app.get_version())
34+
ledger_app.close()

erdpy/cli_shared.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import argparse
22
import ast
3+
import string
34
import sys
45
from argparse import FileType
56
from typing import Any, List, Text
67

78
from erdpy import config, errors, scope, utils
89
from erdpy.accounts import Account
10+
from erdpy.ledger.ledger_functions import do_get_ledger_address
911
from erdpy.proxy.core import ElrondProxy
1012
from erdpy.transactions import Transaction
1113

@@ -69,18 +71,22 @@ def add_tx_args(sub: Any, with_nonce: bool = True, with_receiver: bool = True, w
6971

7072
sub.add_argument("--chain", default=scope.get_chain_id(), help="the chain identifier (default: %(default)s)")
7173
sub.add_argument("--version", type=int, default=scope.get_tx_version(), help="the transaction version (default: %(default)s)")
74+
sub.add_argument("--options", type=int, default=0, help="the transaction options (default: 0)")
7275

7376

7477
def add_wallet_args(sub: Any):
75-
sub.add_argument("--pem", required=not (utils.is_arg_present("--keyfile", sys.argv)), help="🔑 the PEM file, if keyfile not provided")
78+
sub.add_argument("--pem", required=check_if_sign_method_required("--pem"), help="🔑 the PEM file, if keyfile not provided")
7679
sub.add_argument("--pem-index", default=0, help="🔑 the index in the PEM file (default: %(default)s)")
77-
sub.add_argument("--keyfile", required=not (utils.is_arg_present("--pem", sys.argv)), help="🔑 a JSON keyfile, if PEM not provided")
78-
sub.add_argument("--passfile", required=not (utils.is_arg_present("--pem", sys.argv)), help="🔑 a file containing keyfile's password, if keyfile provided")
80+
sub.add_argument("--keyfile", required=check_if_sign_method_required("--keyfile"), help="🔑 a JSON keyfile, if PEM not provided")
81+
sub.add_argument("--passfile", required=(utils.is_arg_present("--keyfile", sys.argv)), help="🔑 a file containing keyfile's password, if keyfile provided")
82+
sub.add_argument("--ledger", action="store_true", required=check_if_sign_method_required("--ledger"), default=False, help="🔐 bool flag for signing transaction using ledger")
83+
sub.add_argument("--ledger-account-index", type=int, default=0, help="🔐 the index of the account when using Ledger")
84+
sub.add_argument("--ledger-address-index", type=int, default=0, help="🔐 the index of the address when using Ledger")
7985
sub.add_argument("--sender-username", required=False, help="🖄 the username of the sender")
8086

8187

8288
def add_proxy_arg(sub: Any):
83-
sub.add_argument("--proxy", default=scope.get_proxy(), help="🖧 the URL of the proxy (default: %(default)s)")
89+
sub.add_argument("--proxy", default=scope.get_proxy(), help="🔗 the URL of the proxy (default: %(default)s)")
8490

8591

8692
def add_outfile_arg(sub: Any, what: str = ""):
@@ -109,6 +115,9 @@ def prepare_nonce_in_args(args: Any):
109115
account = Account(pem_file=args.pem, pem_index=args.pem_index)
110116
elif args.keyfile and args.passfile:
111117
account = Account(key_file=args.keyfile, pass_file=args.passfile)
118+
elif args.ledger:
119+
address = do_get_ledger_address(account_index=args.ledger_account_index, address_index=args.ledger_address_index)
120+
account = Account(address=address)
112121
else:
113122
raise errors.NoWalletProvided()
114123

@@ -138,3 +147,17 @@ def send_or_simulate(tx: Transaction, args: Any):
138147
elif args.simulate:
139148
response = tx.simulate(ElrondProxy(args.proxy))
140149
utils.dump_out_json(response)
150+
151+
152+
def check_if_sign_method_required(checked_method: string) -> bool:
153+
methods = ["--pem", "--keyfile", "--ledger"]
154+
rest_of_methods = []
155+
for method in methods:
156+
if method != checked_method:
157+
rest_of_methods.append(method)
158+
159+
for method in rest_of_methods:
160+
if utils.is_arg_present(method, sys.argv):
161+
return False
162+
163+
return True

erdpy/cli_transactions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from typing import Any
33

44
from erdpy import cli_shared, utils
5+
from erdpy.ledger.ledger_functions import do_get_ledger_address
56
from erdpy.proxy.core import ElrondProxy
67
from erdpy.transactions import Transaction, do_prepare_transaction
78

@@ -51,6 +52,9 @@ def create_transaction(args: Any):
5152
cli_shared.check_broadcast_args(args)
5253
cli_shared.prepare_nonce_in_args(args)
5354

55+
if args.ledger:
56+
args.ledger_address = do_get_ledger_address(account_index=args.ledger_account_index, address_index=args.ledger_address_index)
57+
5458
if args.data_file:
5559
args.data = utils.read_file(args.data_file)
5660

0 commit comments

Comments
 (0)