0

I’m playing around with ethereum and python and I’m running into some weird behavior I can’t make sense of. I’m having trouble understanding how return values work when calling a contract function with the python w3 client. Here’s a minimal example which is confusing me in several different ways:

Contract:

pragma solidity ^0.4.0;

contract test {
    function test(){

    }

    function return_true() public returns (bool) {
        return true;
    }

    function return_address() public returns (address) {
        return 0x111111111111111111111111111111111111111;
    }
}

Python unittest code

from web3 import Web3, EthereumTesterProvider
from solc import compile_source
from web3.contract import ConciseContract
import unittest
import os


def get_contract_source(file_name):
    with open(file_name) as f:
        return f.read()


class TestContract(unittest.TestCase):
    CONTRACT_FILE_PATH = "test.sol"
    DEFAULT_PROPOSAL_ADDRESS = "0x1111111111111111111111111111111111111111"

    def setUp(self):
        # copied from https://github.com/ethereum/web3.py/tree/1802e0f6c7871d921e6c5f6e43db6bf2ef06d8d1 with MIT licence
        # has slight modifications to work with this unittest
        contract_source_code = get_contract_source(self.CONTRACT_FILE_PATH)
        compiled_sol = compile_source(contract_source_code)  # Compiled source code
        contract_interface = compiled_sol[':test']
        # web3.py instance
        self.w3 = Web3(EthereumTesterProvider())
        # Instantiate and deploy contract
        self.contract = self.w3.eth.contract(abi=contract_interface['abi'], bytecode=contract_interface['bin'])
        # Get transaction hash from deployed contract
        tx_hash = self.contract.constructor().transact({'from': self.w3.eth.accounts[0]})
        # Get tx receipt to get contract address
        tx_receipt = self.w3.eth.getTransactionReceipt(tx_hash)
        self.contract_address = tx_receipt['contractAddress']
        # Contract instance in concise mode
        abi = contract_interface['abi']
        self.contract_instance = self.w3.eth.contract(address=self.contract_address, abi=abi,
                                                      ContractFactoryClass=ConciseContract)

    def test_return_true_with_gas(self):
        # Fails with HexBytes('0xd302f7841b5d7c1b6dcff6fca0cd039666dbd0cba6e8827e72edb4d06bbab38f') != True
        self.assertEqual(True, self.contract_instance.return_true(transact={"from": self.w3.eth.accounts[0]}))

    def test_return_true_no_gas(self):
        # passes
        self.assertEqual(True, self.contract_instance.return_true())

    def test_return_address(self):
        # fails with AssertionError: '0x1111111111111111111111111111111111111111' != '0x0111111111111111111111111111111111111111'
        self.assertEqual(self.DEFAULT_PROPOSAL_ADDRESS, self.contract_instance.return_address())

I have three methods performing tests on the functions in the contract. In one of them, a non-True value is returned and instead HexBytes are returned. In another, the contract functions returns an address constant but python sees a different value from what’s expected. In yet another case I call the return_true contract function without gas and the True constant is seen by python.

  1. Why does calling return_true with transact={"from": self.w3.eth.accounts[0]} cause the return value of the function to be HexBytes(...)?
  2. Why does the address returned by return_address differ from what I expect?

I think I have some sort of fundamental misunderstanding of how gas affects function calls.