-
Notifications
You must be signed in to change notification settings - Fork 609
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
Stableswap: LP logic #1419
Stableswap: LP logic #1419
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1419 +/- ##
==========================================
+ Coverage 19.50% 19.55% +0.04%
==========================================
Files 231 235 +4
Lines 31527 31607 +80
==========================================
+ Hits 6150 6181 +31
- Misses 24251 24297 +46
- Partials 1126 1129 +3
Continue to review full report at Codecov.
|
if exitAmt.GTE(asset.Amount) { | ||
return sdk.Coins{}, errors.New("too many shares out") | ||
} |
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 new, but solves an edge case bug with potential rounding errors in the exitAmt multiplication.
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.
thanks for adding more comments as well
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.
srry I'm not sure I follow here, could you explain why we're comparing share amount with the actual asset amount?
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.
exitAmt isn't a share amount. Its a ratio * assets = assets
. (Ratios are unitless!)
TODO: Refactor this, to get more code reuse with existing code
8169edd
to
e9fca05
Compare
Next steps:
If this PR is growing too large, I'm happy to cut out the single asset join logic into a second PR, which is where all the complexity here is. |
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.
Awesome work!
In terms of whether to split this, I think it would be great to get this approved and merged and continue the work in a separate PR / branch
// (by path-independence, swap all of B -> A, and then swap all of C -> A will yield same amount of A, regardless | ||
// of order and interleaving) | ||
// | ||
// This implementation all of pool.GetTotalPoolLiquidity, pool.ExitPool, and pool.SwapExactAmountIn to not |
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 implementation all of pool.GetTotalPoolLiquidity, pool.ExitPool, and pool.SwapExactAmountIn to not | |
// This implementation includes calls to all of pool.GetTotalPoolLiquidity, pool.ExitPool, and pool.SwapExactAmountIn to not |
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.
We might want to consider just mentioning that this function updates state. Function specs shouldn't go into detail about implementation
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.
The point is that we rely on calls to those pool functions not modifying state, only the pool struct, despite the functions taking in the context.
(So it only supports a subset of pools, which is at the moment all of our current pools. I think our DeFi plans should remove this soon)
if exitAmt.GTE(asset.Amount) { | ||
return sdk.Coins{}, errors.New("too many shares out") | ||
} |
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.
thanks for adding more comments as well
|
||
// Creates a pool with tokenIn liquidity added, where it created `sharesIn` number of shares. | ||
// Returns how many tokens you'd get, if you then exited all of `sharesIn` for tokenIn.Denom | ||
estimateCoinOutGivenShares := func(sharesIn sdk.Int) (tokenOut sdk.Int, err error) { |
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.
What do you think about making this an actual function so that it can be unit tested?
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.
+1 on this comment!
Thanks for the review on this!! |
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.
Overall LGTM!
I'm surprised how this PR kinda puts all the particles of stable swap together! 🚀
|
||
expectedCompareResult int8 | ||
}{ | ||
{"0 tolerance: <", ZeroErrTolerance, sdk.NewInt(1000), sdk.NewInt(1001), -1}, |
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.
func TestBinarySearch(t *testing.T) { | ||
// straight line function that returns input. Simplest to binary search on, | ||
// binary search directly reveals one bit of the answer in each iteration with this function. | ||
lineF := func(a sdk.Int) (sdk.Int, error) { |
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.
if exitAmt.GTE(asset.Amount) { | ||
return sdk.Coins{}, errors.New("too many shares out") | ||
} |
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.
srry I'm not sure I follow here, could you explain why we're comparing share amount with the actual asset amount?
correctnessThreshold := sdk.NewInt(2) | ||
maxIterations := 512 | ||
// upperbound of number of LP shares = existingShares * tokenIn.Amount / pool.totalLiquidity.AmountOf(tokenIn.Denom) | ||
existingTokenLiquidity := pool.GetTotalPoolLiquidity(ctx).AmountOf(tokenIn.Denom) |
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.
Why do we need ctx
as a parameter in the first place for GetTotalPoolLiquidity
if we're using a dummy ctx? Do you think we can erase ctx for those methods in poolI that takes ctx but does not use it in a separate issue?
|
||
// Creates a pool with tokenIn liquidity added, where it created `sharesIn` number of shares. | ||
// Returns how many tokens you'd get, if you then exited all of `sharesIn` for tokenIn.Denom | ||
estimateCoinOutGivenShares := func(sharesIn sdk.Int) (tokenOut sdk.Int, err error) { |
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.
+1 on this comment!
|
||
return swapAllCoinsToSingleAsset(poolWithUpdatedLiquidity, ctx, exitedCoins, swapToDenom) | ||
} | ||
// TODO: Come back and revisit err tolerance |
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.
What's the exact task that this TODO is mentioning? Is it referring to creating test cases?
@@ -0,0 +1,95 @@ | |||
package osmoutils |
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.
What's the standard for methods that go into osmoutils
package and methods that stay within a specific package?
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.
Good question. Right now I think the answer is things that seem generally re-usable, but not super well abstracted / haven't found a proper home in the code yet.
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.
In this case, they could plausibly belong in osmomath
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.
Left some of comments!
correctnessThreshold := sdk.NewInt(2) | ||
maxIterations := 512 |
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.
I think 256 is enough to reduce down to single digit while dealing with Int256
Co-authored-by: Matt, Park <[email protected]>
Component of: #1406
What is the purpose of the change
This PR abstracts the Balancer logic for exit pool, and JoinPoolNoSwap logic to internal code usable by both balancer and stableswap.
This PR then adds these functions to stableswap. What is missing from stableswap after this is then the logic for single asset LP'ing. I plan on doing that tomorrow, it will be more logically intense, so I was thinking of doing it in a separate PR to keep this one simple to review, if folks think that is a good plan.
Testing and Verifying
Documentation and Release Note
Unreleased
section inCHANGELOG.md
? No