Error in fetching balance for ERC20 contracts

I am trying to fetch balance of an address for an ERC20 contract using ic-alloy:

#[update(name = "getNetworkTVL")]
async fn get_network_tvl(new_network_rpc: String, chain_id: Nat) -> Result<Nat, String> {
    let (network, pool_contract_address) = STATE.with(|state| {
        let state = state.borrow();

        let network = match state.supported_networks.get(&chain_id) {
            Some(network) => network,
            None => panic!("Network with chain_id {chain_id} not found"),
        };

        let pool_contract_address = match Address::from_str(&state.icp_contract_address) {
            Ok(address) => address,
            Err(_) => panic!("Error parsing pool contract address"),
        };

        (network.clone(), pool_contract_address)
    });

    let mut total_tvl: Nat = Nat::from(0u64);

    for asset in &network.supported_assets {
        let rpc_service = generate_rpc_service(new_network_rpc.clone());
        let config = IcpConfig::new(rpc_service);
        let provider = ProviderBuilder::new().on_icp(config);
        ic_cdk::println!("Asset: {}", asset);
        ic_cdk::println!("Network: {:?}", network);
        let contract = ERC20Token::new(Address::from_str(asset).unwrap(), provider.clone());
        let result = contract.balanceOf(pool_contract_address).call().await;

        ic_cdk::println!("Result: {:?}", result);

        let balance = match result {
            Ok(value) => value.balance,
            Err(e) => return Err(format!("Invalid balance type:{}", e)),
        };

        let balance_as_nat = Nat::from(
            balance.try_into().unwrap_or(u128::MAX), // Fallback to max u128 if conversion fails
        );

        total_tvl += balance_as_nat;
    }

    Ok(total_tvl)
}

and I run into this error:

Err =  "Invalid balance type:server returned an error response: error code 3: Canister 7hfb6-caaaa-aaaar-qadga-cai not found"

Which means that there is an error from the result from the contract call. Can you please let me know why this is.

Also not sure where the canister id 7hfb6-caaaa-aaaar-qadga-cai is gotten from as it is different from my programs canister ids.

1 Like

@kristofer I would appreciate any feedback I can get on this ser

That canister id is the id of the evm_rpc canister. The evm_rpc canister routes requests from ICP to an EVM RPC provider.

Make sure you have this included in your dfx.json:

    "evm_rpc": {
      "candid": "https://github.com/internet-computer-protocol/evm-rpc-canister/releases/latest/download/evm_rpc.did",
      "declarations": {
        "output": "src/evm_rpc/declarations"
      },
      "init_arg": "(record { nodesInSubnet = 28 })",
      "remote": {
        "candid": "packages/evm_rpc/declarations/evm_rpc.did",
        "id": {
          "ic": "7hfb6-caaaa-aaaar-qadga-cai"
        }
      },
      "specified_id": "7hfb6-caaaa-aaaar-qadga-cai",
      "type": "custom",
      "wasm": "https://github.com/internet-computer-protocol/evm-rpc-canister/releases/latest/download/evm_rpc.wasm.gz"
    },

See the examples included with ic-alloy for details on how to set things up, here for instance:

1 Like

Thanks. That works but now i get this error:

Error: Failed update call.
Caused by: The replica returned a rejection error: reject code CanisterError, reject message Error from Canister bd3sg-teaaa-aaaaa-qaaba-cai: Canister trapped: heap out of bounds
Canister Backtrace:
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>:...mt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::write
<&T as core::fmt::Debug>::fmt
core::fmt::builders::DebugTuple::field
core::fmt::Formatter::debug_tuple_field1_finish
<core::result::Result<T,E> as core::fmt::Debug>::fmt
core::fmt::write
alloc::fmt::format::format_inner
icp_backend::get_network_tvl::{{closure}}
icp_backend::__canister_method_get_network_tvl::{{closure}}
ic_cdk::futures::waker::wake
ic_cdk::api::call::callback
call_on_cleanup also failed:
Canister violated contract: "ic0_msg_reply_data_append" cannot be executed in cleanup mode, error code None

I recommend starting with the basic wallet as a template. Once that builds, you can easily adapt it and extend it to suit your needs.

I have adapted to follow the basic wallet template but I still get same error. While my implementation is different as I am trying to get balance of ERC20 tokens and not just native assets I am not sure what the error itself means. Could you please explain what the error means?

One is a stack overflow error in Debug. The most obvious way for this to have happened is to have implemented Debug in terms of "{:?}", which will just recurse into Debug again. If you manually implemented Debug, use the debug_struct method on Formatter. The second error indicates that the CDK’s cleanup handler was not triggered, and so it couldn’t deallocate the half-completed function’s local variables, meaning those resources are leaked. It’s not as clear what you could be doing to cause that - the only thing I can think of is if you used futures’ block_on function or some other equivalent executor instead of awaiting the future.

1 Like

I am trying to call this function Poolwithdraw

#[update(name = "Poolwithdraw")]
async fn pool_withdraw(
    pool_id: u64,
    user: String,
    pool_deposit_type: u8,
    chain_id: u64,
) -> Result<String, String> {
    let nat_chain_id = Nat::from(chain_id);

    let network = STATE.with(|state| {
        let state = state.borrow();

        let network = match state.supported_networks.get(&nat_chain_id) {
            Some(network) => network,
            None => panic!("Network with chain_id {} not found", chain_id),
        };

        network.clone()
    });

    let pool_contract_address = match Address::from_str(&network.evm_pool_contract_address) {
        Ok(address) => address,
        Err(_) => panic!("Error parsing pool contract address"),
    };

    let user_address = match Address::from_str(&user) {
        Ok(address) => address,
        Err(e) => panic!("Error parsing user address {}", e),
    };

    let pdt = match pool_deposit_type {
        0 => DepositType::Normal,
        1 => DepositType::Vault,
        _ => return Err("Invalid deposit type".to_string()),
    };

    let pool_call = IPool::getUserGenericDepositCall {
        _poolId: U256::from(pool_id),
        _user: user_address,
        pdt: pdt,
    };

    let call_data = pool_call.abi_encode();

    let result =
        make_json_rpc_request(&pool_contract_address, call_data, network.rpc_url.clone()).await?;

    let decoded_result = GenericDepositDetails::abi_decode(&result, false)
        .map_err(|e| format!("Failed to decode response: {}", e))?;
    println!("Decoded Result: {:?}", decoded_result);

    let deposit_detail = GenericDepositDetail {
        lp: decoded_result.lp,
        amount: decoded_result.amount,
        pool_id: decoded_result.poolId,
        daily_payout: decoded_result.dailyPayout,
        status: match decoded_result.status {
            Status::Active => 0,
            Status::Due => 1,
            Status::Withdrawn => 2,
            _ => return Err("Invalid status value".to_string()),
        },
        days_left: decoded_result.daysLeft,
        start_date: decoded_result.startDate,
        expiry_date: decoded_result.expiryDate,
        accrued_payout: decoded_result.accruedPayout,
        pdt: pool_deposit_type,
        adt: decoded_result.adt.into(),
        asset: decoded_result.asset,
    };

    if pdt != DepositType::Normal {
        return Err("Must be pool withdrawal".to_string());
    }

    let pool_set_call = IPool::setUserDepositToZeroCall {
        poolId: U256::from(pool_id),
        user: user_address,
        pdt: pdt,
    };

    let set_call_data = pool_set_call.abi_encode();

    let _result = make_json_rpc_request(
        &pool_contract_address,
        set_call_data,
        network.rpc_url.clone(),
    )
    .await?;

    let signer = create_icp_signer().await;
    match deposit_detail.adt {
        0 => {
            let hash = match send_eth(
                signer,
                deposit_detail.amount,
                user_address,
                network.rpc_url,
                chain_id,
            )
            .await
            {
                Ok(hash) => hash,
                Err(e) => {
                    return Err(format!("Could not get transaction: {}", e));
                }
            };

            Ok(hash)
        }
        1 => {
            let hash = match send_erc20_token(
                signer,
                deposit_detail.amount,
                user_address,
                network.rpc_url,
                chain_id,
                deposit_detail.asset,
            )
            .await
            {
                Ok(hash) => hash,
                Err(e) => {
                    return Err(format!("Could not get transaction: {}", e));
                }
            };

            Ok(hash)
        }
        _ => {Preformatted text
            return Err(format!("Wrong Asset Deposit Type: {}", deposit_detail.adt));
        }
    }
}

but i keep getting this error saying my canister has no transform query

dfx canister call icp_backend Poolwithdraw '(3,"0xd9d0Ab6D58Cc17402A47A62fc5D285d620af46D5",0,686868)'
(
  variant {
    Err = "HTTP request failed: CanisterError \"IC0536: Error from Canister bkyz2-fmaaa-aaaaa-qaaaq-cai: Canister has no query method \'transform\'..\\nCheck that the method being called is exported by the target canister. See documentation: http://internetcomputer.org/docs/current/references/execution-errors#method-not-found\""
  },
)

but I add this transform query function got from the exemplar repo on the github but the error still persist

#[query(name = "__transform_json_rpc", hidden = true)]
fn transform(args: TransformArgs) -> HttpResponse {
    transform_http_request(args)
}

pub fn transform_http_request(args: TransformArgs) -> HttpResponse {
    HttpResponse {
        status: args.response.status,
        body: canonicalize_json(&args.response.body).unwrap_or(args.response.body),
        headers: vec![],
    }
}

pub fn canonicalize_json(text: &[u8]) -> Option<Vec<u8>> {
    let json = serde_json::from_slice::<Value>(text).ok()?;
    serde_json::to_vec(&json).ok()
}

I would appreciate any response on how can I fix it or what I am doing wrong here.
@kristofer

I believe the method needs to be named just transform.

use ic_cdk::{
    api::management_canister::http_request::{HttpResponse, TransformArgs},
    query,
};

#[query]
fn transform(raw: TransformArgs) -> HttpResponse {
 // ...
}

Also be sure to include the method in the canister did.

type TransformArgs = record { context : blob; response : HttpResponse };
type HttpHeader = record { value : text; name : text };
type HttpResponse = record {
  status : nat;
  body : blob;
  headers : vec HttpHeader;
};

service : () -> {
  ...
  transform : (TransformArgs) -> (HttpResponse) query;
}

Thanks for the help, I did the changes according to your code but now it says “Json RPC error: execution reverted”, I am not sure exactly from where this is coming. I would really appreciate ur words on it.

This is an error message from the calling ETH contract saying there is something wrong with your call. What that can be is difficult to say. It can be related to gas fees, parameters, etc. See for instance this StackOverflow: solidity - Internal JSON-RPC error. “execution reverted” when using safeTransferFrom with a fee - Stack Overflow

To debug this, I would start by looking at the JSON RPC payload being sent and see if something looks off there. Really make sure all parameters have the correct format to begin with.

Thanks @kristofer we were able to solve that but now within the function itself we make two ETH contract interactions, one being a call and the other being a send that requires a signer:

#[update(name = "Poolwithdraw")]
async fn pool_withdraw(
    pool_id: u64,
    user: String,
    pool_deposit_type: u8,
    chain_id: u64,
) -> Result<String, String> {
    let nat_chain_id = Nat::from(chain_id);

    let network = STATE.with(|state| {
        let state = state.borrow();

        let network = match state.supported_networks.get(&nat_chain_id) {
            Some(network) => network,
            None => panic!("Network with chain_id {} not found", chain_id),
        };

        network.clone()
    });

    let pool_contract_address = match Address::from_str(&network.evm_pool_contract_address) {
        Ok(address) => address,
        Err(_) => panic!("Error parsing pool contract address"),
    };

    let user_address = match Address::from_str(&user) {
        Ok(address) => address,
        Err(e) => panic!("Error parsing user address {}", e),
    };

    let pdt = match pool_deposit_type {
        0 => DepositType::Normal,
        1 => DepositType::Vault,
        _ => return Err("Invalid deposit type".to_string()),
    };

    let signer = create_icp_signer().await;
    let address = signer.address();
    let wallet = EthereumWallet::from(signer);

    let rpc_service = generate_rpc_service(network.rpc_url.clone());
    let config = IcpConfig::new(rpc_service);
    let provider = ProviderBuilder::new()
        .with_recommended_fillers()
        .wallet(wallet)
        .on_icp(config);
    let nonce = provider.get_transaction_count(address).await.unwrap_or(0);
    // let provider_chain_id = provider.get_chain_id().await.unwrap_or(chain_id);

    let pool_contract = InsurancePool::new(pool_contract_address, provider.clone());
    let result = pool_contract
        .getUserGenericDeposit(U256::from(pool_id), user_address, pdt.into())
        .call()
        .await;
    let (
        user,
        amount,
        pool_id,
        daily_payout,
        status,
        days_left,
        start_date,
        exp_date,
        accrued_payout,
        dt,
        adt,
        asset,
    ) = match result {
        Ok(value) => value._0,
        Err(e) => return Err(format!("Error fetching user deposit detials: {}", e)),
    };
    let deposit_detail = GenericDepositDetail {
        lp: user,
        amount,
        pool_id,
        daily_payout,
        status,
        days_left,
        start_date,
        expiry_date: exp_date,
        accrued_payout,
        pdt: dt,
        adt,
        asset,
    };

    if pdt != DepositType::Normal {
        return Err("Must be pool withdrawal".to_string());
    }

    let gas_price = match provider.get_gas_price().await {
        Ok(price) => price,
        Err(_) => 10000000000,
    };

    match pool_contract
        .setUserDepositToZero(U256::from(pool_id), user_address, pdt.into())
        .nonce(nonce)
        .gas(200000)
        .gas_price(gas_price)
        .send()
        .await
    {
        Ok(_) => (),
        Err(e) => return Err(format!("Error setting user deposit to zero: {}", e)),
    }
    let signer = create_icp_signer().await;
    match deposit_detail.adt {
        0 => {
            let hash = match send_eth(
                signer,
                deposit_detail.amount,
                user_address,
                network.rpc_url,
                chain_id,
            )
            .await
            {
                Ok(hash) => hash,
                Err(e) => {
                    return Err(format!("Could not get transaction: {}", e));
                }
            };

            Ok(hash)
        }
        1 => {
            let hash = match send_erc20_token(
                signer,
                deposit_detail.amount,
                user_address,
                network.rpc_url,
                chain_id,
                deposit_detail.asset,
            )
            .await
            {
                Ok(hash) => hash,
                Err(e) => {
                    return Err(format!("Could not get transaction: {}", e));
                }
            };

            Ok(hash)
        }
        _ => {
            return Err(format!("Wrong Asset Deposit Type: {}", deposit_detail.adt));
        }
    }
}

I get no issue when I make the call but when I try to make the send I get this error even when I don’t use the chainId for building the provider or transaction:

dfx canister call icp_backend Poolwithdraw '(3,"0xd9d0Ab6D58Cc17402A47A62fc5D285d620af46D5",0,686868)'
(
  variant {
    Err = "Error setting user deposit to zero: server returned an error response: error code -32000: invalid chain id"
  },
)

I created a simple function just to check if the issue was from my provider or the chainId gotten:

#[update(name = "getNetworkDetails")]
async fn get_network_details(chain_id: u64) -> Result<NetworkDetails, String> {
    let nat_chain_id = Nat::from(chain_id);

    let network = STATE.with(|state| {
        let state = state.borrow();

        let network = match state.supported_networks.get(&nat_chain_id) {
            Some(network) => network,
            None => panic!("Network with chain_id {} not found", chain_id),
        };

        network.clone()
    });

    let signer = create_icp_signer().await;
    let address = signer.address();
    let wallet = EthereumWallet::from(signer);

    let rpc_service = generate_rpc_service(network.rpc_url.clone());
    let config = IcpConfig::new(rpc_service);
    let provider = ProviderBuilder::new()
        .with_recommended_fillers()
        .wallet(wallet)
        .on_icp(config);
    let nonce = provider.get_transaction_count(address).await.unwrap_or(0);
    let gas_price = provider.get_gas_price().await.unwrap_or(0);
    let gas = 1000;
    let provider_chain_id = provider.get_chain_id().await.unwrap_or(0);

    let netdet = NetworkDetails {
        chain_id: provider_chain_id,
        nonce,
        gas,
        gas_price,
    };

    Ok(netdet)
}

but I was successfully retrieve the details:

dfx canister call icp_backend getNetworkDetails '(686868)'                                            
(
  variant {
    Ok = record {
      gas = 1_000 : nat;
      chain_id = 686_868 : nat64;
      nonce = 0 : nat64;
      gas_price = 6_250_000 : nat;
    }
  },
)

I would appreciate any feedback on this, I am bit confused why this error persists.
Thanks

gm @kristofer @AdamS we would appreciate ur feedback on this