Skip to content

feat(minter): record the origin of a queued deposit - #291

Merged
mbjorkqvist merged 119 commits into
mainfrom
DEFI-2780-09a-deposit-origin
Nov 10, 2026
Merged

mbjorkqvist merged 119 commits into
mainfrom
DEFI-2780-09a-deposit-origin

Conversation

@mbjorkqvist

@mbjorkqvist mbjorkqvist commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Whether a deposit was queued manually via deposit_sol or by the automatic flow is currently implied by its fee: no fee means manual. The re-arm of a dropped sweep (#284) and the planned metrics (#287) rely on that rule, which nothing enforces and which breaks as soon as the fee of either flow is chosen otherwise: a manual flow that charges a fee, as the manual ckSPL deposits may, or an automatic flow that charges none, as a ckSPL automatic flow might at launch, like the automatic ckERC20 deposits, which are live and charge no fee yet.

This PR records the origin of a queued deposit explicitly, in memory and in the QueuedDeposit event, next to the fee instead of being inferred from it, so that the fees of both flows can be chosen freely and the same origin can be shared with ckSPL deposits. A QueuedDeposit event recorded without an origin predates the automatic flow and is read as a manual deposit.

This must be merged before any minter version recording automatic deposits is deployed, so that no deployed event log holds an automatic deposit without its origin. Recording the origin later would require keeping the fee rule forever to read the events recorded in between.

🤖 Generated with Claude Code

gregorydemay and others added 30 commits October 14, 2026 06:41
Replace the outdated section 3.1.1, which described per-transaction
crediting with getSignaturesForAddress and quotas replenished by the
removed process_deposit endpoint, with a design that builds on the
balance sweep of deposit_sol: a free registration endpoint, a scan
timer reading watched deposit addresses in batches with
getMultipleAccounts, a schedule adapted from the ckETH minter, a fixed
fee in SOL deducted from the minted amount, and a watchlist persisted
only as a bounded snapshot at pre_upgrade. Every change to the
interface, the arguments and the event log is additive, since the
minter is deployed on mainnet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
State that a re-armed account is reported as watching again, that the
main account receives the sweepable amount minus the fee share, correct
the worst-case cost arithmetic and state its success assumption, and
distinguish a failed upgrade from a reinstallation.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ring

Move the sequence diagram of the automatic flow ahead of its
explanation, remove the semicolon that broke the mermaid parser on
GitHub, and define the deposit id where it is first used in the section.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…he constraint

State that scan ticks are guarded like the other timers so that they
never overlap, and base the automatic deposit fee constraint on the
single-signature fee share that a pending mint actually bears.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Drop the separate status query: calling the registration endpoint again
returns the stored registration or the id of the deposit in flight, as
deposit_eth does in the ckETH minter.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… signature lookup

Spell out that scan ticks never overlap, that the fee derivation is a
single-attempt estimate with re-arm costs absorbed by the minter, and
that only manual deposits are retried by the caller. Remove
getSignaturesForAddress from the RPC inventory and cost tables, since
the minter no longer uses it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A re-armed entry restarts its schedule from the first gap, bounded by
the remaining window, and a re-arm only ever follows a detection.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Record the watchlist snapshot on every upgrade so that an older one
cannot be replayed after the watchlist emptied, and let the transaction
submission section account for deposits queued by the balance scan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The window ends with the last scan, and the next registration call
starts a new window rather than reporting a registration without a
next scan.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…it ids

Follow the ckETH minter: a registration freezes neither the minimum
deposit amount nor the automatic deposit fee, both are quoted and the
values in force at detection apply, the fee being recorded with the
queued deposit. deposit_sol compares the latest deposit id of the
account before and after its balance read, and a registration is
allowed while a deposit_sol call is in progress.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…edule

Measure gaps from the previous scan as before, but end the window at a
fixed 24 hours after registration, as the ckETH minter does, so that
the accumulated tick and call delays have slack and clients and the
re-arm share one unambiguous boundary.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ence

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…owner rationale

The fee recorded at detection matters for deposits an upgrade finds not
yet credited; a credited deposit has its mint amount in CreditedSweep.
The minter's principal is excluded as a deposit owner because its
default account is the ledger's minting account, not because of an
address collision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…xample with the automatic flow

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…er a concurrent scan

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n delay and the fee worst case

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…or to new registrations

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…vents the automatic flow adds

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tric to alert on

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… of chaining gaps

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The re-arm registers the account again instead of restoring the original window, so the
queued deposit carries only the automatic deposit fee.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… classification from deposit_sol

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
mbjorkqvist and others added 13 commits October 27, 2026 10:37
Follows the minter, which no longer passes a response size estimate with
getMultipleAccounts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-sweeps

Keeps the version of Watchlist::register of the base branch, without the
Expired arm that the base removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the state transition

The re-arm ran in the state transition of FailedTransaction and
ExpiredTransaction, so post_upgrade replayed it against a watchlist without the
transient registrations it was made against, and replayed its log line at
every upgrade. The live caller now records the drop and then re-arms through
process_dropped_sweep, which logs the outcome once, with a refused re-arm at
Info. The state transition only drops the deposits again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The docs of the swept deposits and of the Dropped status still said that only
deposit_sol follows a dropped sweep. The re-arm no longer claims to keep an
existing registration, since the account of a deposit in flight is never
watched, and the test of that impossible case goes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… event log

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e live one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace the pins to the dfinity/sol-rpc-canister#394 commit with the released
sol_rpc_client 7.0.0 and sol_rpc_types 4.0.0, and run the integration tests
against the SOL RPC canister v1.4.0 release wasm.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing it from its fee

Whether a deposit was queued by `deposit_sol` or by the automatic flow was
only implied by its fee being absent or present, which the re-arm of a
dropped sweep relied on. A shared `DepositOrigin` now records the flow on
its own, in memory and in the `QueuedDeposit` event, so that a fee can be
charged in either flow. Events recorded without an origin predate the
automatic flow and decode as manual deposits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nual

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Comment thread minter/src/state/deposits/mod.rs Resolved
… deposit fees

The collected automatic deposit fees summed the fees of every deposit of a
credited sweep, which relied on only automatic deposits being charged a fee.
Count the fees by the recorded origin instead, so that a fee charged for
manual deposits is not reported as an automatic deposit fee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from DEFI-2780-09-rearm-dropped-sweeps to main November 9, 2026 16:03
mbjorkqvist and others added 2 commits November 9, 2026 16:18
…-origin

# Conflicts:
#	integration_tests/tests/tests.rs
#	minter/src/deposit/automatic/scan/mod.rs
#	minter/src/deposit/automatic/scan/tests.rs
#	minter/src/deposit/sweep/tests.rs
#	minter/src/state/tests.rs
#	minter/src/state/watchlist/mod.rs
#	minter/src/state/watchlist/tests.rs
…rownout

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@mbjorkqvist
mbjorkqvist marked this pull request as ready for review November 10, 2026 07:37
@mbjorkqvist
mbjorkqvist requested a review from a team as a code owner November 10, 2026 07:37
Copilot AI balanced review requested due to automatic review settings November 10, 2026 07:37
@zeropath-ai

zeropath-ai Bot commented Nov 10, 2026

Copy link
Copy Markdown

✅ No security or compliance issues detected. Reviewed everything up to a2432bb.

Security Overview
Detected Code Changes
Change Type Relevant files
Enhancement ► docs/src/development/design.md
    Change description updated to reflect automatic deposit origin being recorded; additional fields origin (DepositOrigin) added to event log and QueuedDeposit; new DepositOrigin type introduced in various modules
Enhancement ► integration_tests/tests/snapshot.rs
    Update to handle origin-aware events by recording origin via with_recorded_origin function
Enhancement ► integration_tests/tests/tests.rs
    Update expected event structures to include DepositOrigin where applicable; default manual origin added in several test fixtures
Enhancement ► libs/types-internal/src/event.rs
    Add origin: Option to EventType; document behavior for pre-automatic-flow events
Enhancement ► minter/cksol_minter.did
    Introduce DepositOrigin type (Manual, Automatic) for events; extend EventType with origin field
Enhancement ► minter/src/canbench.rs
    Update event construction to populate origin: DepositOrigin::Manual for queued deposits; propagate origin to QueuedDeposit events
Enhancement ► minter/src/deposit/automatic/scan/mod.rs
    Set origin: DepositOrigin::Automatic during balance read handling
Enhancement ► minter/src/deposit/automatic/scan/tests.rs
    Include DepositOrigin in test fixtures and assertions for automatic deposits
Enhancement ► minter/src/deposit/sweep/mod.rs
    Populate origin: DepositOrigin::Manual in deposits and queue handling
Enhancement ► minter/src/deposit/sweep/tests.rs
    Update tests to account for DepositOrigin where deposits originate (Manual/Automatic)
Enhancement ► minter/src/state/audit.rs
    Include origin in applied event transitions forwarded to state machine
Enhancement ► minter/src/state/deposits/mod.rs
    QueuedDeposit now includes origin: DepositOrigin and accompanying adjustments in related methods
Enhancement ► minter/src/state/deposits/sweeps/mod.rs
    Add methods referencing DepositOrigin (automatic deposit fees) and adjust deposit_fees/automatic_deposit_fees calculations
Enhancement ► minter/src/state/deposits/sweeps/tests.rs
    Tests extended to cover automatic deposit origin counting and related behaviors
Enhancement ► minter/src/state/event.rs
    EventType updated to include origin: DepositOrigin; adjust usages accordingly
Enhancement ► minter/src/state/event/candid/mod.rs
    Candid mappings updated to support DepositOrigin for QueuedDeposit origin field
Enhancement ► minter/src/state/event/candid/tests.rs
Tests added for decoding/encoding behavior with missing origin defaulting to Manual; manual origin handling in candid tests
Enhancement ► minter/src/state/event/cbor/mod.rs
    Add deposit_origin module to decode missing origin as DepositOrigin::Manual, and ensure nil/encoding behavior aligns with new origin model
Enhancement ► minter/src/state/event/cbor/tests.rs
    Tests for deposit_origin decoding/encoding and legacy event compatibility with Manual origin
Enhancement ► minter/src/state/mod.rs
    Expose DepositOrigin in state re-exports; adjust state credit logic to use automatic deposit fees from deposits when appropriate
Enhancement ► minter/src/state/watchlist/mod.rs
    Logic updated to treat Manual deposits differently when rearming; Automatic deposits bypass certain manual-deposit rules
Enhancement ► minter/src/state/watchlist/tests.rs
    New test ensuring manual deposits are treated distinctly and automatic deposits can rearm without a fee
Enhancement ► minter/src/test_fixtures/flow/deposit.rs
    Decorator: queue_deposit now publicly accessible for tests, origin handling included
Enhancement ► minter/src/test_fixtures/mod.rs
    Populate test fixtures with DepositOrigin where appropriate (Manual/Automatic) and propagate origin through devnet/devnet_sweep fixtures
Enhancement ► minter/src/test_fixtures/arb.rs (implicitly in diff sections)
    Arbitrary test data updated to include DepositOrigin and origin-aware QueuedDeposit construction
Enhancement ► minter/src/deposit/automatic/scan/mod.rs (see above) and related tests updated to include DepositOrigin where appropriate

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.

🔵 Needs a closer look

Deployment history needs confirmation because automatic deposits previously recorded without an origin would now replay incorrectly as Manual.

No open issues 🎉

What changed in this PR

Records deposit origin explicitly so the minter can distinguish manual and automatic deposits independently of fees. This supports dropped-sweep re-arming and automatic-fee reporting while allowing future fee policies to differ.

Changes:

  • Carries DepositOrigin through deposit creation, events, replay, and the debug Candid interface.
  • Uses origin for watchlist re-arming and automatic-fee totals.
  • Adds compatibility and behavior tests, treating legacy events without an origin as manual; deployment must precede any version recording automatic deposits.
File Description
minter/​src/​test_fixtures/​mod.rs Adds origins to fixtures and generated test data.
minter/​src/​test_fixtures/​flow/​deposit.rs Exposes queueing of custom test deposits.
minter/​src/​state/​watchlist/​tests.rs Tests re-arming independently of fees.
minter/​src/​state/​watchlist/​mod.rs Checks origin when re-arming deposits.
minter/​src/​state/​tests.rs Tests origin-based fee accounting and replay.
minter/​src/​state/​mod.rs Counts only automatic deposits’ fees toward automatic-fee totals.
minter/​src/​state/​event/​cbor/​tests.rs Tests legacy decoding and explicit origin encoding.
minter/​src/​state/​event/​cbor/​mod.rs Defaults missing legacy origins to manual.
minter/​src/​state/​event/​candid/​tests.rs Tests conversion of events without origins.
minter/​src/​state/​event/​candid/​mod.rs Converts origins between internal and Candid events.
minter/​src/​state/​event.rs Adds origin to persisted queued-deposit events.
minter/​src/​state/​deposits/​sweeps/​tests.rs Tests fee totals for mixed deposit origins.
minter/​src/​state/​deposits/​sweeps/​mod.rs Adds automatic-origin fee aggregation.
minter/​src/​state/​deposits/​mod.rs Defines DepositOrigin and stores it on queued deposits.
minter/​src/​state/​audit.rs Restores origin during event replay.
minter/​src/​deposit/​sweep/​tests.rs Checks origins in manual-flow and scan-race events.
minter/​src/​deposit/​sweep/​mod.rs Marks manual deposits and records their origins.
minter/​src/​deposit/​automatic/​scan/​tests.rs Checks automatic origin in scan-generated events.
minter/​src/​deposit/​automatic/​scan/​mod.rs Marks scan-generated deposits as automatic.
minter/​src/​canbench.rs Adds manual origins to benchmark deposits.
minter/​cksol_minter.did Exposes optional origin in debug events.
libs/​types-internal/​src/​event.rs Adds Candid origin types and event fields.
integration_tests/​tests/​tests.rs Updates expected manual and automatic events.
integration_tests/​tests/​snapshot.rs Accounts for legacy origins reported as manual after upgrade.
docs/​src/​development/​design.md Documents explicit origins and compatibility behavior.

Review effort: Balanced

@mbjorkqvist
mbjorkqvist added this pull request to the merge queue Nov 10, 2026
Merged via the queue into main with commit dde9f09 Nov 10, 2026
26 checks passed
@mbjorkqvist
mbjorkqvist deleted the DEFI-2780-09a-deposit-origin branch November 10, 2026 08:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants