Skip to content

Smart Contract

Smart contracts are the key feature of TRON network. Creating and interacting with smart contracts are made easy by TronPy.

Calling smart contract

There are two types of smart contract calls, const call and trigger call. For a const call, the contract method must be marked pure or view. And the calling result will be returned immediately.

While the trigger call is just a type of system contract call, you sign the calling transaction, and broadcast it. The calling result can be fetched and parsed through the API.

Const call

Const call is wrapped as a function object. The result is parsed according to ABI.

Take TRC20 as an example, the balance query is a very common task:

from tronpy import Tron

client = Tron(network="nile")

cntr = client.get_contract("THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia")

print(dir(cntr.functions))  # prints list of contract functions

for f in cntr.functions:
    print(f)  # prints function signature(i.e. type info)

# function allowance(address _owner, address _spender) view returns (uint256 remaining)
# function approve(address _spender, uint256 _amount) returns (bool success)
# function balanceOf(address _owner) view returns (uint256 balance)
# function decimals() view returns (uint8 )
# function name() view returns (string )
# function owner() view returns (address )
# function symbol() view returns (string )
# function totalSupply() view returns (uint256 theTotalSupply)
# function transfer(address _to, uint256 _amount) returns (bool success)
# function transferFrom(address _from, address _to, uint256 _amount) returns (bool success)

print("Symbol:", cntr.functions.symbol())  # The symbol string of the contract
# Symbol: RMB

precision = cntr.functions.decimals()
print("Balance:", cntr.functions.balanceOf("TJRabPrwbZy45sbavfcjinPJC18kjpRTv8") / 10**precision)
# Balance: 100000.0

Trigger call

Trigger call requires sign and broadcast.

>>> from tronpy import Tron
>>> from tronpy.keys import PrivateKey

>>> client = Tron(network='nile')
>>> contract = client.get_contract('THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia')

>>> print(contract.functions.transfer)
function transfer(address _to, uint256 _amount) returns (bool success)

>>> txn = (
...         contract.functions.transfer('TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA', 1_000)
...         .with_owner('TGQgfK497YXmjdgvun9Bg5Zu3xE15v17cu')  # address of the private key
...         .fee_limit(5_000_000)
...         .build()
...         .sign(priv_key)
... )
>>> txn.broadcast()  # or txn.broadcast()
{'result': True, 'txid': '63609d84524b754a97c111eec152700f273979bb00dad993d8dcce5848b4dd9a'}
>>> _.wait()
{'id': '63609d84524b754a97c111eec152700f273979bb00dad993d8dcce5848b4dd9a',
 'blockNumber': 6609475, 'blockTimeStamp': 1592539509000,
 'contractResult': ['0000000000000000000000000000000000000000000000000000000000000001'],
 'contract_address': 'THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia',
 'receipt': {'energy_usage': 13062, 'energy_usage_total': 13062, 'net_usage': 344, 'result': 'SUCCESS'},
 'log': [{'address': 'THi2qJf6XmvTJSpZHc17HgQsmJop6kb3ia',
          'topics': ['ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
                     '00000000000000000000000046a23e25df9a0f6c18729dda9ad1af3b6a131160',
                     '000000000000000000000000d8dd39e2dea27a40001884901735e3940829bb44'],
          'data': '00000000000000000000000000000000000000000000000000000000000003e8'}]}

# trigger output can be parsed manually
>>> contract.functions.transfer.parse_output(_['contractResult'][0])
True
# or use `.result()` to parse it automatically
>>> txn.broadcast().result()
True

Trigger call with transfer

Use with_transfer or with_asset_transfer.

>>> txn = (
...         contract.functions.transfer.with_transfer(100_000_000)
...         .call('TVjsyZ7fYF3qLF6BQgPmTEZy1xrNNyVAAA', 1_000)
...         .with_owner('TGQgfK497YXmjdgvun9Bg5Zu3xE15v17cu')  # address of the private key
...         .fee_limit(5_000_000)
...         .build()
...         .sign(priv_key)
... )

Creating smart contract

When you've compiled your contract code, you can deploy it on chain.

from tronpy import Tron, Contract
from tronpy.keys import PrivateKey

client = Tron(network="nile")
priv_key = PrivateKey(bytes.fromhex("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"))

bytecode = "608060405234801561001057600080fd5b5060c78061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806360fe47b11460375780636d4ce63c146062575b600080fd5b606060048036036020811015604b57600080fd5b8101908080359060200190929190505050607e565b005b60686088565b6040518082815260200191505060405180910390f35b8060008190555050565b6000805490509056fea2646970667358221220c8daade51f673e96205b4a991ab6b94af82edea0f4b57be087ab123f03fc40f264736f6c63430006000033"
abi = [
    {
        "inputs": [],
        "name": "get",
        "outputs": [{"internalType": "uint256", "name": "retVal", "type": "uint256"}],
        "stateMutability": "view",
        "type": "function",
    }
]

cntr = Contract(name="SimpleStore", bytecode=bytecode, abi=abi)

txn = client.trx.deploy_contract("TGQgfK497YXmjdgvun9Bg5Zu3xE15v17cu", cntr).fee_limit(5_000_000).build().sign(priv_key)
print(txn)
result = txn.broadcast().wait()
print(result)
print("Created:", result["contract_address"])

created_cntr = client.get_contract(result["contract_address"])

Note

The constructor's parameters are provided via bytecode.

cntr = Contract(name="SimpleStore", bytecode=bytecode, abi=abi)
parameter = cntr.constructor.encode_parameter("TRh5N2iAmjyeJbbCXsDuo7PNZvyjVWtL2e", 18)
cntr.bytecode = bytecode + parameter

Default Fee Limit

The default fee limit is 5_000_000 for contract deploy or trigger, 0 otherwise.

Fee limit is set by .fee_limit() method of transaction.

It also can be set via conf object:

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

Contract with Un-published ABI

You can set JSON ABI via cntr.abi = [...].

import json

from tronpy import Tron
from tronpy import keys

dzi_trade = "TS..........(omitted).............j"
from_addr = "TV..........(omitted).............a"
priv_key = keys.PrivateKey(bytes.fromhex("975a...............(omitted)..............8d97b"))
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"}]
"""

client = Tron(network="nile")

cntr = client.get_contract(dzi_trade)
cntr.abi = json.loads(abi)  # load ABI, while contract on chain has no ABI set.

# call contract functions with TRX transfer
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())

API reference

Contract

Contract(
    addr=None,
    *,
    bytecode: str | bytes = "",
    name: str = None,
    abi: dict | list[dict] | None = None,
    user_resource_percent: int = 100,
    origin_energy_limit: int = 1,
    origin_address: str = None,
    owner_address: str = "410000000000000000000000000000000000000000",
    code_hash: str = None,
    client=None,
)

A smart contract object.

Methods:

Attributes:

Source code in tronpy/contract.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
def __init__(
    self,
    addr=None,
    *,
    bytecode: str | bytes = "",
    name: str = None,
    abi: dict | list[dict] | None = None,
    user_resource_percent: int = 100,
    origin_energy_limit: int = 1,
    origin_address: str = None,
    owner_address: str = "410000000000000000000000000000000000000000",
    code_hash: str = None,
    client=None,
):
    self.contract_address = addr
    self.address = addr
    """Address of the contract"""

    self.code_hash = code_hash
    """Hash of smart contract bytecode."""

    self._bytecode = assure_bytes(bytecode)

    self.name = name
    """Name of the contract"""

    self.abi = abi or []
    """ABI list of the contract"""

    self.user_resource_percent = user_resource_percent
    """User resource percent, default 100"""

    self.origin_energy_limit = origin_energy_limit
    """Origin energy limit, default 1"""

    self.origin_address = origin_address
    """Origin address of the contract, i.e. contract creator"""

    self.owner_address = owner_address
    """Current transaction owner's address, to call or trigger contract"""

    self._functions = None
    self._events = None
    self._client = client

abi instance-attribute

abi = abi or []

ABI list of the contract

address instance-attribute

address = addr

Address of the contract

bytecode property writable

bytecode

Bytecode of the contract, in hex format

code_hash instance-attribute

code_hash = code_hash

Hash of smart contract bytecode.

constructor property

constructor: ContractConstructor

The constructor of the contract.

events property

The ContractEvents object, wraps all contract events.

functions property

functions: ContractFunctions

The ContractFunctions object, wraps all contract methods.

name instance-attribute

name = name

Name of the contract

origin_address instance-attribute

origin_address = origin_address

Origin address of the contract, i.e. contract creator

origin_energy_limit instance-attribute

origin_energy_limit = origin_energy_limit

Origin energy limit, default 1

owner_address instance-attribute

owner_address = owner_address

Current transaction owner's address, to call or trigger contract

user_resource_percent instance-attribute

user_resource_percent = user_resource_percent

User resource percent, default 100

clear_abi

clear_abi() -> TransactionBuilder

Clear contract ABI.

Source code in tronpy/contract.py
139
140
141
142
143
144
145
146
147
148
149
150
def clear_abi(self) -> "tronpy.tron.TransactionBuilder":
    """Clear contract ABI."""
    if self.origin_address != self.owner_address:
        raise RuntimeError("origin address and owner address mismatch")

    return self._client.trx._build_transaction(
        "ClearAbiContract",
        {
            "owner_address": to_hex_address(self.owner_address),
            "contract_address": to_hex_address(self.contract_address),
        },
    )

update_origin_energy_limit

update_origin_energy_limit(limit: int) -> TransactionBuilder

Create a Transaction to update origin energy limit.

Source code in tronpy/contract.py
125
126
127
128
129
130
131
132
133
134
135
136
137
def update_origin_energy_limit(self, limit: int) -> "tronpy.tron.TransactionBuilder":
    """Create a Transaction to update origin energy limit."""
    if self.origin_address != self.owner_address:
        raise RuntimeError("origin address and owner address mismatch")

    return self._client.trx._build_transaction(
        "UpdateEnergyLimitContract",
        {
            "owner_address": to_hex_address(self.owner_address),
            "contract_address": to_hex_address(self.contract_address),
            "origin_energy_limit": limit,
        },
    )

update_user_resource_percent

update_user_resource_percent(percent: int) -> TransactionBuilder

Create a Transaction to update user resource percent.

Source code in tronpy/contract.py
111
112
113
114
115
116
117
118
119
120
121
122
123
def update_user_resource_percent(self, percent: int) -> "tronpy.tron.TransactionBuilder":
    """Create a Transaction to update user resource percent."""
    if self.origin_address != self.owner_address:
        raise RuntimeError("origin address and owner address mismatch")

    return self._client.trx._build_transaction(
        "UpdateSettingContract",
        {
            "owner_address": to_hex_address(self.owner_address),
            "contract_address": to_hex_address(self.contract_address),
            "consume_user_resource_percent": percent,
        },
    )

ContractFunctions

ContractFunctions(contract)

Methods:

  • __getattr__ –

    Get the actual contract method object.

Source code in tronpy/contract.py
264
265
def __init__(self, contract):
    self._contract = contract

__getattr__

__getattr__(method: str)

Get the actual contract method object.

Source code in tronpy/contract.py
274
275
276
277
278
279
def __getattr__(self, method: str):
    """Get the actual contract method object."""
    try:
        return self[method]
    except KeyError:
        raise AttributeError(f"contract has no method named '{method}'") from None

ContractMethod

ContractMethod(abi: dict, contract: Contract)

Methods:

Source code in tronpy/contract.py
335
336
337
338
339
340
341
342
343
344
345
346
def __init__(self, abi: dict, contract: Contract):
    self._abi = abi
    self._contract = contract
    self._owner_address = contract.owner_address
    self._client = contract._client

    self.inputs = abi.get("inputs", [])
    self.outputs = abi.get("outputs", [])

    self.call_value = 0
    self.call_token_value = 0
    self.call_token_id = 0

__call__

__call__(*args, **kwargs) -> TransactionBuilder

Call the contract method.

Source code in tronpy/contract.py
383
384
385
386
def __call__(self, *args, **kwargs) -> "tronpy.tron.TransactionBuilder":
    """Call the contract method."""
    parameter = self._prepare_parameter(*args, **kwargs)
    return self._trigger_contract(parameter)

call

call(*args, **kwargs) -> TransactionBuilder

Call the contract method.

Source code in tronpy/contract.py
370
371
372
def call(self, *args, **kwargs) -> "tronpy.tron.TransactionBuilder":
    """Call the contract method."""
    return self.__call__(*args, **kwargs)

parse_output

parse_output(raw: str) -> Any

Parse contract result as result.

Source code in tronpy/contract.py
374
375
376
377
378
379
380
381
def parse_output(self, raw: str) -> Any:
    """Parse contract result as result."""
    parsed_result = trx_abi.decode_single(self.output_type, bytes.fromhex(raw))
    if len(self.outputs) == 1:
        return parsed_result[0]
    if len(self.outputs) == 0:
        return None
    return parsed_result

with_asset_transfer

with_asset_transfer(amount: int, token_id: int) -> ContractMethod

Call a contract function with TRC10 token transfer.

Source code in tronpy/contract.py
364
365
366
367
368
def with_asset_transfer(self, amount: int, token_id: int) -> "ContractMethod":
    """Call a contract function with TRC10 token transfer."""
    self.call_token_value = amount
    self.call_token_id = token_id
    return self

with_owner

with_owner(addr: str) -> ContractMethod

Set the calling owner address.

Can also be changed through TransactionBuilder.with_owner().

Source code in tronpy/contract.py
351
352
353
354
355
356
357
def with_owner(self, addr: str) -> "ContractMethod":
    """Set the calling owner address.

    Can also be changed through [TransactionBuilder.with_owner()][tronpy.tron.TransactionBuilder.with_owner].
    """
    self._owner_address = addr
    return self

with_transfer

with_transfer(amount: int) -> ContractMethod

Call a contract function with TRX transfer. amount in SUN.

Source code in tronpy/contract.py
359
360
361
362
def with_transfer(self, amount: int) -> "ContractMethod":
    """Call a contract function with TRX transfer. ``amount`` in `SUN`."""
    self.call_value = amount
    return self

ContractEvents

ContractEvents(contract)

Methods:

  • __getattr__ –

    Get the actual contract event object.

Source code in tronpy/contract.py
192
193
def __init__(self, contract):
    self._contract = contract

__getattr__

__getattr__(event: str)

Get the actual contract event object.

Source code in tronpy/contract.py
202
203
204
205
206
207
def __getattr__(self, event: str):
    """Get the actual contract event object."""
    try:
        return self[event]
    except KeyError:
        raise AttributeError(f"contract has no method named '{event}'") from None