Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
154 commits
Select commit Hold shift + click to select a range
cb7544c
docs(design): automatic deposit flow on top of the balance sweep
gregorydemay Oct 13, 2026
2decad6
docs(design): address review remarks on the automatic flow
gregorydemay Oct 13, 2026
d6f9701
docs(design): show the automatic flow diagram first and fix its rende…
gregorydemay Oct 13, 2026
3203fc0
docs(design): non-overlapping scan ticks and the exact fee share in t…
gregorydemay Oct 13, 2026
6c93f3a
docs(design): report the watch status from watch_deposit_address itself
gregorydemay Oct 13, 2026
c05a015
docs(design): clarify retries, single-round scans and drop the unused…
gregorydemay Oct 13, 2026
257b359
docs(design): define the scan schedule of a re-armed deposit address
gregorydemay Oct 13, 2026
4efb77d
docs(design): always snapshot the watchlist and name both queueing paths
gregorydemay Oct 13, 2026
f8a8a29
docs(design): say what happens after the last scan of a watched address
gregorydemay Oct 13, 2026
9cf5264
docs(design): apply current parameters at detection and compare depos…
gregorydemay Oct 13, 2026
46f47a2
docs(design): fix the watch window at 24 hours over the 23.5 hour sch…
gregorydemay Oct 13, 2026
e513dfd
docs(design): mention the automatic flow in the withdrawal delay sent…
gregorydemay Oct 13, 2026
ea186e2
docs(design): add the state diagram of an account in the automatic flow
gregorydemay Oct 13, 2026
4edf1df
docs(design): state replay of the recorded fee precisely and fix the …
gregorydemay Oct 13, 2026
1163210
docs(design): let the scan discard a read whose window closed meanwhile
gregorydemay Oct 13, 2026
0519a29
docs(design): make the re-arm subject to the watchlist capacity
gregorydemay Oct 13, 2026
c658da2
docs(design): align the diagram, the delay sentence and the minimum e…
gregorydemay Oct 14, 2026
790ad8e
docs(design): state the hard cost bound and the deposit_sol reply aft…
gregorydemay Oct 14, 2026
b52f2d7
docs(design): exclude the minter principal from automatic registrations
gregorydemay Oct 14, 2026
4c0715b
docs(design): qualify the re-arm, the concurrency bound, the detectio…
gregorydemay Oct 14, 2026
cb528cb
docs(design): show the full Watching reply and scope the capacity err…
gregorydemay Oct 14, 2026
3ab9a97
docs(design): define the next scan time as a due time and scope the e…
gregorydemay Oct 14, 2026
656b946
docs(design): drop the superseded proposals and name the watchlist me…
gregorydemay Oct 14, 2026
5bd1619
docs(design): fix the scan slots at the start of the schedule instead…
gregorydemay Oct 14, 2026
e0d1e67
docs(design): re-arm a dropped automatic deposit for a fresh window
gregorydemay Oct 15, 2026
3228518
refactor(minter): extract the queueing of a deposit and the in-flight…
gregorydemay Oct 14, 2026
171686f
docs(minter): describe InFlightDeposit by the account it reserves
gregorydemay Oct 14, 2026
5fc9c9c
docs(minter): state the in-flight precondition of queue_deposit
gregorydemay Oct 14, 2026
42a2ab6
test(minter): pin the in-flight variant at every deposit stage
gregorydemay Oct 14, 2026
ba1c2c5
feat(minter): add the automatic deposit fee parameter
gregorydemay Oct 14, 2026
8c5723d
refactor(minter): report the maximum automatic deposit fee in the val…
gregorydemay Oct 14, 2026
0a4ab21
refactor(minter): drop the comment on the private fee bound helper
gregorydemay Oct 14, 2026
a7bcf3b
feat(minter): add a bounded map of expiring entries
gregorydemay Oct 14, 2026
3441685
refactor(minter): wrap the timestamps of the timed sized map in a new…
gregorydemay Oct 14, 2026
fa252d0
refactor(minter): name the expiry index insertion of the timed sized …
gregorydemay Oct 14, 2026
570cb81
feat(minter): add the watchlist of deposit addresses with its scan sc…
gregorydemay Oct 14, 2026
f9aefd0
refactor(minter): convert the watchlist times at the boundary of the …
gregorydemay Oct 14, 2026
9822993
refactor(minter): take and report timestamps as a newtype in the watc…
gregorydemay Oct 14, 2026
5b8b389
refactor(minter): add saturating duration arithmetic to the timestamps
gregorydemay Oct 14, 2026
e0a39df
refactor(minter): schedule the scans from a burst of gaps and a stead…
gregorydemay Oct 14, 2026
0b7b3bb
refactor(minter): make an unscanned address due at the time of the read
gregorydemay Oct 14, 2026
6412cec
refactor(minter): inline the admission of a registration into register
gregorydemay Oct 14, 2026
a2e16fa
refactor(minter): keep the watchlist module private behind re-exports
gregorydemay Oct 14, 2026
bcb3ed3
refactor(minter): hold the watchlist in the deposits
gregorydemay Oct 14, 2026
de4c736
refactor(minter): index the burst gaps with a plain cast of the scan …
gregorydemay Oct 14, 2026
caeb73c
test(minter): drop the watchlist tests that only re-check the timed s…
gregorydemay Oct 14, 2026
826b50d
test(minter): inline the two watchlist builders into their only callers
gregorydemay Oct 14, 2026
134aed6
refactor(minter): fix the scan slots at the start of the schedule ins…
gregorydemay Oct 14, 2026
4824497
feat(minter): add the watch_deposit_address endpoint
gregorydemay Oct 14, 2026
ee22e15
refactor(minter): convert the registration times of watch_deposit_add…
gregorydemay Oct 14, 2026
82c4e0f
feat(minter): take a queued account off the watchlist
gregorydemay Oct 14, 2026
aa77558
test(minter): expect the next slot of the schedule after a scan at it…
gregorydemay Oct 14, 2026
2e75a32
test(minter): report the same registration on a repeated call before …
gregorydemay Oct 14, 2026
ed2a039
refactor(minter): register inside the status match and convert the re…
gregorydemay Oct 14, 2026
8d12662
docs(minter): point the latest deposit id at deposit_sol_status
gregorydemay Oct 14, 2026
110cc47
refactor(minter): reject the anonymous caller of watch_deposit_addres…
gregorydemay Oct 14, 2026
9672797
test(minter): compare the registration quotes with get_minter_info
gregorydemay Oct 14, 2026
4a33b69
test(minter): name the integration tests of the endpoint after the au…
gregorydemay Oct 14, 2026
dde75c0
test(minter): share the watch window in nanoseconds through the integ…
gregorydemay Oct 14, 2026
6230992
refactor(minter): derive Clone for the state and its collections
gregorydemay Oct 15, 2026
b010146
test(minter): cover the latest deposit id of an account
gregorydemay Oct 15, 2026
40a7ffa
test(minter): assert a rejected registration leaves the state untouched
gregorydemay Oct 15, 2026
505eeb0
test(minter): table-drive the in-flight stages reported as detected
gregorydemay Oct 15, 2026
966c23d
test(minter): assert the whole stored registration against the first one
gregorydemay Oct 15, 2026
7ad4b78
feat(minter): record the automatic part of a queued deposit and deduc…
gregorydemay Oct 14, 2026
4e9f34d
test: expect the collected automatic deposit fees after upgrading the…
gregorydemay Oct 14, 2026
155c282
refactor(minter): record only the automatic deposit fee with a queued…
gregorydemay Oct 15, 2026
bb4f815
refactor(minter): name the fee of a queued deposit without the flow t…
gregorydemay Oct 15, 2026
e267b74
refactor(minter): return the credited sweep and type the error of its…
gregorydemay Oct 15, 2026
991fe51
test(minter): cover the fee deduction through the deposit flow
gregorydemay Oct 15, 2026
f28f6f1
feat(minter): read deposit address balances in batches with getMultip…
gregorydemay Oct 14, 2026
2d4ecc9
build: pin the SOL RPC crates to the commit adding getMultipleAccounts
gregorydemay Oct 15, 2026
de73408
refactor(minter): read the deposit address balances with the typed ge…
gregorydemay Oct 15, 2026
0b1e28a
test(integration-tests): run against the SOL RPC canister built from …
gregorydemay Oct 15, 2026
08ee723
test(integration-tests): download the pinned SOL RPC canister instead…
gregorydemay Oct 15, 2026
b1899c0
test(minter): test only the minter's handling of the getMultipleAccou…
gregorydemay Oct 15, 2026
d1b1bf6
feat(minter): scan the watched deposit addresses on a timer and queue…
gregorydemay Oct 14, 2026
c9f61cd
refactor(minter): take the scan time as a newtype
gregorydemay Oct 14, 2026
c462a45
refactor(minter): read the watchlist limits through the state and rep…
gregorydemay Oct 14, 2026
1612a14
test(minter): expect the fixed scan slots in the scan timer tests
gregorydemay Oct 14, 2026
225a5ed
refactor(minter): record the automatic deposit fee alone when a scan …
gregorydemay Oct 15, 2026
5b094c4
refactor(minter): queue a scanned deposit with its fee under the new …
gregorydemay Oct 15, 2026
e4dfc6a
test(minter): read the scanned addresses from the typed getMultipleAc…
gregorydemay Oct 15, 2026
3d503ac
feat(minter): let deposit_sol yield to a deposit queued by a scan dur…
gregorydemay Oct 14, 2026
b95d04f
test(minter): queue the scanned deposit of the yield test with the fe…
gregorydemay Oct 15, 2026
d2ca808
test(minter): expect the scanned deposit of the yield test with its f…
gregorydemay Oct 15, 2026
132bfa6
docs: state that the balance scan uses the typed getMultipleAccounts …
gregorydemay Oct 15, 2026
6995961
feat(minter): re-arm the automatic deposits of a dropped sweep
gregorydemay Oct 14, 2026
c5638ae
refactor(minter): re-arm with the drop time as a newtype
gregorydemay Oct 14, 2026
1094bee
feat(minter): snapshot the watchlist before every upgrade
gregorydemay Oct 14, 2026
f1bfda8
refactor(minter): re-arm through the watchlist of the deposits
gregorydemay Oct 14, 2026
eaffc66
refactor(minter): convert the snapshot times at the event boundary
gregorydemay Oct 14, 2026
2c14619
test(minter): drop the read time from the watchlist iteration in the …
gregorydemay Oct 14, 2026
fe81cf6
refactor(minter): snapshot and restore the watchlist of the deposits
gregorydemay Oct 14, 2026
3fece15
refactor(minter): re-arm a dropped automatic deposit for a fresh window
gregorydemay Oct 15, 2026
57839e0
test: compare the restored registration up to its due time
gregorydemay Oct 14, 2026
5b7036f
refactor(minter): tell a manual deposit apart by its fee under the ne…
gregorydemay Oct 15, 2026
739b7ce
feat(minter): carry the start of the scan schedule in the watchlist s…
gregorydemay Oct 14, 2026
e2b2f21
test(minter): expect the fresh window of the re-armed account after r…
gregorydemay Oct 15, 2026
2100e68
test(minter): replay the recorded log after given events for the snap…
gregorydemay Oct 15, 2026
745e58a
feat(minter): report the automatic deposit flow in the metrics and on…
gregorydemay Oct 14, 2026
b5da4f5
refactor(minter): convert the watchlist times for the metrics and the…
gregorydemay Oct 14, 2026
69da25a
refactor(minter): list the watched addresses as of the dashboard's time
gregorydemay Oct 14, 2026
2ef7726
refactor(minter): drop the read time from the dashboard's watchlist l…
gregorydemay Oct 14, 2026
1e66a7c
test(minter): credit the dashboard's automatic deposit with the fee a…
gregorydemay Oct 15, 2026
cb9e085
refactor(minter): report the automatically queued deposits through th…
gregorydemay Oct 15, 2026
abe8d75
Merge branch 'main' into DEFI-2780-07-get-multiple-accounts
mbjorkqvist Oct 27, 2026
8de0d0a
fix(minter): reject a getMultipleAccounts response without one accoun…
mbjorkqvist Oct 27, 2026
d6c7759
test(minter): check the getMultipleAccounts request of a balance read
mbjorkqvist Oct 27, 2026
3352ecb
refactor(minter): leave the getMultipleAccounts response size estimat…
mbjorkqvist Oct 27, 2026
987ea9e
Merge branch 'DEFI-2780-07-get-multiple-accounts' into DEFI-2780-08-s…
mbjorkqvist Oct 27, 2026
31d2976
fix(minter): record a scan at the time its reads are sent
mbjorkqvist Oct 27, 2026
d7febe9
test(minter): cover the guard of a tick and the registration check of…
mbjorkqvist Oct 27, 2026
aaa0560
test(minter): return the deposit a scan queued and minted during the …
mbjorkqvist Oct 27, 2026
bee57d8
test(minter): shorten a test name that rustfmt could not fit on one line
mbjorkqvist Oct 27, 2026
039c606
docs(minter): say which deposit deposit_sol returns when a scan queue…
mbjorkqvist Oct 27, 2026
522423d
docs: leave the response size of a balance scan to the SOL RPC canister
mbjorkqvist Oct 27, 2026
2874774
Merge branch 'DEFI-2780-08-scan-timer' into DEFI-2780-09-rearm-droppe…
mbjorkqvist Oct 30, 2026
a27df5b
refactor(minter): re-arm a dropped sweep's automatic deposits outside…
mbjorkqvist Oct 30, 2026
1b04ccb
docs(minter): document the re-arm of dropped automatic deposits
mbjorkqvist Oct 30, 2026
cd92fe4
docs: say that the re-arm of a dropped sweep is not replayed from the…
mbjorkqvist Oct 30, 2026
abc7749
test(minter): compare the whole replayed state after a re-arm with th…
mbjorkqvist Oct 30, 2026
dda2fc8
docs(minter): clarify which drops go through process_dropped_sweep
mbjorkqvist Oct 30, 2026
562fe59
Merge branch 'DEFI-2780-09-rearm-dropped-sweeps' into DEFI-2780-10-wa…
mbjorkqvist Oct 30, 2026
0c7c1cc
fix(minter): restore the watchlist under the window and capacity of t…
mbjorkqvist Oct 30, 2026
24c527b
fix(minter): report the registration time of a watched address from i…
mbjorkqvist Oct 30, 2026
aa02e48
feat(minter): record no snapshot of an empty watchlist, as the ckETH …
mbjorkqvist Oct 30, 2026
633de57
test(minter): replay the snapshot recorded by pre_upgrade in the watc…
mbjorkqvist Oct 30, 2026
aac4631
docs: name skip_pre_upgrade as the way out of a pre_upgrade that keep…
mbjorkqvist Oct 30, 2026
1f35c9d
build: depend on the SOL RPC release that adds getMultipleAccounts
mbjorkqvist Nov 3, 2026
7f83b64
Merge branch 'DEFI-2780-07-get-multiple-accounts' into DEFI-2780-08-s…
mbjorkqvist Nov 3, 2026
7b13495
Merge branch 'DEFI-2780-08-scan-timer' into DEFI-2780-09-rearm-droppe…
mbjorkqvist Nov 3, 2026
14e35d0
Merge branch 'DEFI-2780-09-rearm-dropped-sweeps' into DEFI-2780-10-wa…
mbjorkqvist Nov 3, 2026
e1ed582
docs: qualify the replayed watchlist equality and what skip_pre_upgra…
mbjorkqvist Nov 4, 2026
d9b2b7d
test(minter): check that a restored expired entry is not due and can …
mbjorkqvist Nov 4, 2026
74e5f24
docs: state the measured size of a full watchlist snapshot
mbjorkqvist Nov 4, 2026
e8d61a6
Merge branch 'DEFI-2780-10-watchlist-snapshot' into DEFI-2780-12-auto…
mbjorkqvist Nov 5, 2026
0cdf3a4
fix(minter): export the collected automatic deposit fees as a counter
mbjorkqvist Nov 5, 2026
1062e18
feat(minter): export the age of the earliest due scan instead of the …
mbjorkqvist Nov 5, 2026
10f7c59
feat(minter): export the capacity of the watchlist
mbjorkqvist Nov 5, 2026
edfdb1c
test(integration): check the watchlist metrics while a scan is due an…
mbjorkqvist Nov 5, 2026
510c84b
fix(minter): list only the live registrations on the dashboard
mbjorkqvist Nov 5, 2026
f03c39f
feat(minter): show the times of the watched addresses as dates, with …
mbjorkqvist Nov 5, 2026
2def85f
Merge remote-tracking branch 'origin/main' into DEFI-2780-10-watchlis…
mbjorkqvist Nov 10, 2026
d8677c9
Merge remote-tracking branch 'origin/DEFI-2780-10-watchlist-snapshot'…
mbjorkqvist Nov 10, 2026
d35c748
refactor(minter): remove the unused registration time of the oldest w…
mbjorkqvist Nov 10, 2026
a748de8
docs: tie the rollback of an upgrade to the automatic flow being disa…
mbjorkqvist Nov 10, 2026
13700bb
Merge remote-tracking branch 'origin/DEFI-2780-10-watchlist-snapshot'…
mbjorkqvist Nov 10, 2026
60bd931
feat(minter): export the outcomes of re-arming dropped automatic depo…
mbjorkqvist Nov 10, 2026
c960107
feat(minter): export the dropped deposits by origin
mbjorkqvist Nov 10, 2026
eb61e53
Merge remote-tracking branch 'origin/main' into DEFI-2780-12-automati…
mbjorkqvist Nov 10, 2026
ed84e0f
fix(minter): span the pagination of the watched addresses across all …
mbjorkqvist Nov 10, 2026
91bdceb
test(integration): mint a detected automatic deposit minus its fee an…
mbjorkqvist Nov 10, 2026
f3e4b38
test(integration): share the sweep and the finalization of a deposit …
mbjorkqvist Nov 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ solana-transaction-status-client-types = "3.1.8"
tar = "0.4.44"
tempfile = "3.26.0"
thiserror = "2.0.18"
time = "0.3.47"
tokio = "1.49.0"

[profile.release]
Expand Down
210 changes: 142 additions & 68 deletions integration_tests/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use cksol_int_tests::{
validator::FEE_PER_SIGNATURE,
};
use cksol_types::{
DepositSolArgs, DepositSolError, DepositSolStatus, GetDepositAddressArgs, Lamport, MinterInfo,
TxFinalizedStatus, WatchDepositAddressError, WatchDepositAddressStatus, WithdrawSolArgs,
WithdrawSolError, WithdrawSolStatus,
DepositSolArgs, DepositSolError, DepositSolId, DepositSolStatus, GetDepositAddressArgs,
Lamport, MinterInfo, Signature, TxFinalizedStatus, WatchDepositAddressError,
WatchDepositAddressStatus, WithdrawSolArgs, WithdrawSolError, WithdrawSolStatus,
};
use cksol_types_internal::{
UpgradeArgs,
Expand Down Expand Up @@ -49,7 +49,14 @@ async fn deposit_and_credit_funds(setup: &Setup) {
.await
.expect("deposit_sol should queue a sweep");

// Sweep
let sweep_signature = sweep_deposit(setup, deposit_id).await;
finalize_and_credit_sweep(setup, &sweep_signature, SWEEPABLE_AMOUNT).await;
}

/// Runs the sweep timer on the deposit queued by the first batch of HTTP outcalls (IDs 0 to
/// 3, a `getBalance` of `deposit_sol` or a `getMultipleAccounts` of the scan timer) and
/// returns the signature of its sweep.
async fn sweep_deposit(setup: &Setup, deposit_id: DepositSolId) -> Signature {
setup.advance_time(SWEEP_DEPOSITS_DELAY).await;
setup
.execute_http_mocks(
Expand All @@ -58,19 +65,23 @@ async fn deposit_and_credit_funds(setup: &Setup) {
.build(),
)
.await;
let sweep_signature = assert_matches!(
assert_matches!(
setup.minter().deposit_sol_status(deposit_id).await,
DepositSolStatus::Swept { signature } => signature
);
)
}

// Finalize and credit
/// Runs the finalization timer on the sweep submitted by [`sweep_deposit`], reporting that
/// the minter's main account received `received`, which credits the sweep if it matches
/// its plan.
async fn finalize_and_credit_sweep(setup: &Setup, sweep_signature: &Signature, received: Lamport) {
setup.advance_time(FINALIZE_TRANSACTIONS_DELAY).await;
let sweep = setup.minter().signed_transaction(&sweep_signature).await;
let sweep = setup.minter().signed_transaction(sweep_signature).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(16)
.finalize_transaction(&sweep_signature, SUBMISSION_BLOCK_HEIGHT)
.get_sweep_transaction(&sweep, SWEEPABLE_AMOUNT)
.finalize_transaction(sweep_signature, SUBMISSION_BLOCK_HEIGHT)
.get_sweep_transaction(&sweep, received)
.build(),
)
.await;
Expand Down Expand Up @@ -975,19 +986,7 @@ mod deposit_sol_tests {
.await
.expect("deposit_sol should queue a sweep");

setup.advance_time(SWEEP_DEPOSITS_DELAY).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(4)
.submit_transaction(SUBMISSION_BLOCK_HEIGHT)
.build(),
)
.await;

let sweep_signature = assert_matches!(
setup.minter().deposit_sol_status(deposit_id).await,
DepositSolStatus::Swept { signature } => signature
);
let sweep_signature = sweep_deposit(&setup, deposit_id).await;
setup.minter().assert_that_events().await.satisfy(|events| {
check!(events.iter().any(|e| matches!(
e,
Expand All @@ -999,16 +998,7 @@ mod deposit_sol_tests {
)));
});

setup.advance_time(FINALIZE_TRANSACTIONS_DELAY).await;
let sweep = setup.minter().signed_transaction(&sweep_signature).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(16)
.finalize_transaction(&sweep_signature, SUBMISSION_BLOCK_HEIGHT)
.get_sweep_transaction(&sweep, SWEEPABLE_AMOUNT)
.build(),
)
.await;
finalize_and_credit_sweep(&setup, &sweep_signature, SWEEPABLE_AMOUNT).await;

setup.wait_for_deposit_minted(deposit_id).await;

Expand Down Expand Up @@ -1041,18 +1031,7 @@ mod deposit_sol_tests {
.deposit_sol(DEFAULT_CALLER_ACCOUNT)
.await
.expect("deposit_sol should queue a sweep");
setup.advance_time(SWEEP_DEPOSITS_DELAY).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(4)
.submit_transaction(SUBMISSION_BLOCK_HEIGHT)
.build(),
)
.await;
let sweep_signature = assert_matches!(
setup.minter().deposit_sol_status(deposit_id).await,
DepositSolStatus::Swept { signature } => signature
);
let sweep_signature = sweep_deposit(&setup, deposit_id).await;

setup.advance_time(FINALIZE_TRANSACTIONS_DELAY).await;
setup
Expand All @@ -1078,7 +1057,8 @@ mod deposit_sol_tests {
let setup = setup
.check_metrics()
.await
.assert_contains_metric_matching(r"dropped_deposits 1 \d+")
.assert_contains_metric_matching(r#"dropped_deposits\{origin="manual"\} 1 \d+"#)
.assert_contains_metric_matching(r#"dropped_deposits\{origin="automatic"\} 0 \d+"#)
.into();

let deposit_id_after_drop = setup
Expand Down Expand Up @@ -1109,30 +1089,10 @@ mod deposit_sol_tests {
.deposit_sol(DEFAULT_CALLER_ACCOUNT)
.await
.expect("deposit_sol should queue a sweep");
setup.advance_time(SWEEP_DEPOSITS_DELAY).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(4)
.submit_transaction(SUBMISSION_BLOCK_HEIGHT)
.build(),
)
.await;
let sweep_signature = assert_matches!(
setup.minter().deposit_sol_status(deposit_id).await,
DepositSolStatus::Swept { signature } => signature
);
let sweep_signature = sweep_deposit(&setup, deposit_id).await;

setup.advance_time(FINALIZE_TRANSACTIONS_DELAY).await;
let amount_not_in_the_plan = BALANCE_ABOVE_MINIMUM - RENT_EXEMPTION_THRESHOLD + 1;
let sweep = setup.minter().signed_transaction(&sweep_signature).await;
setup
.execute_http_mocks(
MockBuilder::with_start_id(16)
.finalize_transaction(&sweep_signature, SUBMISSION_BLOCK_HEIGHT)
.get_sweep_transaction(&sweep, amount_not_in_the_plan)
.build(),
)
.await;
finalize_and_credit_sweep(&setup, &sweep_signature, amount_not_in_the_plan).await;

assert_eq!(
setup.minter().deposit_sol_status(deposit_id).await,
Expand Down Expand Up @@ -1192,6 +1152,7 @@ mod automatic_deposit_sol_tests {

const SCAN_WATCHED_ADDRESSES_DELAY: Duration = Duration::from_mins(1);
const BALANCE_ABOVE_MINIMUM: Lamport = Setup::DEFAULT_MINIMUM_DEPOSIT_AMOUNT + 1;
const BALANCE_BELOW_MINIMUM: Lamport = Setup::DEFAULT_MINIMUM_DEPOSIT_AMOUNT - 1;

#[tokio::test]
async fn should_register_the_caller() {
Expand Down Expand Up @@ -1387,11 +1348,106 @@ mod automatic_deposit_sol_tests {
setup
.check_metrics()
.await
.assert_contains_metric_matching(r#"watched_deposit_addresses 0 \d+"#)
.assert_contains_metric_matching(r#"deposit_address_scans\{outcome="queued"\} 1 \d+"#)
.into()
.drop()
.await;
}

#[tokio::test]
async fn should_sweep_finalize_and_mint_the_detected_deposit_minus_the_automatic_fee() {
const SWEEPABLE_AMOUNT: Lamport = BALANCE_ABOVE_MINIMUM - RENT_EXEMPTION_THRESHOLD;
const FEE: Lamport = Setup::DEFAULT_AUTOMATIC_DEPOSIT_FEE;
let setup = SetupBuilder::new().build().await;
setup
.minter()
.watch_deposit_address(None)
.await
.expect("watch_deposit_address should register the caller");
setup.advance_time(SCAN_WATCHED_ADDRESSES_DELAY).await;
setup
.execute_http_mocks(
MockBuilder::new()
.get_multiple_accounts(&[BALANCE_ABOVE_MINIMUM])
.build(),
)
.await;

let sweep_signature = sweep_deposit(&setup, 0).await;
finalize_and_credit_sweep(&setup, &sweep_signature, SWEEPABLE_AMOUNT).await;
setup.wait_for_deposit_minted(0).await;

let minted_amount = SWEEPABLE_AMOUNT - FEE_PER_SIGNATURE - FEE;
assert_eq!(
setup.minter().deposit_sol_status(0).await,
DepositSolStatus::Minted {
block_index: 0,
minted_amount,
}
);
assert_eq!(
setup.ledger().balance_of(DEFAULT_CALLER_ACCOUNT).await,
minted_amount
);
assert_eq!(
setup
.minter()
.get_minter_info()
.await
.collected_automatic_deposit_fees,
Some(FEE)
);
setup
.check_metrics()
.await
.assert_contains_metric_matching(format!(r"collected_automatic_deposit_fees {FEE} \d+"))
.into()
.drop()
.await;
}

#[tokio::test]
async fn should_report_a_due_scan_in_the_metrics_until_it_runs() {
let setup = SetupBuilder::new().build().await;
setup
.minter()
.watch_deposit_address(None)
.await
.expect("watch_deposit_address should register the caller");

// The tick starts the scan, whose call stays pending until the mocks below answer it.
setup.advance_time(SCAN_WATCHED_ADDRESSES_DELAY).await;
setup.tick().await;
let setup = setup
.check_metrics()
.await
.assert_contains_metric_matching(r#"watched_deposit_addresses 1 \d+"#)
.assert_contains_metric_matching(
r#"oldest_due_deposit_address_scan_age_seconds 60 \d+"#,
)
.into();

setup
.execute_http_mocks(
MockBuilder::new()
.get_multiple_accounts(&[BALANCE_BELOW_MINIMUM])
.build(),
)
.await;

setup
.check_metrics()
.await
.assert_contains_metric_matching(r#"watched_deposit_addresses 1 \d+"#)
.assert_contains_metric_matching(r#"oldest_due_deposit_address_scan_age_seconds 0 \d+"#)
.assert_contains_metric_matching(
r#"deposit_address_scans\{outcome="below_minimum"\} 1 \d+"#,
)
.into()
.drop()
.await;
}
}

mod anonymous_caller_tests {
Expand Down Expand Up @@ -1497,6 +1553,24 @@ mod metrics_tests {
.assert_contains_metric_matching(r#"minter_balance 0 \d+"#)
.assert_contains_metric_matching(r#"oldest_in_flight_deposit_age_seconds 0 \d+"#)
.assert_contains_metric_matching(r#"oldest_pending_mint_age_seconds 0 \d+"#)
.assert_contains_metric_matching(r#"watched_deposit_addresses 0 \d+"#)
.assert_contains_metric_matching(r#"watched_deposit_address_capacity 1000 \d+"#)
.assert_contains_metric_matching(r#"oldest_due_deposit_address_scan_age_seconds 0 \d+"#)
.assert_contains_metric_matching(r#"collected_automatic_deposit_fees 0 \d+"#)
.assert_contains_metric_matching(r"# TYPE collected_automatic_deposit_fees counter")
.assert_contains_metric_matching(r#"deposit_address_scans\{outcome="queued"\} 0 \d+"#)
.assert_contains_metric_matching(
r#"deposit_address_scans\{outcome="below_minimum"\} 0 \d+"#,
)
.assert_contains_metric_matching(r#"deposit_address_scans\{outcome="failed"\} 0 \d+"#)
.assert_contains_metric_matching(
r#"automatic_deposit_rearms\{outcome="rearmed"\} 0 \d+"#,
)
.assert_contains_metric_matching(
r#"automatic_deposit_rearms\{outcome="watchlist_full"\} 0 \d+"#,
)
.assert_contains_metric_matching(r#"dropped_deposits\{origin="manual"\} 0 \d+"#)
.assert_contains_metric_matching(r#"dropped_deposits\{origin="automatic"\} 0 \d+"#)
.assert_contains_metric_matching(
r#"failed_credit_attempts\{reason="not_found"\} 0 \d+"#,
)
Expand Down
1 change: 1 addition & 0 deletions minter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ solana-system-interface = { workspace = true, features = ["bincode"] }
solana-transaction = { workspace = true, features = ["bincode", "verify"] }
solana-transaction-status-client-types = { workspace = true }
thiserror = { workspace = true }
time = { workspace = true }

[dev-dependencies]
agave-reserved-account-keys = { workspace = true }
Expand Down
Loading
Loading