Skip to content

Instantly share code, notes, and snippets.

@andelf
Created February 28, 2020 18:14
Show Gist options
  • Save andelf/7c2b6b176872ff0e83086828e6f16d2b to your computer and use it in GitHub Desktop.
Save andelf/7c2b6b176872ff0e83086828e6f16d2b to your computer and use it in GitHub Desktop.
Get TRC20 balance and Transfer TRC20 tokens
import requests
import base58
import base64
from pprint import pprint
ADDRESS = "T....your address"
PRIV_KEY = 'hex private key'
CONTRACT = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t" # USDT
CONTRACT = "T,,,,,,,,,,,,,,,,,,,,,,ia"
API_URL_BASE = 'https://api.trongrid.io/'
# API_URL_BASE = 'https://api.shasta.trongrid.io/'
# API_URL_BASE = 'https://api.nileex.io/'
# 70a08231: balanceOf(address)
METHOD_BALANCE_OF = 'balanceOf(address)'
# a9059cbb: transfer(address,uint256)
METHOD_TRANSFER = 'transfer(address,uint256)'
DEFAULT_FEE_LIMIT = 1_000_000 # 1 TRX
def address_to_parameter(addr):
return "0" * 24 + base58.b58decode_check(addr)[1:].hex()
def amount_to_parameter(amount):
return '%064x' % amount
def get_balance(address=ADDRESS):
url = API_URL_BASE + 'wallet/triggerconstantcontract'
payload = {
'owner_address': base58.b58decode_check(ADDRESS).hex(),
'contract_address': base58.b58decode_check(CONTRACT).hex(),
'function_selector': METHOD_BALANCE_OF,
'parameter': address_to_parameter(address),
}
resp = requests.post(url, json=payload)
data = resp.json()
if data['result'].get('result', None):
print(data['constant_result'])
val = data['constant_result'][0]
print('balance =', int(val, 16))
else:
print('error:', bytes.fromhex(data['result']['message']).decode())
def get_trc20_transaction(to, amount, memo=''):
url = API_URL_BASE + 'wallet/triggersmartcontract'
payload = {
'owner_address': base58.b58decode_check(ADDRESS).hex(),
'contract_address': base58.b58decode_check(CONTRACT).hex(),
'function_selector': METHOD_TRANSFER,
'parameter': address_to_parameter(to) + amount_to_parameter(amount),
"fee_limit": DEFAULT_FEE_LIMIT,
'extra_data': base64.b64encode(memo.encode()).decode(), # TODO: not supported yet
}
resp = requests.post(url, json=payload)
data = resp.json()
if data['result'].get('result', None):
transaction = data['transaction']
return transaction
else:
print('error:', bytes.fromhex(data['result']['message']).decode())
raise RuntimeError
def sign_transaction(transaction, private_key=PRIV_KEY):
url = API_URL_BASE + 'wallet/addtransactionsign'
payload = {'transaction': transaction, 'privateKey': private_key}
resp = requests.post(url, json=payload)
data = resp.json()
if 'Error' in data:
print('error:', data)
raise RuntimeError
return data
def broadcast_transaction(transaction):
url = API_URL_BASE + 'wallet/broadcasttransaction'
resp = requests.post(url, json=transaction)
data = resp.json()
print(data)
def transfer(to, amount, memo=''):
transaction = get_trc20_transaction(to, amount, memo)
pprint(transaction)
transaction = sign_transaction(transaction)
broadcast_transaction(transaction)
get_balance()
transfer('T..............q', 5_000, 'test from python')
@olamigokayphils
Copy link

Thanks @ygboucherk; Correct tronpy has a .sign function. See Here
cc: @EUA @G33kNoob

Here's a custom extract leveraging the tronpy library

from tronpy.keys import PrivateKey

def sign_transaction_offine(self, transaction_object, your_tron_private_key):
        # CONVERT PRIVATE KEY TO BYTES
        private_key_bytes = bytes.fromhex(your_tron_private_key)
        
        # CONVERT INITIATED TRANSACTION ID TO BYTES
        transaction_id_bytes = bytes.fromhex(transaction_object["txID"]

        # SIGN TRANSACTION
        data = PrivateKey(private_key_bytes=private_key_bytes).sign_msg_hash(transaction_id_bytes))
        return data

Call your function and proceed to broadcast

@zigomi
Copy link

zigomi commented Dec 13, 2022

How to get the balance of a TRX wallet itself? Not a TRC20 token, but TRX

@ikamran
Copy link

ikamran commented Jan 17, 2023

can anybody give more information (best practice) how to transfer trc20 and sign it offline?

@andelf
Copy link
Author

andelf commented Jan 17, 2023

@olamigokayphils
Copy link

Hiii @ikamran, to further buttress what my Boss @andelf posted. Let's presume you want to transfer USDT (a trc20 contract). There are 3 basic steps:

  1. Create transaction
  2. Sign transaction (Offline)
  3. Broadcast transaction to the network

Create

Use the get_trc20_transaction function in @andelf's initial gist above to Create your transaction

Sign Transaction

from tronpy.keys import PrivateKey

def sign_transaction_offine(self, transaction_object, your_tron_private_key):
        # CONVERT PRIVATE KEY TO BYTES
        private_key_bytes = bytes.fromhex(your_tron_private_key)
        
        # CONVERT INITIATED TRANSACTION ID TO BYTES
        transaction_id_bytes = bytes.fromhex(transaction_object["txID"]

        # SIGN TRANSACTION
        data = PrivateKey(private_key_bytes=private_key_bytes).sign_msg_hash(transaction_id_bytes))
        return data

Broadcast

Use the broadcast_transaction function in @andelf's initial gist to Broadcast the transaction to the network

@ikamran
Copy link

ikamran commented Jan 21, 2023

Thanks for your help, according to your last email I already did it successfully without any issue the issue comes when I want to send from a multi-signiture address

from tronpy import Tron
from tronpy.keys import PrivateKey

client = Tron(network='nile', conf={'fee_limit': 10_000_000})

priv_key = PrivateKey(bytes.fromhex("e5f4a4acb3ecef9e8589d69c1aa9fa5723b04ff724b1c04ebe6cd746b241ad04"))
contractAddress = 'TLBaRhANQoJFTqre9Nf1mjuwNWjCJeYqUL'
myAddress = 'TGihHToJJ9B1NFi2Vx5bTM8muRmsbdAaJ8'
contract = client.get_contract(contractAddress)

oneUSDJ = 1_000_000_000_000_000_000

class Tron:
    def __init__(self):
        self.contract = contract
        self.myAdd = myAddress

    def getTokenBalance(self):
        return contract.functions.balanceOf(myAddress) / 10 ** 6

    def getBalance(self, address):
        return client.get_account_balance(address)

    def getAccount(self, address):
        return client.get_account(address)


    def sendTRX(self, accountAddress):
        txn = (
            client.trx.transfer(myAddress, accountAddress, 100_000_000)
                .with_owner(myAddress)
                .permission_id(2)
                .build()
                .sign(priv_key)
        )

        print(txn.txid)
        print(txn.broadcast().wait())
        return txn

tron = Tron()

I included permission_id but I got this error:

>>> tron.sendTRX('TC2r7dPEfMaMt8ToJ3rQ7SuVhX391cFhv5')
{'address': '414a0a401872a17daf91e753226e8981e434e97452', 'weight': 1}
a6c802ca8cbfd1562e6203a43437b98f30aef7b09f2d968be213afe56746c864
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\itk\dev\test\tronpy\example1.py", line 38, in sendTRX
    print(txn.broadcast().wait())
  File "C:\Users\itk\dev\test\venv\lib\site-packages\tronpy\tron.py", line 154, in broadcast
    return TransactionRet(self._client. Broadcast(self), client=self._client, method=self._method)
  File "C:\Users\itk\dev\test\venv\lib\site-packages\tronpy\tron.py", line 827, in broadcast
    self._handle_api_error(payload)
  File "C:\Users\itk\dev\test\venv\lib\site-packages\tronpy\tron.py", line 467, in _handle_api_error
    raise BadSignature(msg)
tronpy.exceptions.BadSignature: Validate signature error: sig error

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment