TransportError(ErrorResp(ErrorPayload { code: -32000, message: \"already known\", data: None }))

alloy is used to send evm transactions. In the local environment, the transaction can be successfully sent, but on the main ICP network, a message is displayed indicating a TransportError(ErrorResp(ErrorPayload {code: -32000, message: "already known", data: None})), it seems to be because of the error caused by ICP consensus, please help me

se crate::types::{CHAIN_INFO, CONFIG_INFO};
use alloy::primitives::PrimitiveSignature;
use alloy::{
    network::EthereumWallet,
    primitives::{Address, Bytes, U256},
    providers::{Provider, ProviderBuilder},
    signers::local::PrivateKeySigner,
    sol,
    transports::icp::{IcpConfig, RpcApi, RpcService},
};
use common_utils::decrypt_keystore::{decrypt_keystore, get_private_key};
use common_utils::get_time;
use common_utils::types::PRIVATE_KEY_STORAGE;
use hex::FromHex;

sol!(
    #[allow(missing_docs, clippy::too_many_arguments)]
    #[sol(rpc)]
    MarketCapManager,
    "abi/abi.json"
);

pub async fn send_withdraw(
    cycle: u64,
    amount: u128,
    inter_nonce: u64,
    sign_time: u64,
    signature: String,
    swap_amount: u128,
) -> Result<String, String> {
    let private_key = get_private_key();
    let signer = private_key
        .parse::<PrivateKeySigner>()
        .map_err(|e| format!("Private key conversion error, {}", e))?;
    let address = signer.address();

    let wallet = EthereumWallet::from(signer);
    let chain_info = CHAIN_INFO.with(|chain| chain.borrow().get().clone());

    let rpc_service = RpcService::Custom(RpcApi {
        url: chain_info.chain_url.clone(),
        headers: None,
    });
    let config = IcpConfig::new(rpc_service);
    let provider = ProviderBuilder::new()
        .with_gas_estimation()
        .wallet(wallet)
        .on_icp(config);

    let contract_address: Address = chain_info
        .contract_address
        .parse()
        .map_err(|e| format!("Invalid contract address: {}", e))?;
    let contract = MarketCapManager::new(contract_address, provider.clone());

    let signature_bytes = match Vec::from_hex(&signature) {
        Ok(bytes) => bytes,
        Err(e) => return Err(format!("Invalid signature hex: {}", e)),
    };

    let result = provider.get_transaction_count(address).await;
    let nonce = match result {
        Ok(n) => n,
        Err(e) => {
            return Err(format!("Description Failed to obtain the nonce, {}", e));
        }
    };

    //first withdraw -> swap
    match contract
        .withdrawFromMiningPool(
            U256::from(cycle),
            U256::from(amount),
            U256::from(inter_nonce),
            U256::from(sign_time),
            Bytes::from(signature_bytes.clone()),
            U256::from(swap_amount),
        )
        .nonce(nonce)
        .chain_id(chain_info.chain_id)
        .from(address)
        .gas_price(3000000000)
        .send()
        .await
    {
        Ok(builder) => {
            let node_hash = *builder.tx_hash();
            let tx_response = provider.get_transaction_by_hash(node_hash).await.unwrap();

            match tx_response {
                Some(tx) => return Ok(format!("The 1 task is successfully, {}", tx.hash)),
                None => return Err(format!(
                    "The 1 task failed, could not get transaction, {}",
                    node_hash
                )),
            }
        },
        Err(e) => {
            return Err(format!(
                "The 1 task failed, could not send transaction, {:?}",
                e
            ));
        }
    };
    Err("The 1 task exception error".to_string())
}
1 Like

We are facing same error on some of our canisters. Any ideas?

1 Like

@kristofer maybe you have an idea?

1 Like

@yunmin574269, first thing I notice is this:

Most likely you don’t want to use a private key signer when on ICP, but instead the IcpSigner provided by ic-alloy. That signer uses the chain key features of ICP and eliminates the need to handle a private key. Storing the private key in a canister is not safe as there is no guarantee nodes are not malicious.

Regarding the error you are receiving. It most likely means that the transaction you are doing is already in the mempool, that you are effectively sending the transaction multiple times.

ErrAlreadyKnown is returned if the transactions is already contained within the pool.

1 Like

You’re welcome! I’m glad the suggestion was helpful.

Regarding the issue, it seems that the error doesn’t occur in the local network but only on the mainnet, which is likely due to ICP consensus. Actually, the transaction is successfully sent, but how can we determine in the code whether it was successful or not?