1

I'm new to the world of Web3.py however I have an ERC20 compliant contract and I am tryin to call the transfer() method on it to simply transfer some tokens from sender to receiver.

I am testing it by running a local Parity node on the dev chain parity --tracing=on --chain=dev (so everything is local)

My ERC20 contract has been created and tested externally so I know the contract itself works.

However, my python code is thus:

from web3 import Web3

### sender_address, receiver_address and private keys are initialised here ###

url = 'http://localhost:8545'
web3 = Web3(Web3.HTTPProvider(url))
print('Connected?', web3.isConnected()) # OUTPUT ==> True

token_address = '0x731a10897d267e19B34503aD902d0A29173Ba4B1'
abi = open('contract.abi', 'r').read()
contract = web3.eth.contract(address=token_address, abi=abi)

print(contract.functions.balanceOf(sender_address).call())   # OUTPUT ==> 3000000000000000000
print(contract.functions.balanceOf(receiver_address).call()) # OUTPUT ==> 0

nonce = w3.eth.getTransactionCount(sender_address)
tx = contract.functions.transfer(receiver_address, 123456789).buildTransaction({
    'chainId': web3.eth.chainId,
    'gas': 70000,
    'gasPrice': w3.toWei('1', 'gwei'),
    'nonce': nonce
})
signed_tx = w3.eth.account.signTransaction(tx, private_key=sender_pk)    
tx_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)

mine_receipt = w3.eth.waitForTransactionReceipt(tx_hash)
print(mine_receipt)

print(contract.functions.balanceOf(sender_address).call())   # OUTPUT STILL ==> 3000000000000000000
print(contract.functions.balanceOf(receiver_address).call()) # OUTPUT STILL ==> 0

The mine_receipt outputs:

AttributeDict({
    'blockHash': HexBytes('0x688e1f44bd2720d9581ad1e24c765bc1ef210d6431dd39d7d26c73a58f1ea11a'),
    'blockNumber': 60,
    'contractAddress': None,
    'cumulativeGasUsed': 24300,
    'from': '0x0D366F25CC54F1DCfD15c135C03a3D493636A730',
    'gasUsed': 24300,
    'logs': [],
    'logsBloom': HexBytes('0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'),
    'status': 0,
    'to': '0x731a10897d267e19B34503aD902d0A29173Ba4B1',
    'transactionHash': HexBytes('0x76d8adb4489a3c725665694da173b800691b2f20778f96eb1c5c66abb18a4b20'),
    'transactionIndex': 0
})

And the Parity node logs:

2020-05-09 01:04:29  Transaction mined (hash 0x76d8adb4489a3c725665694da173b800691b2f20778f96eb1c5c66abb18a4b20)
2020-05-09 01:04:29  Imported #60 0x688e…a11a (1 txs, 0.02 Mgas, 0 ms, 0.66 KiB)

So it clearly accepts the transfer call, however as you can see by the last two print lines calling the balanceOf of both users again their balnce values have not changed at all.

Any help on what might be happening here would be appreciated.

1

There's some leads in the transaction receipt

'gasUsed': 24300,
'status': 0,

When status is set to 0 it means the transaction has failed.

The base transaction cost is 21000 which is close to 24300 gas used, so the contract didn't avanced very much, for example it didn't write to storage yet.

You will have to check the conditions used by requires early in transfer function.

| improve this answer | |
  • You were correct, it was in fact this require condition in the transfer function: require(_whitelist[recipient], "Recipient is not in the whitelist");... But I see the require has the error return "Recipient is not in the whitelist", but I couldn't see that anywhere in the receipt, all I saw was the 'status':0. Is there a way to capture that handy error retrned by the contract when doing the transaction? – Jakob May 9 at 17:06
  • @Jakob I haven't seen a standard solution, both geth and parity have their own api to trace transactions. – Ismael May 9 at 18:38
1

Your code seems to be correct.

The Ethereum network is asynchronous. The new state may have not propagated fully through network in the point you are doing read immediately after read. Also there might be minor blockchain reorganisations making transactions hop between blocks.

However none of thus should be a concern for a local chain with a single miner, so I do not know what is going on here. Any case, the safe solution would be listen for the contract state to the change, for example using events (wait until the contract has emitted a relevant event to the state change), instead of naively relying on the transaction receipt.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.