Repository: multiversx/mx-sdk-py-cli (language: python)

Issue:
Prepare version 1.0.22



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/erdpy.yml b/.github/workflows/erdpy.yml
index e23df4a3..2816da7f 100644
--- a/.github/workflows/erdpy.yml
+++ b/.github/workflows/erdpy.yml
@@ -1,7 +1,7 @@
 # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
 
-name: Build and Test erdpy
+name: erdpy-build
 
 on:
   pull_request:
@@ -10,11 +10,13 @@ on:
 
 jobs:
   build-erdpy:
-
-    runs-on: ubuntu-latest
+    name: Build and Test erdpy for ${{ matrix.os }}, python ${{ matrix.python-version }}
+    
+    runs-on: ${{ matrix.os }}
 
     strategy:
       matrix:
+        os: [ubuntu-latest, macos-latest]
         python-version: [3.8]
 
     steps:
diff --git a/erdpy-up.py b/erdpy-up.py
index 032a5f8b..d430ad44 100644
--- a/erdpy-up.py
+++ b/erdpy-up.py
@@ -16,23 +16,27 @@
 
 elrondsdk_path = None
 exact_version = None
+from_branch = None
 
 
 def main():
     global elrondsdk_path
     global exact_version
+    global from_branch
 
     parser = ArgumentParser()
     parser.add_argument("--modify-path", dest="modify_path", action="store_true", help="whether to modify $PATH (in profile file)")
     parser.add_argument("--no-modify-path", dest="modify_path", action="store_false", help="whether to modify $PATH (in profile file)")
     parser.add_argument("--elrondsdk-path", default=get_elrond_sdk_path_default(), help="where to install elrond-sdk")
     parser.add_argument("--exact-version", help="the exact version of erdpy to install")
+    parser.add_argument("--from-branch", help="use a branch of ElrondNetwork/elrond-sdk-erdpy")
     parser.set_defaults(modify_path=True)
     args = parser.parse_args()
 
     elrondsdk_path = os.path.expanduser(args.elrondsdk_path)
     modify_path = args.modify_path
     exact_version = args.exact_version
+    from_branch = args.from_branch
 
     logging.basicConfig(level=logging.DEBUG)
 
@@ -155,11 +159,15 @@ def ensure_folder(folder):
 
 def install_erdpy():
     logger.info("Installing erdpy in virtual environment...")
-    erpy_versioned = "erdpy" if not exact_version else f"erdpy=={exact_version}"
+    if from_branch:
+        erdpy_to_install = f"https://github.com/ElrondNetwork/elrond-sdk-erdpy/archive/refs/heads/{from_branch}.zip"
+    else:
+        erdpy_to_install = "erdpy" if not exact_version else f"erdpy=={exact_version}"
+
     return_code = run_in_venv(["python3", "-m", "pip", "install", "--upgrade", "pip"])
     if return_code != 0:
         raise InstallError("Could not upgrade pip.")
-    return_code = run_in_venv(["pip3", "install", "--no-cache-dir", erpy_versioned])
+    return_code = run_in_venv(["pip3", "install", "--no-cache-dir", erdpy_to_install])
     if return_code != 0:
         raise InstallError("Could not install erdpy.")
     return_code = run_in_venv(["erdpy", "--version"])
diff --git a/erdpy/CHANGELOG.md b/erdpy/CHANGELOG.md
index 7dcf2610..740862c6 100644
--- a/erdpy/CHANGELOG.md
+++ b/erdpy/CHANGELOG.md
@@ -4,6 +4,13 @@ All notable changes will be documented in this file.
 
 Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
 
+## [1.0.22] - 22.11.2021
+ - Fix the patching of the `meta` subproject
+ - Add a check when signing transactions so that the gas limit is below the maximum allowed
+ - Add `--from-branch` option for `erdpy-up.py`
+ - Add github actions check for MacOS
+ - Update help strings and `CLI.md`
+
 ## [1.0.21] - 01.11.2021
  - New command `erdpy wallet new`, which generates a new wallet mnemonic and optionally saves it to JSON or PEM
  - Add support for Rust contract `meta` crates
diff --git a/erdpy/CLI.md b/erdpy/CLI.md
index b5a6f2fc..dcce168d 100644
--- a/erdpy/CLI.md
+++ b/erdpy/CLI.md
@@ -172,7 +172,7 @@ optional arguments:
   --ledger-account-index LEDGER_ACCOUNT_INDEX  🔐 the index of the account when using Ledger
   --ledger-address-index LEDGER_ADDRESS_INDEX  🔐 the index of the address when using Ledger
   --sender-username SENDER_USERNAME            🖄 the username of the sender
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --nonce NONCE                                # the nonce for the transaction
   --recall-nonce                               ⭮ whether to recall the nonce when creating the transaction (default:
                                                False)
@@ -214,7 +214,7 @@ optional arguments:
   --ledger-account-index LEDGER_ACCOUNT_INDEX  🔐 the index of the account when using Ledger
   --ledger-address-index LEDGER_ADDRESS_INDEX  🔐 the index of the address when using Ledger
   --sender-username SENDER_USERNAME            🖄 the username of the sender
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --nonce NONCE                                # the nonce for the transaction
   --recall-nonce                               ⭮ whether to recall the nonce when creating the transaction (default:
                                                False)
@@ -262,7 +262,7 @@ optional arguments:
   --ledger-account-index LEDGER_ACCOUNT_INDEX  🔐 the index of the account when using Ledger
   --ledger-address-index LEDGER_ADDRESS_INDEX  🔐 the index of the address when using Ledger
   --sender-username SENDER_USERNAME            🖄 the username of the sender
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --nonce NONCE                                # the nonce for the transaction
   --recall-nonce                               ⭮ whether to recall the nonce when creating the transaction (default:
                                                False)
@@ -295,7 +295,7 @@ positional arguments:
 
 optional arguments:
   -h, --help                             show this help message and exit
-  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --function FUNCTION                    the function to call
   --arguments ARGUMENTS [ARGUMENTS ...]  arguments for the contract transaction, as numbers or hex-encoded. E.g.
                                          --arguments 42 0x64 1000 0xabba
@@ -360,7 +360,7 @@ optional arguments:
   --send                                       ✓ whether to broadcast the transaction (default: False)
   --simulate                                   whether to simulate the transaction (default: False)
   --relay                                      whether to relay the transaction (default: False)
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --wait-result                                signal to wait for the transaction result - only valid if --send is set
   --timeout TIMEOUT                            max num of seconds to wait for result - only valid if --wait-result is
                                                set
@@ -379,7 +379,7 @@ optional arguments:
   -h, --help         show this help message and exit
   --infile INFILE    input file (a previously saved transaction)
   --outfile OUTFILE  where to save the output (the hash) (default: stdout)
-  --proxy PROXY      🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY      🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
 
 ```
 ### Transactions.Get
@@ -396,7 +396,7 @@ optional arguments:
   --hash HASH                the hash
   --sender SENDER            the sender address
   --with-results             will also return the results of transaction
-  --proxy PROXY              🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY              🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --omit-fields OMIT_FIELDS  omit fields in the output payload (default: [])
 
 ```
@@ -427,7 +427,7 @@ Get hyperblock
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --key KEY      the hash or the nonce of the hyperblock
 
 ```
@@ -474,7 +474,7 @@ Stake value into the Network
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -512,7 +512,7 @@ Unstake value
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -548,7 +548,7 @@ Unjail a Validator Node
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -584,7 +584,7 @@ Unbond tokens for a bls key
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -620,7 +620,7 @@ Change the reward address
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -656,7 +656,7 @@ Claim rewards
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -713,7 +713,7 @@ Query account details (nonce, balance etc.)
 
 optional arguments:
   -h, --help                 show this help message and exit
-  --proxy PROXY              🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY              🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --address ADDRESS          🖄 the address to query
   --balance                  whether to only fetch the balance
   --nonce                    whether to only fetch the nonce
@@ -732,7 +732,7 @@ Query account transactions
 
 optional arguments:
   -h, --help         show this help message and exit
-  --proxy PROXY      🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY      🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --outfile OUTFILE  where to save the output (default: stdout)
   --address ADDRESS  🖄 the address to query
 
@@ -921,7 +921,7 @@ Get the number of shards.
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
 
 ```
 ### Network.BlockNonce
@@ -935,7 +935,7 @@ Get the latest block nonce, by shard.
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --shard SHARD  the shard ID (use 4294967295 for metachain)
 
 ```
@@ -950,7 +950,7 @@ Get the chain identifier.
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
 
 ```
 ## Group **Cost**
@@ -988,7 +988,7 @@ Query minimum gas price
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
 
 ```
 ### Cost.TxTransfer
@@ -1002,7 +1002,7 @@ Query cost of regular transaction (transfer)
 
 optional arguments:
   -h, --help     show this help message and exit
-  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY  🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --data DATA    a transaction payload, required to estimate the cost
 
 ```
@@ -1017,7 +1017,7 @@ Query cost of Smart Contract deploy transaction
 
 optional arguments:
   -h, --help                             show this help message and exit
-  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --project PROJECT                      🗀 the project directory (default: current directory)
   --bytecode BYTECODE                    the file containing the WASM bytecode
   --arguments ARGUMENTS [ARGUMENTS ...]  arguments for the contract transaction, as numbers or hex-encoded. E.g.
@@ -1038,7 +1038,7 @@ positional arguments:
 
 optional arguments:
   -h, --help                             show this help message and exit
-  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                          🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --function FUNCTION                    the function to call
   --arguments ARGUMENTS [ARGUMENTS ...]  arguments for the contract transaction, as numbers or hex-encoded. E.g.
                                          --arguments 42 0x64 1000 0xabba
@@ -1101,7 +1101,7 @@ Dispatch queued transactions
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
@@ -1123,7 +1123,7 @@ Continuously dispatch queued transactions
 
 optional arguments:
   -h, --help                                   show this help message and exit
-  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-api.elrond.com)
+  --proxy PROXY                                🔗 the URL of the proxy (default: https://testnet-gateway.elrond.com)
   --pem PEM                                    🔑 the PEM file, if keyfile not provided
   --pem-index PEM_INDEX                        🔑 the index in the PEM file (default: 0)
   --keyfile KEYFILE                            🔑 a JSON keyfile, if PEM not provided
diff --git a/erdpy/_version.py b/erdpy/_version.py
index c916e680..edb5a8e7 100644
--- a/erdpy/_version.py
+++ b/erdpy/_version.py
@@ -1,1 +1,1 @@
-__version__ = "1.0.21"
+__version__ = "1.0.22"
diff --git a/erdpy/accounts.py b/erdpy/accounts.py
index da43c376..52b6e7f6 100644
--- a/erdpy/accounts.py
+++ b/erdpy/accounts.py
@@ -1,7 +1,6 @@
 import logging
-import os
 from binascii import unhexlify
-from os import path
+from pathlib import Path
 from typing import Any, Optional
 
 from erdpy import constants, errors, utils
@@ -14,13 +13,13 @@
 
 
 class AccountsRepository:
-    def __init__(self, folder):
+    def __init__(self, folder: Path):
         utils.ensure_folder(folder)
         self.folder = folder
 
     def get_account(self, name):
-        pem_file = path.join(self.folder, f"{name}.pem")
-        return Account(pem_file=pem_file)
+        pem_file = self.folder / f"{name}.pem"
+        return Account(pem_file=str(pem_file))
 
     def generate_accounts(self, count):
         for i in range(count):
@@ -30,14 +29,13 @@ def generate_account(self, name):
         secret_key, pubkey = generate_pair()
         address = Address(pubkey).bech32()
 
-        pem_file = f"{name}_{address}.pem"
-        pem_file = path.join(self.folder, pem_file)
+        pem_file = self.folder / f"{name}_{address}.pem"
         pem.write(pem_file, secret_key, pubkey, name=f"{name}:{address}")
 
     def get_all(self):
         accounts = []
-        for pem_file in os.listdir(self.folder):
-            pem_file = path.join(self.folder, pem_file)
+        for pem_file in self.folder.iterdir():
+            pem_file = self.folder / pem_file
             account = Account(pem_file=pem_file)
             accounts.append(account)
 
@@ -59,7 +57,7 @@ def __init__(self,
         self.ledger = ledger
 
         if self.pem_file:
-            secret_key, pubkey = pem.parse(self.pem_file, self.pem_index)
+            secret_key, pubkey = pem.parse(Path(self.pem_file), self.pem_index)
             self.secret_key = secret_key.hex()
             self.address = Address(pubkey)
         elif key_file and pass_file:
diff --git a/erdpy/cli_contracts.py b/erdpy/cli_contracts.py
index 558d26a8..6a728d87 100644
--- a/erdpy/cli_contracts.py
+++ b/erdpy/cli_contracts.py
@@ -143,18 +143,18 @@ def list_templates(args: Any):
 def create(args: Any):
     name = args.name
     template = args.template
-    directory = args.directory
+    directory = Path(args.directory)
 
     projects.create_from_template(name, template, directory)
 
 
 def clean(args: Any):
-    project = args.project
+    project = Path(args.project)
     projects.clean_project(project)
 
 
 def build(args: Any):
-    project = args.project
+    project = Path(args.project)
     options = {
         "debug": args.debug,
         "optimized": not args.no_optimization,
@@ -225,7 +225,8 @@ def _prepare_contract(args: Any) -> SmartContract:
     if args.bytecode and len(args.bytecode):
         bytecode = utils.read_binary_file(Path(args.bytecode)).hex()
     else:
-        project = load_project(args.project)
+        project_path = Path(args.project)
+        project = load_project(project_path)
         bytecode = project.get_bytecode()
 
     metadata = CodeMetadata(args.metadata_upgradeable, args.metadata_payable)
diff --git a/erdpy/cli_wallet.py b/erdpy/cli_wallet.py
index 7ef7f202..2c247b13 100644
--- a/erdpy/cli_wallet.py
+++ b/erdpy/cli_wallet.py
@@ -111,7 +111,7 @@ def prepare_file(output_path: str, suffix: str) -> Path:
 
 
 def generate_pem(args: Any):
-    pem_file = args.pem
+    pem_file = Path(args.pem)
     mnemonic = args.mnemonic
     index = args.index
 
diff --git a/erdpy/config.py b/erdpy/config.py
index 101584c7..fa2e8fb8 100644
--- a/erdpy/config.py
+++ b/erdpy/config.py
@@ -12,6 +12,7 @@
 DEFAULT_GAS_PRICE = 1000000000
 GAS_PER_DATA_BYTE = 1500
 MIN_GAS_LIMIT = 50000
+MAX_GAS_LIMIT = 600000000
 
 
 class MetaChainSystemSCsCost:
diff --git a/erdpy/dispatcher/scenarios/example.py b/erdpy/dispatcher/scenarios/example.py
index 33d88ffd..26bdf94b 100644
--- a/erdpy/dispatcher/scenarios/example.py
+++ b/erdpy/dispatcher/scenarios/example.py
@@ -1,4 +1,5 @@
 import logging
+from pathlib import Path
 import sys
 from argparse import ArgumentParser
 
@@ -19,7 +20,8 @@ def main():
     logging.basicConfig(level=logging.DEBUG)
 
     proxy = ElrondProxy(args.proxy)
-    accounts_repository = AccountsRepository(args.pem_folder)
+    pem_folder = Path(args.pem_folder)
+    accounts_repository = AccountsRepository(pem_folder)
     bob = accounts_repository.get_account("bob")
     bob.sync_nonce(proxy)
 
diff --git a/erdpy/dispatcher/scenarios/mint.py b/erdpy/dispatcher/scenarios/mint.py
index 970ccfa0..59726970 100644
--- a/erdpy/dispatcher/scenarios/mint.py
+++ b/erdpy/dispatcher/scenarios/mint.py
@@ -1,4 +1,5 @@
 import logging
+from pathlib import Path
 import sys
 from argparse import ArgumentParser
 
@@ -28,7 +29,8 @@ def main():
     proxy = ElrondProxy(args.proxy)
     bunch = BunchOfTransactions()
     minter = Account(pem_file=args.minter)
-    minted_repository = AccountsRepository(args.minted_folder)
+    minted_folder = Path(args.minted_folder)
+    minted_repository = AccountsRepository(minted_folder)
 
     if args.minted_count:
         minted_repository.generate_accounts(args.minted_count)
diff --git a/erdpy/dispatcher/scenarios/sweepable.py b/erdpy/dispatcher/scenarios/sweepable.py
index 94ed1cb9..8a07bc67 100644
--- a/erdpy/dispatcher/scenarios/sweepable.py
+++ b/erdpy/dispatcher/scenarios/sweepable.py
@@ -1,6 +1,7 @@
 # This is an scenario example used to test a particular scenario within our system tests
 
 import logging
+from pathlib import Path
 import sys
 from argparse import ArgumentParser
 
@@ -25,7 +26,8 @@ def main():
 
     proxy = ElrondProxy(args.proxy)
     bunch = BunchOfTransactions()
-    senders_repository = AccountsRepository(args.senders_folder)
+    senders_folder = Path(args.senders_folder)
+    senders_repository = AccountsRepository(senders_folder)
 
     for sender in senders_repository.get_all():
         sender.sync_nonce(proxy)
diff --git a/erdpy/dispatcher/transactions/queue.py b/erdpy/dispatcher/transactions/queue.py
index 1d0d515f..f4c50cd2 100644
--- a/erdpy/dispatcher/transactions/queue.py
+++ b/erdpy/dispatcher/transactions/queue.py
@@ -1,6 +1,7 @@
 import json
 import logging
 import os
+from pathlib import Path
 import time
 from collections import OrderedDict
 from os import path
@@ -51,18 +52,18 @@ class TransactionQueue:
     def __init__(self):
         tools_folder = get_tools_folder()
 
-        txs_file_dir = path.join(tools_folder, "transactions")
+        txs_file_dir = tools_folder / "transactions"
         # create transactions directory if not exits
-        if not os.path.exists(txs_file_dir):
-            os.mkdir(txs_file_dir)
+        if not txs_file_dir.exists():
+            txs_file_dir.mkdir()
 
-        self.txs_file_path = path.join(txs_file_dir, self._TXS_FILE_NAME)
-        if not os.path.exists(self.txs_file_path):
+        self.txs_file_path = txs_file_dir / self._TXS_FILE_NAME
+        if not self.txs_file_path.exists():
             # create transactions file if not exits
             utils.write_file(self.txs_file_path, '{"' + self._TXS_FIELD_NAME + '":[]}')
 
-        self.txs_info_file_path = path.join(txs_file_dir, self._TXS_INFO_FILE_NAME)
-        if not os.path.exists(self.txs_info_file_path):
+        self.txs_info_file_path = txs_file_dir / self._TXS_INFO_FILE_NAME
+        if not self.txs_info_file_path.exists():
             utils.write_file(self.txs_info_file_path, f"index:{0}")
 
     def enqueue_transaction(self, args):
@@ -86,7 +87,7 @@ def _write_json_file(self, data):
             json.dump(data, json_file, indent=1)
 
     def _remove_all_transactions(self):
-        os.remove(self.txs_file_path)
+        self.txs_file_path.unlink()
 
     def _read_index(self):
         info = utils.read_file(self.txs_info_file_path)
@@ -157,6 +158,6 @@ def dispatch_transactions(self, args):
         for key in hashes:
             print(f"tx {txs_index+int(key)}: hash => {hashes[key]}")
 
-        utils.write_file(self.txs_info_file_path, f"index:{len(txs)}")
+        utils.write_file(Path(self.txs_info_file_path), f"index:{len(txs)}")
         # wait until transactions are executed
         _wait_to_execute_txs(proxy, owner, old_nonce + num_sent)
diff --git a/erdpy/errors.py b/erdpy/errors.py
index b400ab73..be36a96e 100644
--- a/erdpy/errors.py
+++ b/erdpy/errors.py
@@ -144,6 +144,11 @@ def __init__(self, name: str):
         super().__init__(f"Unknown cipher: {name}.")
 
 
+class GasLimitTooLarge(KnownError):
+    def __init__(self, current: int, limit: int):
+        super().__init__(f"The gas limit provided ({current}) exceeds the max gas limit of allowed for a transaction ({limit})")
+
+
 class InvalidKeystoreFilePassword(KnownError):
     def __init__(self):
         super().__init__("Provided keystore file password is invalid.")
diff --git a/erdpy/guards.py b/erdpy/guards.py
index 8397d35b..ee584bda 100644
--- a/erdpy/guards.py
+++ b/erdpy/guards.py
@@ -1,15 +1,15 @@
-import os.path
+from pathlib import Path
 from erdpy import errors
 
 
-def is_file(input):
-    if not os.path.isfile(input):
-        raise errors.BadInputError(input, "is not a valid file")
+def is_file(input: Path):
+    if not input.is_file():
+        raise errors.BadInputError(str(input), "is not a valid file")
 
 
-def is_directory(directory: str):
-    if not os.path.isdir(directory):
-        raise errors.BadDirectory(directory)
+def is_directory(directory: Path):
+    if not directory.is_dir():
+        raise errors.BadDirectory(str(directory))
 
 
 def is_hex_address(input):
diff --git a/erdpy/ide/core.py b/erdpy/ide/core.py
index d2130a57..6294ea08 100644
--- a/erdpy/ide/core.py
+++ b/erdpy/ide/core.py
@@ -17,7 +17,7 @@ def index():
 
 @app.route("/workspace/contracts")
 def workspace_contracts():
-    projects = get_projects_in_workspace(global_workspace)
+    projects = get_projects_in_workspace(Path(global_workspace))
     models = [SmartContractViewModel(project).__dict__ for project in projects]
     return dict(data=models)
 
diff --git a/erdpy/projects/core.py b/erdpy/projects/core.py
index 8add3b2e..b9c83f1b 100644
--- a/erdpy/projects/core.py
+++ b/erdpy/projects/core.py
@@ -14,7 +14,7 @@
 logger = logging.getLogger("projects.core")
 
 
-def load_project(directory):
+def load_project(directory: Path):
     guards.is_directory(directory)
 
     if shared.is_source_clang(directory):
@@ -26,11 +26,11 @@ def load_project(directory):
     if shared.is_source_rust(directory):
         return ProjectRust(directory)
     else:
-        raise errors.NotSupportedProject(directory)
+        raise errors.NotSupportedProject(str(directory))
 
 
-def build_project(directory: str, options: Dict[str, Any]):
-    directory = path.expanduser(directory)
+def build_project(directory: Path, options: Dict[str, Any]):
+    directory = directory.expanduser()
 
     logger.info("build_project.directory: %s", directory)
     logger.info("build_project.debug: %s", options['debug'])
@@ -43,8 +43,8 @@ def build_project(directory: str, options: Dict[str, Any]):
     logger.info(f"WASM file generated: {relative_wasm_path}")
 
 
-def clean_project(directory: str):
-    directory = path.expanduser(directory)
+def clean_project(directory: Path):
+    directory = directory.expanduser()
     guards.is_directory(directory)
     project = load_project(directory)
     project.clean()
@@ -52,26 +52,26 @@ def clean_project(directory: str):
 
 
 def run_tests(args: Any):
-    project = args.project
-    directory = args.directory
+    project_path = Path(args.project)
+    directory = Path(args.directory)
     wildcard = args.wildcard
 
-    logger.info("run_tests.project: %s", project)
+    logger.info("run_tests.project: %s", project_path)
 
     dependencies.install_module("vmtools")
 
-    guards.is_directory(project)
-    project = load_project(project)
+    guards.is_directory(project_path)
+    project = load_project(project_path)
     project.run_tests(directory, wildcard)
 
 
-def get_projects_in_workspace(workspace):
+def get_projects_in_workspace(workspace: Path):
     guards.is_directory(workspace)
     subfolders = utils.get_subfolders(workspace)
     projects = []
 
     for folder in subfolders:
-        project_directory = path.join(workspace, folder)
+        project_directory = workspace / folder
 
         try:
             project = load_project(project_directory)
diff --git a/erdpy/projects/project_base.py b/erdpy/projects/project_base.py
index ab4c84fb..18d62f63 100644
--- a/erdpy/projects/project_base.py
+++ b/erdpy/projects/project_base.py
@@ -11,11 +11,10 @@
 logger = logging.getLogger("Project")
 
 
-# TODO use pathlib.Path everywhere
 class Project:
 
-    def __init__(self, directory: Union[Path, str]):
-        self.path = Path(directory).expanduser().resolve()
+    def __init__(self, directory: Path):
+        self.path = directory.expanduser().resolve()
         self.directory = str(self.path)
 
     def build(self, options: Union[Dict[str, Any], None] = None) -> Path:
@@ -95,18 +94,18 @@ def ensure_config_file(self):
     def default_config(self):
         return dict()
 
-    def run_tests(self, tests_directory: str, wildcard: str = ""):
+    def run_tests(self, tests_directory: Path, wildcard: str = ""):
         vmtools = cast(StandaloneModule, dependencies.get_module_by_key("vmtools"))
         tool_env = vmtools.get_env()
         tool = path.join(vmtools.get_parent_directory(), "mandos-test")
-        test_folder = path.join(self.directory, tests_directory)
+        test_folder = self.directory / tests_directory
 
         if not wildcard:
-            args = [tool, test_folder]
+            args = [tool, str(test_folder)]
             myprocess.run_process(args, env=tool_env)
         else:
-            pattern = path.join(test_folder, wildcard)
-            test_files = glob.glob(pattern)
+            pattern = test_folder / wildcard
+            test_files = glob.glob(str(pattern))
 
             for test_file in test_files:
                 print("Run test for:", test_file)
diff --git a/erdpy/projects/project_clang.py b/erdpy/projects/project_clang.py
index 10327a66..f827933b 100644
--- a/erdpy/projects/project_clang.py
+++ b/erdpy/projects/project_clang.py
@@ -138,7 +138,7 @@ def ensure_source_files(self):
 
     def get_exported_functions(self) -> List[str]:
         file_export = self.find_file_globally('*.export')
-        lines = utils.read_lines(str(file_export))
+        lines = utils.read_lines(file_export)
         return lines
 
     def default_config(self):
diff --git a/erdpy/projects/project_cpp.py b/erdpy/projects/project_cpp.py
index 1580abd7..2ba0154a 100644
--- a/erdpy/projects/project_cpp.py
+++ b/erdpy/projects/project_cpp.py
@@ -96,7 +96,7 @@ def get_dependencies(self):
 
 
 class CppBuildConfiguration:
-    def __init__(self, project, debug):
+    def __init__(self, project: Project, debug):
         self.project = project
         self.debug = debug
         self.exports = self._get_exports()
diff --git a/erdpy/projects/project_rust.py b/erdpy/projects/project_rust.py
index cfef1c27..6b439c3f 100644
--- a/erdpy/projects/project_rust.py
+++ b/erdpy/projects/project_rust.py
@@ -3,7 +3,7 @@
 import subprocess
 from os import path
 from pathlib import Path
-from typing import Any, Dict, cast
+from typing import Any, Dict, List, cast
 
 from erdpy import dependencies, errors, myprocess, utils
 from erdpy.projects.project_base import Project
@@ -31,30 +31,35 @@ def perform_build(self):
         meta = self.has_meta()
         try:
             if meta:
-                # The meta crate allows contract developers to add extra
+                # The meta crate handles the build process, ABI generation and
+                # allows contract developers to add extra
                 # preparation steps before building.
                 self.run_meta()
-            self.run_cargo()
-
-            # ABI generated separately for backwards compatibility
-            if not meta:
+            else:
+                # For backwards compatibility - build the wasm and generate the ABI
+                self.run_cargo()
                 self.generate_abi()
         except subprocess.CalledProcessError as err:
             raise errors.BuildError(err.output)
 
-    def run_cargo(self):
-        env = self.get_env()
-
-        args = [
-            "cargo",
-            "build",
+    def prepare_build_wasm_args(self, args: List[str]):
+        args.extend([
             "--target=wasm32-unknown-unknown",
             "--release",
             "--out-dir",
             self.get_output_folder(),
             "-Z"
             "unstable-options"
+        ])
+
+    def run_cargo(self):
+        env = self.get_env()
+
+        args = [
+            "cargo",
+            "build",
         ]
+        self.prepare_build_wasm_args(args)
         self.decorate_cargo_args(args)
 
         if not self.options.get("wasm_symbols"):
@@ -69,10 +74,20 @@ def run_meta(self):
         cwd = self.get_meta_folder()
         env = self.get_env()
 
+        # run the meta executable with the arguments `build --target=...`
         args = [
             "cargo",
             "run",
+            "build",
         ]
+        self.prepare_build_wasm_args(args)
+
+        for (option, value) in self.options.items():
+            if isinstance(value, bool):
+                if value:
+                    args.extend([f"--{option}"])
+            else:
+                args.extend([f"--{option}", str(value)])
 
         return_code = myprocess.run_process_async(args, env=env, cwd=str(cwd))
         if return_code != 0:
@@ -116,18 +131,20 @@ def get_abi_folder(self):
 
     def _do_after_build(self) -> Path:
         original_name = self.cargo_file.package_name
-        wasm_base_name = self.cargo_file.package_name.replace("-", "_")
-        wasm_file = Path(self.get_output_folder(), f"{wasm_base_name}_wasm.wasm").resolve()
         wasm_file_renamed = self.options.get("wasm_name")
         if not wasm_file_renamed:
             wasm_file_renamed = f"{original_name}.wasm"
         wasm_file_renamed_path = Path(self.get_output_folder(), wasm_file_renamed)
-        shutil.move(wasm_file, wasm_file_renamed_path)
 
-        if self.has_abi():
-            abi_file = self.get_abi_filepath()
-            abi_file_renamed = Path(self.get_output_folder(), f"{original_name}.abi.json")
-            shutil.move(abi_file, abi_file_renamed)
+        if not self.has_meta():
+            wasm_base_name = self.cargo_file.package_name.replace("-", "_")
+            wasm_file = Path(self.get_output_folder(), f"{wasm_base_name}_wasm.wasm").resolve()
+            shutil.move(str(wasm_file), wasm_file_renamed_path)
+
+            if self.has_abi():
+                abi_file = self.get_abi_filepath()
+                abi_file_renamed = Path(self.get_output_folder(), f"{original_name}.abi.json")
+                shutil.move(abi_file, abi_file_renamed)
 
         return wasm_file_renamed_path
 
diff --git a/erdpy/projects/shared.py b/erdpy/projects/shared.py
index 2eb9224e..ef7f375d 100644
--- a/erdpy/projects/shared.py
+++ b/erdpy/projects/shared.py
@@ -1,23 +1,24 @@
-import os
+from pathlib import Path
 
 
-def is_source_clang(directory):
+def is_source_clang(directory: Path) -> bool:
     return _directory_contains_file(directory, ".c")
 
 
-def is_source_cpp(directory):
+def is_source_cpp(directory: Path) -> bool:
     return _directory_contains_file(directory, ".cpp")
 
 
-def is_source_sol(directory):
+def is_source_sol(directory: Path) -> bool:
     return _directory_contains_file(directory, ".sol")
 
 
-def is_source_rust(directory):
+def is_source_rust(directory: Path) -> bool:
     return _directory_contains_file(directory, "Cargo.toml")
 
 
-def _directory_contains_file(directory, name_suffix):
-    for file in os.listdir(directory):
-        if file.lower().endswith(name_suffix.lower()):
+def _directory_contains_file(directory: Path, name_suffix: str) -> bool:
+    for file in directory.iterdir():
+        if str(file).lower().endswith(name_suffix.lower()):
             return True
+    return False
diff --git a/erdpy/projects/templates.py b/erdpy/projects/templates.py
index 5748752f..3c5414c1 100644
--- a/erdpy/projects/templates.py
+++ b/erdpy/projects/templates.py
@@ -3,7 +3,8 @@
 import os
 from os import path
 from pathlib import Path
-from typing import Union
+import pathlib
+from typing import List, Tuple, Union
 
 from erdpy import errors, utils
 from erdpy.projects import shared
@@ -35,8 +36,8 @@ def __init__(self, name, repository):
         self.language = repository.get_language(name)
 
 
-def create_from_template(project_name: str, template_name: str, directory: Union[Path, str]):
-    directory = path.expanduser(directory)
+def create_from_template(project_name: str, template_name: str, directory: Path):
+    directory = directory.expanduser()
 
     logger.info("create_from_template.project_name: %s", project_name)
     logger.info("create_from_template.template_name: %s", template_name)
@@ -44,11 +45,11 @@ def create_from_template(project_name: str, template_name: str, directory: Union
 
     if not directory:
         logger.info("Using current directory")
-        directory = os.getcwd()
+        directory = Path.cwd()
 
-    project_directory = path.join(directory, project_name)
-    if path.exists(project_directory):
-        raise errors.BadDirectory(project_directory)
+    project_directory = Path(directory) / project_name
+    if project_directory.exists():
+        raise errors.BadDirectory(str(project_directory))
 
     _download_templates_repositories()
     _copy_template(template_name, project_directory)
@@ -68,7 +69,7 @@ def _download_templates_repositories():
         repo.download()
 
 
-def _copy_template(template, destination_path):
+def _copy_template(template: str, destination_path: Path):
     for repo in get_templates_repositories():
         if repo.has_template(template):
             repo.copy_template(template, destination_path)
@@ -77,7 +78,7 @@ def _copy_template(template, destination_path):
     raise errors.TemplateMissingError(template)
 
 
-def _load_as_template(directory):
+def _load_as_template(directory: Path):
     if shared.is_source_clang(directory):
         return TemplateClang(directory)
     if shared.is_source_sol(directory):
@@ -87,7 +88,7 @@ def _load_as_template(directory):
 
 
 class Template:
-    def __init__(self, directory):
+    def __init__(self, directory: Path):
         self.directory = directory
 
     def apply(self, template_name, project_name):
@@ -110,19 +111,23 @@ class TemplateRust(Template):
     def _patch(self):
         logger.info("Patching cargo files...")
         self._patch_cargo()
-        self._patch_cargo_wasm()
-        self._patch_cargo_abi()
+        self._patch_sub_crate("wasm")
+        self._patch_sub_crate("abi")
+        self._patch_sub_crate("meta")
 
         logger.info("Patching source code...")
-        self._patch_source_code_wasm()
-        self._patch_source_code_abi()
+        self._patch_source_code_files([
+            self.directory / "abi" / "src" / "main.rs",
+            self.directory / "wasm" / "src" / "lib.rs",
+            self.directory / "meta" / "src" / "main.rs",
+        ], ignore_missing=True)
         self._patch_source_code_tests()
 
         logger.info("Patching test files...")
         self._patch_mandos_tests()
 
     def _patch_cargo(self):
-        cargo_path = path.join(self.directory, TemplateRust.CARGO_TOML)
+        cargo_path = self.directory / TemplateRust.CARGO_TOML
 
         cargo_file = CargoFile(cargo_path)
         cargo_file.package_name = self.project_name
@@ -138,41 +143,13 @@ def _patch_cargo(self):
 
         cargo_file.save()
 
-    def _patch_cargo_wasm(self):
-        cargo_path = path.join(self.directory, "wasm", TemplateRust.CARGO_TOML)
-
-        cargo_file = CargoFile(cargo_path)
-        cargo_file.package_name = f"{self.project_name}-wasm"
-        cargo_file.version = "0.0.0"
-        cargo_file.authors = ["you"]
-        cargo_file.edition = "2018"
-        cargo_file.publish = False
-
-        for dependency in cargo_file.get_dependencies().values():
-            del dependency["path"]
-        # Currently, the following logic is not really needed (we don't have dev-dependencies in wasm/Cargo.toml):
-        for dependency in cargo_file.get_dev_dependencies().values():
-            del dependency["path"]
-
-        # Patch the path towards the project crate (one folder above):
-        cargo_file.get_dependency(self.template_name)["path"] = ".."
-
-        cargo_file.save()
-
-        self._replace_in_files(
-            [cargo_path],
-            [
-                (f"[dependencies.{self.template_name}]", f"[dependencies.{self.project_name}]")
-            ]
-        )
-
-    def _patch_cargo_abi(self):
-        cargo_path = path.join(self.directory, "abi", TemplateRust.CARGO_TOML)
-        if not path.isfile(cargo_path):
+    def _patch_sub_crate(self, sub_name: str) -> None:
+        cargo_path = self.directory / sub_name / TemplateRust.CARGO_TOML
+        if not cargo_path.is_file():
             return
 
         cargo_file = CargoFile(cargo_path)
-        cargo_file.package_name = f"{self.project_name}-abi"
+        cargo_file.package_name = f"{self.project_name}-{sub_name}"
         cargo_file.version = "0.0.0"
         cargo_file.authors = ["you"]
         cargo_file.edition = "2018"
@@ -192,55 +169,41 @@ def _patch_cargo_abi(self):
             [cargo_path],
             [
                 (f"[dependencies.{self.template_name}]", f"[dependencies.{self.project_name}]")
-            ]
-        )
-
-    def _patch_source_code_wasm(self):
-        lib_path = path.join(self.directory, "wasm", "src", "lib.rs")
-
-        self._replace_in_files(
-            [lib_path],
-            [
-                (f"use {self.template_name.replace('-', '_')}::*", f"use {self.project_name.replace('-', '_')}::*")
-            ]
+            ],
+            ignore_missing=False
         )
 
-    def _patch_source_code_abi(self):
-        abi_main_path = path.join(self.directory, "abi", "src", "main.rs")
-        if not path.exists(abi_main_path):
-            return
+    def _with_underscores(self, name: str) -> str:
+        return name.replace('-', '_')
 
-        template_name = self.template_name.replace('-', '_')
-        project_name = self.project_name.replace('-', '_')
+    def _patch_source_code_files(self, source_paths: List[Path], ignore_missing: bool) -> None:
+        template_name = self._with_underscores(self.template_name)
+        project_name = self._with_underscores(self.project_name)
 
         self._replace_in_files(
-            [abi_main_path],
+            source_paths,
             [
-                # Example: replace "use simple-erc20::*" to "use my_token::*"
+                # Example: replace "use simple_erc20::*" to "use my_token::*"
                 (f"use {template_name}::*", f"use {project_name}::*"),
-                (f"<{template_name}::AbiProvider>()", f"<{project_name}::AbiProvider>()")
-            ]
+                # Example: replace "<simple_erc20::AbiProvider>()" to "<my_token::AbiProvider>()"
+                (f"<{template_name}::AbiProvider>()", f"<{project_name}::AbiProvider>()"),
+                # Example: replace "extern crate adder;" to "extern crate myadder;"
+                (f"extern crate {template_name};", f"extern crate {project_name};"),
+            ],
+            ignore_missing
         )
 
     def _patch_source_code_tests(self):
-        test_dir_path = path.join(self.directory, "tests")
-        if not path.isdir(test_dir_path):
+        test_dir_path = self.directory / "tests"
+        if not test_dir_path.is_dir():
             return
 
         test_paths = utils.list_files(test_dir_path)
-        self._replace_in_files(
-            test_paths,
-            [
-                # Example: replace "use simple-erc20::*" to "use my_token::*"
-                (f"use {self.template_name.replace('-', '_')}::*", f"use {self.project_name.replace('-', '_')}::*"),
-                # Example: replace "extern crate adder;" to "extern crate myadder"
-                (f"extern crate {self.template_name.replace('-', '_')};", f"extern crate {self.project_name.replace('-', '_')};")
-            ]
-        )
+        self._patch_source_code_files(test_paths, ignore_missing=False)
 
     def _patch_mandos_tests(self):
-        test_dir_path = path.join(self.directory, "mandos")
-        if not path.isdir(test_dir_path):
+        test_dir_path = self.directory / "mandos"
+        if not test_dir_path.is_dir():
             return
 
         test_paths = [e for e in utils.list_files(test_dir_path, suffix=".json")]
@@ -248,7 +211,8 @@ def _patch_mandos_tests(self):
             test_paths,
             [
                 (f"{self.template_name}.wasm", f"{self.project_name}.wasm")
-            ]
+            ],
+            ignore_missing=False
         )
 
         for file in test_paths:
@@ -257,9 +221,12 @@ def _patch_mandos_tests(self):
             data["name"] = data.get("name", "").replace(self.template_name, self.project_name)
             utils.write_json_file(file, data)
 
-    def _replace_in_files(self, files, replacements):
+    def _replace_in_files(self, files: List[Path], replacements, ignore_missing: bool) -> None:
         for file in files:
+            if ignore_missing and not file.exists():
+                continue
             content = utils.read_file(file)
+            assert isinstance(content, str)
 
             for to_replace, replacement in replacements:
                 content = content.replace(to_replace, replacement)
diff --git a/erdpy/projects/templates_repository.py b/erdpy/projects/templates_repository.py
index 30f1f072..af64d9dc 100644
--- a/erdpy/projects/templates_repository.py
+++ b/erdpy/projects/templates_repository.py
@@ -1,3 +1,4 @@
+from pathlib import Path
 import shutil
 import time
 from os import path
@@ -6,7 +7,7 @@
 
 
 class TemplatesRepository:
-    def __init__(self, key, url, github, relative_path):
+    def __init__(self, key: str, url: str, github: str, relative_path: str):
         self.key = key
         self.url = url
         self.github = github
@@ -39,33 +40,33 @@ def _get_archive_path(self):
         archive = path.join(tools_folder, f"{self.key}.zip")
         return archive
 
-    def get_folder(self):
+    def get_folder(self) -> Path:
         tools_folder = workstation.get_tools_folder()
-        folder = path.join(tools_folder, "templates", self.key)
+        folder = tools_folder / "templates" / self.key
         return folder
 
-    def has_template(self, template):
+    def has_template(self, template: str) -> bool:
         folder = self.get_template_folder(template)
-        has = path.isdir(folder)
+        has = folder.is_dir()
         return has
 
-    def get_template_folder(self, template):
-        return path.join(self.get_folder(), self.relative_path, template)
+    def get_template_folder(self, template: str) -> Path:
+        return self.get_folder() / self.relative_path / template
 
     def get_templates(self):
-        folder = path.join(self.get_folder(), self.relative_path)
+        folder = self.get_folder() / self.relative_path
         templates = utils.get_subfolders(folder)
         templates = [item for item in templates if self.is_template(item)]
         return templates
 
-    def is_template(self, subfolder):
+    def is_template(self, subfolder: str) -> bool:
         elrond_json_file = self.get_metadata_file(subfolder)
-        return path.isfile(elrond_json_file)
+        return elrond_json_file.is_file()
 
-    def get_metadata_file(self, template_folder):
-        return path.join(self.get_folder(), self.relative_path, template_folder, "elrond.json")
+    def get_metadata_file(self, template_folder: str) -> Path:
+        return self.get_folder() / self.relative_path / template_folder / "elrond.json"
 
-    def copy_template(self, template, destination_path):
+    def copy_template(self, template: str, destination_path: Path):
         if not self.has_template(template):
             raise errors.TemplateMissingError(template)
 
diff --git a/erdpy/transactions.py b/erdpy/transactions.py
index 71d27d62..4d1369c9 100644
--- a/erdpy/transactions.py
+++ b/erdpy/transactions.py
@@ -4,7 +4,7 @@
 from collections import OrderedDict
 from typing import Any, Dict, List, TextIO
 
-from erdpy import errors, utils
+from erdpy import config, errors, utils
 from erdpy.accounts import Account, Address
 from erdpy.interfaces import IElrondProxy, ITransaction
 from erdpy.ledger.config import compare_versions
@@ -62,8 +62,13 @@ def _field_decoded(self, field: str) -> str:
         return base64.b64decode(self.__dict__.get(field, None)).decode()
 
     def sign(self, account: Account):
+        self.validate()
         self.signature = signing.sign_transaction(self, account)
 
+    def validate(self) -> None:
+        if self.gasLimit > config.MAX_GAS_LIMIT:
+            raise errors.GasLimitTooLarge(self.gasLimit, config.MAX_GAS_LIMIT)
+
     def serialize(self) -> bytes:
         dictionary = self.to_dictionary()
         serialized = self._dict_to_json(dictionary)
diff --git a/erdpy/utils.py b/erdpy/utils.py
index 5e98521e..9ef3bba7 100644
--- a/erdpy/utils.py
+++ b/erdpy/utils.py
@@ -1,7 +1,6 @@
 import json
 import logging
 import os
-import os.path
 import pathlib
 import shutil
 import stat
@@ -79,7 +78,7 @@ def uniquify(path: Path) -> Path:
     return path
 
 
-def read_lines(file: str) -> List[str]:
+def read_lines(file: Path) -> List[str]:
     with open(file) as f:
         lines = f.readlines()
     lines = [line.strip() for line in lines]
@@ -118,11 +117,9 @@ def read_text_file(path: Path) -> str:
         raise errors.BadFile(str(path), err) from None
 
 
-def write_file(f: Any, text: str):
-    if isinstance(f, str) or isinstance(f, pathlib.PosixPath):
-        with open(f, "w") as f:
-            return f.write(text)
-    return f.write(text)
+def write_file(file_path: Path, text: str):
+    with open(file_path, "w") as file:
+        return file.write(text)
 
 
 def read_toml_file(filename):
@@ -159,7 +156,7 @@ def prettify_json_file(filename: str):
     write_json_file(filename, data)
 
 
-def get_subfolders(folder):
+def get_subfolders(folder: Path) -> List[str]:
     return [item.name for item in os.scandir(folder) if item.is_dir() and not item.name.startswith(".")]
 
 
diff --git a/erdpy/validators/validators_file.py b/erdpy/validators/validators_file.py
index df4025e0..c08b7ab3 100644
--- a/erdpy/validators/validators_file.py
+++ b/erdpy/validators/validators_file.py
@@ -1,5 +1,5 @@
 import json
-from os import path
+from pathlib import Path
 
 from erdpy import guards
 from erdpy.errors import CannotReadValidatorsData
@@ -7,7 +7,7 @@
 
 class ValidatorsFile:
     def __init__(self, validators_file_path: str):
-        self.validators_file_path = validators_file_path
+        self.validators_file_path = Path(validators_file_path)
         self._validators_data = self._read_json_file_validators()
 
     def get_num_of_nodes(self) -> int:
@@ -17,7 +17,7 @@ def get_validators_list(self):
         return self._validators_data.get("validators", [])
 
     def _read_json_file_validators(self):
-        val_file = path.expanduser(self.validators_file_path)
+        val_file = self.validators_file_path.expanduser()
         guards.is_file(val_file)
         with open(self.validators_file_path, "r") as json_file:
             try:
diff --git a/erdpy/wallet/pem.py b/erdpy/wallet/pem.py
index 7a3db41a..7ce783bc 100644
--- a/erdpy/wallet/pem.py
+++ b/erdpy/wallet/pem.py
@@ -1,20 +1,19 @@
 import base64
 import itertools
 import textwrap
-from os import path
 from pathlib import Path
 from typing import List, Tuple, Union
 
 from erdpy import guards, utils
 
 
-def get_pubkey(pem_file: Union[str, Path]):
+def get_pubkey(pem_file: Path):
     _, pubkey = parse(pem_file)
     return pubkey
 
 
-def parse(pem_file: Union[str, Path], index: int = 0) -> Tuple[bytes, bytes]:
-    pem_file = path.expanduser(pem_file)
+def parse(pem_file: Path, index: int = 0) -> Tuple[bytes, bytes]:
+    pem_file = pem_file.expanduser()
     guards.is_file(pem_file)
 
     lines = utils.read_lines(pem_file)
@@ -31,8 +30,8 @@ def parse(pem_file: Union[str, Path], index: int = 0) -> Tuple[bytes, bytes]:
     return secret_key, pubkey
 
 
-def parse_all(pem_file: Union[str, Path]) -> List[Tuple[bytes, bytes]]:
-    pem_file = path.expanduser(pem_file)
+def parse_all(pem_file: Path) -> List[Tuple[bytes, bytes]]:
+    pem_file = pem_file.expanduser()
     guards.is_file(pem_file)
 
     lines = utils.read_lines(pem_file)
@@ -53,8 +52,8 @@ def parse_all(pem_file: Union[str, Path]) -> List[Tuple[bytes, bytes]]:
     return result
 
 
-def parse_validator_pem(pem_file, index: int = 0):
-    pem_file = path.expanduser(pem_file)
+def parse_validator_pem(pem_file: Path, index: int = 0):
+    pem_file = pem_file.expanduser()
     guards.is_file(pem_file)
 
     lines = utils.read_lines(pem_file)
@@ -104,8 +103,8 @@ def get_bytes_from_secret_key(secret_key):
     return key_bytes
 
 
-def write(pem_file: Union[str, Path], secret_key: bytes, pubkey: bytes, name: str = ""):
-    pem_file = path.expanduser(pem_file)
+def write(pem_file: Path, secret_key: bytes, pubkey: bytes, name: str = ""):
+    pem_file = pem_file.expanduser()
 
     if not name:
         name = pubkey.hex()
diff --git a/erdpy/workstation.py b/erdpy/workstation.py
index 880a0486..42089cc5 100644
--- a/erdpy/workstation.py
+++ b/erdpy/workstation.py
@@ -4,7 +4,7 @@
 from erdpy import config, utils
 
 
-def get_tools_folder():
+def get_tools_folder() -> Path:
     folder = Path.home() / config.ROOT_FOLDER_NAME
     utils.ensure_folder(folder)
     return folder
diff --git a/setup.py b/setup.py
index fec684a8..de6c6483 100644
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
 with open("README.md", "r") as fh:
     long_description = "https://github.com/ElrondNetwork/elrond-sdk-erdpy"
 
-VERSION = "1.0.21"
+VERSION = "1.0.22"
 
 try:
     with open('./erdpy/_version.py', 'wt') as versionfile:


Test patch:
diff --git a/erdpy/testnet/setup.py b/erdpy/testnet/setup.py
index d8c35fee..98d8bac7 100644
--- a/erdpy/testnet/setup.py
+++ b/erdpy/testnet/setup.py
@@ -275,7 +275,7 @@ def build_binaries(testnet_config: TestnetConfiguration):
 
 def _get_wasm_vm_version(testnet_config: TestnetConfiguration):
     go_mod = testnet_config.node_source() / "go.mod"
-    lines = utils.read_lines(str(go_mod))
+    lines = utils.read_lines(go_mod)
     line = next(line for line in lines if "github.com/ElrondNetwork/arwen-wasm-vm" in line)
     parts = line.split()
     return parts[1]
diff --git a/erdpy/tests/shared.sh b/erdpy/tests/shared.sh
old mode 100644
new mode 100755
index 3b885cf0..721d40f1
--- a/erdpy/tests/shared.sh
+++ b/erdpy/tests/shared.sh
@@ -1,4 +1,10 @@
-export PYTHONPATH=$(realpath ../../)
+# compatibility with MacOS ( https://stackoverflow.com/a/9484017 )
+function absolute_path() {
+  DIR="${1%/*}"
+  (cd "$DIR" && echo "$(pwd -P)")
+}
+
+export PYTHONPATH=$(absolute_path ../../)
 echo "PYTHONPATH = ${PYTHONPATH}"
 
 ERDPY="python3 -m erdpy.cli"
@@ -23,3 +29,13 @@ assertFileExists() {
 
     return 0
 }
+
+assertFileDoesNotExist() {
+    if [ -f "$1" ]
+    then
+        echo "Error: expected file [$1] to be missing, but it exists!" 1>&2
+        return 1
+    fi
+
+    return 0
+}
diff --git a/erdpy/tests/test_cli_contracts.sh b/erdpy/tests/test_cli_contracts.sh
index d7274e2c..58dd432c 100755
--- a/erdpy/tests/test_cli_contracts.sh
+++ b/erdpy/tests/test_cli_contracts.sh
@@ -66,10 +66,61 @@ testRunMandos() {
     ${ERDPY} --verbose contract test --directory="mandos" ${SANDBOX}/myfunding-rs || return 1
 }
 
+testCleanContracts() {
+    assertFileExists ${SANDBOX}/myanswer-c/output/answer.wasm || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myanswer-c || return 1
+    assertFileDoesNotExist ${SANDBOX}/myanswer-c/output/answer.wasm || return 1
+
+    assertFileExists ${SANDBOX}/mycounter-c/output/counter.wasm || return 1
+    ${ERDPY} contract clean ${SANDBOX}/mycounter-c || return 1
+    assertFileDoesNotExist ${SANDBOX}/mycounter-c/output/counter.wasm || return 1
+
+    assertFileExists ${SANDBOX}/myerc20-c/output/erc20.wasm || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myerc20-c || return 1
+    assertFileDoesNotExist ${SANDBOX}/myerc20-c/output/erc20.wasm || return 1
+
+    assertFileExists ${SANDBOX}/myadder-rs/output/myadder-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/myadder-rs/output/myadder-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myadder-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/myadder-rs/output/myadder-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/myadder-rs/output/myadder-rs.abi.json || return 1
+
+    assertFileExists ${SANDBOX}/myfactorial-rs/output/myfactorial-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/myfactorial-rs/output/myfactorial-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myfactorial-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/myfactorial-rs/output/myfactorial-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/myfactorial-rs/output/myfactorial-rs.abi.json || return 1
+
+    assertFileExists ${SANDBOX}/myerc20-rs/output/myerc20-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/myerc20-rs/output/myerc20-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myerc20-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/myerc20-rs/output/myerc20-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/myerc20-rs/output/myerc20-rs.abi.json || return 1
+
+    assertFileExists ${SANDBOX}/mybubbles-rs/output/mybubbles-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/mybubbles-rs/output/mybubbles-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/mybubbles-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/mybubbles-rs/output/mybubbles-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/mybubbles-rs/output/mybubbles-rs.abi.json || return 1
+
+    assertFileExists ${SANDBOX}/mylottery-rs/output/mylottery-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/mylottery-rs/output/mylottery-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/mylottery-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/mylottery-rs/output/mylottery-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/mylottery-rs/output/mylottery-rs.abi.json || return 1
+
+    assertFileExists ${SANDBOX}/myfunding-rs/output/myfunding-rs.wasm || return 1
+    assertFileExists ${SANDBOX}/myfunding-rs/output/myfunding-rs.abi.json || return 1
+    ${ERDPY} contract clean ${SANDBOX}/myfunding-rs || return 1
+    assertFileDoesNotExist ${SANDBOX}/myfunding-rs/output/myfunding-rs.wasm || return 1
+    assertFileDoesNotExist ${SANDBOX}/myfunding-rs/output/myfunding-rs.abi.json || return 1
+}
+
 testAll() {
     cleanSandbox || return 1
     testTrivialCommands || return 1
     testCreateContracts || return 1
     testBuildContracts || return 1
     testRunMandos || return 1
+    testCleanContracts || return 1
 }
diff --git a/erdpy/tests/test_wallet.py b/erdpy/tests/test_wallet.py
index e2bc1a3a..9fb95e25 100644
--- a/erdpy/tests/test_wallet.py
+++ b/erdpy/tests/test_wallet.py
@@ -8,6 +8,7 @@
 import nacl.encoding
 import nacl.signing
 from erdpy.accounts import Account, Address
+from erdpy.errors import GasLimitTooLarge
 from erdpy.tests.utils import MyTestCase
 from erdpy.transactions import Transaction
 from erdpy.wallet import (bip39seed_to_secret_key, generate_pair,
@@ -128,6 +129,21 @@ def test_sign_transaction(self):
 
         self.assertEqual("83efd1bc35790ecc220b0ed6ddd1fcb44af6653dd74e37b3a49dcc1f002a1b98b6f79779192cca68bdfefd037bc81f4fa606628b751023122191f8c062362805", transaction.signature)
 
+    def test_gas_limit_too_large(self):
+        # With data
+        transaction = Transaction()
+        transaction.nonce = 0
+        transaction.value = "0"
+        transaction.sender = "erd1l453hd0gt5gzdp7czpuall8ggt2dcv5zwmfdf3sd3lguxseux2fsmsgldz"
+        transaction.receiver = "erd188nydpkagtpwvfklkl2tn0w6g40zdxkwfgwpjqc2a2m2n7ne9g8q2t22sr"
+        transaction.gasPrice = 200000000000000
+        transaction.gasLimit = 1500000000
+        transaction.data = "foo"
+        transaction.chainID = "chainID"
+        transaction.version = 1
+
+        self.assertRaises(GasLimitTooLarge, lambda: transaction.sign(self.alice))
+
     def test_generate_pair_pem(self):
         secret_key, pubkey = generate_pair()
         pem_file = Path(self.testdata_out, "foo.pem")