Skip to content

Instantly share code, notes, and snippets.

@andelf
Created February 28, 2020 18:14
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • 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')
@ygboucherk
Copy link

Thanks very much. U're saving me so much programming time !!!

@andelf
Copy link
Author

andelf commented Dec 12, 2020

@ygboucherk
Copy link

Thanks

@boopathiviky
Copy link

Expecting value: line 1 column 1 (char 0)

@boopathiviky
Copy link

Not working

@usamasiddiqui5131
Copy link

need this code in java, can any one help me?

@Mrllee1
Copy link

Mrllee1 commented Jun 8, 2022

addtransactionsign 404!
how to do?

@LuttaKing
Copy link

@Mrllee1 i have the same problem too, did you find a solution

@olamigokayphils
Copy link

olamigokayphils commented Sep 8, 2022

@Mrllee1 @LuttaKing I think there's a typo error with the sign_transaction function; The endpoint is supposed to be: /wallet/gettransactionsign

From documentation:

Sign the Transaction: The /wallet/gettransactionsign API call takes in two parameters. One is the transaction parameters, which takes in the JSON output from the previous step. The other is the privateKey parameter, which requires the private key associated with the staked TRX address to sign the transaction.

Documention: https://developers.tron.network/docs/api-signature-and-broadcast-flow

Still Expreriencing issues?:: This stackoverflow post explain why. You may need to sign programmatically:: https://stackoverflow.com/questions/66633139/trongrid-api-get-transaction-sign-not-found

@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