tronpy

repository·master·Indexed 42 days ago

https://github.com/andelf/tronpy

A Python client library for interacting with the TRON blockchain. It provides synchronous (Tron) and asynchronous (AsyncTron) interfaces for managing transactions, querying network state, and interacting with smart contracts. Features include ABI encoding and decoding via tronpy.abi.trx_abi, support for custom HTTP providers, and integration with TronGrid API keys.

Tokens
18.6K
Snippets
69
Records
83
Agent score
48%

What's inside tronpy

  1. Overview of TronPy

    master

    TronPy is an easy-to-use TRON HTTP API client that supports most of the java-tron 4.1 APIs. It is designed with security and control in mind, following these key principles:

    • Offline Signing: TronPy always signs transactions offline to avoid using unsafe APIs.
    • Base58Check Addresses: The library consistently uses Base58Check addresses for API responses, ABI decoding, and encoding.
    • Offline Transaction Construction: Users can construct transactions offline, providing greater control over parameters like ref_block and expiration.
  2. The transaction lifecycle in TronPy

    master

    A complete transaction workflow in TronPy consists of these steps:

    1. Initialize Client: Create a tronpy.Tron API client.
    2. Build Transaction: Use functions from client.trx (which return a tronpy.tron.TransactionBuilder) to define the transaction.
    3. Configure (Optional): Set parameters like .fee_limit(), .memo(), or .permission_id() on the builder.
    4. Build: Call .build() on the builder to create a tronpy.tron.Transaction object.
    5. Sign: Call .sign(private_key) on the transaction object. You can call this multiple times for multi-signature transactions.
    6. Broadcast: Call .broadcast() on the signed transaction to send it to the network.
    7. Wait (Optional): Call .wait() on the returned tronpy.tron.TransactionRet object to block until the transaction receipt is available.
  3. Understand Tron address formats

    master

    TronPy supports two address formats:

    1. base58check: The user-friendly variant (preferred).
    2. hex str: The hexadecimal string representation.

    TronPy can accept and process both types, but it is recommended to use base58check for general user interactions.

  4. How smart contract calls work in TronPy

    master

    TronPy supports two types of smart contract interactions:

    1. Const call: Used for methods marked as pure or view in the contract. These calls do not modify the blockchain state, return results immediately, and are wrapped as function objects where results are parsed according to the ABI.
    2. Trigger call: Used for methods that modify state. These are treated as system contract calls that must be signed and broadcasted. The result is fetched and parsed from the transaction receipt after broadcasting.

    To inspect available functions on a contract, you can use dir(cntr.functions) or iterate over cntr.functions to see function signatures.

    from tronpy import Tron
    
    client = Tron(network='nile')
    cntr = client.get_contract("THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia")
    
    # Inspect functions
    print(dir(cntr.functions))
    for f in cntr.functions:
        print(f)  # prints function signature
  5. How Shielded TRC20 works

    master

    Shielded TRC20 allows for private token transfers and is currently only available on the Nile Testnet. It uses a ShieldedTRC20 wrapper to manage three types of operations:

    1. mint: Moves tokens from a standard T-address to a shielded z-address.
    2. transfer: Moves tokens from one z-address to another z-address.
    3. burn: Moves tokens from a z-address back to a standard T-address.

    To interact with these features, you must acquire the wrapper using client.get_contract_as_shielded_trc20(contract_address).

  6. Deploy a smart contract

    master

    To deploy a contract, create a Contract object with the compiled bytecode and abi. Use client.trx.deploy_contract to initiate the deployment.

    Note: Constructor parameters are provided by appending the encoded parameters to the bytecode.

    Example:

    from tronpy import Tron, Contract
    from tronpy.keys import PrivateKey
    
    client = Tron(network='nile')
    priv_key = PrivateKey(bytes.fromhex("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"))
    
    bytecode = "60806040..." # your bytecode
    abi = [...] # your ABI
    
    # Handle constructor parameters if needed
    cntr = Contract(name="SimpleStore", bytecode=bytecode, abi=abi)
    parameter = cntr.constructor.encode_parameter("TRh5N2iAmjyeJbbCXsDuo7PNZvyjVWtL2e", 18)
    cntr.bytecode = bytecode + parameter
    
    txn = (
        client.trx.deploy_contract('TGQgfK497YXmjdgvun9Bg5Zu3xE15v17cu', cntr)
        .fee_limit(5_000_000)
        .build()
        .sign(priv_key)
    )
    
    result = txn.broadcast().wait()
    print('Created:', result['contract_address'])
    from tronpy import Tron, Contract
    from tronpy.keys import PrivateKey
    
    client = Tron(network='nile')
    priv_key = PrivateKey(bytes.fromhex("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"))
    
    bytecode = "60806040..."
    abi = [...]
    
    cntr = Contract(name="SimpleStore", bytecode=bytecode, abi=abi)
    parameter = cntr.constructor.encode_parameter("TRh5N2iAmjyeJbbCXsDuo7PNZvyjVWtL2e", 18)
    cntr.bytecode = bytecode + parameter
    
    txn = (
        client.trx.deploy_contract('TGQgfK497YXmjdgvun9Bg5Zu3xE15v17cu', cntr)
        .fee_limit(5_000_000)
        .build()
        .sign(priv_key)
    )
    
    result = txn.broadcast().wait()
    print('Created:', result['contract_address'])
  7. Use a contract with an unpublished ABI

    master

    If a contract on-chain does not have an ABI published, you can manually provide one by setting the abi property on the contract object. This allows you to call its functions using the expected signatures.

    Example:

    import json
    from tronpy import Tron, keys
    
    client = Tron(network='nile')
    contract_address = 'TS..........'
    
    # Define the ABI as a JSON string or list
    abi_json = '''
    [
      {
        "constant": false,
        "inputs": [{"name": "min_tokens", "type": "uint256"}, {"name": "deadline", "type": "uint256"}],
        "name": "trxToTokenSwapInput",
        "outputs": [{"name": "", "type": "uint256"}],
        "payable": true,
        "stateMutability": "payable",
        "type": "Function"
      }
    ]
    '''
    
    cntr = client.get_contract(contract_address)
    cntr.abi = json.loads(abi_json)
    
    # Now you can call functions defined in the ABI
    txn = (
        cntr.functions.trxToTokenSwapInput.with_transfer(1_000_000_000)(1_000_000_000_000, 5)
        .with_owner(from_addr)
        .fee_limit(1_000_000_000)
        .build()
        .sign(priv_key)
    )
    import json
    from tronpy import Tron, keys
    
    client = Tron(network='nile')
    
    dzi_trade = 'TS..........'
    from_addr = 'TV..........'
    priv_key = keys.PrivateKey(bytes.fromhex("975a..."))
    
    abi = '''
    [{"constant":false,
      "inputs":[{"name":"min_tokens","type":"uint256"},{"name":"deadline","type":"uint256"}],
      "name":"trxToTokenSwapInput",
      "outputs":[{"name":"","type":"uint256"}],
      "payable":true,
      "stateMutability":"payable",
      "type":"Function"}]
    '''
    
    cntr = client.get_contract(dzi_trade)
    cntr.abi = json.loads(abi)
    
    txn = (
        cntr.functions.trxToTokenSwapInput.with_transfer(1_000_000_000)(1_000_000_000_000, 5)
        .with_owner(from_addr)
        .fee_limit(1_000_000_000)
        .build()
        .sign(priv_key)
    )
    print("txn =>", txn)
    print("broadcast and result =>", txn.broadcast().wait())
  8. Perform a Const call (view/pure functions)

    master

    To call a view or pure function (like balanceOf or symbol in TRC20), access the function via cntr.functions.<method_name>(). The result is returned immediately and parsed according to the ABI.

    Example for TRC20:

    from tronpy import Tron
    
    client = Tron(network='nile')
    cntr = client.get_contract("THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia")
    
    # Get contract symbol
    print('Symbol:', cntr.functions.symbol())
    
    # Get balance with decimal adjustment
    precision = cntr.functions.decimals()
    balance = cntr.functions.balanceOf('TJRabPrwbZy45sbavfcjinPJC18kjpRTv8') / 10 ** precision
    print('Balance:', balance)
    from tronpy import Tron
    
    client = Tron(network='nile')
    cntr = client.get_contract("THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia")
    
    print('Symbol:', cntr.functions.symbol())
    
    precision = cntr.functions.decimals()
    print('Balance:', cntr.functions.balanceOf('TJRabPrwbZy45sbavfcjinPJC18kjpRTv8') / 10 ** precision)
  9. Use the Async API with AsyncTron

    master

    TronPy provides an asynchronous client called AsyncTron built on top of httpx. The AsyncTron API is nearly identical to the synchronous Tron client, but requires using await for network-bound operations and managing the client lifecycle using an async context manager (async with).

    To perform a transaction asynchronously:

    1. Initialize AsyncTron with the desired network (e.g., 'nile').
    2. Build the transaction using the client.trx.transfer method and chain configuration methods like .memo() and .fee_limit().
    3. await the .build() method to construct the transaction.
    4. await the .sign(priv_key).broadcast() chain to sign and send the transaction.
    5. await the .wait() method on the result to poll for transaction confirmation and retrieve receipt details.
    import asyncio
    from tronpy import AsyncTron
    from tronpy.keys import PrivateKey
    
    # Initialize private key
    priv_key = PrivateKey(bytes.fromhex("8888888888888888888888888888888888888888888888888888888888888888"))
    
    async def transfer():
        # Use AsyncTron as an async context manager
        async with AsyncTron(network='nile') as client:
            # Build the transaction
            txb = (
                client.trx.transfer("TJzXt1sZautjqXnpjQT4xSCBHNSYgBkDr3", "TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA", 1_000)
                .memo("test memo")
                .fee_limit(100_000_000)
            )
            
            # Build, sign, and broadcast
            txn = await txb.build()
            txn_ret = await txn.sign(priv_key).broadcast()
            
            # Wait for confirmation and get receipt
            receipt = await txn_ret.wait()
            print(receipt)
    
    if __name__ == '__main__':
        asyncio.run(transfer())
  10. Use the synchronous Tron client

    master

    To interact with the TRON network synchronously, use the Tron class. You can specify the network (e.g., network='nile') during initialization. Transactions are constructed using a builder pattern, allowing you to chain methods like .memo() and .build(), then .sign(priv_key) and .broadcast() to execute them. Use .wait() on the broadcast result to poll for transaction confirmation and retrieve receipt details.

    from tronpy import Tron
    from tronpy.keys import PrivateKey
    
    client = Tron(network='nile')
    priv_key = PrivateKey(bytes.fromhex("8888888888888888888888888888888888888888888888888888888888888888"))
    
    txn = (
        client.trx.transfer("TJzXt1sZautjqXnpjQT4xSCBHNSYgBkDr3", "TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA", 1_000)
        .memo("test memo")
        .build()
        .inspect()
        .sign(priv_key)
        .broadcast()
    )
    
    print(txn)
    print(txn.wait())