Working with Contracts
Deploying Contracts
Each time Brownie is loaded it will automatically compile your project and create ContractContainer
objects for each deployable contract. This object is a container used to access individual deployments. It is also used to deploy new contracts.
>>> Token
[]
>>> type(Token)
<class 'brownie.network.contract.ContractContainer'>
>>> Token.deploy
<ContractConstructor object 'Token.constructor(string _symbol, string _name, uint256 _decimals, uint256 _totalSupply)'>
ContractContainer.deploy
is used to deploy a new contract.
>>> Token.deploy
<ContractConstructor object 'Token.constructor(string _symbol, string _name, uint256 _decimals, uint256 _totalSupply)'>
It must be called with the contract constructor arguments, and a dictionary of transaction parameters containing a from
field that specifies which Account
to deploy the contract from.
>>> Token.deploy("Test Token", "TST", 18, 1e23, {'from': accounts[1]})
Transaction sent: 0x2e3cab83342edda14141714ced002e1326ecd8cded4cd0cf14b2f037b690b976
Transaction confirmed - block: 1 gas spent: 594186
Contract deployed at: 0x5419710735c2D6c3e4db8F30EF2d361F70a4b380
<Token Contract object '0x5419710735c2D6c3e4db8F30EF2d361F70a4b380'>
Calling ContractContainer.deploy
returns a ProjectContract
object. The returned object is also appended to the ContractContainer
.
>>> t = Token.deploy("Test Token", "TST", 18, 1e23, {'from': accounts[1]})
Transaction sent: 0x2e3cab83342edda14141714ced002e1326ecd8cded4cd0cf14b2f037b690b976
Transaction confirmed - block: 1 gas spent: 594186
Contract deployed at: 0x5419710735c2D6c3e4db8F30EF2d361F70a4b380
<Token Contract object '0x5419710735c2D6c3e4db8F30EF2d361F70a4b380'>
>>> t
<Token Contract object '0x5419710735c2D6c3e4db8F30EF2d361F70a4b380'>
>>> Token
[<Token Contract object '0x5419710735c2D6c3e4db8F30EF2d361F70a4b380'>]
Unlinked Libraries
If a contract requires a library, Brownie will automatically link to the most recently deployed one. If the required library has not been deployed yet an UndeployedLibrary
exception is raised.
>>> MetaCoin.deploy({'from': accounts[0]})
File "brownie/network/contract.py", line 167, in __call__
f"Contract requires '{library}' library but it has not been deployed yet"
UndeployedLibrary: Contract requires 'ConvertLib' library but it has not been deployed yet
>>> Convert.deploy({'from': accounts[0]})
Transaction sent: 0xff3f5cff35c68a73658ad367850b6fa34783b4d59026520bd61b72b6613d871c
ConvertLib.constructor confirmed - block: 1 gas used: 95101 (48.74%)
ConvertLib deployed at: 0x08c4C7F19200d5636A1665f6048105b0686DFf01
<ConvertLib Contract object '0x08c4C7F19200d5636A1665f6048105b0686DFf01'>
>>> MetaCoin.deploy({'from': accounts[0]})
Transaction sent: 0xd0969b36819337fc3bac27194c1ff0294dd65da8f57c729b5efd7d256b9ecfb3
MetaCoin.constructor confirmed - block: 2 gas used: 231857 (69.87%)
MetaCoin deployed at: 0x8954d0c17F3056A6C98c7A6056C63aBFD3e8FA6f
<MetaCoin Contract object '0x8954d0c17F3056A6C98c7A6056C63aBFD3e8FA6f'>
Interacting with your Contracts
Once a contract has been deployed, you can interact with it via via calls and transactions.
Transactions are broadcast to the network and recorded on the blockchain. They cost ether to run, and are able to alter the state to the blockchain.
Calls are used to execute code on the network without broadcasting a transaction. They are free to run, and cannot alter the state of the blockchain in any way. Calls are typically used to retrieve a storage value from a contract using a getter method.
You may call or send a transaction to any public function within a contract. However, depending on the code, there is always a preferred method:
All public contract methods are available from the ProjectContract
object via class methods of the same name.
>>> Token[0].transfer
<ContractTx object 'transfer(address _to, uint256 _value)'>
>>> Token[0].balanceOf
<ContractCall object 'balanceOf(address _owner)'>
When a contract source includes NatSpec documentation, you can view it via the ContractCall.info
method:
>>> Token[0].transfer.info()
transfer(address _to, uint256 _value)
@dev transfer token for a specified address
@param _to The address to transfer to.
@param _value The amount to be transferred.
Transactions
State-changing contract methods are called via a ContractTx
object. This object performs a transaction and returns a TransactionReceipt
.
You may optionally include a dictionary of transaction parameters as the final argument. If you do not do this, or do not specify a from
value within the parameters, the transaction is sent from the same address that deployed the contract.
>>> Token[0].transfer(accounts[1], 1e18, {'from': accounts[0]})
Transaction sent: 0x6e557594e657faf1270235bf4b3f27be7f5a3cb8a9c981cfffb12133cbaa165e
Token.transfer confirmed - block: 4 gas used: 51019 (33.78%)
<Transaction object '0x6e557594e657faf1270235bf4b3f27be7f5a3cb8a9c981cfffb12133cbaa165e'>
If you wish to call the contract method without a transaction, use the ContractTx.call
method.
>>> Token[0].transfer.call(accounts[1], 1e18, {'from': accounts[0]})
True
Transaction Parameters
When executing a transaction to a contract, you can optionally include a dict
of transaction parameters as the final input. It may contain the following values:
from
: theAccount
that the transaction it sent from. If not given, the transaction is sent from the account that deployed the contract.
gas_limit
: The amount of gas provided for transaction execution, in wei. If not given, the gas limit is determined usingweb3.eth.estimate_gas
.
gas_buffer
: A multiplier applied toweb3.eth.estimate_gas
when setting gas limit automatically.gas_limit
andgas_buffer
cannot be given at the same time.
gas_price
: The gas price for legacy transaction, in wei. If not given, the gas price is set according toweb3.eth.gas_price
.
max_fee
: Max fee per gas of dynamic fee transaction.
priority_fee
: Max priority fee per gas of dynamic fee transaction.
amount
: The amount of Ether to include with the transaction, in wei.
nonce
: The nonce for the transaction. If not given, the nonce is set according toweb3.eth.get_transaction_count
while taking pending transactions from the sender into account.
required_confs
: The requiredconfirmations
before theTransactionReceipt
is processed. If none is given, defaults to 1 confirmation. If 0 is given, immediately returns a pendingTransactionReceipt
, while waiting for a confirmation in a separate thread.
allow_revert
: Boolean indicating whether the transaction should be broadcasted when it is expected to revert. If not set, the default behaviour is to allow reverting transactions in development and disallow them in a live environment.
All currency integer values can also be given as strings that will be converted by Wei
.
Hint
When working in development environment, the from
field can be any address given as a string. In this way you can broadcast a transaction from an address without having it’s private key. It is even possible to send transactions from contracts!
Calls
Contract methods that do not alter the state are called via a ContractCall
object. This object will call the contract method without broadcasting a transaction, and return the result.
>>> Token[0].balanceOf(accounts[0])
1000000000000000000000
If you wish to access the method via a transaction you can use ContractCall.transact
.
>>> tx = Token[0].balanceOf.transact(accounts[0])
Transaction sent: 0xe803698b0ade1598c594b2c73ad6a656560a4a4292cc7211b53ffda4a1dbfbe8
Token.balanceOf confirmed - block: 3 gas used: 23222 (18.85%)
<Transaction object '0xe803698b0ade1598c594b2c73ad6a656560a4a4292cc7211b53ffda4a1dbfbe8'>
>>> tx.return_value
1000000000000000000000
Contracts Outside of your Project
When working in a live environment or forked development network, you can create Contract
objects to interact with already-deployed contracts.
Contract
objects may be created from interfaces within the interfaces/
folder of your project, or by fetching information from a remote source such as a block explorer.
Using Local Interfaces
The InterfaceContainer
object (available as interface
) provides access to the interfaces within your project’s interfaces/
folder.
For example, to create a Contract
object from an interface named Dai
:
>>> interface.Dai
<InterfaceConstructor 'Dai'>
>>> interface.Dai("0x6B175474E89094C44Da98b954EedeAC495271d0F")
<Dai Contract object '0x6B175474E89094C44Da98b954EedeAC495271d0F'>
You can also use the Contract.from_abi
classmethod to instatiate from an ABI as a dictionary:
>>> Contract.from_abi("Token", "0x79447c97b6543F6eFBC91613C655977806CB18b0", abi)
<Token Contract object '0x79447c97b6543F6eFBC91613C655977806CB18b0'>
Fetching from a Remote Source
Contract objects may also be created by fetching data from a remote source. For example, use Contract.from_explorer
to create an object by querying Etherscan:
>>> Contract.from_explorer("0x6b175474e89094c44da98b954eedeac495271d0f")
Fetching source of 0x6B175474E89094C44Da98b954EedeAC495271d0F from api.etherscan.io...
<Dai Contract '0x6B175474E89094C44Da98b954EedeAC495271d0F'>
Persisting Contracts between Sessions
The data used to create Contract
objects is stored in a local database and persists between sessions. After the initial creation via a class method, you can recreate an object by initializing Contract
with an address:
>>> Contract("0x6b175474e89094c44da98b954eedeac495271d0f")
<Dai Contract '0x6B175474E89094C44Da98b954EedeAC495271d0F'>
Alternatively, Contract.set_alias
allows you to create an alias for quicker access. Aliases also persist between sessions.
>>> contract = Contract("0x6b175474e89094c44da98b954eedeac495271d0f")
>>> contract.set_alias('dai')
>>> Contract('dai')
<Dai Contract '0x6B175474E89094C44Da98b954EedeAC495271d0F'>