kisstron

KISS-friendly Tron CLI wallet
git clone git://git.luxferre.top/kisstron.git
Log | Files | Refs | README

commit ce7ad5189c4d29344daa69779ebf5c2aec87152f
Author: Luxferre <lux@ferre>
Date:   Wed, 14 Feb 2024 23:56:51 +0200

initial upload

Diffstat:
AREADME | 196+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Akisstron.py | 459+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arequirements.txt | 7+++++++
3 files changed, 662 insertions(+), 0 deletions(-)

diff --git a/README b/README @@ -0,0 +1,196 @@ +kisstron: KISS-friendly Tron CLI wallet +--------------------------------------- +This is a simple, no-nonsense Tron cryptocurrency wallet written in Python 3 +on top of the open-source tronpy library. The kisstron project aims to enable +CLI users to easily use Tron cryptocurrency, including stablecoins like USDT +or USDC, without all the hassle of "native" (Java-based) Tron's wallet-cli or +similar third-party projects. + +== Features == + +* Support for mainnet Tron as well as Nile and Shasta testnets +* Small codebase size that can be easily audited +* Full BIP-39 support for mnemonic wallet generation and import (English only) +* Account info display: TRX/USDT/USDC balances, frozen assets, remaining + energy and bandwidth +* Sending TRX/USDT/USDC on all supported networks +* Freezing (for energy) and unfreezing TRX on all supported networks +* Ability to connect to custom nodes or TronGrid API (with your own API key) +* No filesystem interaction (that's a feature, not a bug: see the FAQ section) +* Full scriptability: send/freeze/unfreeze transactions require manual approval + but it actually can be turned off with a special commandline parameter for the + kisstron wallet to be used in various automation tasks +* The kisstron.py file can be used as a library itself for different projects + +== Dependency setup == + +First, install Python 3, pip and the required dependencies (from this dir): + +pip install -r requirements.txt + +In some distributions, you may need to run pip3 instead of pip. Also, your +distribution might not contain the required development packages. For instance, +in Alpine Linux, you might have to run the following command first (as root): + +apk add python3-dev libtool autoconf automake musl-dev libffi-dev + +Optionally, make the kisstron.py file executable: + +chmod +x kisstron.py + +After everything is set up, you should be able to run the help screen: + +./kisstron.py -h + +== Usage == + +Now, let's cover typical kisstron wallet usage scenarios. + +-- Generate a new wallet -- + +./kisstron.py new trx [-pp "passphrase"] [-wc word_count] [-dp derivation_path] + +This will generate a new Tron wallet and display all its information. You must +then store this information in a secure place. +If -pp option is specified, your custom passphrase will be used to encrypt the +mnemonic phrase. If -wc option is specified, you can set to generate 15, 18, 21 +or 24 words of mnemonic instead of 12. If -dp option is specified, you can set +a non-standard BIP-39 derivation path. It is generally NOT recommended to ever +use this option unless you are an expert. + +-- Import an existing wallet from a mnemonic -- + +./kisstron.py import trx [-pp "passphrase"] [-dp derivation_path] + +This will ask you to enter your BIP-39 mnemonic and import your Tron wallet by +recovering and displaying all information about it. You must then store this +information in a secure place. +If -pp option is specified, your custom passphrase will be used to decrypt the +mnemonic phrase. The -dp option is the same as for the "new trx" subcommand, so +the same precautions apply. +Note that entering a wrong passphrase won't generate an error: kisstron will +just restore a different wallet from the same mnemonic, so be careful. + +-- Network options -- + +For any of the further operations in this section, the following options can +be added and are quite important: + +* -net selects the network: mainnet (default), nile, shasta +* -node allows to specify custom Tron node URL (must be HTTP URL, not RPC) +* -tgkey allows to specify custom TronGrid API key (overrides -node) + +These options will be marked as [netopts] in the further reference. + +Note that -tgkey is only relevant for mainnet and has no effect on testnets. + +-- Displaying account information -- + +Display account information based on any Tron address: + +./kisstron.py info trx -addr tron_address [netopts] + +Display your account information based on your private key (in hex): + +./kisstron.py info trx -pk private_key [netopts] + +Either of these commands will display the following information: + +* Tron address (in Base58check format) +* Balances in TRX, USDT and USDC +* Frozen TRX assets (if any) +* Remaining bandwidth amount +* Remaining energy amount + +If you supply both -pk and -addr to "info trx" subcommand, -pk will take +precedence. + +-- Sending funds -- + +Send TRX: + +./kisstron.py send trx -addr dest_addr -amt amount -pk private_key [netopts] + +Send USDT: + +./kisstron.py send usdt -addr dest_addr -amt amount -pk private_key [netopts] + +Send USDC: + +./kisstron.py send usdc -addr dest_addr -amt amount -pk private_key [netopts] + +Any of these commands will ask you for confirmation by entering "yes" and +pressing Enter to send the transaction. If you don't want to do this or use +the send subcommands in some kind of automation, append -nc flag to them. +The amount is entered in floating point TRX/USDT/USDC values respectively, +not in minimal units. +When transfering USDT/USDC, you may get error messages related to lack of +energy to conduct the transaction. Please refer to the next section of TRX +freezing to get energy. + +-- Freezing and unfreezing TRX funds -- + +Freeze TRX for energy: + +./kisstron.py fre trx -amt amount -pk private_key [netopts] + +Unfreeze TRX: + +./kisstron.py unf trx -amt amount -pk private_key [netopts] + +Any of these commands will ask you for confirmation by entering "yes" and +pressing Enter to send the transaction. If you don't want to do this or use +the subcommands in some kind of automation, append -nc flag to them. +Again, the amount of TRX is entered as a floating point value. + +Keep in mind that it takes up to 72 hours to unfreeze your TRX assets. + +== FAQ == + +- Can I trust it? + +The author trusts it. In fact, kisstron was created because of lack of decent +FOSS Tron wallets for Linux/BSD desktop operating systems. However, it cannot +be considered production-ready as it hasn't undergone rigorous testing and +independent audit. The kisstron codebase is small and well-commented though, +so you can trust it exactly to the same extent as you trust the underlying +tronpy library and its dependencies. +You can study tronpy source code here: https://github.com/andelf/tronpy + +- Why are TRC-20 tokens hardcoded? + +To simplify the logic and avoid any user mistakes when manually pasting token +contract addresses. If you need to add custom token support, you can always +modify the KT_TRC20_TOKENS dictionary in the source code directly. + +- Why doesn't kisstron do anything to keep private keys and passphrases secure? + +Because it only cares about interacting with Tron network and creating or +restoring your wallets. For key/password management, there are dedicated tools +like pass (https://www.passwordstore.org/) that do their job extremely well. +For example, instead of -pk private_key, you could write -pk $(pass kt/pkey) or +something like that if you stored your wallet key under kt/pkey path in pass. +This way, your keys can stay secure without adding any filesystem interaction +to kisstron itself, reducing the overall attack surface. + +- Does kisstron use any remote API to sign transactions? + +No. The underlying tronpy library signs all transactions offline, so the +private key never leaves your machine. It uses libsecp256k1 via coincurve +library binding for transaction signing. + +Ability to implement offline mode (transactions are created and signed on one +kisstron instance without Internet connectivity, then broadcast from another) +in the future versions is being studied. + +- Why doesn't kisstron support TRC-10, multisig, witnesses, history etc? + +Support for some of these things may come in the future. But kisstron was +created as a minimum viable wallet to operate on TRX and select stablecoins. It +adheres to the KISS principle ("keep it simple, stupid"), hence the name. + +== Credits == + +Created by Luxferre in 2024, released into public domain with no warranties. + +Made in Ukraine. diff --git a/kisstron.py b/kisstron.py @@ -0,0 +1,459 @@ +#!/usr/bin/env python3 +# kisstron: a simple to use TRON CLI wallet based on tronpy +# +# Supports: +# * BIP39 mnemonic wallet generation/restoration +# * converting private keys to BIP39 mnemonics and vice versa +# * basic TRX parameters display (balance, energy, bandwidth) +# * basic TRX operations (transfer, freeze for energy, unfreeze) +# * basic TRC20 token operations (balance display, transfer) for select tokens +# (contract addresses are hardcoded, see KT_TRC20_TOKENS dictionary) +# * ability to select testnets (nile, shasta) if necessary +# * ability to connect to custom mainnet nodes if necessary +# * switching to TronGrid API by providing your own API key +# +# As of now, the main goal is to be able to operate on TRX and stablecoins. +# For mnemonic phrase generation, only English is currently supported. +# +# See README for more information, run with -h flag for usage instructions +# +# Created by Luxferre in 2024, released into public domain + +import sys +from tronpy import Tron +from tronpy.keys import PrivateKey +from tronpy.providers import HTTPProvider +from tronpy.exceptions import BadAddress, AddressNotFound, BadKey, ValidationError +import mnemonic, eth_utils + +# some global parameters (hardcoded) +KT_TIMEOUT = 20.0 # network operation timeout +KT_FEE_LIMIT = 10000000 # transaction fee limit for mainnet (in Sun) +KT_TEST_FEE_LIMIT = 1000000000 # transaction fee limit for testnets (in Sun) +TRX_SCALE = 1000000.0 # TRX scale (million Suns in 1 TRX) +TG_API_KEY = None # TronGrid API Key (unused by default) +NODE_ADDR = 'http://18.196.99.16:8090' # public Tron node (Germany) + +# TRC20 token definitions (also hardcoded) +# Make sure the mainnet tokens match the contract addresses +# specified on these pages: +# USDT: https://tron.network/usdt +# USDC: https://tron.network/usdc + +KT_TRC20_TOKENS = { + 'mainnet': { + 'usdt': { + 'contract': 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', + 'ticker': 'USDT', + 'show_balance': True + }, + 'usdc': { + 'contract': 'TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8', + 'ticker': 'USDC', + 'show_balance': True + } + }, + 'nile': { + 'usdt': { + 'contract': 'TXLAQ63Xg1NAzckPwKHvzw7CSEmLMEqcdj', + 'ticker': 'USDT', + 'show_balance': True + }, + 'usdc': { + 'contract': 'TEMVynQpntMqkPxP6wXTW2K7e4sM3cRmWz', + 'ticker': 'USDC', + 'show_balance': True + } + }, + 'shasta': { + 'usdt': { + 'contract': 'TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs', + 'ticker': 'USDT', + 'show_balance': True + }, + 'usdc': { + 'contract': 'TSdZwNqpHofzP6BsBKGQUWdBeJphLmF6id', + 'ticker': 'USDC', + 'show_balance': True + } + } +} + +# helper functions + +# print operation result according to the below convention +def presult(resdict: dict): + if 'success' not in resdict: + resdict['success'] = False + if 'message' not in resdict: + resdict['message'] = '' + if resdict['success'] == True: + print ('Success:\n%s\n' % resdict['message']) + else: + print ('Error:\n%s\n' % resdict['message']) + +# confirm transactions +def trconfirm(promptstr: str): + choice = input(promptstr).lower() + if choice != 'yes': + print('Transaction not confirmed, aborting!') + sys.exit(0) + +# instantiate tronpy client +def tronclient(network='mainnet'): + network = network.lower() # lowercase network name + if network == 'mainnet': # use a public node or TronGrid API for mainnet + if TG_API_KEY is not None: # TG_API_KEY must be set + client = Tron(network=network, conf={'fee_limit': KT_FEE_LIMIT}, + provider=HTTPProvider(api_key=TG_API_KEY, timeout=KT_TIMEOUT)) + else: # NODE_ADDR must be set + client = Tron(network=network, conf={'fee_limit': KT_FEE_LIMIT}, + provider=HTTPProvider(NODE_ADDR, timeout=KT_TIMEOUT)) + else: # any default API is fine for a testnet + client = Tron(network=network, + conf={'fee_limit': KT_TEST_FEE_LIMIT, 'timeout': KT_TIMEOUT}) + return client + +# get TRC20 token balance of an address (in float) +def trc20balance(client: Tron, contractaddr: str, targetaddr: str): + cntr = client.get_contract(contractaddr) + precision = cntr.functions.decimals() + return cntr.functions['balanceOf'](targetaddr) / (10 ** precision) + +# operation definitions +# all operations return a dictionary with the following fields: +# success: True/False +# message: string +# (optional) result: dictionary + +# create a new wallet +# input: network, passphrase, word count (12, 15, 18, 21, 24), derivation path +# output: private key, address, mnemonic +def newwallet(network: str, passphrase: str, wordcount: int, dpath: str): + res = {'success': False, 'message': '', 'result': {}} + if wordcount not in [12, 15, 18, 21, 24]: + res['message'] = 'Invalid word count for mnemonic generation' + return res + client = tronclient(network) + wallet, mnemo = client.generate_address_with_mnemonic(passphrase = passphrase, + num_words = wordcount, language = 'english', account_path = dpath) + wallet['mnemonic'] = mnemo + res['success'] = True + res['result'] = wallet + res['message'] = 'Wallet created, save this info in a secure place!\n\n' + res['message'] += 'Address: %s\n' % wallet['base58check_address'] + res['message'] += 'Address (hex): %s\n' % wallet['hex_address'] + res['message'] += 'Private key (hex): %s\n' % wallet['private_key'] + res['message'] += 'Public key (hex): %s\n' % wallet['public_key'] + res['message'] += 'Mnemonic: %s' % wallet['mnemonic'] + return res + +# import a wallet +# input: network, mnemonic, passphrase, derivation path +# output: private key, address +def importwallet(network: str, mnemo: str, passphrase: str, dpath: str): + res = {'success': False, 'message': '', 'result': {}} + client = tronclient(network) + try: + wallet = client.generate_address_from_mnemonic(mnemonic = mnemo, + passphrase = passphrase, account_path = dpath) + except (mnemonic.mnemonic.ConfigurationError, + eth_utils.exceptions.ValidationError): + res['message'] = 'Invalid mnemonic phrase!' + return res + wallet['mnemonic'] = mnemo + res['success'] = True + res['result'] = wallet + res['message'] = 'Wallet imported, save this info in a secure place!\n\n' + res['message'] += 'Address: %s\n' % wallet['base58check_address'] + res['message'] += 'Address (hex): %s\n' % wallet['hex_address'] + res['message'] += 'Private key (hex): %s\n' % wallet['private_key'] + res['message'] += 'Public key (hex): %s\n' % wallet['public_key'] + res['message'] += 'Mnemonic: %s' % wallet['mnemonic'] + return res + +# get account information by Tron address +# input: network, address +# output: info(dictionary) +def infobyaddr(network: str, address: str): + res = {'success': False, 'message': '', 'result': {}} + client = tronclient(network) + try: + rawinfo = client.get_account(address) + except BadAddress: + res['message'] = 'Invalid address! Set the correct one with -addr flag' + return res + except AddressNotFound: + res['message'] = 'Address not found on the blockchain! Set the correct one with -addr flag' + return res + # get bandwidth and energy information + rawresinfo = client.get_account_resource(address) + # some values used in bandwidth calculation + freenetlimit = 0 + freenetused = 0 + netlimit = 0 + netused = 0 + if 'freeNetLimit' in rawresinfo: + freenetlimit = rawresinfo['freeNetLimit'] + if 'freeNetUsed' in rawresinfo: + freenetlimit = rawresinfo['freeNetUsed'] + if 'NetLimit' in rawresinfo: + netlimit = rawresinfo['NetLimit'] + if 'NetUsed' in rawresinfo: + netused = rawresinfo['NetUsed'] + bandwidth = freenetlimit - freenetused + netlimit - netused + # some values used in energy calculation + energylimit = 0 + energyused = 0 + if 'EnergyLimit' in rawresinfo: + energylimit = rawresinfo['EnergyLimit'] + if 'EnergyUsed' in rawresinfo: + energyused = rawresinfo['EnergyUsed'] + # info dictionary received, parse it + info = { + 'address': rawinfo['address'], # wallet address + 'trxbalance': rawinfo['balance'], # TRX wallet balance (in Sun) + 'frozen': [], # hold frozen TRX info here + 'trc20balance': {}, # hold TRC20 token balances here + 'bandwidth': bandwidth, # bandwidth + 'energy': energylimit - energyused # energy + } + # iterate over TRX freeze records + for el in rawinfo['frozenV2']: + if 'type' in el and 'amount' in el and el['amount'] > 0: + info['frozen'].append({'type': el['type'], 'amount': el['amount']}) + # iterate over hardcoded TRC20 tokens + for tkn in KT_TRC20_TOKENS[network]: + tknobj = KT_TRC20_TOKENS[network][tkn] + if tknobj['show_balance'] == True: + info['trc20balance'][tknobj['ticker']] = trc20balance(client, + tknobj['contract'], address) + res['success'] = True + res['result'] = info + res['message'] = 'Address: %s\nBalance:\n' % info['address'] + res['message'] += '%f TRX\n' % (info['trxbalance'] / TRX_SCALE) + for ticker in info['trc20balance']: + res['message'] += '%f %s\n' % (info['trc20balance'][ticker], ticker) + res['message'] += 'Frozen assets:\n' + for el in info['frozen']: + res['message'] += '%s\t%f TRX\n' % (el['type'], el['amount'] / TRX_SCALE) + res['message'] += 'Remaining bandwidth: %s\n' % str(info['bandwidth']) + res['message'] += 'Remaining energy: %s' % str(info['energy']) + return res + +# get own account information by private key +# input: network, private key +# output: info(dictionary) +def infobypk(network: str, pk: str): + res = {'success': False, 'message': '', 'result': {}} + try: + rpk = PrivateKey(bytes.fromhex(pk)) + except (BadKey, ValueError): + res['message'] = 'Invalid private key!' + return res + client = tronclient(network) + addr = client.generate_address(rpk)['base58check_address'] + return infobyaddr(network, addr) + +# send TRX transaction (and wait for its completion) +# input: network, private key, target address, amount (in TRX) +# output: transaction receipt object +def sendtrx(network: str, pk: str, toaddr: str, amt: float): + res = {'success': False, 'message': '', 'result': {}} + if amt < 1: + res['message'] = 'Nothing to transfer!' + return res + try: + rpk = PrivateKey(bytes.fromhex(pk)) + except (BadKey, ValueError): + res['message'] = 'Invalid private key!' + return res + client = tronclient(network) + fromaddr = client.generate_address(rpk)['base58check_address'] + # build and sign the transaction + txn = client.trx.transfer(fromaddr, toaddr, + int(amt * TRX_SCALE)).build().sign(rpk) + # send the transaction + try: + receipt = txn.broadcast().wait() + except ValidationError: + res['message'] = 'Insufficient balance or other validation error!' + return res + res['success'] = True + res['message'] = 'Sent %f TRX to %s\nTransaction ID %s' % (amt, + toaddr, receipt['id']) + res['result'] = receipt + return res + +# send TRC20 token transaction (and wait for its completion) +# input: network, private key, token ticker, target address, amount (in tokens) +# output: transaction receipt object +def sendtrc20(network: str, pk: str, ticker: str, toaddr: str, amt: float): + res = {'success': False, 'message': '', 'result': {}} + ticker = ticker.lower() + if ticker not in KT_TRC20_TOKENS[network]: + res['message'] = 'Token not supported by kisstron for %s!' % network + return res + if amt < 1: + res['message'] = 'Nothing to transfer!' + return res + try: + rpk = PrivateKey(bytes.fromhex(pk)) + except (BadKey, ValueError): + res['message'] = 'Invalid private key!' + return res + client = tronclient(network) + fromaddr = client.generate_address(rpk)['base58check_address'] + # instantiate the contract + contractaddr = KT_TRC20_TOKENS[network][ticker]['contract'] + cntr = client.get_contract(contractaddr) + precision = cntr.functions.decimals() + scaler = 10 ** precision + # build and sign the transaction + txn = cntr.functions.transfer(toaddr, + int(amt * scaler)).with_owner(fromaddr).build().sign(rpk) + # send the transaction + try: + receipt = txn.broadcast().wait() + except ValidationError: + res['message'] = 'Insufficient balance or other validation error!' + return res + res['result'] = receipt + if receipt['receipt']['result'] == 'OUT_OF_ENERGY': + res['message'] = 'Not enough energy for transaction!' + return res + res['success'] = True + res['message'] = 'Sent %f %s to %s\nTransaction ID %s' % (amt, + KT_TRC20_TOKENS[network][ticker]['ticker'], + toaddr, receipt['id']) + return res + +# freeze TRX for energy (and wait for its completion) +# input: network, private key, amount +# output: transaction receipt object +def freezetrx(network: str, pk: str, amt: float): + res = {'success': False, 'message': '', 'result': {}} + if amt < 1: + res['message'] = 'Nothing to freeze!' + return res + try: + rpk = PrivateKey(bytes.fromhex(pk)) + except (BadKey, ValueError): + res['message'] = 'Invalid private key!' + return res + client = tronclient(network) + myaddr = client.generate_address(rpk)['base58check_address'] + # build and sign the transaction + txn = client.trx.freeze_balance(myaddr, int(amt * TRX_SCALE), + "ENERGY").build().sign(rpk) + # send the transaction + try: + receipt = txn.broadcast().wait() + except ValidationError: + res['message'] = 'Insufficient balance or other validation error!' + return res + res['success'] = True + res['message'] = 'Frozen %f TRX at %s\nTransaction ID %s' % (amt, + myaddr, receipt['id']) + res['result'] = receipt + return res + +# unfreeze TRX for energy (and wait for its completion) +# input: network, private key, amount +# output: transaction receipt object +def unfreezetrx(network: str, pk: str, amt: float): + res = {'success': False, 'message': '', 'result': {}} + if amt < 1: + res['message'] = 'Nothing to unfreeze!' + return res + try: + rpk = PrivateKey(bytes.fromhex(pk)) + except (BadKey, ValueError): + res['message'] = 'Invalid private key!' + return res + client = tronclient(network) + myaddr = client.generate_address(rpk)['base58check_address'] + # build and sign the transaction + txn = client.trx.unfreeze_balance(owner = myaddr, + unfreeze_balance = int(amt * TRX_SCALE), + resource = "ENERGY").build().sign(rpk) + # send the transaction + try: + receipt = txn.broadcast().wait() + except ValidationError: + res['message'] = 'Insufficient balance or other validation error!' + return res + res['success'] = True + res['message'] = 'Unfrozen %f TRX at %s\nTransaction ID %s' % (amt, + myaddr, receipt['id']) + res['result'] = receipt + return res + +if __name__ == '__main__': # main app start + from argparse import ArgumentParser + parser = ArgumentParser(description='kisstron: a simple CLI wallet for Tron blockchain', epilog='(c) Luxferre 2024 --- No rights reserved <https://unlicense.org>') + parser.add_argument('op', help='Operation: new, import, info, send, fre[eze], unf[reeze]') + parser.add_argument('coin', help='Coin to operate on: trx (default), usdt, usdc', default='trx') + parser.add_argument('-net', '--network', default='mainnet', help='(op: all) Network: mainnet (default), nile, shasta') + parser.add_argument('-node', '--tron-node', default=NODE_ADDR, help='(op: all) Custom Tron node HTTP address (default %s)' % NODE_ADDR) + parser.add_argument('-tgkey', '--trongrid-api-key', default=None, help='(op: all) TronGrid API key (default None)') + parser.add_argument('-addr', '--address', default=None, help='(op: info, send) Tron address to send to or display information about (-pk overrides this to display own info)') + parser.add_argument('-amt', '--amount', type=float, default=0, help='(op: send, fre, unf) Amount to send/freeze/unfreeze') + parser.add_argument('-pk', '--private-key', default=None, help='(op: info, send, fre, unf) Private key for transactions or own information, in hex') + parser.add_argument('-pp', '--passphrase', default='', help='(op: new, import) Mnemonic encryption passphrase (default empty)') + parser.add_argument('-wc', '--words-count', type=int, default=12, help='(op: new) Word count in the mnemonic: 12 (default), 15, 18, 21, 24') + parser.add_argument('-dp', '--derivation-path', default="m/44'/195'/0'/0/0", help="(op: new, import) BIP-39 derivation path for mnemonic (default is m/44'/195'/0'/0/0)") + parser.add_argument('-nc', '--no-confirm', action='store_true', help='(op: send, fre, unf) Do not ask to type "yes" for transaction confirmation (dangerous)') + + args = parser.parse_args() + + # fix all relevant parameters (and lowercase where applicable) + op = args.op.lower() # operation + coin = args.coin.lower() # coin/token + tronnet = args.network.lower() # Tron network (mainnet, nile, shasta) + targetaddr = args.address # address parameter + amount = float(args.amount) # amount to send/freeze/unfreeze + pk = args.private_key # private key (in hex) + passphrase = args.passphrase # mnemonic encryption passphrase + wcount = int(args.words_count) # mnemonic word count (for new operation) + dpath = args.derivation_path # mnemonic derivation path (for new/import) + trans_confirm = True # ask for transaction confirmation ("yes") + NODE_ADDR = args.tron_node # custom Tron node HTTP address + TG_API_KEY = args.trongrid_api_key # optional TronGrid API key + if args.no_confirm == True: + trans_confirm = False + + # check the constraints for every action and call appropriate functions + + opres = {} # operation result placeholder + if op == 'new': # new wallet + opres = newwallet(tronnet, passphrase, wcount, dpath) + elif op == 'import': # import wallet + mnemo = input('Your mnemonic phrase: ') + opres = importwallet(tronnet, mnemo, passphrase, dpath) + elif op == 'info': # info on own or foreign address + if pk == None: # public info, address must be set + opres = infobyaddr(tronnet, targetaddr) + else: # own info, private key must be set + opres = infobypk(tronnet, pk) + elif op == 'send': # send transaction + if trans_confirm == True: + trconfirm('Enter "yes" to confirm sending funds: ') + if coin == 'trx': # send TRX + opres = sendtrx(tronnet, pk, targetaddr, amount) + else: # send one of the predefined TRC20 tokens + opres = sendtrc20(tronnet, pk, coin, targetaddr, amount) + elif op == 'fre' or op == 'freeze': # freeze TRX for energy + if trans_confirm == True: + trconfirm('Enter "yes" to confirm freezing funds: ') + opres = freezetrx(tronnet, pk, amount) + elif op == 'unf' or op == 'unfreeze': # unfreeze TRX + if trans_confirm == True: + trconfirm('Enter "yes" to confirm unfreezing funds: ') + opres = unfreezetrx(tronnet, pk, amount) + else: # unknown operation + print('Unknown operation!') + print('Run the script with -h parameter to see available options') + sys.exit(1) + + presult(opres) # print out the result diff --git a/requirements.txt b/requirements.txt @@ -0,0 +1,7 @@ +cffi>=1.16.0 +coincurve>=18.0.0 +cytoolz>=0.12.3 +mnemonic>=0.20 +pycryptodome>=3.18.0 +pycryptodomex>=3.18.0 +tronpy>=0.4.0