feat(executor): EIP-1559 gas oracle + concurrent nonce manager#43
Merged
feat(executor): EIP-1559 gas oracle + concurrent nonce manager#43
Conversation
New `gas.rs`. Three responsibilities: - `GasOracle::fetch_params(provider)` — pulls `baseFeePerGas` from the latest block header, computes `maxFeePerGas = base × 1.25`, attaches the per-chain priority fee. Returns `Ok(None)` (skip the tx) when the resulting `max_fee` exceeds `bot.max_gas_gwei`. - `GasOracle::estimate_gas_units(provider, tx)` — `eth_estimateGas` wrapper. - `gas_cost_usd_cents(units, max_fee, native_price, native_decimals)` — converts wei cost into integer USD cents using a Chainlink reading. Will replace `PLACEHOLDER_GAS_USD_CENTS` in CLI once nonce + submit land alongside. Config: - `ChainConfig.priority_fee_gwei` (`#[serde(default = 1)]`) — added per-chain because BSC's validator-friendly tip differs from L2s - `config/default.toml` BSC entry now sets `priority_fee_gwei = 1` Math: - 25 % cushion over base fee matches PRD §5c - USD-cents conversion uses U256 throughout to avoid u128 overflow on large-fee × large-price products; saturates to `u64::MAX` if asked for an absurd cost rather than panicking - Three unit tests cover the BNB-priced happy path, zero-units early-out, and overflow saturation
`NonceManager` for one (chain × signer) pair. - `init(provider, signer)` async — pulls `eth_getTransactionCount` on startup - `next()` — atomic `fetch_add` claim, sequential under any concurrency - `current()` — peek without consuming (logging) - `resync(provider)` — re-fetch on-chain nonce after a failed tx or long idle, drops gaps left by stuck/replaced transactions - Optimistic by default: bumps locally on every issue so multiple in-flight txs hold contiguous nonces without an extra round-trip Two unit tests: - sequential issue from a single thread - 32 threads × 100 issues each, asserts no duplicates and final counter matches start + total
This was referenced Apr 22, 2026
Closed
Closed
Closed
Closed
Closed
Closed
Closed
obchain
added a commit
that referenced
this pull request
Apr 23, 2026
- Redact PrivateKeySigner in TxBuilder Debug to avoid leaking the k256 scalar into logs (#158). - Pull nonce from the pending block tag so queued tx don't collide with newly built ones; TODO flags NonceManager in PR #43 (#159). - Reject build_tx calls where priority tip exceeds max fee per gas before any RPC round-trip (#160). - Require an explicit gas_limit on Simulator::simulate so the simulation burns the same gas ceiling as the real broadcast (#161). - Add Simulator::from_builder so the simulated sender is always the builder's hot wallet (onlyOwner alignment), plus debug_assert on non-zero sender (#162). - Replace anyhow on the public lib surface with BuilderError and SimulationError thiserror enums (#163). - Pin PROTOCOL_VENUS to the Solidity constant with an ABI-level unit test and line-number comment referencing CharonLiquidator.sol:49 (#164). - Adopt workspace lints (unsafe_code=forbid, arithmetic_side_effects, cast_possible_truncation, unwrap_used) and opt charon-executor in via [lints] workspace = true (#165). - Decode revert payload in Simulator::simulate into a 4-byte selector plus full hex body so logs are greppable cross-protocol (#166). - Add #[ignore]d fork tests covering the happy path and the onlyOwner adversarial path to pin the sim gate's safety invariant (#167). Closes #158 Closes #159 Closes #160 Closes #161 Closes #162 Closes #163 Closes #164 Closes #165 Closes #166 Closes #167
Gas oracle (gas.rs): - fix ceiling unit mismatch: compare max_fee_per_gas against max_gas_gwei * 1e9 (wei), not gwei (#179). - fall back to eth_gasPrice when header lacks baseFeePerGas so a flaky RPC or pre-EIP-1559 chain does not block pricing (#180). - switch max_fee formula to canonical EIP-1559 `2 * base + priority` so the tx survives a single-block base-fee doubling (#181). - apply a 20% safety buffer on eth_estimateGas output (#182). - fix gas_cost_usd_cents to include the Chainlink 8-decimal factor explicitly and expose CHAINLINK_DECIMALS (#183). - convert priority_fee_gwei -> wei via checked_mul (#186). - introduce GasError (thiserror) and replace Ok(None) skip-signal with a typed GasDecision::{Proceed, SkipCeilingExceeded} enum (#187, #188). - add per-block cache keyed on the caller-supplied block number to avoid duplicate fee-market lookups inside one tick (#189). - add ignored live-BSC integration test for fetch_params (#191). Nonce manager (nonce.rs): - query eth_getTransactionCount on the `pending` block tag in both init and resync to count in-flight txs (#184). - add high-water-mark guard so resync never rolls next backwards past an already-issued nonce (#185). - introduce NonceError (thiserror) (#187). Config: - mark ChainConfig `#[non_exhaustive]` so new per-chain knobs stay non-breaking for downstream crates (#190). Deps: - add thiserror to the workspace and to charon-executor.
# Conflicts: # Cargo.lock # Cargo.toml # crates/charon-core/src/config.rs # crates/charon-executor/src/lib.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two bundled additions to
charon-executor. Both sit in the pre-broadcast path and slot in between the tx builder and the (future) submit layer.gas.rs—GasOracle:fetch_params(provider)— pullsbaseFeePerGasfrom the latest block header, computesmaxFeePerGas = base × 1.25, attaches the per-chain priority fee. ReturnsOk(None)(skip the tx) when the resultingmax_feeexceedsbot.max_gas_gweiestimate_gas_units(provider, tx)—eth_estimateGaswrappergas_cost_usd_cents(units, max_fee, native_price, native_decimals)— converts wei cost into integer USD cents using a Chainlink reading; U256 arithmetic throughout with saturating fallbacksChainConfig.priority_fee_gweifield (#[serde(default = 1)]); BSC entry set to 1nonce.rs—NonceManager:init(provider, signer)async — pullseth_getTransactionCounton startupnext()— atomicfetch_addclaim, sequential under any concurrencycurrent()— peek without consuming (logging)resync(provider)— re-fetch on-chain nonce after a failed tx or long idle; drops gaps left by stuck/replaced transactionsDepends on
feat/17-cli-e2e-pipeline.