{ code: -32003, message: "insufficient funds for gas * price + value: have 0 want 53901949584960" }

{ code: -32003, message: “insufficient funds for gas * price + value: have 0 want 53901949584960” }

Hello developers!
I’m getting this error while trying to transfer an ERC-20 token through the ICP chain fusion concept.

this is frontend code for authentication from metamask

  const TransferToken = async () => {
   const canisterEthAddress = "0x260A5568d2002B8F601Fe1001BD2D93A212F087b";
    console.log("Canister ETH address:", canisterEthAddress);

    const provider = new ethers.providers.JsonRpcProvider(
      "https://eth-sepolia.g.alchemy.com/v2/userAPIKey"
    );
    const nonce = await provider.getTransactionCount(canisterEthAddress);
    const feeData: any = await provider.getFeeData();
    const chainId: any = (await provider.getNetwork()).chainId;

    const tokenABI = [
      "function transfer(address to,uint256 amount) returns (bool)",
    ];
    const token = new ethers.Contract(
      "0xDFdA108391A1EDa23CB0f6546e9F9386E4227994",
      tokenABI,
      provider
    );

    const burnAddress = "0x259B2BdaD6228bdC5Eb48c7A8c244f5F798113Dd";
    const amount = ethers.utils.parseUnits("1", 18);
    const txRequest = await token.populateTransaction.transfer(
      burnAddress,
      amount
    );
    console.log("Max Fee Per Gas:", feeData.maxFeePerGas.toNumber());
    console.log(
      "Max Priority Fee Per Gas:",
      feeData.maxPriorityFeePerGas.toNumber()
    );

    const gasLimit = await provider.estimateGas({
      from: canisterEthAddress,
      to: txRequest.to,
      data: txRequest.data,
      value: 0,
    });

    console.log("gasLimit : ", gasLimit.toNumber());

    const tx = {
      type: 2,
      chainId,
      nonce,
      to: txRequest.to,
      data: txRequest.data,
      value: 0,
      maxFeePerGas: feeData.maxFeePerGas,
      maxPriorityFeePerGas: feeData.maxPriorityFeePerGas,
      gasLimit,
    };

    const unsignedTx = ethers.utils.serializeTransaction(tx);
    const txHash = ethers.utils.keccak256(unsignedTx);

    const ic = icblast({ ic: true });
    const backendActor = await ic("vrqyr-saaaa-aaaan-qzn4q-cai");
    const sig = await backendActor.sign_with_ecdsa({
      key_id: { name: "insecure_test_key_1", curve: { secp256k1: null } },
      derivation_path: [],
      message_hash: Array.from(ethers.utils.arrayify(txHash)),
    });
    const signature = new Uint8Array(sig.signature);
    const r = ethers.utils.hexlify(signature.slice(0, 32));
    const s = ethers.utils.hexlify(signature.slice(32, 64));
    const v = 27 + (sig.recovery_id ?? 0);
    const rawTx = ethers.utils.serializeTransaction(tx, { v, r, s });
    console.log("rawTx:", rawTx);
    const txHashRes = await actor.send_raw_transaction(rawTx);
    console.log("Broadcast success:", txHashRes);
  };

backend code

#[update]
pub async fn send_raw_transaction(rawSignedTransactionHex: String) -> Result<String, String> {
    let RpcServices: RpcServices = RpcServices::Custom {
        chainId: ChainId::from(1u64),
        services: vec![RpcApi {
            url: "https://eth-sepolia.g.alchemy.com/v2/userAPIKEy"
                .to_string(),
            headers: None,
        }],
    };

    let cycles = 2_000_000_000_000;
    let canister_id =
        Principal::from_text("7hfb6-caaaa-aaaar-qadga-cai").expect("principal should be valid");
    let RpcConfig: Option<()> = None;
    let args = (RpcServices, RpcConfig, rawSignedTransactionHex);

    let result: (MultiSendRawTransactionResult,) =
        call_with_payment128(canister_id, "eth_sendRawTransaction", args, cycles)
            .await
            .expect("Failed to call eth_sendRawTransaction");

    match result.0 {
        MultiSendRawTransactionResult::Consistent(SendRawTransactionResult::Ok(status)) => {
            match status {
                SendRawTransactionStatus::Ok(Some(tx_hash)) => Ok(tx_hash),
                SendRawTransactionStatus::Ok(None) => {
                    Err("Transaction succeeded but tx_hash missing".to_string())
                }
                SendRawTransactionStatus::NonceTooLow => Err("Nonce too low".to_string()),
                SendRawTransactionStatus::NonceTooHigh => Err("Nonce too high".to_string()),
                SendRawTransactionStatus::InsufficientFunds => {
                    Err("Insufficient funds".to_string())
                }
            }
        }
        MultiSendRawTransactionResult::Consistent(SendRawTransactionResult::Err(e)) => {
            Err(format!("SendRawTransaction failed: {:?}", e))
        }
        MultiSendRawTransactionResult::Inconsistent(_) => {
            Err("Transaction is inconsistent".to_string())
        }
    }
}

Error

“SendRawTransaction failed: JsonRpcError(JsonRpcError { code: -32003, message: “insufficient funds for gas * price + value: have 0 want 53901949584960” })”

I've tried this multiple times but still getting the same error. Can anyone help me fix this issue?

you let backendActor create the signature, but then actor is called for send_raw_transaction. my first guess would be that backendActor doesn’t have any ETH to pay for gas.

why do you have a two canister setup? if you return the signed transaction to the user frontend, why not send it to an RPC provider from there instead of proxying that through a canister?

in general it would be helpful if you could provide the a minimum example that reproduces the issue.

let backendActor is for the vrqyr-saaaa-aaaan-qzn4q-cai canister actor for getting signature through sign_with_ecdsa api so that we can send that signature to the eth_sendRawTransaction function and secound actor actor.send_raw_transaction(rawTx); is my own backend canister which have above provided rust code, where i’m calling eth_sendRawTransaction function in backend

vrqyr-saaaa-aaaan-qzn4q-cai this is the canister provided by definity or icp for getting signature

I don’t recognize this principal vrqyr-saaaa-aaaan-qzn4q-cai. But I think my initial assessment holds

i have enough sepoliaETH to my wallet sender and receiver address still getting same error

can you correct some code in backend or frontend

The address you are signing for with backendActor needs to hold enough ETH to pay for gas. Find the address and check if it holds ETH. If it doesn’t, transfer ETH there and try again.