Skip to content

feat(cketh): drive the sweeper transaction pipeline from its own timer task - #11237

Draft
gregorydemay wants to merge 2 commits into
greg/sweeper-eip7702from
greg/DEFI-2926-sweeper-send-task
Draft

feat(cketh): drive the sweeper transaction pipeline from its own timer task#11237
gregorydemay wants to merge 2 commits into
greg/sweeper-eip7702from
greg/DEFI-2926-sweeper-send-task

Conversation

@gregorydemay

@gregorydemay gregorydemay commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

DEFI-2926, stacked on #11144 which instantiates the pipeline this drives.

Why

#11144 gives the sweeper address its own transaction pipeline, but nothing moves requests through it. This is the task that does.

What

sweep::process_sweeper_transactions runs the sweeper pipeline through the same create → sign → send → resubmit → finalize cycle as withdrawals, signing with the sweeper derivation path [3] and reading the sweeper address' own transaction count. It takes its own TaskType::SweeperSend timer guard, so a slow sweep round cannot block the withdrawal task or be blocked by it — which is the point of the separate nonce sequence in the first place.

The first commit is a prerequisite worth reading on its own: four RPC helpers in withdraw were implicitly about the main address, calling minter_address() themselves or keying receipts by LedgerBurnIndex. Reading a transaction count, broadcasting signed transactions and fetching finalized receipts are none of them specific to a sender or to what a request is keyed by, so they take the sender as an argument and the receipt fetch becomes generic over the pipeline id. Behaviour is unchanged: the receipt loop moves into fetch_finalized_receipts, which returns None where the loop used to return early.

SweepRequest::Error being Infallible pays off here: creating a sweep transaction is destructured with let Ok(transaction) = ..., with no arm for a failure the type system rules out. That is what #11178 made possible by turning the creation error into an associated type.

Scope

Nothing enqueues a SweepRequest yet, so the task early-returns on an empty pipeline and this is inert in production. Still to come: the sweep-queue→SweepRequest source, EIP-7702 (0x04) first-time delegation, and gating on prepaid sweep gas. EIP-1559 (0x02) only.

Note for reviewers

The sweep driver and the withdrawal driver are now near-identical five-stage loops over different request types, down to a private trio of batch-size constants that are 5 in both files. Deduplicating them is the obvious follow-up now that the pipeline is generic, but it would bury this change, so it is deliberately left for its own PR.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an independent timer-driven transaction pipeline for the ckETH sweeper address.

Changes:

  • Adds the sweeper create/sign/send/resubmit/finalize driver.
  • Generalizes withdrawal RPC helpers by sender and pipeline ID.
  • Registers a dedicated timer guard and periodic task.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
withdraw.rs Generalizes shared RPC helpers.
sweep.rs Implements the sweeper pipeline driver.
state/transactions/tests.rs Reuses the sweeper gas-limit constant.
state.rs Adds sweeper address access and task type.
main.rs Registers the sweeper timer.
lib.rs Exports the sweep module.
deposit_address/mod.rs Exposes the sweeper derivation path internally.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

const SWEEP_TRANSACTIONS_TO_SIGN_BATCH_SIZE: usize = 5;
const SWEEP_TRANSACTIONS_TO_SEND_BATCH_SIZE: usize = 5;

pub async fn process_sweeper_transactions() {
Comment on lines +71 to +80
let gas_fee_estimate = match lazy_refresh_gas_fee_estimate().await {
Some(gas_fee_estimate) => gas_fee_estimate,
None => {
log!(
INFO,
"[process_sweeper_transactions]: failed retrieving gas fee estimate",
);
return;
}
};
@gregorydemay
gregorydemay force-pushed the greg/sweeper-send-lane branch from de2eb00 to 6fbf7c1 Compare August 21, 2026 09:13
@gregorydemay
gregorydemay force-pushed the greg/DEFI-2926-sweeper-send-task branch 2 times, most recently from a8b30dc to 02f02c0 Compare August 21, 2026 10:57
@gregorydemay
gregorydemay changed the base branch from greg/sweeper-send-lane to greg/sweeper-eip7702 August 21, 2026 10:57
@gregorydemay
gregorydemay force-pushed the greg/DEFI-2926-sweeper-send-task branch from 02f02c0 to 73c453c Compare August 21, 2026 11:12
gregorydemay and others added 2 commits August 21, 2026 14:44
Four helpers in `withdraw` were implicitly about the main address: they called
`minter_address()` themselves, or keyed receipts by `LedgerBurnIndex`. Reading a
transaction count, broadcasting signed transactions and fetching finalized
receipts are none of them specific to a sender or to what a request is keyed by.

Take the sender as an argument and make the receipt fetch generic over the
pipeline id, so a second pipeline sending from a different address can reuse
them. `finalize_transactions_batch` keeps its behaviour: the receipt loop moves
into `fetch_finalized_receipts`, which returns `None` where the loop used to
return early.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sweeper transaction pipeline exists but nothing moves requests through it.

Add `sweep::process_sweeper_transactions`, running the pipeline through the same
create → sign → send → resubmit → finalize cycle as withdrawals, but signing
with the sweeper derivation path `[3]` and reading the sweeper address' own
transaction count. It takes its own `TaskType::SweeperSend` timer guard, so a
slow sweep round cannot block the withdrawal task and vice versa.

`SweepRequest::Error` being `Infallible` pays off at the call site: creating a
sweep transaction is destructured with `let Ok(transaction) = ...`, with no arm
for a failure the type system rules out.

Nothing enqueues a sweep yet — the sweep-queue source, EIP-7702 (`0x04`)
first-time delegation and gating on prepaid sweep gas are follow-ups — so the
task early-returns on an empty pipeline.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gregorydemay
gregorydemay force-pushed the greg/DEFI-2926-sweeper-send-task branch from 73c453c to 68f9246 Compare August 21, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants