alloy-rs / alloy-rs/alloy

[Bug] Nonce Conflict with Cloned Provider in CachedNonceManager

Offen
#3,770 1 Kommentar 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen
bug
Vorherrschende Sprache
Rust
Sterne
1.3k
Forks
668
Ø Merge
2 T. 2 Std.
Gemergte PRs (30 T.)
29

Beschreibung

### Component

rpc, other, contract

### What version of Alloy are you on?

1.7.3

### Operating System

macOS (Apple Silicon)

### Describe the bug

We observed this error in our production log:

```
error=Alloy contract, error: server returned an error response: error code 3: nonce too low: next nonce 87, tx nonce 86
```

**Cause:**
We were using `CachedNonceManager` and cloning the provider in two places, which caused nonce conflicts. Switching to `Arc` before cloning mitigated the issue.

**Observation:**
I could not reproduce the issue in my test. Even when sending multiple transactions sequentially with the same provider clone, the nonces were correctly handled:

* Test setup uses `ProviderType::from_signer_ws` and clones the provider twice.
* Approve transactions (`tx_1` → `tx_4`) all succeeded.
* Expected `tx_3` to fail due to a nonce conflict, but it passed.

**Test code snippet:**

```rust
let provider = ProviderType::from_signer_ws(&signer, ws.clone()).await?;
let provider_1 = provider.clone();
let provider_2 = provider.clone();

let approve_one = Address::from_str("0x0000000000000000000000000000000000000001")?;
let approve_two = Address::from_str("0x0000000000000000000000000000000000000002")?;
let contract_one = IERC20Instance::new(WBTC_OFT_ADDRESS, &provider_1);
let contract_two = IERC20Instance::new(BASE_USDC_ADDRESS, &provider_2);

let tx_1 = contract_one.approve(addr1, U256::ONE).send().await?.get_receipt().await?; # `CachedNonceManager->get_next_nonce` should internally call `get_transaction_count`
let tx_2 = contract_two.approve(addr2, U256::ONE).send().await?.get_receipt().await?; # `CachedNonceManager->get_next_nonce` should internally call `get_transaction_count`
let tx_3 = contract_one.approve(addr1, U256::ONE).send().await?.get_receipt().await?; # `CachedNonceManager->get_next_nonce` should now use the nonce of tx_2 and should have failed.
let tx_4 = contract_two.approve(addr2, U256::ONE).send().await?.get_receipt().await?;
```

**Question:**
Why doesn’t the nonce conflict reproduce in testing even with cloned providers?

**Note:**
- We’re using a single dedicated account for this code, so no nonce conflicts can occur from transactions sent elsewhere.

Beitragsleitfaden

Beitragsleitfaden öffnen

Bewertung

Dieses Issue wurde noch nicht bewertet.

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.