Unverified Commit 805f80ce authored by Dev Ojha's avatar Dev Ojha Committed by GitHub
Browse files

Stableswap implement JoinPoolNoSwap (#2942)


* ...

* add tests and denom validation check

* make format

Co-authored-by: default avataralpo <yukseloglua@berkeley.edu>
Co-authored-by: default avataralpo <62043214+AlpinYukseloglu@users.noreply.github.com>
parent 184a85cd
Showing with 211 additions and 9 deletions
+211 -9
......@@ -282,7 +282,8 @@ func (suite *KeeperTestSuite) TestGetPoolAndPoke() {
},
sdk.NewCoins(sdk.NewCoin(defaultAcctFunds[0].Denom, defaultAcctFunds[0].Amount.QuoRaw(2)), sdk.NewCoin(defaultAcctFunds[1].Denom, defaultAcctFunds[1].Amount.QuoRaw(2))),
[]uint64{1, 1},
)},
),
},
}
for name, tc := range tests {
......
......@@ -297,19 +297,48 @@ func (p *Pool) CalcJoinPoolShares(ctx sdk.Context, tokensIn sdk.Coins, swapFee s
return pCopy.joinPoolSharesInternal(ctx, tokensIn, swapFee)
}
// TODO: implement this
func (p *Pool) CalcJoinPoolNoSwapShares(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, newLiquidity sdk.Coins, err error) {
return sdk.ZeroInt(), nil, err
// CalcJoinPoolNoSwapShares calculates the number of shares created to execute an all-asset pool join with the provided amount of `tokensIn`.
// The input tokens must contain the same tokens as in the pool.
//
// Returns the number of shares created, the amount of coins actually joined into the pool as not all may tokens may be joinable.
// If an all-asset join is not possible, returns an error.
func (p Pool) CalcJoinPoolNoSwapShares(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, tokensJoined sdk.Coins, err error) {
// ensure that there aren't too many or too few assets in `tokensIn`
if tokensIn.Len() != p.NumAssets() || !tokensIn.DenomsSubsetOf(p.GetTotalPoolLiquidity(ctx)) {
return sdk.ZeroInt(), sdk.NewCoins(), errors.New("no-swap joins require LP'ing with all assets in pool")
}
// execute a no-swap join with as many tokens as possible given a perfect ratio:
// * numShares is how many shares are perfectly matched.
// * remainingTokensIn is how many coins we have left to join that have not already been used.
numShares, remainingTokensIn, err := cfmm_common.MaximalExactRatioJoin(&p, ctx, tokensIn)
if err != nil {
return sdk.ZeroInt(), sdk.NewCoins(), err
}
// ensure that no more tokens have been joined than is possible with the given `tokensIn`
tokensJoined = tokensIn.Sub(remainingTokensIn)
if tokensJoined.IsAnyGT(tokensIn) {
return sdk.ZeroInt(), sdk.NewCoins(), errors.New("an error has occurred, more coins joined than token In")
}
return numShares, tokensJoined, nil
}
func (p *Pool) JoinPool(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, err error) {
numShares, _, err = p.joinPoolSharesInternal(ctx, tokensIn, swapFee)
func (p *Pool) JoinPool(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (sdk.Int, error) {
numShares, _, err := p.joinPoolSharesInternal(ctx, tokensIn, swapFee)
return numShares, err
}
// TODO: implement this
func (p *Pool) JoinPoolNoSwap(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (numShares sdk.Int, err error) {
return sdk.ZeroInt(), err
func (p *Pool) JoinPoolNoSwap(ctx sdk.Context, tokensIn sdk.Coins, swapFee sdk.Dec) (sdk.Int, error) {
newShares, tokensJoined, err := p.CalcJoinPoolNoSwapShares(ctx, tokensIn, swapFee)
if err != nil {
return sdk.Int{}, err
}
// update pool with the calculated share and liquidity needed to join pool
p.updatePoolForJoin(tokensJoined, newShares)
return newShares, nil
}
func (p *Pool) ExitPool(ctx sdk.Context, exitingShares sdk.Int, exitFee sdk.Dec) (exitingCoins sdk.Coins, err error) {
......
......@@ -507,6 +507,178 @@ func TestScaleCoin(t *testing.T) {
}
}
func TestCalcJoinPoolNoSwapShares(t *testing.T) {
tenPercentOfTwoPool := int64(1000000000 / 10)
tenPercentOfThreePool := int64(1000000 / 10)
tests := map[string]struct {
tokensIn sdk.Coins
poolAssets sdk.Coins
scalingFactors []uint64
expNumShare sdk.Int
expTokensJoined sdk.Coins
expPoolAssets sdk.Coins
expectPass bool
}{
"even two asset pool, same tokenIn ratio": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
poolAssets: twoEvenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
expPoolAssets: twoEvenStablePoolAssets,
expectPass: true,
},
"even two asset pool, different tokenIn ratio with pool": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool+1))),
poolAssets: twoEvenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
expPoolAssets: twoEvenStablePoolAssets,
expectPass: true,
},
"uneven two asset pool, same tokenIn ratio": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(2*tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
poolAssets: twoUnevenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(2*tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
expPoolAssets: twoUnevenStablePoolAssets,
expectPass: true,
},
"uneven two asset pool, different tokenIn ratio with pool": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(2*tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool+1))),
poolAssets: twoUnevenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(2*tenPercentOfTwoPool)), sdk.NewCoin("bar", sdk.NewInt(tenPercentOfTwoPool))),
expPoolAssets: twoUnevenStablePoolAssets,
expectPass: true,
},
"even three asset pool, same tokenIn ratio": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(tenPercentOfThreePool))),
poolAssets: threeEvenStablePoolAssets,
scalingFactors: defaultThreeAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(tenPercentOfThreePool))),
expPoolAssets: threeEvenStablePoolAssets,
expectPass: true,
},
"even three asset pool, different tokenIn ratio with pool": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(tenPercentOfThreePool+1))),
poolAssets: threeEvenStablePoolAssets,
scalingFactors: defaultThreeAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(tenPercentOfThreePool))),
expPoolAssets: threeEvenStablePoolAssets,
expectPass: true,
},
"uneven three asset pool, same tokenIn ratio": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool))),
poolAssets: threeUnevenStablePoolAssets,
scalingFactors: defaultThreeAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool))),
expPoolAssets: threeUnevenStablePoolAssets,
expectPass: true,
},
"uneven three asset pool, different tokenIn ratio with pool": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool+1))),
poolAssets: threeUnevenStablePoolAssets,
scalingFactors: defaultThreeAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool))),
expPoolAssets: threeUnevenStablePoolAssets,
expectPass: true,
},
"uneven three asset pool, uneven scaling factors": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool))),
poolAssets: threeUnevenStablePoolAssets,
scalingFactors: []uint64{5, 9, 175},
expNumShare: sdk.NewIntFromUint64(10000000000000000000),
expTokensJoined: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(2*tenPercentOfThreePool)), sdk.NewCoin("asset/c", sdk.NewInt(3*tenPercentOfThreePool))),
expPoolAssets: threeUnevenStablePoolAssets,
expectPass: true,
},
// error catching
"even two asset pool, no-swap join attempt with one asset": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool))),
poolAssets: twoEvenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: twoEvenStablePoolAssets,
expectPass: false,
},
"even two asset pool, no-swap join attempt with one valid and one invalid asset": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("baz", sdk.NewInt(tenPercentOfTwoPool))),
poolAssets: twoEvenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: twoEvenStablePoolAssets,
expectPass: false,
},
"even two asset pool, no-swap join attempt with two invalid assets": {
tokensIn: sdk.NewCoins(sdk.NewCoin("baz", sdk.NewInt(tenPercentOfTwoPool)), sdk.NewCoin("qux", sdk.NewInt(tenPercentOfTwoPool))),
poolAssets: twoEvenStablePoolAssets,
scalingFactors: defaultTwoAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: twoEvenStablePoolAssets,
expectPass: false,
},
"even three asset pool, no-swap join attempt with an invalid asset": {
tokensIn: sdk.NewCoins(sdk.NewCoin("asset/a", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("asset/b", sdk.NewInt(tenPercentOfThreePool)), sdk.NewCoin("qux", sdk.NewInt(tenPercentOfThreePool))),
poolAssets: threeEvenStablePoolAssets,
scalingFactors: defaultThreeAssetScalingFactors,
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: threeEvenStablePoolAssets,
expectPass: false,
},
"single asset pool, no-swap join attempt with one asset": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(sdk.MaxSortableDec.TruncateInt64()))),
poolAssets: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1))),
scalingFactors: []uint64{1},
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1))),
expectPass: false,
},
"attempt joining pool with no assets in it": {
tokensIn: sdk.NewCoins(sdk.NewCoin("foo", sdk.NewInt(1))),
poolAssets: sdk.Coins{},
scalingFactors: []uint64{},
expNumShare: sdk.NewIntFromUint64(0),
expTokensJoined: sdk.Coins{},
expPoolAssets: sdk.Coins{},
expectPass: false,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
ctx := sdk.Context{}
pool := poolStructFromAssets(test.poolAssets, test.scalingFactors)
numShare, tokensJoined, err := pool.CalcJoinPoolNoSwapShares(ctx, test.tokensIn, pool.GetSwapFee(ctx))
if test.expectPass {
require.NoError(t, err)
require.Equal(t, test.expPoolAssets, pool.GetTotalPoolLiquidity(ctx))
require.Equal(t, test.expNumShare, numShare)
require.Equal(t, test.expTokensJoined, tokensJoined)
} else {
require.Error(t, err)
require.Equal(t, test.expPoolAssets, pool.GetTotalPoolLiquidity(ctx))
require.Equal(t, test.expNumShare, numShare)
require.Equal(t, test.expTokensJoined, tokensJoined)
}
})
}
}
func TestSwapOutAmtGivenIn(t *testing.T) {
tests := map[string]struct {
poolAssets sdk.Coins
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment