fix(farming-pool): add derived value ceilings to multiplier and credit-rate setters - #98
Merged
prodbycorne merged 2 commits intoJul 26, 2026
Conversation
…t-rate setters (closes SmartDropLabs#89)
✅ Deploy Preview for sdcontracts ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Contributor
Author
|
@prodbycorne Please Review |
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.
Closes #89
Adds sanity ceilings to
set_global_multiplier,set_credit_rate, andinitialize, with the constants derived fromcompute_credits' actual overflow boundary rather than guessed — per the issue's explicit requirement ("this should be computed, not guessed").Derivation
Worst-case overflow chain:
amount_max × multiplier_max × credit_rate_max × elapsed_max ≤ i128::MAX / 16(16x headroom).compute_total_stake(amount, allocation_pct, multiplier)reduces to exactlyamount × multiplieratallocation_pct = 100— confirming the/100division does not loosen the bound at the boundary; the naive product is tight, not conservative (stated explicitly per the issue's Additional Notes).Inputs:
amount_max = 10^18— 100 billion whole tokens at Stellar's 7-decimal stroop convention; far above any realistic pool TVLelapsed_max = 63_072_000ledgers — ~10 years at 5s/ledgerSolving:
(i128::MAX / 16) / (amount_max × elapsed_max) ≈ 1.686 × 10^11budget formultiplier_max × credit_rate_max.Chosen constants:
MAX_GLOBAL_MULTIPLIER = 1_000,MAX_CREDIT_RATE = 100_000_000(product = 10^11, under budget).Verification:
10^18 × 1_000 × 10^8 × 63_072_000 ≈ 6.3 × 10^36vsi128::MAX ≈ 1.7 × 10^38→ ~27x headroom, exceeding the required 16x. Confirmed at runtime bytest_compute_credits_no_overflow_at_ceilingsat these exact ceilings.Note: the issue sketch's pair (
1_000/1_000_000_000) does not survive the derivation — its worst case fits under rawi128::MAXbut with only ~2.7x headroom, failing the 16x margin. This is whyMAX_CREDIT_RATEhere is 10x smaller than the sketch. The full derivation is also documented as a doc comment on the constants inlib.rs.Acceptance criteria
MAX_GLOBAL_MULTIPLIER/MAX_CREDIT_RATEconstants added, chosen and documented with the derivation reasoning —lib.rs:56-57, full worked derivation as doc comment immediately above (formula,amount_max,elapsed_max, headroom factor, and the note that the issue sketch's10^9fails the 16x margin)set_global_multiplier,set_credit_rate, andinitializeall reject values above the ceilings with a typedPoolError—lib.rs:558(InvalidGlobalMultiplier),lib.rs:578(InvalidCreditRate),lib.rs:240,243(initialize, both)test_set_global_multiplier_rejects_above_ceilingandtest_set_credit_rate_rejects_above_ceilingadded —test.rs, both passingcompute_creditsnever overflows within the accepted ranges —test_compute_credits_no_overflow_at_ceilings: both ceilings,allocation_pct = 100,amount_max = 10^18,elapsed_max = 63,072,000ledgers; checkpoints without panic, credit value matches the derivation exactly. Constants intended for reuse by farming-pool: no fuzz/boundary test exercises i128 overflow boundaries in compute_credits/compute_total_stake #76's boundary/fuzz tests, tying this fix to the companion issues per the criterion's wordingTests (7 new, 110 total passing, 0 failures)
test_set_global_multiplier_rejects_above_ceiling/test_set_credit_rate_rejects_above_ceiling(named in acceptance criteria)test_initialize_rejects_global_multiplier_above_ceiling/test_initialize_rejects_credit_rate_above_ceiling(initialize-path gap from Additional Notes)test_set_global_multiplier_accepts_exactly_the_ceiling/test_set_credit_rate_accepts_exactly_the_ceiling(ceilings are inclusive as documented)test_compute_credits_no_overflow_at_ceilings— pool at both ceilings,allocation_pct = 100,amount_max, ledgers advanced toelapsed_max; checkpoints without panic, credit value matches the derivationExisting test updated
test_admin_multiplier_rejects_zeropreviously matched the literal panic string of theassert!this PR converts to a typed error. Updated minimally to assert rejection viatry_set_global_multiplier— identical rejection behavior (multiplier = 0still rejected), now via the typed-error mechanism. No other existing test used values above the new ceilings.Cross-references
i128::MAX/16headroom,amount_max = 10^18,elapsed_max = 63_072_000) so both issues assert against the same deliberately-derived numbers rather than two independent guesses, per this issue's Additional Notes.compute_creditsitself; this PR closes the input-validation path that most directly triggers it.Task completed
@prodbycorne Please Review