A Python framework for Ethereum smart contract deployment, testing and interaction.
—
Smart contract deployment, method calls, event handling, and transaction management with comprehensive debugging and error reporting capabilities.
The core Contract class provides the interface for interacting with deployed smart contracts, handling method calls, transactions, and event parsing.
class Contract:
"""
Interface for interacting with deployed smart contracts.
Attributes:
address (str): Contract's deployed address
abi (list): Contract's ABI (Application Binary Interface)
bytecode (str): Contract's bytecode
tx (TransactionReceipt): Deployment transaction receipt
"""
def __init__(self, address: str, abi: list, owner: Account = None):
"""
Initialize contract interface.
Args:
address: Contract's deployed address
abi: Contract's ABI definition
owner: Account that deployed the contract
"""
def __getattr__(self, name: str):
"""
Access contract methods and attributes dynamically.
Args:
name: Method or attribute name
Returns:
ContractCall or ContractTx: Callable for contract interaction
"""
def balance(self) -> Wei:
"""Get contract's ether balance."""
def selectors(self) -> dict:
"""Get mapping of function names to their selectors."""
def topics(self) -> dict:
"""Get mapping of event names to their topic hashes."""
def decode_logs(self, logs: list) -> list:
"""
Decode raw event logs using contract ABI.
Args:
logs: Raw event logs from transaction receipt
Returns:
list: Decoded event data
"""
def get_method(self, calldata: str):
"""
Get method information from transaction calldata.
Args:
calldata: Transaction input data
Returns:
Method information and decoded parameters
"""Enhanced contract class with build information and deployment artifacts, providing additional metadata and debugging capabilities.
class ProjectContract(Contract):
"""
Contract with associated build information and deployment artifacts.
Additional attributes:
_name (str): Contract name from source
_sources (Sources): Associated source files
_build (dict): Build artifacts and metadata
"""
@classmethod
def deploy(cls, *args, **kwargs) -> 'ProjectContract':
"""
Deploy new instance of this contract.
Args:
*args: Constructor arguments
**kwargs: Transaction parameters (from, gas_limit, etc.)
Returns:
ProjectContract: Deployed contract instance
"""
@classmethod
def at(cls, address: str, owner: Account = None) -> 'ProjectContract':
"""
Connect to existing deployed contract.
Args:
address: Contract address
owner: Account that owns the contract
Returns:
ProjectContract: Contract instance at address
"""
def get_verification_info(self) -> dict:
"""Get contract verification information for block explorers."""
def publish_source(self, silent: bool = False) -> str:
"""
Publish contract source to block explorer.
Args:
silent: Suppress console output
Returns:
str: Verification URL or status
"""Container class that manages multiple contract instances and provides deployment and access methods for contract types.
class ContractContainer:
"""
Container for managing contracts of the same type.
Attributes:
_name (str): Contract name
abi (list): Contract ABI
bytecode (str): Contract bytecode
"""
def __init__(self, project, build: dict):
"""Initialize container with project and build data."""
def __call__(self, *args, **kwargs) -> ProjectContract:
"""Deploy new contract instance (alias for deploy)."""
def __iter__(self):
"""Iterate over deployed contract instances."""
def __getitem__(self, index: int) -> ProjectContract:
"""Get deployed contract by index."""
def __len__(self) -> int:
"""Get number of deployed contracts."""
def deploy(self, *args, **kwargs) -> ProjectContract:
"""
Deploy new contract instance.
Args:
*args: Constructor arguments
**kwargs: Transaction parameters
Returns:
ProjectContract: Newly deployed contract
"""
def at(self, address: str, owner: Account = None) -> ProjectContract:
"""
Connect to existing contract at address.
Args:
address: Contract address
owner: Contract owner account
Returns:
ProjectContract: Contract instance
"""
def remove(self, contract: ProjectContract) -> None:
"""Remove contract from container."""
def estimate_gas(self, *args) -> int:
"""
Estimate gas for contract deployment.
Args:
*args: Constructor arguments
Returns:
int: Estimated gas amount
"""Container for managing contract interfaces (ABI without bytecode) for interacting with contracts not deployed through the current project.
class InterfaceContainer:
"""
Container for contract interfaces (ABI-only contracts).
Used for interacting with contracts deployed elsewhere.
"""
def __init__(self, name: str, abi: list):
"""
Initialize interface container.
Args:
name: Interface name
abi: Contract ABI
"""
def __call__(self, address: str, owner: Account = None) -> Contract:
"""Create contract instance at address."""
def at(self, address: str, owner: Account = None) -> Contract:
"""
Connect to contract at address using this interface.
Args:
address: Contract address
owner: Account for transactions
Returns:
Contract: Contract instance with interface ABI
"""Contract methods are accessed dynamically and return callable objects that handle both read operations and transactions.
class ContractCall:
"""
Callable for contract view/pure methods (read-only operations).
These methods don't modify blockchain state and don't require gas.
"""
def __call__(self, *args, block_identifier: Union[int, str] = 'latest') -> Any:
"""
Call contract method.
Args:
*args: Method arguments
block_identifier: Block number or 'latest'/'pending'
Returns:
Any: Method return value
"""
def call(self, tx_params: dict = None, block_identifier: Union[int, str] = 'latest') -> Any:
"""Call method with custom transaction parameters."""
def estimate_gas(self, *args) -> int:
"""Estimate gas if method were called as transaction."""
class ContractTx:
"""
Callable for contract state-changing methods (transactions).
These methods modify blockchain state and require gas.
"""
def __call__(self, *args, **kwargs) -> TransactionReceipt:
"""
Execute contract method as transaction.
Args:
*args: Method arguments
**kwargs: Transaction parameters (from, gas_limit, etc.)
Returns:
TransactionReceipt: Transaction receipt
"""
def call(self, *args, tx_params: dict = None, block_identifier: Union[int, str] = 'latest') -> Any:
"""Call method without sending transaction (simulation)."""
def estimate_gas(self, *args, tx_params: dict = None) -> int:
"""
Estimate gas for method execution.
Args:
*args: Method arguments
tx_params: Transaction parameters
Returns:
int: Estimated gas amount
"""
def transact(self, *args, **kwargs) -> TransactionReceipt:
"""Execute method as transaction (alias for __call__)."""class EventDict(dict):
"""
Dictionary-like container for contract events with additional filtering methods.
"""
def get_sequence(self) -> list:
"""Get events in chronological order."""
def count(self) -> int:
"""Get total number of events."""
class Log:
"""
Individual event log with decoded data.
Attributes:
address (str): Contract address that emitted the event
topics (list): Event topics (including signature)
data (str): Raw event data
block_number (int): Block number
transaction_hash (str): Transaction hash
log_index (int): Log index within transaction
event (str): Event name
args (dict): Decoded event arguments
"""
def __init__(self, log_dict: dict, abi: dict):
"""Initialize log with raw data and ABI."""from brownie import accounts, project
# Load project and account
p = project.load()
account = accounts[0]
# Deploy contract with constructor arguments
contract = p.MyContract.deploy(
"constructor_string",
42,
["array", "values"],
{'from': account, 'gas_limit': 3000000}
)
print(f"Contract deployed at: {contract.address}")
print(f"Transaction hash: {contract.tx.txid}")# Call view/pure methods (no gas required)
result = contract.myViewMethod(arg1, arg2)
print(f"Method returned: {result}")
# Call at specific block
historical_result = contract.myViewMethod(arg1, block_identifier=1000000)
# Execute state-changing methods (requires gas)
tx = contract.myStateMethod(
arg1,
arg2,
{'from': account, 'gas_limit': 200000}
)
print(f"Transaction hash: {tx.txid}")
print(f"Gas used: {tx.gas_used}")
print(f"Events emitted: {tx.events}")# Connect to deployed contract by address
existing_contract = p.MyContract.at("0x742d35Cc6634C0532925a3b8D8D944d0Cdbc-1234")
# Use interface for contracts deployed elsewhere
interface_contract = p.interface.IERC20.at("0xA0b86a33E6442496c5D58f95EF3cE-5678")
# Call methods on existing contract
balance = interface_contract.balanceOf(account.address)# Get all events from transaction
tx = contract.myMethod({'from': account})
all_events = tx.events
# Access specific event type
my_events = tx.events['MyEvent']
print(f"Number of MyEvent events: {len(my_events)}")
# Iterate through events
for event in my_events:
print(f"Event args: {event.args}")
print(f"Block number: {event.block_number}")
# Filter events from multiple transactions
transfer_filter = contract.events.Transfer.createFilter(
fromBlock=1000000,
argument_filters={'from': account.address}
)
transfers = transfer_filter.get_all_entries()from brownie import reverts
# Handle expected reverts
with reverts("Insufficient balance"):
contract.withdraw(1000000, {'from': account})
# Allow reverted transactions for analysis
tx = contract.riskyMethod({'from': account, 'allow_revert': True})
if tx.status == 0:
print(f"Transaction reverted: {tx.revert_msg}")
tx.traceback() # Detailed error trace
tx.call_trace() # Step-by-step execution trace
# Simulate transaction before sending
try:
result = contract.myMethod.call(arg1, {'from': account})
print(f"Method would return: {result}")
except ValueError as e:
print(f"Method would revert: {e}")# Estimate gas for deployment
estimated_gas = p.MyContract.estimate_gas("constructor_arg", 42)
print(f"Deployment will use ~{estimated_gas} gas")
# Estimate gas for method call
method_gas = contract.myMethod.estimate_gas(arg1, {'from': account})
print(f"Method call will use ~{method_gas} gas")
# Use gas strategies for automatic optimization
from brownie.network.gas import LinearScalingStrategy
gas_strategy = LinearScalingStrategy("20 gwei", "50 gwei", 1.1)
tx = contract.myMethod(arg1, {'from': account, 'gas_strategy': gas_strategy})from brownie import multicall
# Batch multiple contract calls
with multicall:
balance1 = token.balanceOf(account1)
balance2 = token.balanceOf(account2)
total_supply = token.totalSupply()
# All calls executed in single transaction
print(f"Balance 1: {balance1}")
print(f"Balance 2: {balance2}")
print(f"Total supply: {total_supply}")# Publish contract source to block explorer
verification_url = contract.publish_source()
print(f"Contract verified at: {verification_url}")
# Get verification info
verification_info = contract.get_verification_info()
print(f"Compiler version: {verification_info['compiler_version']}")# Type aliases for contract operations
ContractType = Union[Contract, ProjectContract]
AbiType = List[Dict[str, Any]]
EventFilter = Dict[str, Any]
CallResult = Any
TxReceipt = TransactionReceiptInstall with Tessl CLI
npx tessl i tessl/pypi-eth-brownie@1.21.1