Offline mode implemented
This commit is contained in:
@@ -16,6 +16,7 @@ similar third-party projects.
|
|||||||
* Sending TRX/USDT/USDC on all supported networks
|
* Sending TRX/USDT/USDC on all supported networks
|
||||||
* Freezing (for energy) and unfreezing TRX 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)
|
* Ability to connect to custom nodes or TronGrid API (with your own API key)
|
||||||
|
* Offline mode for send/freeze/unfreeze transactions
|
||||||
* No filesystem interaction (that's a feature, not a bug: see the FAQ section)
|
* No filesystem interaction (that's a feature, not a bug: see the FAQ section)
|
||||||
* Full scriptability: send/freeze/unfreeze transactions require manual approval
|
* Full scriptability: send/freeze/unfreeze transactions require manual approval
|
||||||
but it actually can be turned off with a special commandline parameter for the
|
but it actually can be turned off with a special commandline parameter for the
|
||||||
@@ -145,9 +146,25 @@ 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.
|
Keep in mind that it takes up to 72 hours to unfreeze your TRX assets.
|
||||||
|
|
||||||
|
-- Offline operations --
|
||||||
|
|
||||||
|
The send trx, send usdt, send usdc, fre trx and unf trx subcommands support the
|
||||||
|
-o flag that, after the "Success:" line, outputs a JSON of a signed transaction.
|
||||||
|
You can copy or otherwise transfer this output to a different machine running
|
||||||
|
kisstron and broadcast it to the network with bro trx subcommand:
|
||||||
|
|
||||||
|
./kisstron.py bro trx [netopts]
|
||||||
|
|
||||||
|
You can then paste the transaction JSON into the input field, press Enter and,
|
||||||
|
unless -nc flag is passed too, confirm sending the transaction by entering yes.
|
||||||
|
The transaction will be broadcast and processed as usual.
|
||||||
|
|
||||||
|
Keep in mind that Tron transactions expire very quickly, so you have a limited
|
||||||
|
time window between signing and publishing them.
|
||||||
|
|
||||||
== FAQ ==
|
== FAQ ==
|
||||||
|
|
||||||
- Can I trust it?
|
- Can I trust this wallet?
|
||||||
|
|
||||||
The author trusts it. In fact, kisstron was created because of lack of decent
|
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
|
FOSS Tron wallets for Linux/BSD desktop operating systems. However, it cannot
|
||||||
@@ -179,9 +196,8 @@ No. The underlying tronpy library signs all transactions offline, so the
|
|||||||
private key never leaves your machine. It uses libsecp256k1 via coincurve
|
private key never leaves your machine. It uses libsecp256k1 via coincurve
|
||||||
library binding for transaction signing.
|
library binding for transaction signing.
|
||||||
|
|
||||||
Ability to implement offline mode (transactions are created and signed on one
|
You can also use offline mode to split signing and sending transactions, as
|
||||||
kisstron instance without Internet connectivity, then broadcast from another)
|
shown in this document.
|
||||||
in the future versions is being studied.
|
|
||||||
|
|
||||||
- Why doesn't kisstron support TRC-10, multisig, witnesses, history etc?
|
- Why doesn't kisstron support TRC-10, multisig, witnesses, history etc?
|
||||||
|
|
||||||
|
|||||||
+119
-50
@@ -11,6 +11,7 @@
|
|||||||
# * ability to select testnets (nile, shasta) if necessary
|
# * ability to select testnets (nile, shasta) if necessary
|
||||||
# * ability to connect to custom mainnet nodes if necessary
|
# * ability to connect to custom mainnet nodes if necessary
|
||||||
# * switching to TronGrid API by providing your own API key
|
# * switching to TronGrid API by providing your own API key
|
||||||
|
# * offline mode with -o switch and bro[adcast] operation
|
||||||
#
|
#
|
||||||
# As of now, the main goal is to be able to operate on TRX and stablecoins.
|
# 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.
|
# For mnemonic phrase generation, only English is currently supported.
|
||||||
@@ -19,11 +20,14 @@
|
|||||||
#
|
#
|
||||||
# Created by Luxferre in 2024, released into public domain
|
# Created by Luxferre in 2024, released into public domain
|
||||||
|
|
||||||
import sys
|
import sys, json
|
||||||
from tronpy import Tron
|
from tronpy import Tron
|
||||||
|
from tronpy.tron import Transaction
|
||||||
from tronpy.keys import PrivateKey
|
from tronpy.keys import PrivateKey
|
||||||
from tronpy.providers import HTTPProvider
|
from tronpy.providers import HTTPProvider
|
||||||
from tronpy.exceptions import BadAddress, AddressNotFound, BadKey, ValidationError
|
from tronpy.exceptions import (BadAddress, AddressNotFound,
|
||||||
|
BadKey, ValidationError, TransactionError)
|
||||||
|
from json.decoder import JSONDecodeError
|
||||||
import mnemonic, eth_utils
|
import mnemonic, eth_utils
|
||||||
|
|
||||||
# some global parameters (hardcoded)
|
# some global parameters (hardcoded)
|
||||||
@@ -258,7 +262,8 @@ def infobypk(network: str, pk: str):
|
|||||||
# send TRX transaction (and wait for its completion)
|
# send TRX transaction (and wait for its completion)
|
||||||
# input: network, private key, target address, amount (in TRX)
|
# input: network, private key, target address, amount (in TRX)
|
||||||
# output: transaction receipt object
|
# output: transaction receipt object
|
||||||
def sendtrx(network: str, pk: str, toaddr: str, amt: float):
|
def sendtrx(network: str, pk: str, toaddr: str, amt: float,
|
||||||
|
offline: bool = False):
|
||||||
res = {'success': False, 'message': '', 'result': {}}
|
res = {'success': False, 'message': '', 'result': {}}
|
||||||
if amt < 1:
|
if amt < 1:
|
||||||
res['message'] = 'Nothing to transfer!'
|
res['message'] = 'Nothing to transfer!'
|
||||||
@@ -273,22 +278,27 @@ def sendtrx(network: str, pk: str, toaddr: str, amt: float):
|
|||||||
# build and sign the transaction
|
# build and sign the transaction
|
||||||
txn = client.trx.transfer(fromaddr, toaddr,
|
txn = client.trx.transfer(fromaddr, toaddr,
|
||||||
int(amt * TRX_SCALE)).build().sign(rpk)
|
int(amt * TRX_SCALE)).build().sign(rpk)
|
||||||
# send the transaction
|
if offline == True: # generate transaction JSON
|
||||||
try:
|
res['success'] = True
|
||||||
receipt = txn.broadcast().wait()
|
res['result'] = txn
|
||||||
except ValidationError:
|
res['message'] = json.dumps(txn.to_json(), separators=(',', ':'))
|
||||||
res['message'] = 'Insufficient balance or other validation error!'
|
else: # send the transaction
|
||||||
return res
|
try:
|
||||||
res['success'] = True
|
receipt = txn.broadcast().wait()
|
||||||
res['message'] = 'Sent %f TRX to %s\nTransaction ID %s' % (amt,
|
except ValidationError as e:
|
||||||
toaddr, receipt['id'])
|
res['message'] = str(e)
|
||||||
res['result'] = receipt
|
return res
|
||||||
|
res['success'] = True
|
||||||
|
res['message'] = 'Sent %f TRX to %s\nTransaction ID %s' % (amt,
|
||||||
|
toaddr, receipt['id'])
|
||||||
|
res['result'] = receipt
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# send TRC20 token transaction (and wait for its completion)
|
# send TRC20 token transaction (and wait for its completion)
|
||||||
# input: network, private key, token ticker, target address, amount (in tokens)
|
# input: network, private key, token ticker, target address, amount (in tokens)
|
||||||
# output: transaction receipt object
|
# output: transaction receipt object
|
||||||
def sendtrc20(network: str, pk: str, ticker: str, toaddr: str, amt: float):
|
def sendtrc20(network: str, pk: str, ticker: str, toaddr: str,
|
||||||
|
amt: float, offline: bool = False):
|
||||||
res = {'success': False, 'message': '', 'result': {}}
|
res = {'success': False, 'message': '', 'result': {}}
|
||||||
ticker = ticker.lower()
|
ticker = ticker.lower()
|
||||||
if ticker not in KT_TRC20_TOKENS[network]:
|
if ticker not in KT_TRC20_TOKENS[network]:
|
||||||
@@ -312,26 +322,30 @@ def sendtrc20(network: str, pk: str, ticker: str, toaddr: str, amt: float):
|
|||||||
# build and sign the transaction
|
# build and sign the transaction
|
||||||
txn = cntr.functions.transfer(toaddr,
|
txn = cntr.functions.transfer(toaddr,
|
||||||
int(amt * scaler)).with_owner(fromaddr).build().sign(rpk)
|
int(amt * scaler)).with_owner(fromaddr).build().sign(rpk)
|
||||||
# send the transaction
|
if offline == True: # generate transaction JSON
|
||||||
try:
|
res['success'] = True
|
||||||
receipt = txn.broadcast().wait()
|
res['result'] = txn
|
||||||
except ValidationError:
|
res['message'] = json.dumps(txn.to_json(), separators=(',', ':'))
|
||||||
res['message'] = 'Insufficient balance or other validation error!'
|
else: # send the transaction
|
||||||
return res
|
try:
|
||||||
res['result'] = receipt
|
receipt = txn.broadcast().wait()
|
||||||
if receipt['receipt']['result'] == 'OUT_OF_ENERGY':
|
except ValidationError as e:
|
||||||
res['message'] = 'Not enough energy for transaction!'
|
res['message'] = str(e)
|
||||||
return res
|
return res
|
||||||
res['success'] = True
|
res['result'] = receipt
|
||||||
res['message'] = 'Sent %f %s to %s\nTransaction ID %s' % (amt,
|
if receipt['receipt']['result'] == 'OUT_OF_ENERGY':
|
||||||
KT_TRC20_TOKENS[network][ticker]['ticker'],
|
res['message'] = 'Not enough energy for transaction!'
|
||||||
toaddr, receipt['id'])
|
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
|
return res
|
||||||
|
|
||||||
# freeze TRX for energy (and wait for its completion)
|
# freeze TRX for energy (and wait for its completion)
|
||||||
# input: network, private key, amount
|
# input: network, private key, amount
|
||||||
# output: transaction receipt object
|
# output: transaction receipt object
|
||||||
def freezetrx(network: str, pk: str, amt: float):
|
def freezetrx(network: str, pk: str, amt: float, offline: bool = False):
|
||||||
res = {'success': False, 'message': '', 'result': {}}
|
res = {'success': False, 'message': '', 'result': {}}
|
||||||
if amt < 1:
|
if amt < 1:
|
||||||
res['message'] = 'Nothing to freeze!'
|
res['message'] = 'Nothing to freeze!'
|
||||||
@@ -346,22 +360,26 @@ def freezetrx(network: str, pk: str, amt: float):
|
|||||||
# build and sign the transaction
|
# build and sign the transaction
|
||||||
txn = client.trx.freeze_balance(myaddr, int(amt * TRX_SCALE),
|
txn = client.trx.freeze_balance(myaddr, int(amt * TRX_SCALE),
|
||||||
"ENERGY").build().sign(rpk)
|
"ENERGY").build().sign(rpk)
|
||||||
# send the transaction
|
if offline == True: # generate transaction JSON
|
||||||
try:
|
res['success'] = True
|
||||||
receipt = txn.broadcast().wait()
|
res['result'] = txn
|
||||||
except ValidationError:
|
res['message'] = json.dumps(txn.to_json(), separators=(',', ':'))
|
||||||
res['message'] = 'Insufficient balance or other validation error!'
|
else: # send the transaction
|
||||||
return res
|
try:
|
||||||
res['success'] = True
|
receipt = txn.broadcast().wait()
|
||||||
res['message'] = 'Frozen %f TRX at %s\nTransaction ID %s' % (amt,
|
except ValidationError as e:
|
||||||
myaddr, receipt['id'])
|
res['message'] = str(e)
|
||||||
res['result'] = receipt
|
return res
|
||||||
|
res['success'] = True
|
||||||
|
res['message'] = 'Frozen %f TRX at %s\nTransaction ID %s' % (amt,
|
||||||
|
myaddr, receipt['id'])
|
||||||
|
res['result'] = receipt
|
||||||
return res
|
return res
|
||||||
|
|
||||||
# unfreeze TRX for energy (and wait for its completion)
|
# unfreeze TRX for energy (and wait for its completion)
|
||||||
# input: network, private key, amount
|
# input: network, private key, amount
|
||||||
# output: transaction receipt object
|
# output: transaction receipt object
|
||||||
def unfreezetrx(network: str, pk: str, amt: float):
|
def unfreezetrx(network: str, pk: str, amt: float, offline: bool = False):
|
||||||
res = {'success': False, 'message': '', 'result': {}}
|
res = {'success': False, 'message': '', 'result': {}}
|
||||||
if amt < 1:
|
if amt < 1:
|
||||||
res['message'] = 'Nothing to unfreeze!'
|
res['message'] = 'Nothing to unfreeze!'
|
||||||
@@ -377,22 +395,58 @@ def unfreezetrx(network: str, pk: str, amt: float):
|
|||||||
txn = client.trx.unfreeze_balance(owner = myaddr,
|
txn = client.trx.unfreeze_balance(owner = myaddr,
|
||||||
unfreeze_balance = int(amt * TRX_SCALE),
|
unfreeze_balance = int(amt * TRX_SCALE),
|
||||||
resource = "ENERGY").build().sign(rpk)
|
resource = "ENERGY").build().sign(rpk)
|
||||||
|
if offline == True: # generate transaction JSON
|
||||||
|
res['success'] = True
|
||||||
|
res['result'] = txn
|
||||||
|
res['message'] = json.dumps(txn.to_json(), separators=(',', ':'))
|
||||||
|
else: # send the transaction
|
||||||
|
try:
|
||||||
|
receipt = txn.broadcast().wait()
|
||||||
|
except ValidationError as e:
|
||||||
|
res['message'] = str(e)
|
||||||
|
return res
|
||||||
|
res['success'] = True
|
||||||
|
res['message'] = 'Unfrozen %f TRX at %s\nTransaction ID %s' % (amt,
|
||||||
|
myaddr, receipt['id'])
|
||||||
|
res['result'] = receipt
|
||||||
|
return res
|
||||||
|
|
||||||
|
# Broadcast transaction JSON saved in kisstron offline mode
|
||||||
|
# input: network, transaction JSON string
|
||||||
|
# output: transaction receipt object
|
||||||
|
def broadcastjson(network, tjson):
|
||||||
|
res = {'success': False, 'message': '', 'result': {}}
|
||||||
|
# import the transaction from JSON
|
||||||
|
try:
|
||||||
|
txn = Transaction.from_json(json.loads(tjson))
|
||||||
|
except (TypeError, ValidationError, TransactionError,
|
||||||
|
JSONDecodeError, KeyError):
|
||||||
|
res['message'] = 'Malformed transaction JSON!'
|
||||||
|
return res
|
||||||
|
# instantiate a client for the transaction
|
||||||
|
txn._client = tronclient(network)
|
||||||
# send the transaction
|
# send the transaction
|
||||||
try:
|
try:
|
||||||
receipt = txn.broadcast().wait()
|
receipt = txn.broadcast().wait()
|
||||||
except ValidationError:
|
except ValidationError as e:
|
||||||
res['message'] = 'Insufficient balance or other validation error!'
|
res['message'] = str(e)
|
||||||
|
return res
|
||||||
|
except TransactionError as e:
|
||||||
|
res['message'] = str(e)
|
||||||
|
return res
|
||||||
|
res['result'] = receipt
|
||||||
|
if ('receipt' in receipt and 'result' in receipt['receipt']
|
||||||
|
and receipt['receipt']['result'] == 'OUT_OF_ENERGY'):
|
||||||
|
res['message'] = 'Not enough energy for transaction!'
|
||||||
return res
|
return res
|
||||||
res['success'] = True
|
res['success'] = True
|
||||||
res['message'] = 'Unfrozen %f TRX at %s\nTransaction ID %s' % (amt,
|
res['message'] = 'Transaction sent\nTransaction ID %s' % receipt['id']
|
||||||
myaddr, receipt['id'])
|
|
||||||
res['result'] = receipt
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
if __name__ == '__main__': # main app start
|
if __name__ == '__main__': # main app start
|
||||||
from argparse import ArgumentParser
|
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 = 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('op', help='Operation: new, import, info, send, fre[eze], unf[reeze], bro[adcast]')
|
||||||
parser.add_argument('coin', help='Coin to operate on: trx (default), usdt, usdc', default='trx')
|
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('-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('-node', '--tron-node', default=NODE_ADDR, help='(op: all) Custom Tron node HTTP address (default %s)' % NODE_ADDR)
|
||||||
@@ -400,6 +454,7 @@ if __name__ == '__main__': # main app start
|
|||||||
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('-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('-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('-pk', '--private-key', default=None, help='(op: info, send, fre, unf) Private key for transactions or own information, in hex')
|
||||||
|
parser.add_argument('-o', '--offline', action='store_true', help='(op: send, fre, unf) Build and sign the transaction in offline mode and output its JSON')
|
||||||
parser.add_argument('-pp', '--passphrase', default='', help='(op: new, import) Mnemonic encryption passphrase (default empty)')
|
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('-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('-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)")
|
||||||
@@ -420,6 +475,9 @@ if __name__ == '__main__': # main app start
|
|||||||
trans_confirm = True # ask for transaction confirmation ("yes")
|
trans_confirm = True # ask for transaction confirmation ("yes")
|
||||||
NODE_ADDR = args.tron_node # custom Tron node HTTP address
|
NODE_ADDR = args.tron_node # custom Tron node HTTP address
|
||||||
TG_API_KEY = args.trongrid_api_key # optional TronGrid API key
|
TG_API_KEY = args.trongrid_api_key # optional TronGrid API key
|
||||||
|
offline_mode = False # offline mode
|
||||||
|
if args.offline == True:
|
||||||
|
offline_mode = True
|
||||||
if args.no_confirm == True:
|
if args.no_confirm == True:
|
||||||
trans_confirm = False
|
trans_confirm = False
|
||||||
|
|
||||||
@@ -432,6 +490,9 @@ if __name__ == '__main__': # main app start
|
|||||||
mnemo = input('Your mnemonic phrase: ')
|
mnemo = input('Your mnemonic phrase: ')
|
||||||
opres = importwallet(tronnet, mnemo, passphrase, dpath)
|
opres = importwallet(tronnet, mnemo, passphrase, dpath)
|
||||||
elif op == 'info': # info on own or foreign address
|
elif op == 'info': # info on own or foreign address
|
||||||
|
if offline_mode == True:
|
||||||
|
print('Info operation is not available in offline mode!')
|
||||||
|
sys.exit(1)
|
||||||
if pk == None: # public info, address must be set
|
if pk == None: # public info, address must be set
|
||||||
opres = infobyaddr(tronnet, targetaddr)
|
opres = infobyaddr(tronnet, targetaddr)
|
||||||
else: # own info, private key must be set
|
else: # own info, private key must be set
|
||||||
@@ -440,17 +501,25 @@ if __name__ == '__main__': # main app start
|
|||||||
if trans_confirm == True:
|
if trans_confirm == True:
|
||||||
trconfirm('Enter "yes" to confirm sending funds: ')
|
trconfirm('Enter "yes" to confirm sending funds: ')
|
||||||
if coin == 'trx': # send TRX
|
if coin == 'trx': # send TRX
|
||||||
opres = sendtrx(tronnet, pk, targetaddr, amount)
|
opres = sendtrx(tronnet, pk, targetaddr, amount, offline_mode)
|
||||||
else: # send one of the predefined TRC20 tokens
|
else: # send one of the predefined TRC20 tokens
|
||||||
opres = sendtrc20(tronnet, pk, coin, targetaddr, amount)
|
opres = sendtrc20(tronnet, pk, coin, targetaddr, amount, offline_mode)
|
||||||
elif op == 'fre' or op == 'freeze': # freeze TRX for energy
|
elif op == 'fre' or op == 'freeze': # freeze TRX for energy
|
||||||
if trans_confirm == True:
|
if trans_confirm == True:
|
||||||
trconfirm('Enter "yes" to confirm freezing funds: ')
|
trconfirm('Enter "yes" to confirm freezing funds: ')
|
||||||
opres = freezetrx(tronnet, pk, amount)
|
opres = freezetrx(tronnet, pk, amount, offline_mode)
|
||||||
elif op == 'unf' or op == 'unfreeze': # unfreeze TRX
|
elif op == 'unf' or op == 'unfreeze': # unfreeze TRX
|
||||||
if trans_confirm == True:
|
if trans_confirm == True:
|
||||||
trconfirm('Enter "yes" to confirm unfreezing funds: ')
|
trconfirm('Enter "yes" to confirm unfreezing funds: ')
|
||||||
opres = unfreezetrx(tronnet, pk, amount)
|
opres = unfreezetrx(tronnet, pk, amount, offline_mode)
|
||||||
|
elif op == 'bro' or op == 'broadcast': # broadcast signed JSON transaction
|
||||||
|
if offline_mode == True:
|
||||||
|
print('Broadcast operation is not available in offline mode!')
|
||||||
|
sys.exit(1)
|
||||||
|
transjson = input('Input transaction JSON: ')
|
||||||
|
if trans_confirm == True:
|
||||||
|
trconfirm('Enter "yes" to confirm sending the transaction: ')
|
||||||
|
opres = broadcastjson(tronnet, transjson)
|
||||||
else: # unknown operation
|
else: # unknown operation
|
||||||
print('Unknown operation!')
|
print('Unknown operation!')
|
||||||
print('Run the script with -h parameter to see available options')
|
print('Run the script with -h parameter to see available options')
|
||||||
|
|||||||
Reference in New Issue
Block a user