0
# Contract Interaction
1
2
Smart contract deployment, method calls, event handling, and transaction management with comprehensive debugging and error reporting capabilities.
3
4
## Capabilities
5
6
### Contract Base Class
7
8
The core Contract class provides the interface for interacting with deployed smart contracts, handling method calls, transactions, and event parsing.
9
10
```python { .api }
11
class Contract:
12
"""
13
Interface for interacting with deployed smart contracts.
14
15
Attributes:
16
address (str): Contract's deployed address
17
abi (list): Contract's ABI (Application Binary Interface)
18
bytecode (str): Contract's bytecode
19
tx (TransactionReceipt): Deployment transaction receipt
20
"""
21
22
def __init__(self, address: str, abi: list, owner: Account = None):
23
"""
24
Initialize contract interface.
25
26
Args:
27
address: Contract's deployed address
28
abi: Contract's ABI definition
29
owner: Account that deployed the contract
30
"""
31
32
def __getattr__(self, name: str):
33
"""
34
Access contract methods and attributes dynamically.
35
36
Args:
37
name: Method or attribute name
38
39
Returns:
40
ContractCall or ContractTx: Callable for contract interaction
41
"""
42
43
def balance(self) -> Wei:
44
"""Get contract's ether balance."""
45
46
def selectors(self) -> dict:
47
"""Get mapping of function names to their selectors."""
48
49
def topics(self) -> dict:
50
"""Get mapping of event names to their topic hashes."""
51
52
def decode_logs(self, logs: list) -> list:
53
"""
54
Decode raw event logs using contract ABI.
55
56
Args:
57
logs: Raw event logs from transaction receipt
58
59
Returns:
60
list: Decoded event data
61
"""
62
63
def get_method(self, calldata: str):
64
"""
65
Get method information from transaction calldata.
66
67
Args:
68
calldata: Transaction input data
69
70
Returns:
71
Method information and decoded parameters
72
"""
73
```
74
75
### Project Contract
76
77
Enhanced contract class with build information and deployment artifacts, providing additional metadata and debugging capabilities.
78
79
```python { .api }
80
class ProjectContract(Contract):
81
"""
82
Contract with associated build information and deployment artifacts.
83
84
Additional attributes:
85
_name (str): Contract name from source
86
_sources (Sources): Associated source files
87
_build (dict): Build artifacts and metadata
88
"""
89
90
@classmethod
91
def deploy(cls, *args, **kwargs) -> 'ProjectContract':
92
"""
93
Deploy new instance of this contract.
94
95
Args:
96
*args: Constructor arguments
97
**kwargs: Transaction parameters (from, gas_limit, etc.)
98
99
Returns:
100
ProjectContract: Deployed contract instance
101
"""
102
103
@classmethod
104
def at(cls, address: str, owner: Account = None) -> 'ProjectContract':
105
"""
106
Connect to existing deployed contract.
107
108
Args:
109
address: Contract address
110
owner: Account that owns the contract
111
112
Returns:
113
ProjectContract: Contract instance at address
114
"""
115
116
def get_verification_info(self) -> dict:
117
"""Get contract verification information for block explorers."""
118
119
def publish_source(self, silent: bool = False) -> str:
120
"""
121
Publish contract source to block explorer.
122
123
Args:
124
silent: Suppress console output
125
126
Returns:
127
str: Verification URL or status
128
"""
129
```
130
131
### Contract Container
132
133
Container class that manages multiple contract instances and provides deployment and access methods for contract types.
134
135
```python { .api }
136
class ContractContainer:
137
"""
138
Container for managing contracts of the same type.
139
140
Attributes:
141
_name (str): Contract name
142
abi (list): Contract ABI
143
bytecode (str): Contract bytecode
144
"""
145
146
def __init__(self, project, build: dict):
147
"""Initialize container with project and build data."""
148
149
def __call__(self, *args, **kwargs) -> ProjectContract:
150
"""Deploy new contract instance (alias for deploy)."""
151
152
def __iter__(self):
153
"""Iterate over deployed contract instances."""
154
155
def __getitem__(self, index: int) -> ProjectContract:
156
"""Get deployed contract by index."""
157
158
def __len__(self) -> int:
159
"""Get number of deployed contracts."""
160
161
def deploy(self, *args, **kwargs) -> ProjectContract:
162
"""
163
Deploy new contract instance.
164
165
Args:
166
*args: Constructor arguments
167
**kwargs: Transaction parameters
168
169
Returns:
170
ProjectContract: Newly deployed contract
171
"""
172
173
def at(self, address: str, owner: Account = None) -> ProjectContract:
174
"""
175
Connect to existing contract at address.
176
177
Args:
178
address: Contract address
179
owner: Contract owner account
180
181
Returns:
182
ProjectContract: Contract instance
183
"""
184
185
def remove(self, contract: ProjectContract) -> None:
186
"""Remove contract from container."""
187
188
def estimate_gas(self, *args) -> int:
189
"""
190
Estimate gas for contract deployment.
191
192
Args:
193
*args: Constructor arguments
194
195
Returns:
196
int: Estimated gas amount
197
"""
198
```
199
200
### Interface Container
201
202
Container for managing contract interfaces (ABI without bytecode) for interacting with contracts not deployed through the current project.
203
204
```python { .api }
205
class InterfaceContainer:
206
"""
207
Container for contract interfaces (ABI-only contracts).
208
209
Used for interacting with contracts deployed elsewhere.
210
"""
211
212
def __init__(self, name: str, abi: list):
213
"""
214
Initialize interface container.
215
216
Args:
217
name: Interface name
218
abi: Contract ABI
219
"""
220
221
def __call__(self, address: str, owner: Account = None) -> Contract:
222
"""Create contract instance at address."""
223
224
def at(self, address: str, owner: Account = None) -> Contract:
225
"""
226
Connect to contract at address using this interface.
227
228
Args:
229
address: Contract address
230
owner: Account for transactions
231
232
Returns:
233
Contract: Contract instance with interface ABI
234
"""
235
```
236
237
### Contract Methods
238
239
Contract methods are accessed dynamically and return callable objects that handle both read operations and transactions.
240
241
```python { .api }
242
class ContractCall:
243
"""
244
Callable for contract view/pure methods (read-only operations).
245
246
These methods don't modify blockchain state and don't require gas.
247
"""
248
249
def __call__(self, *args, block_identifier: Union[int, str] = 'latest') -> Any:
250
"""
251
Call contract method.
252
253
Args:
254
*args: Method arguments
255
block_identifier: Block number or 'latest'/'pending'
256
257
Returns:
258
Any: Method return value
259
"""
260
261
def call(self, tx_params: dict = None, block_identifier: Union[int, str] = 'latest') -> Any:
262
"""Call method with custom transaction parameters."""
263
264
def estimate_gas(self, *args) -> int:
265
"""Estimate gas if method were called as transaction."""
266
267
class ContractTx:
268
"""
269
Callable for contract state-changing methods (transactions).
270
271
These methods modify blockchain state and require gas.
272
"""
273
274
def __call__(self, *args, **kwargs) -> TransactionReceipt:
275
"""
276
Execute contract method as transaction.
277
278
Args:
279
*args: Method arguments
280
**kwargs: Transaction parameters (from, gas_limit, etc.)
281
282
Returns:
283
TransactionReceipt: Transaction receipt
284
"""
285
286
def call(self, *args, tx_params: dict = None, block_identifier: Union[int, str] = 'latest') -> Any:
287
"""Call method without sending transaction (simulation)."""
288
289
def estimate_gas(self, *args, tx_params: dict = None) -> int:
290
"""
291
Estimate gas for method execution.
292
293
Args:
294
*args: Method arguments
295
tx_params: Transaction parameters
296
297
Returns:
298
int: Estimated gas amount
299
"""
300
301
def transact(self, *args, **kwargs) -> TransactionReceipt:
302
"""Execute method as transaction (alias for __call__)."""
303
```
304
305
### Event Handling
306
307
```python { .api }
308
class EventDict(dict):
309
"""
310
Dictionary-like container for contract events with additional filtering methods.
311
"""
312
313
def get_sequence(self) -> list:
314
"""Get events in chronological order."""
315
316
def count(self) -> int:
317
"""Get total number of events."""
318
319
class Log:
320
"""
321
Individual event log with decoded data.
322
323
Attributes:
324
address (str): Contract address that emitted the event
325
topics (list): Event topics (including signature)
326
data (str): Raw event data
327
block_number (int): Block number
328
transaction_hash (str): Transaction hash
329
log_index (int): Log index within transaction
330
event (str): Event name
331
args (dict): Decoded event arguments
332
"""
333
334
def __init__(self, log_dict: dict, abi: dict):
335
"""Initialize log with raw data and ABI."""
336
```
337
338
## Usage Examples
339
340
### Contract Deployment
341
342
```python
343
from brownie import accounts, project
344
345
# Load project and account
346
p = project.load()
347
account = accounts[0]
348
349
# Deploy contract with constructor arguments
350
contract = p.MyContract.deploy(
351
"constructor_string",
352
42,
353
["array", "values"],
354
{'from': account, 'gas_limit': 3000000}
355
)
356
357
print(f"Contract deployed at: {contract.address}")
358
print(f"Transaction hash: {contract.tx.txid}")
359
```
360
361
### Contract Method Calls
362
363
```python
364
# Call view/pure methods (no gas required)
365
result = contract.myViewMethod(arg1, arg2)
366
print(f"Method returned: {result}")
367
368
# Call at specific block
369
historical_result = contract.myViewMethod(arg1, block_identifier=1000000)
370
371
# Execute state-changing methods (requires gas)
372
tx = contract.myStateMethod(
373
arg1,
374
arg2,
375
{'from': account, 'gas_limit': 200000}
376
)
377
378
print(f"Transaction hash: {tx.txid}")
379
print(f"Gas used: {tx.gas_used}")
380
print(f"Events emitted: {tx.events}")
381
```
382
383
### Interacting with Existing Contracts
384
385
```python
386
# Connect to deployed contract by address
387
existing_contract = p.MyContract.at("0x742d35Cc6634C0532925a3b8D8D944d0Cdbc-1234")
388
389
# Use interface for contracts deployed elsewhere
390
interface_contract = p.interface.IERC20.at("0xA0b86a33E6442496c5D58f95EF3cE-5678")
391
392
# Call methods on existing contract
393
balance = interface_contract.balanceOf(account.address)
394
```
395
396
### Event Filtering and Analysis
397
398
```python
399
# Get all events from transaction
400
tx = contract.myMethod({'from': account})
401
all_events = tx.events
402
403
# Access specific event type
404
my_events = tx.events['MyEvent']
405
print(f"Number of MyEvent events: {len(my_events)}")
406
407
# Iterate through events
408
for event in my_events:
409
print(f"Event args: {event.args}")
410
print(f"Block number: {event.block_number}")
411
412
# Filter events from multiple transactions
413
transfer_filter = contract.events.Transfer.createFilter(
414
fromBlock=1000000,
415
argument_filters={'from': account.address}
416
)
417
transfers = transfer_filter.get_all_entries()
418
```
419
420
### Error Handling and Debugging
421
422
```python
423
from brownie import reverts
424
425
# Handle expected reverts
426
with reverts("Insufficient balance"):
427
contract.withdraw(1000000, {'from': account})
428
429
# Allow reverted transactions for analysis
430
tx = contract.riskyMethod({'from': account, 'allow_revert': True})
431
432
if tx.status == 0:
433
print(f"Transaction reverted: {tx.revert_msg}")
434
tx.traceback() # Detailed error trace
435
tx.call_trace() # Step-by-step execution trace
436
437
# Simulate transaction before sending
438
try:
439
result = contract.myMethod.call(arg1, {'from': account})
440
print(f"Method would return: {result}")
441
except ValueError as e:
442
print(f"Method would revert: {e}")
443
```
444
445
### Gas Estimation and Optimization
446
447
```python
448
# Estimate gas for deployment
449
estimated_gas = p.MyContract.estimate_gas("constructor_arg", 42)
450
print(f"Deployment will use ~{estimated_gas} gas")
451
452
# Estimate gas for method call
453
method_gas = contract.myMethod.estimate_gas(arg1, {'from': account})
454
print(f"Method call will use ~{method_gas} gas")
455
456
# Use gas strategies for automatic optimization
457
from brownie.network.gas import LinearScalingStrategy
458
459
gas_strategy = LinearScalingStrategy("20 gwei", "50 gwei", 1.1)
460
tx = contract.myMethod(arg1, {'from': account, 'gas_strategy': gas_strategy})
461
```
462
463
## Advanced Features
464
465
### Multicall Operations
466
467
```python
468
from brownie import multicall
469
470
# Batch multiple contract calls
471
with multicall:
472
balance1 = token.balanceOf(account1)
473
balance2 = token.balanceOf(account2)
474
total_supply = token.totalSupply()
475
476
# All calls executed in single transaction
477
print(f"Balance 1: {balance1}")
478
print(f"Balance 2: {balance2}")
479
print(f"Total supply: {total_supply}")
480
```
481
482
### Contract Verification
483
484
```python
485
# Publish contract source to block explorer
486
verification_url = contract.publish_source()
487
print(f"Contract verified at: {verification_url}")
488
489
# Get verification info
490
verification_info = contract.get_verification_info()
491
print(f"Compiler version: {verification_info['compiler_version']}")
492
```
493
494
## Type Definitions
495
496
```python { .api }
497
# Type aliases for contract operations
498
ContractType = Union[Contract, ProjectContract]
499
AbiType = List[Dict[str, Any]]
500
EventFilter = Dict[str, Any]
501
CallResult = Any
502
TxReceipt = TransactionReceipt
503
```