web3.py – How to Send Currency Other than ETH Using web3.py

web3.py

How do you send a withdrawal transaction (i.e. transfer from one wallet to another) in currency other than ETH? I been following this youtube from Dapp University here, which is awesome. Example 3 shows how to do it, if you're sending ETH:
https://www.youtube.com/watch?v=pZSegEXtgAE
https://github.com/dappuniversity/web3_py_examples/blob/master/examples/3_send_transactions.py

But I want to send for example VVS or SINGLE, which is on CRONOS chain only. I see the biggest problem is "value" tag. web3.py toWei the "currency" argument only accepts 'ether' or 'gwei'. How can I specify I want to move my VVS or SINGLE (Not ETH) balance? I want to move my SINGLE balance from wallet1 to wallet2, without converting to any other coins.

Below is my code (I wrap code snippnet from Dapp University inside a function)

def build_withdrawal_transaction(self, chain_id : int, ccy : str, amount_in_eth : float, from_address : str, destination_address : str, gas_limit : int) -> TxParams:
    wallet_address = Web3.toChecksumAddress(from_address)
    destination_wallet_address = Web3.toChecksumAddress(destination_address)

    gas_price : int = self.w3.eth.gasPrice

    nonce = self.w3.eth.getTransactionCount(wallet_address)

    trx_params = {
                'nonce': nonce,
                'chainId': chain_id,
                'from': wallet_address,
                'to': destination_wallet_address,
                'gas': gas_limit,
                'gasPrice': gas_price,
                'value': self.w3.toWei(amount_in_eth, 'ether'),  <-- STUCK HERE!
                'gas': gas_limit,
                'gasPrice': gas_price,
        }

    return trx_params

I am making guesses now, I can "transfer" of the token I want to transfer. If it's SINGLE, I need to call "transfer" on SINGLE's contract: https://cronos.org/explorer/address/0x0804702a4E749d39A35FDe73d1DF0B1f1D6b8347/contracts

I tried but failed because the contract isn't marked "Payable" in smart contract. So I was using wrong contract function perhaps.

Reference:

a. TxParams: https://github.com/ethereum/web3.py/blob/master/web3/types.py#L204

b. Doing a swap on CRONOS: https://norman-lm-fung.medium.com/interact-with-cronos-single-usdc-lp-with-web3-py-1e14c62a0d9c

c. Doing same in web3.js: https://piyopiyo.medium.com/how-to-send-erc20-token-with-web3-js-99ed040693ce

Best Answer

Got it!!! I changed ABI for "transfer" function, make sure payable from false to true.

EIP20_ABI = '... "name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":true ...' 

Then:

def build_withdrawal_transaction(ccy : str, token_amount : uint, my_wallet_address : str, destination_address : str, gas_limit : int, params={}) -> TxParams:
    cronos_mainnet_rpc = "https://evm.cronos.org"
    w3 = Web3(Web3.HTTPProvider(cronos_mainnet_rpc))
    chain_id = w3.eth.chainId

    wallet_address = Web3.toChecksumAddress(my_wallet_address)

    gas_price : int = w3.eth.gasPrice

    nonce = self.w3.eth.getTransactionCount(wallet_address)

    token_adddress = Web3.toChecksumAddress("0x0804702a4E749d39A35FDe73d1DF0B1f1D6b8347")
    token_contract =  = w3.eth.contract(address=token_address, abi=EIP20_ABI)
    token_decimals = token_contract.functions.decimals().call()
    # Make sure for amount, you multiply by "decimals" (Or you'd be sending gwei!!!)
    function_params = token_contract.functions.transfer(
                   destination_address, 
                   token_amount * pow(10, token_decimals)
             ) 

    nonce = w3.eth.getTransactionCount(wallet_address)

    trx_params = function_params.buildTransaction(
        {
            'from': wallet_address,
            'gas': gas_limit,
            'gasPrice': gas_price,
            'nonce': nonce,
            'chainId': chain_id,
            'value': 0 # Set to zero here!
        }
    )

    return trx_params
Related Topic