Skip to content

Latest commit

 

History

History
236 lines (151 loc) · 11.7 KB

core-contracts.rst

File metadata and controls

236 lines (151 loc) · 11.7 KB

Working with Contracts

Deploying Contracts

Each time Brownie is loaded it will automatically compile your project and create ContractContainer <brownie.network.contract.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 <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 <transaction-parameters> containing a from field that specifies which Account <brownie.network.account.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 <ContractContainer.deploy> returns a ProjectContract <brownie.network.contract.ProjectContract> object. The returned object is also appended to the ContractContainer <brownie.network.contract.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 <brownie.exceptions.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:

  • In Solidity, callable methods are labelled as view or pure
  • In Vyper, callable methods include the @constant decorator.

All public contract methods are available from the ProjectContract <brownie.network.contract.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 <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 <brownie.network.contract.ContractTx> object. This object performs a transaction and returns a TransactionReceipt <brownie.network.transaction.TransactionReceipt>.

You may optionally include a dictionary of transaction parameters <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 <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 :pydict of transaction parameters as the final input. It may contain the following values:

  • from: the Account <brownie.network.account.Account> 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 using web3.eth.estimateGas <web3.eth.Eth.estimateGas>.
  • gas_price: The gas price for the transaction, in wei. If not given, the gas price is set according to web3.eth.gasPrice <web3.eth.Eth.gasPrice>.
  • 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 to web3.eth.getTransactionCount <web3.eth.Eth.getTransactionCount>.

All currency integer values can also be given as strings that will be converted by Wei <brownie.convert.datatypes.Wei>.

Note

To maintain compatibility with web3.eth.sendTransaction <web3.eth.Eth.sendTransaction>, you can use gas, gasPrice and value as aliases for gas_limit, gas_price, and amount.

Calls

Contract methods that do not alter the state are called via a ContractCall <brownie.network.contract.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 <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 <network-management-live> or forked development network <network-management-fork>, you can create Contract <brownie.network.contract.Contract> objects to interact with already-deployed contracts.

Contract <brownie.network.contract.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 or ethPM registry.

Using Local Interfaces

The InterfaceContainer <brownie.network.contract.InterfaceContainer> object (available as interface) provides access to the interfaces within your project's interfaces/ folder.

For example, to create a Contract <brownie.network.contract.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 <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 <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 <brownie.network.contract.Contract> objects is stored in a local database and persists between sessions. After the initial creation via a class method <api-network-contract-classmethods>, you can recreate an object by initializing Contract <brownie.network.contract.Contract> with an address:

>>> Contract("0x6b175474e89094c44da98b954eedeac495271d0f")
<Dai Contract '0x6B175474E89094C44Da98b954EedeAC495271d0F'>

Alternatively, Contract.set_alias <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'>