Wallet Transactions – Sending Signed Transactions of Wallet A Using Wallet B to Pay for Gas

ethers.jsgasmeta-transactionstransactionswallet-transfer

I have multiple wallet created in my nodejs app.
i can successfully send transactions from one and consolidate all funds.
my problem is when a wallet only have an ERC20 token in it and doesnt have ETH to cover the gas.
suppose it has 100 USDC token and i want to send those tokens to another wallet, how can i do that?

what i have tried:

  • Meta transaction: this doesnt work, at least for the USDC contract, it sends the transaction to a forwarder that i have created myself (which works on another contract of mine) but when the forwarder sends a transaction to USDC/USDT contract, it always gets rejected. i have tried to increase the gas but whatever i do it just reject (the USDC contract rejects the forwarder) transaction.
  • Sending a signed transaction by Wallet "A" (which has the ERC20 token but no ETH to pay the gas) using Wallet "B" which has ETH and would pay for the gas, but cannot figure it out, it always gets an error.
    This is my code to create and send the signed transaction
 const wallet = new ethers.Wallet(privateKey_signer, provider);
    const wallet2 = new ethers.Wallet(privateKey_sender, provider);
    const nonce = await provider.getTransactionCount(wallet.address);

const txObject = {
      to: usdc.address,
      nonce: ethers.utils.hexlify(nonce),
      gasLimit: ethers.utils.hexlify(gasLimit),
      gasPrice: ethers.utils.hexlify(gasPrice),
      value: ethers.utils.hexlify(0n),
      data: usdc_contract.interface.encodeFunctionData("transfer", [
        destination_address,
        amount.toString(),
      ]),
    };

    const signedTx = await wallet.signTransaction(txObject);
    const txResponse = await wallet2.sendTransaction(signedTx);

i'm using ethersjs lib and alchemy provider.

i would appreciate any pointers.
thanks

Best Answer

A basic Ethereum transaction looks like this:

from – the address of the sender, that will be signing the transaction. 
recipient – the receiving address. If a contract account, the transaction will execute the contract code)
signature – the identifier of the sender. This is generated when the sender's private key signs the transaction and confirms the sender has authorized this transaction
nonce - 
value – 
data – 
gasLimit –
maxPriorityFeePerGas - 
maxFeePerGas -

Source

The gas fee is deducted from the sender account. And the signature needs to be generated using the sender's private key. Unfortunately, sender=wallet2 and signature=wallt1 simply doesn't work.

For tokens, you can use wallet2 to send a transaction on behalf of wallet1 to transfer x amount of tokens to another address. Firstly, you need to call the approve method to set the allowance limit, then call the transferFrom method. But calling the approve method costs gas too. I guess the most gas-efficient way is to transfer some eth to wallet1 and have wallet1 transfer tokens directly.

In your sample code, you are calling the transfer method, which is transferring from the msg.sender(wallet2 in this case) to the destination. If it can be successfully executed it may not work as you expect.

Related Topic