Skip to content

Add a vesting drip that batches due claims - #186

Merged
illuzen merged 3 commits into
mainfrom
vesting/drip
Oct 19, 2026
Merged

illuzen merged 3 commits into
mainfrom
vesting/drip

Conversation

@illuzen

@illuzen illuzen commented Oct 19, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add quantus vesting drip, which watches vesting schedules and claims each one as soon as the next non-final payout (25 QTC) has vested.
  • Due claims go out as one utility.batch_all. A single due schedule stays a plain claim. Batches split only when they exceed Utility::batched_calls_limit.
  • A cliff or a late start still pays the whole aligned amount in one claim; the drip warns when a payout is larger than one step.

Test plan

  • cargo test --lib cli::vesting::tests --locked
  • quantus vesting drip --from <wallet> against a chain with several schedules, and confirm multiple due claims land in one extrinsic
  • Confirm a single due schedule is submitted as vesting.claim, not a one-call batch
  • Ctrl-C stops the drip while it is waiting for the next step

Made with Cursor

A running drip submits every due schedule together so payouts stay at the minimum step without a separate extrinsic per claim.

Co-authored-by: Cursor <cursoragent@cursor.com>
@illuzen illuzen added the bot-review Request automated review from review-bot label Oct 19, 2026

@n13 n13 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.

Reviewer model: GPT-6.1 Sol

REQUEST_CHANGES — the drip must respect the runtime's minimum interval between claims.

[P1] Keep interval-locked schedules out of the due batch — src/cli/vesting.rs:691–701

VestingTerms drops schedule.last_claim_at, and both claimable and next_claim_ms consider only accrued amounts. The checked-in runtime metadata sets Vesting::MinClaimInterval to 86,400,000 ms (24 hours), and the pallet rejects earlier claims with ClaimTooSoon, including final payouts. For example, a 100 QTC schedule over four hours can pay its first 25 QTC at hour 1; this loop selects its next 25 QTC at hour 2, although the next permitted claim is hour 25. The transaction fails, batch_all rolls back every other claim in that batch, and the propagated error terminates the drip. Restarting during the cooldown selects the same failing schedule again.

Read MinClaimInterval, preserve last_claim_at, and use the later of the vesting boundary and last_claim_at + MinClaimInterval for both due selection and the next wake-up. Add coverage for an accrued-but-interval-locked schedule, including a final remainder and a batch containing another eligible schedule.

Reviewed head: d5748f342c6460514ed7b6a0bdc040b2389e997e.

Validation:

  • SKIP_CIRCUIT_BUILD=1 cargo test --lib cli::vesting::tests --locked: 9 passed.
  • cargo +nightly-2026-08-31 fmt --all -- --check and git diff --check: passed.
  • Standalone probe using the PR's unchanged calculation functions reproduced the hour-2 selection during the 24-hour cooldown.
  • GitHub format, dependency cooldown, and security audit checks passed; build/test, Clippy/docs, and examples were still pending. No live-chain transaction was submitted.

@n13 n13 removed the bot-review Request automated review from review-bot label Oct 19, 2026
The chain no longer exposes MinClaimInterval or ClaimTooSoon, and the later vesting error indices shifted down by one.

Co-authored-by: Cursor <cursoragent@cursor.com>
@illuzen illuzen added the bot-review Request automated review from review-bot label Oct 19, 2026

@n13 n13 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.

Reviewer model: GPT-6.1 Sol

REQUEST_CHANGES — preserve Ctrl-C requests while a claim is in flight.

[P2] Keep the shutdown listener alive between idle waits — src/cli/vesting.rs:717–723

After the first idle sleep wins this select!, its ctrl_c() future is dropped, but Tokio's process-wide SIGINT handler remains installed. A Ctrl-C received during the subsequent RPC reads or submit_due_claims(...).await is therefore swallowed: there is no live listener, and the next idle iteration creates a new listener that only receives future signals. The drip continues after the transaction completes and can submit further fee-paying claims despite the stop request. Inclusion/finalization waits make this a substantial window, and repeated Ctrl-C during that window does not terminate the process either.

Keep a persistent shutdown listener/flag for the lifetime of the drip, including both the single-claim and batch paths. Latch a stop request while a transaction is in flight and exit before submitting another claim/batch. Add a regression that sends SIGINT after the initial idle wait, during a simulated submission, and verifies that the loop stops afterward.

The previous cooldown finding is resolved: chain PR #712 merged the removal of MinClaimInterval and ClaimTooSoon.

Reviewed head: 4f4640eb051c706f37c08cf0b7c92a6660f6b141 (base a74fd735fdb7cae2381b66dbaaa513630c0d58d9).

Validation:

  • SKIP_CIRCUIT_BUILD=1 cargo test --lib cli::vesting::tests --locked: 9 passed.
  • cargo +nightly-2026-08-31 fmt --all -- --check and git diff --check: passed.
  • Standalone Tokio probe mirroring idle/select → simulated submission → idle/select reproduced the lost SIGINT; it continued into the next wait.
  • GitHub format, dependency cooldown, security audit, and Clippy/docs checks passed; build/test and examples remained pending. No live-chain transaction was submitted.

@n13 n13 removed the bot-review Request automated review from review-bot label Oct 19, 2026
A signal during submission is latched and the drip exits before the next claim or batch chunk.

Co-authored-by: Cursor <cursoragent@cursor.com>
@illuzen illuzen added the bot-review Request automated review from review-bot label Oct 19, 2026

@n13 n13 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.

Reviewer model: GPT-6.1 Sol

APPROVE — no blocking findings at 96495aea6ca70517a08c67e888b4f3d94903ac78 (base a74fd735fdb7cae2381b66dbaaa513630c0d58d9).

Reviewed the complete diff, payout/cliff/remainder calculations, beneficiary filtering, batching limits, and shared transaction submission paths. The previous findings are resolved: chain PR #712 removed the claim cooldown, and the persistent Ctrl-C listener now latches shutdown during RPC/submission and prevents subsequent claims or batch chunks.

Validation:

  • SKIP_CIRCUIT_BUILD=1 cargo test --lib cli::vesting::tests --locked: 11 passed, including actual SIGINT during submission and stopping between batch chunks.
  • cargo +nightly-2026-08-31 fmt --all -- --check and git diff --check: passed.
  • GitHub format, dependency cooldown, security audit, and Clippy/docs checks passed. Both build/test jobs and examples remain in progress.

No live-chain transaction was submitted; end-to-end payout behavior remains unverified on a deployed node.

@n13 n13 removed the bot-review Request automated review from review-bot label Oct 19, 2026
@illuzen
illuzen merged commit 0dc3f0c into main Oct 19, 2026
9 checks passed
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.

2 participants