-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
VAL-69 Constant-time, per-user "crank" #88
Merged
Merged
Conversation
This file contains 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
venables
previously approved these changes
Nov 14, 2022
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wow this looks great
venables
previously approved these changes
Nov 17, 2022
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💪 This is a lot but it looks correct to me
venables
approved these changes
Nov 22, 2022
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.
Notes
For consideration / notes:
Next step
I'm looking for feedback on this approach at a high-level, and if we're in consensus, then on the specifics of the PR. If we don't feel comfortable with this approach, I can revert back to the per-user for-loop approach in this branch: https://github.com/ams9198/valyria-core/compare/master...VAL-69-rebased
Formula
The formula used here is:
lenderEligibleBalance *( r1 + r2*(1 - r1) + r3 * (1 - r1) * 1 - r2) ... rN (1 - rN)...)
, where rN is the redeemableRate at snapshot N (the redeemableRate is the value [0, 1] indicating what % of eligible requested withdrawals can be fulfilled by that snapshot).In plain English, each term that's added together in parentheses is the amount that the lender receives in snapshot N. So adding them together gives you the total balance at that period N (when multiplied by the lender's eligible balance). Let's call this aggregate total
aggregateSumN
(aggregate sum of 1..N).Additionally, let's call the inner, accumulating terms (1 - r1) * (1 - r2) ... (1 - rn) as
productDifferencesN
(accumulation of terms up to period N).This works for a base case where the lender has eligible shares from the get-go. However, if they request mid-way, then we need to offset it by the prior periods before they requested (or were last cranked at).
This ends up being, assuming a lender requested at period X:
lenderEligibleBalance * (aggregateSumN - aggregateSumX) * 1/productDifferencesX
.If you expand out the full formula, you see within the parentheses, the prior snapshots (that the lender wasn't "present" for) cancels out through the subtraction. Then, the division by 1/productDifferencesX divides out the prior ratios that the lender wasn't present for in the remaining terms.
In each crank, we accumulate both the aggregateSumN value and the productDifferencesN term with each successive snapshot. This is persisted as snapshot metadata.
Dust / math
One important edge case I ran into was the scenario where the snapshot can 100% fulfill all the requests (aka redeemRatio is 1.0). This has the effect of zero-ing out the
productDifferencesX
value from then on to eternity (which accumulates terms of (1 - redeemRatioN), which reduces to (1 - 1) = 0, which is then multiplied by all other terms), which is OK algebraically (since that zero term will cancel out through the division of 1/productDifferencesX, when you incorporate the offsets), but in practice using actual calculated numbers, throws everything off.I solved for this by never actually allowing a redeemRatio of 1.0 -- only release 1 less than the available shares, in that case. This works out in practice, but leaves dust. This affected a bunch of our tests, which use small values so it was more apparent.
I think in practice with real numbers it's OK, but a consideration with this approach. Couldn't find another way to manage this.