Skip to content

Interacting with contracts#

Contracts can be interacted with either using methods generated in pytypes or using low-level methods.

Request types#

There are 4 low-level methods that represent different request types:

  • tx - a request that sends a transaction (even if the function being called does not modify the blockchain state),
  • call - a request that returns the return value of the function being called, does not modify the blockchain state (even if the function being called modifies the blockchain state),
  • estimate - a request that returns an estimated amount of gas needed to perform the transaction,
  • access_list - a request that returns an access list (addresses and storage keys, see EIP-2930) and an estimated amount of gas needed to perform the transaction when the access list is used.

The low-level methods are named .transact(), .call(), .estimate(), and .access_list() respectively. Each request type has its default account used when no from_ argument is provided. The default accounts are properties of the Chain object:

  • chain.default_tx_account for tx request type,
  • chain.default_call_account for call request type,
  • chain.default_estimate_account for estimate request type,
  • chain.default_access_list_account for access_list request type.

All default accounts use chain.accounts[0] as the default value. The default accounts can be changed by assigning a new value to the corresponding property or by using the set_default_accounts() method.

from wake.testing import *


@chain.connect()
def test_accounts():
    # assign each default account manually
    chain.default_tx_account = chain.accounts[1]
    chain.default_call_account = chain.accounts[1]
    chain.default_estimate_account = chain.accounts[1]
    chain.default_access_list_account = chain.accounts[1]

    # or assign all default accounts at once
    chain.set_default_accounts(chain.accounts[2])

Note

It is recommended to set default_estimate_account and default_access_list_account to the same account as default_tx_account to ensure that the returned gas estimate is accurate.

In pytypes, the default request type is tx for non-pure non-view functions and call for pure and view functions.

from wake.testing import *
from pytypes.contracts.Counter import Counter


@chain.connect()
def test_accounts():
    chain.set_default_accounts(chain.accounts[0])
    counter = Counter.deploy()

    # performs a call
    count = counter.count()

    # sends a transaction
    tx = counter.increment()

The request type can be changed using the request_type flag.

# does not increment the counter
ret_val = counter.increment(request_type="call")

# "tx" request type is the default for non-pure non-view functions
tx = counter.increment(request_type="tx")

# amount of gas needed to send as a transaction
gas_estimate = counter.increment(request_type="estimate")

# access list and amount of gas needed to send as a transaction
access_list, gas_estimate = counter.increment(request_type="access_list")

The call request type used on the .deploy() method returns runtime code of the contract that would be deployed if the method was called with tx request type.

# does not deploy the contract
runtime_code = Counter.deploy(request_type="call")

# deploys the contract and returns the contract instance, the default behavior
counter = Counter.deploy(request_type="tx")

# deploys the contract and returns the transaction object
tx = Counter.deploy(request_type="tx", return_tx=True)

# amount of gas needed to deploy the contract
gas_estimate = Counter.deploy(request_type="estimate")

# access list and amount of gas needed to deploy the contract
access_list, gas_estimate = Counter.deploy(request_type="access_list")

Warning

The call request type used to return empty bytes for deploy methods in earlier versions of Anvil.

Keyword arguments#

Both methods generated in pytypes and low-level methods accept the following keyword arguments common for all request types:

Argument Description
from_ Account or Address used as a sender of a transaction/call. If not provided, the default account for the request type will be used.
value Amount of Ether to be sent. Can be either an int in Wei or a string with a unit (e.g. "1 ether").
gas_limit Maximum amount of gas that can be consumed by the transaction.
gas_price Gas price to be used for type 0 and type 1 transactions. Can be either an int in Wei or a string with a unit (e.g. "10 gwei").
max_fee_per_gas Maximum fee per gas to be used for type 2 transactions. Can be either an int in Wei or a string with a unit (e.g. "10 gwei").
max_priority_fee_per_gas Maximum priority fee per gas to be used for type 2 transactions. Can be either an int in Wei or a string with a unit (e.g. "10 gwei").
access_list Access list to be used for type 1 and type 2 transactions. See EIP-2930 for more information.
type Transaction type to be used. Can be either 0, 1, or 2.

Low-level methods also accept the data keyword argument (of type bytes or bytearray) that can be used to specify the data to be sent to a contract.

Encoding data for low-level calls and transactions

To prepare the data payload, the Abi helper class can be used. It offers the same ABI encoding functions as the abi global object in Solidity.

from wake.testing import *
from pytypes.contracts.Counter import Counter

@chain.connect()
def test_low_level_transact():
    chain.default_tx_account = chain.accounts[0]

    counter = Counter.deploy()

    # execute counter.setCount(100) using a low-level transaction
    counter.transact(data=Abi.encode_call(Counter.setCount, [100]))
    assert counter.count() == 100

Methods generated in pytypes accept the to keyword argument (of type Account, Address or hex-encoded string address) that can be used to override the address of the contract being called.

Calling contracts through a proxy

Using the to keyword argument can be useful when a contract should be called through a proxy contract.

contract.initialize(owner, to=proxy)

tx request type#

The tx request type is used to send a transaction. It accepts one more keyword argument, confirmations, that can be used to specify the number of blocks that should be mined before a transaction object is returned. Setting confirmations to 0 returns a transaction object immediately after the transaction is sent.

Sending transactions from any account

The from_ argument can be used to send transactions from any account (including contract) or address. However, this may come at a cost of decreased performance (see Performance considerations).

When sending transactions from an account with code (contract), the contract behaves as if it had no code during the execution of the transaction!

call request type#

The call request type is used to execute a call. It accepts one more keyword argument, block, that can be used to specify the number of the block to be used as a context for the call. The default value is latest which means that the call will be executed in the context of the latest block.

estimate request type#

The estimate request type is used to estimate the amount of gas needed to execute a transaction. It accepts one more keyword argument, block, that can be used to specify the number of the block to be used as a context for the estimation. The default value is pending which means that the estimation will be executed in the context of the pending block.

access_list request type#

The access_list request type is used to estimate the access list and the amount of gas needed to execute a transaction when using the returned access list. It accepts one more keyword argument, block, that can be used to specify the number of the block to be used as a context for the estimation. The default value is pending which means that the estimation will be executed in the context of the pending block.