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')
@EUA
Copy link

EUA commented Oct 5, 2022

Sending the pkey to some site? Even if it's api.trongrid.io...
Why we can't sign our transaction? It comes super weird to me.
Is there are any way to do it without sending pkey?
Edit: Just use this...

@G33kNoob
Copy link

agree it, this method seem not secure

@ygboucherk
Copy link

@EUA @G33kNoob I suggest you to check tronpy library, which signs transactions locally :D

@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