Search code examples
pythonethereumsmartcontractscryptocurrencyganache

Sync with ganache-cli in python


I want to test a simple Ethereum smart contract ganache prints accounts in lower-case and web3 gives me an error:

web3.exceptions.InvalidAddress: ('Web3.py only accepts checksum addresses. The software that gave you this non-checksum address should be considered unsafe, please file it as a bug on their platform. Try using an ENS name instead. Or, if you must accept lower safety, use Web3.toChecksumAddress(lower_case_address).', '0xfcad0b19bb29d4674531d6f115237e16afce377c')

I then convert the address to mixed address using:

Web3.toChecksumAddress(the_lower_case_ganache_address)

and it rises an error:

File "/usr/local/lib/python3.7/site-packages/web3/contract.py", line 1385, in call_contract_function raise BadFunctionCallOutput(msg) from e web3.exceptions.BadFunctionCallOutput: Could not transact with/call contract function, is contract deployed correctly and chain synced? 127.0.0.1 - - [25/Jan/2019 21:35:21] "POST /blockchain/user HTTP/1.1" 500 -

it's my python code:

def check_gender(data):
    valid_list = ["male", "female"]
    if data not in valid_list:
        raise ValidationError(
            'Invalid gender. Valid choices are'+ valid_list
        )

class UserSchema(Schema):
    name = fields.String(required=True)
    gender = fields.String(required=True, validate=check_gender)


app = Flask(__name__)


# api to set new user every api call
@app.route("/blockchain/user", methods=['POST'])
def transaction():

    w3.eth.defaultAccount = w3.eth.accounts[0]
    with open("data.json", 'r') as f:
        datastore = json.load(f)
    abi = datastore["abi"]
    contract_address = datastore["contract_address"]

    # Create the contract instance with the newly-deployed address
    user = w3.eth.contract(
        address=contract_address, abi=abi,
    )
    body = request.get_json()
    result, error = UserSchema().load(body)
    if error:        
        return jsonify(error), 422
    tx_hash = user.functions.setUser(
        result['name'], result['gender']
    )
    tx_hash = tx_hash.transact()
    # Wait for transaction to be mined...
    w3.eth.waitForTransactionReceipt(tx_hash)
    user_data = user.functions.getUser().call()
    return jsonify({"data": user_data}), 200



if __name__ == '__main__':
    app.run()

` and the json file:

{
    "abi": [
        {
            "constant": false,
            "inputs": [
                {
                    "name": "name",
                    "type": "string"
                },
                {
                    "name": "gender",
                    "type": "string"
                }
            ],
            "name": "setUser",
            "outputs": [],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        },
        {
            "constant": false,
            "inputs": [],
            "name": "getUser",
            "outputs": [
                {
                    "name": "",
                    "type": "string"
                },
                {
                    "name": "",
                    "type": "string"
                }
            ],
            "payable": false,
            "stateMutability": "nonpayable",
            "type": "function"
        }
    ],
    "contract_address": "0xFCAd0B19bB29D4674531d6f115237E16AfCE377c"
}

Solution

  • The error is stating that Ganache cannot find a deployed contract to interact with.

    Your code seems valid, but the error likely occurs on this line:

    tx_hash = user.functions.setUser(
        result['name'], result['gender']
    )
    

    The code attempts to set the user, but cannot find a contract to interact with (even if the ABI and contract instance are valid).

    If you are using Ganache, it is likely that you are redeploying the contract each time you run the code, so the following line will likely not be working, provided you are pulling from a static file:

    contract_address = datastore["contract_address"]
    

    You will need to dynamically get the contract address from Ganache if you are deploying it each time.