7

how to send an erc20 in web3py please, I have that :

tx_hash = contract.functions.transfer(destination_address, value).transact({'from': myaddress })

and I get this error :

ValueError: {'code': -32601, 'message': 'The method eth_sendTransaction does not exist/is not available'}

I guess am missing signing that or something like that, how can I make a successful erc20 transfer.

3
  • A couple clarification questions: what node are you connecting to? (I'm guessing Infura.) Also, where are your private keys stored?
    – carver
    Aug 3, 2020 at 17:31
  • Yes I am using Infura, and the private key is at the same file.
    – Mr_Mo
    Aug 3, 2020 at 19:56
  • 1
    You should be able to find the appropriate documentation to answer your question here: web3py.readthedocs.io/en/latest/… Aug 3, 2020 at 21:24

2 Answers 2

4

transact() asks the node to sign your transaction using eth_sendTransaction. Infura can't do that, because they don't have your keys.

Instead, you can use the contract object to create an unsigned transaction like:

contract_call = contract.functions.transfer(destination_address, value)
unsigned_txn = contract_call.buildTransaction({'chainId': 1, 'gasPrice': w3.toWei(100, 'gwei')})

Then you can sign the transaction with your private key, and broadcast it:

signed_txn = w3.eth.account.sign_transaction(unsigned_txn, private_key=private_key)
w3.eth.sendRawTransaction(signed_txn.rawTransaction)
0

Here is a script that does an ERC-20 transfer in modern web3.py:

"""Manual transfer script.

- For a hardcoded token, asks to address and amount where to transfer tokens.

- Waits for the transaction to complete
"""
import datetime
import os
import sys
from decimal import Decimal

from eth_account import Account
from eth_account.signers.local import LocalAccount
from web3 import HTTPProvider, Web3
from web3.middleware import construct_sign_and_send_raw_middleware

from eth_defi.abi import get_deployed_contract
from eth_defi.token import fetch_erc20_details
from eth_defi.txmonitor import wait_transactions_to_complete

# What is the token we are transferring.
# Replace with your own token address.
ERC_20_TOKEN_ADDRESS = "0x0aC7B3733cBeE5D87A80fbf331f4A0bD01f17386"

# Connect to JSON-RPC node
json_rpc_url = os.environ["JSON_RPC_URL"]
web3 = Web3(HTTPProvider(json_rpc_url))
print(f"Connected to blockchain, chain id is {web3.eth.chain_id}. the latest block is {web3.eth.block_number:,}")

# Read and setup a local private key
private_key = os.environ.get("PRIVATE_KEY")
assert private_key is not None, "You must set PRIVATE_KEY environment variable"
assert private_key.startswith("0x"), "Private key must start with 0x hex prefix"
account: LocalAccount = Account.from_key(private_key)
web3.middleware_onion.add(construct_sign_and_send_raw_middleware(account))

# Show users the current status of token and his address
erc_20 = get_deployed_contract(web3, "ERC20MockDecimals.json", ERC_20_TOKEN_ADDRESS)
token_details = fetch_erc20_details(web3, ERC_20_TOKEN_ADDRESS)

print(f"Token details are {token_details}")

balance = erc_20.functions.balanceOf(account.address).call()
eth_balance = web3.eth.getBalance(account.address)

print(f"Your balance is: {token_details.convert_to_decimals(balance)} {token_details.symbol}")
print(f"Your have : {eth_balance/(10**18)} ETH for gas fees")

# Ask for transfer details
decimal_amount = input("How many tokens to transfer? ")
to_address = input("Give destination Ethereum address? ")

# Some input validation
try:
    decimal_amount = Decimal(decimal_amount)
except ValueError as e:
    raise AssertionError(f"Not a good decimal amount: {decimal_amount}") from e

assert web3.isChecksumAddress(to_address), f"Not a valid address: {to_address}"

# Fat-fingering check
print(f"Confirm transfering {decimal_amount} {token_details.symbol} to {to_address}")
confirm = input("Ok [y/n]?")
if not confirm.lower().startswith("y"):
    print("Aborted")
    sys.exit(1)

# Convert a human-readable number to fixed decimal with 18 decimal places
raw_amount = token_details.convert_to_raw(decimal_amount)
tx_hash = erc_20.functions.transfer(to_address, raw_amount).transact({"from": account.address})

# This will raise an exception if we do not confirm within the timeout
print(f"Broadcasted transaction {tx_hash.hex()}, now waiting 5 minutes for mining")
wait_transactions_to_complete(web3, [tx_hash], max_timeout=datetime.timedelta(minutes=5))

print("All ok!")

For further information read the full ERC-20 transfer tutorial.

1
  • 1
    Seems like your link is broken
    – merc1er
    Aug 24, 2022 at 7:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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