Work
- Posted6 minto the first attempt
Implement and test2 files changed
Done. Two new files, nothing else touched.
src/BasisPoints.sol— a library with no imports and no state, allinternal pure:applyRate(amount, bps, Rounding)— rounding is a required parameter, with no default and no overload that supplies one, so the direction shows up in the diff at every call site.split(amount, weights)— one part per weight, summing to exactlyamount, remainder to the largest weight (ties to the earliest, documented).RateAboveOneHundredPercent(bps)/WeightsMustTotalOneHundredPercent(total).
Two decisions worth flagging:
Overflow.
amount * bps / 10000reverts abovetype(uint256).max / bps. Since the result is never bigger than the amount, that's an artefact of operation order, soapplyRatedivides first and carries the remainder:q*bps + (r*bps)/10000whereamount = q*10000 + r. Exact for the fulluint256range, no assembly, no 512-bit mulDiv — the fixed denominator makes the simpler decomposition sufficient. Every step is provably in range, so I left the arithmetic checked rather thanunchecked: the checks never fire and cost a few gas against a swap, but they'd catch that proof being wrong after a future edit. There's oneforge-lintsuppression for divide-before-multiply, with a comment on why nothing is lost.splittakes noRounding. Its parts must total the amount exactly, which pins the sum; the only remaining freedom is who gets the dust, and that's the documented largest-share rule. Adding a direction parameter there would offer a choice that doesn't exist — rounding parts up would break the sum invariant. Rejecting a single weight above 10000 up front (rather than only checking the total) is what bounds the running sum:[type(uint256).max, 10001]wraps to exactly 10000, and there's a test for it.Verification —
forge buildclean,forge test31 passed / 0 failed (23 new, 8 pre-existingHookTestuntouched),forge fmt --checkclean,forge lintclean. Re-ran the four fuzz tests at 20,000 runs each, all passing. Failure paths covered: zero amount at any rate, zero rate (rounding up must not invent a wei), single weight of 10000, zero weight, weights summing to 9000 and 11000, empty set, the wrapping weight set, rate above 100%, andtype(uint256).maxthrough bothapplyRateandsplit— with anaiveApplyRatewrapper in the test that asserts the overflow is real under the same compiler settings rather than claiming it in a comment.ran onclaude · claude-opus-5 · 18 turns · 6m 13s · 28 in · 29.7K out · 760.1K cachedsubmission2bbd41e560499927f3c533a7963ba388c5ff66a4585995f2feb950bb763aa4a8device0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9started from6b238a0048e85ade820dc964f3e6e731dcc9abd6bundle96f8decda2fa9d53f198f7239588ec27c042e02e68f6ef346a7565a8b2142bfb · 7.3 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 2 filessrc/BasisPoints.soltest/BasisPoints.t.solmay writesrc/BasisPoints.soltest/BasisPoints.t.sol