diff --git a/CHANGELOG.md b/CHANGELOG.md index cc5def2b6..50dda4df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -123,6 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * [#985](https://github.com/NibiruChain/nibiru/pull/985) - feat: query all active positions for a trader * [#997](https://github.com/NibiruChain/nibiru/pull/997) - feat: emit `ReserveSnapshotSavedEvent` in vpool EndBlocker * [#1011](https://github.com/NibiruChain/nibiru/pull/1011) - feat(perp): add DonateToEF cli command +* [#1038](https://github.com/NibiruChain/nibiru/pull/1038) - feat(dex): add single asset join ### Fixes * [#1023](https://github.com/NibiruChain/nibiru/pull/1023) - collections: golang compiler bug diff --git a/proto/dex/v1/tx.proto b/proto/dex/v1/tx.proto index cc9e3aea4..7749e6eaf 100644 --- a/proto/dex/v1/tx.proto +++ b/proto/dex/v1/tx.proto @@ -56,6 +56,8 @@ message MsgJoinPool { (gogoproto.moretags) = "yaml:\"tokens_in\"", (gogoproto.nullable) = false ]; + + bool use_all_coins = 4 [(gogoproto.moretags) = "yaml:\"use_all_coins\""]; } /* diff --git a/x/dex/client/cli/flags.go b/x/dex/client/cli/flags.go index ce1cf18ab..6703eb723 100644 --- a/x/dex/client/cli/flags.go +++ b/x/dex/client/cli/flags.go @@ -11,6 +11,9 @@ const ( // FlagPoolId Will be parsed to uint64. FlagPoolId = "pool-id" + // FlagUseAllCoins Will be parsed to uint64. + FlagUseAllCoins = "use-all-coins" + // FlagTokensIn Will be parsed to []sdk.Coin. FlagTokensIn = "tokens-in" @@ -43,6 +46,7 @@ func FlagSetJoinPool() *flag.FlagSet { fs.Uint64(FlagPoolId, 0, "The id of pool") fs.StringArray(FlagTokensIn, []string{""}, "Amount of each denom to send into the pool (specify multiple denoms with: --tokens-in=1uusdc --tokens-in=1unusd)") + fs.Bool(FlagUseAllCoins, false, "Whether to use all the tokens in tokens-in to maximize shares out with a swap first") return fs } diff --git a/x/dex/client/cli/tx.go b/x/dex/client/cli/tx.go index 4ba76eede..8380f44cf 100644 --- a/x/dex/client/cli/tx.go +++ b/x/dex/client/cli/tx.go @@ -132,10 +132,16 @@ func CmdJoinPool() *cobra.Command { tokensIn = tokensIn.Add(parsed...) } + useAllCoins, err := flagSet.GetBool(FlagUseAllCoins) + if err != nil { + return err + } + msg := types.NewMsgJoinPool( /*sender=*/ clientCtx.GetFromAddress().String(), poolId, tokensIn, + useAllCoins, ) return tx.GenerateOrBroadcastTxCLI(clientCtx, flagSet, msg) @@ -147,6 +153,7 @@ func CmdJoinPool() *cobra.Command { _ = cmd.MarkFlagRequired(FlagPoolId) _ = cmd.MarkFlagRequired(FlagTokensIn) + _ = cmd.MarkFlagRequired(FlagUseAllCoins) return cmd } diff --git a/x/dex/client/testutil/suite.go b/x/dex/client/testutil/suite.go index 25d7ecbf8..08713a175 100644 --- a/x/dex/client/testutil/suite.go +++ b/x/dex/client/testutil/suite.go @@ -195,7 +195,7 @@ func (s *IntegrationTestSuite) TestNewJoinPoolCmd() { s.Run(tc.name, func() { ctx := val.ClientCtx - out, err := ExecMsgJoinPool(ctx, tc.poolId, val.Address, tc.tokensIn) + out, err := ExecMsgJoinPool(ctx, tc.poolId, val.Address, tc.tokensIn, "false") if tc.expectErr { s.Require().Error(err) } else { diff --git a/x/dex/client/testutil/test_helpers.go b/x/dex/client/testutil/test_helpers.go index 5e6b573e6..42d74f592 100644 --- a/x/dex/client/testutil/test_helpers.go +++ b/x/dex/client/testutil/test_helpers.go @@ -73,11 +73,13 @@ func ExecMsgJoinPool( poolId uint64, sender fmt.Stringer, tokensIn string, + useAllCoins string, extraArgs ...string, ) (testutil.BufferWriter, error) { args := []string{ fmt.Sprintf("--%s=%d", cli.FlagPoolId, poolId), fmt.Sprintf("--%s=%s", cli.FlagTokensIn, tokensIn), + fmt.Sprintf("--%s=%s", cli.FlagUseAllCoins, useAllCoins), fmt.Sprintf("--%s=%s", flags.FlagFrom, sender.String()), fmt.Sprintf("--%s=%d", flags.FlagGas, 300000), } diff --git a/x/dex/keeper/grpc_query.go b/x/dex/keeper/grpc_query.go index d75ca4ec0..f143204a4 100644 --- a/x/dex/keeper/grpc_query.go +++ b/x/dex/keeper/grpc_query.go @@ -249,7 +249,7 @@ func (k queryServer) EstimateSwapExactAmountIn( return nil, err } - tokenOut, err := pool.CalcOutAmtGivenIn(req.TokenIn, req.TokenOutDenom) + tokenOut, err := pool.CalcOutAmtGivenIn(req.TokenIn, req.TokenOutDenom, false) if err != nil { return nil, err } diff --git a/x/dex/keeper/keeper.go b/x/dex/keeper/keeper.go index 79478f8df..df496bfb8 100644 --- a/x/dex/keeper/keeper.go +++ b/x/dex/keeper/keeper.go @@ -436,6 +436,7 @@ func (k Keeper) JoinPool( joinerAddr sdk.AccAddress, poolId uint64, tokensIn sdk.Coins, + shouldSwap bool, ) (pool types.Pool, numSharesOut sdk.Coin, remCoins sdk.Coins, err error) { pool, _ = k.FetchPool(ctx, poolId) @@ -445,7 +446,12 @@ func (k Keeper) JoinPool( poolAddr := pool.GetAddress() - numShares, remCoins, err := pool.AddTokensToPool(tokensIn) + var numShares sdk.Int + if !shouldSwap { + numShares, remCoins, err = pool.AddTokensToPool(tokensIn) + } else { + numShares, remCoins, err = pool.AddAllTokensToPool(tokensIn) + } if err != nil { return types.Pool{}, sdk.Coin{}, sdk.Coins{}, err } diff --git a/x/dex/keeper/keeper_test.go b/x/dex/keeper/keeper_test.go index b2ea02503..49e09caf8 100644 --- a/x/dex/keeper/keeper_test.go +++ b/x/dex/keeper/keeper_test.go @@ -526,7 +526,136 @@ func TestJoinPool(t *testing.T) { joinerAddr := testutil.AccAddress() require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, joinerAddr, tc.joinerInitialFunds)) - pool, numSharesOut, remCoins, err := app.DexKeeper.JoinPool(ctx, joinerAddr, 1, tc.tokensIn) + pool, numSharesOut, remCoins, err := app.DexKeeper.JoinPool(ctx, joinerAddr, 1, tc.tokensIn, false) + require.NoError(t, err) + require.Equal(t, tc.expectedFinalPool, pool) + require.Equal(t, tc.expectedNumSharesOut, numSharesOut) + require.Equal(t, tc.expectedRemCoins, remCoins) + }) + } +} + +func TestJoinPoolAllAssets(t *testing.T) { + const shareDenom = "nibiru/pool/1" + + tests := []struct { + name string + joinerInitialFunds sdk.Coins + initialPool types.Pool + tokensIn sdk.Coins + expectedNumSharesOut sdk.Coin + expectedRemCoins sdk.Coins + expectedJoinerFinalFunds sdk.Coins + expectedFinalPool types.Pool + }{ + { + name: "join with all assets", + joinerInitialFunds: sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + initialPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + /*shares=*/ 100), + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + expectedNumSharesOut: sdk.NewInt64Coin(shareDenom, 100), + expectedRemCoins: sdk.NewCoins(), + expectedJoinerFinalFunds: sdk.NewCoins(sdk.NewInt64Coin(shareDenom, 100)), + expectedFinalPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 200), + sdk.NewInt64Coin("foo", 200), + ), + /*shares=*/ 200), + }, + { + name: "join with some assets, none remaining", + joinerInitialFunds: sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + initialPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + /*shares=*/ 100), + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("bar", 50), + sdk.NewInt64Coin("foo", 50), + ), + expectedNumSharesOut: sdk.NewInt64Coin(shareDenom, 50), + expectedRemCoins: sdk.NewCoins(), + expectedJoinerFinalFunds: sdk.NewCoins( + sdk.NewInt64Coin(shareDenom, 50), + sdk.NewInt64Coin("bar", 50), + sdk.NewInt64Coin("foo", 50), + ), + expectedFinalPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 150), + sdk.NewInt64Coin("foo", 150), + ), + /*shares=*/ 150), + }, + { + name: "join with some assets, but swap done", + joinerInitialFunds: sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + initialPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 100), + sdk.NewInt64Coin("foo", 100), + ), + /*shares=*/ 100), + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("bar", 50), + sdk.NewInt64Coin("foo", 75), + ), + expectedNumSharesOut: sdk.NewInt64Coin(shareDenom, 61), + expectedRemCoins: sdk.NewCoins(), + expectedJoinerFinalFunds: sdk.NewCoins( + sdk.NewInt64Coin(shareDenom, 50), + sdk.NewInt64Coin("bar", 35), + sdk.NewInt64Coin("foo", 35), + ), + expectedFinalPool: mock.DexPool( + /*poolId=*/ 1, + /*assets=*/ sdk.NewCoins( + sdk.NewInt64Coin("bar", 150), + sdk.NewInt64Coin("foo", 175), + ), + /*shares=*/ 161), + }, + } + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + app, ctx := simapp2.NewTestNibiruAppAndContext(true) + + poolAddr := testutil.AccAddress() + tc.initialPool.Address = poolAddr.String() + tc.expectedFinalPool.Address = poolAddr.String() + app.DexKeeper.SetPool(ctx, tc.initialPool) + + joinerAddr := testutil.AccAddress() + require.NoError(t, simapp.FundAccount(app.BankKeeper, ctx, joinerAddr, tc.joinerInitialFunds)) + + pool, numSharesOut, remCoins, err := app.DexKeeper.JoinPool(ctx, joinerAddr, 1, tc.tokensIn, true) require.NoError(t, err) require.Equal(t, tc.expectedFinalPool, pool) require.Equal(t, tc.expectedNumSharesOut, numSharesOut) diff --git a/x/dex/keeper/msg_server.go b/x/dex/keeper/msg_server.go index ac275f2ba..da15f4fae 100644 --- a/x/dex/keeper/msg_server.go +++ b/x/dex/keeper/msg_server.go @@ -77,6 +77,7 @@ func (k msgServer) JoinPool(ctx context.Context, msg *types.MsgJoinPool) (*types sender, msg.PoolId, msg.TokensIn, + msg.UseAllCoins, ) if err != nil { return nil, err diff --git a/x/dex/keeper/msg_server_test.go b/x/dex/keeper/msg_server_test.go index 2f45390e5..e5433e636 100644 --- a/x/dex/keeper/msg_server_test.go +++ b/x/dex/keeper/msg_server_test.go @@ -312,7 +312,7 @@ func TestMsgServerJoinPool(t *testing.T) { msgServer := keeper.NewMsgServerImpl(app.DexKeeper) resp, err := msgServer.JoinPool( sdk.WrapSDKContext(ctx), - types.NewMsgJoinPool(joinerAddr.String(), tc.initialPool.Id, tc.tokensIn), + types.NewMsgJoinPool(joinerAddr.String(), tc.initialPool.Id, tc.tokensIn, false), ) require.NoError(t, err) diff --git a/x/dex/keeper/swap.go b/x/dex/keeper/swap.go index 7c9fad2be..a0e0ccac1 100644 --- a/x/dex/keeper/swap.go +++ b/x/dex/keeper/swap.go @@ -79,7 +79,7 @@ func (k Keeper) SwapExactAmountIn( } // calculate tokenOut and validate - tokenOut, err = pool.CalcOutAmtGivenIn(tokenIn, tokenOutDenom) + tokenOut, err = pool.CalcOutAmtGivenIn(tokenIn, tokenOutDenom, false) if err != nil { return sdk.Coin{}, err } diff --git a/x/dex/types/message.go b/x/dex/types/message.go index ec9d074f1..cff45f31f 100644 --- a/x/dex/types/message.go +++ b/x/dex/types/message.go @@ -51,11 +51,12 @@ func (msg *MsgExitPool) ValidateBasic() error { var _ sdk.Msg = &MsgJoinPool{} -func NewMsgJoinPool(sender string, poolId uint64, tokensIn sdk.Coins) *MsgJoinPool { +func NewMsgJoinPool(sender string, poolId uint64, tokensIn sdk.Coins, useAllCoins bool) *MsgJoinPool { return &MsgJoinPool{ - Sender: sender, - PoolId: poolId, - TokensIn: tokensIn, + Sender: sender, + PoolId: poolId, + TokensIn: tokensIn, + UseAllCoins: useAllCoins, } } diff --git a/x/dex/types/pool.go b/x/dex/types/pool.go index b85b50959..be8d53c01 100644 --- a/x/dex/types/pool.go +++ b/x/dex/types/pool.go @@ -110,6 +110,65 @@ func (pool *Pool) AddTokensToPool(tokensIn sdk.Coins) ( return numShares, remCoins, nil } +/* +Adds tokens to a pool optimizing the amount of shares (swap + join) and updates the pool balances (i.e. liquidity). +We compute the swap and then join the pool. + +args: + - tokensIn: the tokens to add to the pool + +ret: + - numShares: the number of LP shares given to the user for the deposit + - remCoins: the number of coins remaining after the deposit + - err: error if any +*/ +func (pool *Pool) AddAllTokensToPool(tokensIn sdk.Coins) ( + numShares sdk.Int, remCoins sdk.Coins, err error, +) { + swapToken, err := pool.SwapForSwapAndJoin(tokensIn) + if err != nil { + return + } + if swapToken.Amount.LT(sdk.OneInt()) { + return pool.AddTokensToPool(tokensIn) + } + + index, _, err := pool.getPoolAssetAndIndex(swapToken.Denom) + + if err != nil { + return + } + + otherDenom := pool.PoolAssets[1-index].Token.Denom + tokenOut, err := pool.CalcOutAmtGivenIn( + /*tokenIn=*/ swapToken, + /*tokenOutDenom=*/ otherDenom, + /*noFee=*/ true, + ) + + if err != nil { + return + } + + err = pool.ApplySwap(swapToken, tokenOut) + + if err != nil { + return + } + + tokensIn = sdk.Coins{ + { + Denom: swapToken.Denom, + Amount: tokensIn.AmountOfNoDenomValidation(swapToken.Denom).Sub(swapToken.Amount), + }, + { + Denom: otherDenom, + Amount: tokensIn.AmountOfNoDenomValidation(otherDenom).Add(tokenOut.Amount), + }, + }.Sort() + return pool.AddTokensToPool(tokensIn) +} + /* Fetch the pool's address as an sdk.Address. */ diff --git a/x/dex/types/pool_test.go b/x/dex/types/pool_test.go index cf051b28b..3c9971076 100644 --- a/x/dex/types/pool_test.go +++ b/x/dex/types/pool_test.go @@ -218,6 +218,148 @@ func TestJoinPoolHappyPath(t *testing.T) { } } +func TestJoinPoolAllTokens(t *testing.T) { + for _, tc := range []struct { + name string + pool Pool + tokensIn sdk.Coins + expectedNumShares sdk.Int + expectedRemCoins sdk.Coins + expectedPool Pool + }{ + { + name: "all coins deposited", + pool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 100), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 200), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 100), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 10), + sdk.NewInt64Coin("bbb", 20), + ), + expectedNumShares: sdk.NewInt(10), + expectedRemCoins: sdk.NewCoins(), + expectedPool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 110), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 220), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 110), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + }, + { + name: "partial coins deposited", + pool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 100), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 200), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 100), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 10), + sdk.NewInt64Coin("bbb", 10), + ), + expectedNumShares: sdk.NewInt(6), + expectedRemCoins: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 1), + ), + expectedPool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 109), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 210), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 106), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + }, + { + name: "difficult numbers", + pool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 3_498_579), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1_403_945), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 1_000_000), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 4859), // 0.138885 % of pool + sdk.NewInt64Coin("bbb", 1345), // 0.09580147 % of pool + ), + expectedNumShares: sdk.NewInt(1172), + expectedRemCoins: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 3), + ), + expectedPool: Pool{ + PoolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 3_503_435), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1_405_290), + Weight: sdk.NewInt(1 << 30), + }, + }, + TotalShares: sdk.NewInt64Coin("nibiru/pool/1", 1_001_172), + TotalWeight: sdk.NewInt(2 << 30), + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + }, + }, + } { + tc := tc + t.Run(tc.name, func(t *testing.T) { + numShares, remCoins, err := tc.pool.AddAllTokensToPool(tc.tokensIn) + require.NoError(t, err) + require.Equal(t, tc.expectedNumShares, numShares) + require.Equal(t, tc.expectedRemCoins, remCoins) + require.Equal(t, tc.expectedPool, tc.pool) + }) + } +} + func TestJoinPoolInvalidInput(t *testing.T) { for _, tc := range []struct { name string diff --git a/x/dex/types/shares.go b/x/dex/types/shares.go index 0ad59465e..745a49cab 100644 --- a/x/dex/types/shares.go +++ b/x/dex/types/shares.go @@ -2,10 +2,76 @@ package types import ( "errors" + math "math" sdk "github.com/cosmos/cosmos-sdk/types" ) +/* +For 2 asset pools, swap first to maximize the amount of tokens deposited in the pool. +A user can deposit either one or 2 tokens, and we will swap first the biggest individual share and then join the pool. + +args: + - tokensIn: the tokens to add to the pool + +ret: + - out: the tokens to swap before joining the pool + - remCoins: the number of coins remaining after the deposit + - err: error if any +*/ +func (pool *Pool) SwapForSwapAndJoin(tokensIn sdk.Coins) ( + out sdk.Coin, err error, +) { + if len(pool.PoolAssets) != 2 { + err = errors.New("swap and add tokens to pool only available for 2 assets pool") + return + } + + var xAmt sdk.Int + var yAmt sdk.Int + var xDenom string + + // check who's x and y (x/) + if len(tokensIn) == 1 { + xAmt = tokensIn[0].Amount + xDenom = tokensIn[0].Denom + + yAmt = sdk.ZeroInt() + } else { + // 2 assets + poolLiquidity := pool.PoolBalances() + + sharePctX := tokensIn[0].Amount.ToDec().Quo(poolLiquidity.AmountOfNoDenomValidation(tokensIn[0].Denom).ToDec()) + sharePctY := tokensIn[1].Amount.ToDec().Quo(poolLiquidity.AmountOfNoDenomValidation(tokensIn[1].Denom).ToDec()) + + if sharePctX.GTE(sharePctY) { + xAmt = tokensIn[0].Amount + yAmt = tokensIn[1].Amount + + xDenom = tokensIn[0].Denom + } else { + xAmt = tokensIn[1].Amount + yAmt = tokensIn[0].Amount + + xDenom = tokensIn[1].Denom + } + } + + xIndex, xPoolAsset, err := pool.getPoolAssetAndIndex(xDenom) + liquidityX := xPoolAsset.Token.Amount + liquidityY := pool.PoolAssets[1-xIndex].Token.Amount + + // x'=\sqrt{\frac{xk+kl_x}{y+l_y}}-l_x;\:x'=-\sqrt{\frac{xk+kl_x}{y+l_y}}-l_x + invariant := liquidityX.Mul(liquidityY) + + xSwap := sdk.NewInt( + int64(math.Sqrt( + (xAmt.Mul(invariant).Add(invariant.Mul(liquidityX))).ToDec().Quo( + yAmt.Add(liquidityY).ToDec()).MustFloat64()))).Sub(liquidityX) + + return sdk.NewCoin(pool.PoolAssets[xIndex].Token.Denom, xSwap), err +} + /* Takes a pool and the amount of tokens desired to add to the pool, and calculates the number of pool shares and remaining coins after theoretically diff --git a/x/dex/types/shares_test.go b/x/dex/types/shares_test.go index d90cb03e3..a6bc68a62 100644 --- a/x/dex/types/shares_test.go +++ b/x/dex/types/shares_test.go @@ -1,6 +1,7 @@ package types import ( + "errors" "testing" sdk "github.com/cosmos/cosmos-sdk/types" @@ -132,6 +133,216 @@ func TestMaximalSharesFromExactRatioJoin(t *testing.T) { } } +func TestSwapForSwapAndJoin(t *testing.T) { + for _, tc := range []struct { + name string + poolAssets []PoolAsset + existingShares int64 + tokensIn sdk.Coins + expectedX0Denom string + expectedX0Amount int64 + err error + }{ + { + name: "tokens bbb need to be swapped, but no swap necessary", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 100), + sdk.NewInt64Coin("bbb", 101), + ), + expectedX0Denom: "bbb", + expectedX0Amount: 0, + }, + { + name: "tokens aaa need to be swapped, but no swap necessary", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 101), + sdk.NewInt64Coin("bbb", 100), + ), + expectedX0Denom: "aaa", + expectedX0Amount: 0, + }, + { + name: "tokens aaa need to be swapped", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 101), + sdk.NewInt64Coin("bbb", 43), + ), + expectedX0Denom: "aaa", + expectedX0Amount: 27, + }, + { + name: "tokens bbb need to be swapped", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 43), + sdk.NewInt64Coin("bbb", 101), + ), + expectedX0Denom: "bbb", + expectedX0Amount: 27, + }, + { + name: "single asset join, aaa", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 230), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 928), + ), + expectedX0Denom: "aaa", + expectedX0Amount: 286, + }, + { + name: "single asset join, bbb", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 230), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("bbb", 928), + ), + expectedX0Denom: "bbb", + expectedX0Amount: 388, + }, + + { + name: "3 asset pool, raise issue", + poolAssets: []PoolAsset{ + { + Token: sdk.NewInt64Coin("aaa", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("bbb", 1000), + Weight: sdk.NewInt(1 << 30), + }, + { + Token: sdk.NewInt64Coin("ccc", 1000), + Weight: sdk.NewInt(1 << 30), + }, + }, + existingShares: 100, + tokensIn: sdk.NewCoins( + sdk.NewInt64Coin("aaa", 43), + sdk.NewInt64Coin("bbb", 101), + ), + err: errors.New("swap and add tokens to pool only available for 2 assets pool"), + }, + } { + tc := tc + t.Run(tc.name, func(t *testing.T) { + pool := Pool{ + Id: 1, + Address: "some_address", + PoolParams: PoolParams{SwapFee: sdk.ZeroDec()}, + PoolAssets: tc.poolAssets, + TotalWeight: sdk.NewInt(2 << 30), + TotalShares: sdk.NewCoin("nibiru/pool/1", sdk.NewIntWithDecimal(100, 18)), + } + swapCoin, err := pool.SwapForSwapAndJoin(tc.tokensIn) + if tc.err == nil { + require.NoError(t, err) + require.EqualValues(t, tc.expectedX0Denom, swapCoin.Denom) + require.EqualValues(t, tc.expectedX0Amount, swapCoin.Amount.Int64()) + + if swapCoin.Amount.GT(sdk.ZeroInt()) { + index, _, err := pool.getPoolAssetAndIndex(swapCoin.Denom) + + require.NoError(t, err) + otherDenom := pool.PoolAssets[1-index].Token.Denom + + tokenOut, err := pool.CalcOutAmtGivenIn(swapCoin, otherDenom, false) + require.NoError(t, err) + + err = pool.ApplySwap(swapCoin, tokenOut) + require.NoError(t, err) + + tokensIn := sdk.Coins{ + { + Denom: swapCoin.Denom, + Amount: tc.tokensIn.AmountOfNoDenomValidation(swapCoin.Denom).Sub(swapCoin.Amount), + }, + { + Denom: otherDenom, + Amount: tc.tokensIn.AmountOfNoDenomValidation(otherDenom).Add(tokenOut.Amount), + }, + } + + _, remCoins, err := pool.numSharesOutFromTokensIn(tokensIn) + require.NoError(t, err) + + // Because of rounding errors, we might receive remcoins up to ~ly/lx + _, assetX, _ := pool.getPoolAssetAndIndex(swapCoin.Denom) + _, assetY, _ := pool.getPoolAssetAndIndex(otherDenom) + + maxError := assetX.Token.Amount.ToDec().Quo(assetY.Token.Amount.ToDec()) + + require.LessOrEqual(t, remCoins.AmountOf(swapCoin.Denom).Int64(), maxError.TruncateInt64()) + } + } else { + require.Error(t, err) + } + }) + } +} + func TestTokensOutFromExactSharesHappyPath(t *testing.T) { for _, tc := range []struct { name string diff --git a/x/dex/types/swap.go b/x/dex/types/swap.go index 2ef6a972c..93121b241 100644 --- a/x/dex/types/swap.go +++ b/x/dex/types/swap.go @@ -16,12 +16,13 @@ Only supports single asset swaps. args: - tokenIn: the amount of tokens to swap - tokenOutDenom: the target token denom + - noFee: whether we want to bypass swap fee (for single asset join) ret: - tokenOut: the tokens received from the swap - err: error if any */ -func (pool Pool) CalcOutAmtGivenIn(tokenIn sdk.Coin, tokenOutDenom string) ( +func (pool Pool) CalcOutAmtGivenIn(tokenIn sdk.Coin, tokenOutDenom string, noFee bool) ( tokenOut sdk.Coin, err error, ) { _, poolAssetIn, err := pool.getPoolAssetAndIndex(tokenIn.Denom) @@ -34,7 +35,13 @@ func (pool Pool) CalcOutAmtGivenIn(tokenIn sdk.Coin, tokenOutDenom string) ( return tokenOut, err } - tokenAmountInAfterFee := tokenIn.Amount.ToDec().Mul(sdk.OneDec().Sub(pool.PoolParams.SwapFee)) + var tokenAmountInAfterFee sdk.Dec + if noFee { + tokenAmountInAfterFee = tokenIn.Amount.ToDec() + } else { + tokenAmountInAfterFee = tokenIn.Amount.ToDec().Mul(sdk.OneDec().Sub(pool.PoolParams.SwapFee)) + } + poolTokenInBalance := poolAssetIn.Token.Amount.ToDec() poolTokenInBalancePostSwap := poolTokenInBalance.Add(tokenAmountInAfterFee) diff --git a/x/dex/types/swap_test.go b/x/dex/types/swap_test.go index 8562513a9..3ddb87409 100644 --- a/x/dex/types/swap_test.go +++ b/x/dex/types/swap_test.go @@ -130,7 +130,7 @@ func TestCalcOutAmtGivenIn(t *testing.T) { } { tc := tc t.Run(tc.name, func(t *testing.T) { - tokenOut, err := tc.pool.CalcOutAmtGivenIn(tc.tokenIn, tc.tokenOutDenom) + tokenOut, err := tc.pool.CalcOutAmtGivenIn(tc.tokenIn, tc.tokenOutDenom, false) if tc.shouldError { require.Error(t, err) } else { diff --git a/x/dex/types/tx.pb.go b/x/dex/types/tx.pb.go index c250982bb..59347bfbf 100644 --- a/x/dex/types/tx.pb.go +++ b/x/dex/types/tx.pb.go @@ -137,9 +137,10 @@ func (m *MsgCreatePoolResponse) GetPoolId() uint64 { // //Message to join a pool (identified by poolId) with a set of tokens to deposit. type MsgJoinPool struct { - Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` - PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` - TokensIn []types.Coin `protobuf:"bytes,3,rep,name=tokens_in,json=tokensIn,proto3" json:"tokens_in" yaml:"tokens_in"` + Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty" yaml:"sender"` + PoolId uint64 `protobuf:"varint,2,opt,name=pool_id,json=poolId,proto3" json:"pool_id,omitempty" yaml:"pool_id"` + TokensIn []types.Coin `protobuf:"bytes,3,rep,name=tokens_in,json=tokensIn,proto3" json:"tokens_in" yaml:"tokens_in"` + UseAllCoins bool `protobuf:"varint,4,opt,name=use_all_coins,json=useAllCoins,proto3" json:"use_all_coins,omitempty" yaml:"use_all_coins"` } func (m *MsgJoinPool) Reset() { *m = MsgJoinPool{} } @@ -196,6 +197,13 @@ func (m *MsgJoinPool) GetTokensIn() []types.Coin { return nil } +func (m *MsgJoinPool) GetUseAllCoins() bool { + if m != nil { + return m.UseAllCoins + } + return false +} + // //Response when a user joins a pool. type MsgJoinPoolResponse struct { @@ -491,57 +499,59 @@ func init() { func init() { proto.RegisterFile("dex/v1/tx.proto", fileDescriptor_18e8aa85ff669608) } var fileDescriptor_18e8aa85ff669608 = []byte{ - // 795 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcf, 0x6b, 0xdb, 0x48, - 0x14, 0xb6, 0x62, 0x93, 0x1f, 0x63, 0x1c, 0x27, 0xca, 0x6e, 0xa2, 0x88, 0x60, 0x87, 0x61, 0x61, - 0x13, 0x16, 0xa4, 0xb5, 0xf7, 0xb6, 0x97, 0x65, 0xed, 0xf6, 0x90, 0x82, 0x9b, 0xa0, 0x40, 0x0f, - 0xa5, 0x60, 0x64, 0x7b, 0x50, 0x26, 0xb5, 0x66, 0x84, 0x67, 0x94, 0x38, 0x94, 0x5e, 0x7a, 0xec, - 0xa9, 0xd0, 0x3f, 0xa6, 0xa7, 0xf6, 0xda, 0x1c, 0x03, 0xbd, 0xf4, 0x64, 0x4a, 0xd2, 0xbf, 0xc0, - 0xc7, 0x9e, 0xca, 0xfc, 0x90, 0x22, 0x27, 0x4e, 0x4d, 0x29, 0xb9, 0x69, 0xe6, 0xbd, 0x79, 0xdf, - 0xf7, 0xbe, 0xef, 0xcd, 0x08, 0x94, 0x7b, 0x68, 0xe8, 0x9e, 0xd4, 0x5c, 0x3e, 0x74, 0xa2, 0x01, - 0xe5, 0xd4, 0x2c, 0x11, 0xdc, 0xc1, 0x83, 0xd8, 0xe9, 0xa1, 0xa1, 0x73, 0x52, 0xb3, 0x57, 0x75, - 0x3c, 0xa2, 0xb4, 0xaf, 0x32, 0xec, 0xdf, 0x02, 0x1a, 0x50, 0xf9, 0xe9, 0x8a, 0x2f, 0xbd, 0x5b, - 0xe9, 0x52, 0x16, 0x52, 0xe6, 0x76, 0x7c, 0x86, 0xdc, 0x93, 0x5a, 0x07, 0x71, 0xbf, 0xe6, 0x76, - 0x29, 0x26, 0x3a, 0xbe, 0x15, 0x50, 0x1a, 0xf4, 0x91, 0xeb, 0x47, 0xd8, 0xf5, 0x09, 0xa1, 0xdc, - 0xe7, 0x98, 0x12, 0xa6, 0xa2, 0xf0, 0x83, 0x01, 0x4a, 0x2d, 0x16, 0x34, 0x07, 0xc8, 0xe7, 0xe8, - 0x80, 0xd2, 0xbe, 0x69, 0x81, 0x85, 0xae, 0x58, 0xd1, 0x81, 0x65, 0x6c, 0x1b, 0x3b, 0x4b, 0x5e, - 0xb2, 0x34, 0x3d, 0x50, 0x14, 0x6c, 0xda, 0x91, 0x3f, 0xf0, 0x43, 0x66, 0xcd, 0x6d, 0x1b, 0x3b, - 0xc5, 0xfa, 0xa6, 0x33, 0xc1, 0xdb, 0x11, 0x35, 0x0e, 0x64, 0x42, 0x63, 0x7d, 0x3c, 0xaa, 0x9a, - 0x67, 0x7e, 0xd8, 0xff, 0x17, 0x66, 0xce, 0x41, 0x0f, 0x44, 0x69, 0x8e, 0xf9, 0x9f, 0xae, 0xe9, - 0x33, 0x86, 0x38, 0xb3, 0xf2, 0xdb, 0xf9, 0x9d, 0x62, 0xdd, 0x9a, 0x52, 0xf3, 0x7f, 0x91, 0xd0, - 0x28, 0x9c, 0x8f, 0xaa, 0x39, 0x55, 0x40, 0x6e, 0x30, 0xf8, 0x37, 0xf8, 0x7d, 0x82, 0xbf, 0x87, - 0x58, 0x44, 0x09, 0x43, 0xe6, 0x06, 0x58, 0x90, 0x95, 0x71, 0x4f, 0xf6, 0x51, 0xf0, 0xe6, 0xc5, - 0x72, 0xaf, 0x07, 0xdf, 0x19, 0xa0, 0xd8, 0x62, 0xc1, 0x23, 0x8a, 0x89, 0x6c, 0x78, 0x17, 0xcc, - 0x33, 0x44, 0x7a, 0x48, 0xf7, 0xdb, 0x58, 0x1d, 0x8f, 0xaa, 0x25, 0x45, 0x5b, 0xed, 0x43, 0x4f, - 0x27, 0x98, 0x7f, 0x5d, 0xd7, 0x14, 0xdd, 0x17, 0x1a, 0xe6, 0x78, 0x54, 0x5d, 0xce, 0xb4, 0x88, - 0x7b, 0x30, 0xc1, 0x31, 0x0f, 0xc0, 0x12, 0xa7, 0xcf, 0x11, 0x61, 0x6d, 0x4c, 0x74, 0x63, 0x9b, - 0x8e, 0x32, 0xcb, 0x11, 0x66, 0x39, 0xda, 0x2c, 0xa7, 0x49, 0x31, 0x69, 0x58, 0xa2, 0xb3, 0xf1, - 0xa8, 0xba, 0xa2, 0xaa, 0xa5, 0x27, 0xa1, 0xb7, 0xa8, 0xbe, 0xf7, 0x08, 0x7c, 0x3d, 0x07, 0xd6, - 0x32, 0xcc, 0xd3, 0x56, 0xff, 0x04, 0x05, 0x81, 0x29, 0xf9, 0x17, 0xeb, 0x6b, 0x53, 0xd4, 0xf3, - 0x64, 0x82, 0xd9, 0x07, 0x6b, 0x24, 0x0e, 0xdb, 0x92, 0x2a, 0x3b, 0xf2, 0x07, 0x88, 0xb5, 0x69, - 0xcc, 0x53, 0x27, 0xef, 0x24, 0x07, 0x35, 0x39, 0x5b, 0x91, 0x9b, 0x52, 0x03, 0x7a, 0x2b, 0x24, - 0x0e, 0x05, 0xd4, 0xa1, 0xdc, 0xdb, 0x8f, 0xb9, 0xf9, 0x0c, 0x94, 0x07, 0x28, 0xf4, 0x31, 0xc1, - 0x24, 0x68, 0x8b, 0x89, 0x64, 0xbf, 0x22, 0xc3, 0x72, 0x5a, 0x4b, 0x24, 0x32, 0xf8, 0x5e, 0xd9, - 0xf8, 0x70, 0x88, 0xf9, 0xbd, 0xda, 0xf8, 0x44, 0x4f, 0xa8, 0xea, 0xd5, 0xca, 0xcf, 0xd2, 0xca, - 0xd6, 0x1d, 0x64, 0x27, 0x5f, 0x9d, 0xd5, 0x93, 0xaf, 0x04, 0x82, 0xc7, 0xd2, 0xcb, 0x84, 0x7e, - 0xea, 0xe5, 0x21, 0x00, 0xba, 0x69, 0xe1, 0xcc, 0x4c, 0xbd, 0x36, 0x35, 0xda, 0xea, 0x84, 0x5e, - 0xd2, 0x10, 0x3d, 0x7d, 0xfb, 0x31, 0x87, 0xdf, 0xd4, 0x2d, 0x3f, 0x3c, 0xf5, 0x23, 0x75, 0x6d, - 0xee, 0x4d, 0xad, 0x16, 0x50, 0xe3, 0xaa, 0x66, 0x7e, 0x86, 0x54, 0x1b, 0x9a, 0x7c, 0x39, 0x43, - 0x5e, 0x7a, 0xbd, 0x20, 0x3f, 0xf7, 0x88, 0xd9, 0x00, 0x65, 0xb5, 0x4b, 0x63, 0xde, 0xee, 0x21, - 0x42, 0x43, 0xab, 0x20, 0xf9, 0xda, 0xe3, 0x51, 0x75, 0x3d, 0x7b, 0x2c, 0x4d, 0x80, 0x5e, 0x49, - 0xee, 0xec, 0xc7, 0xfc, 0x81, 0x5c, 0x63, 0xf9, 0x42, 0x5c, 0xf7, 0x9e, 0x4a, 0x9d, 0x5c, 0x50, - 0xad, 0xb4, 0xf1, 0xf3, 0x93, 0xa9, 0x84, 0x5e, 0x4c, 0xf0, 0xea, 0x1f, 0xf3, 0x20, 0xdf, 0x62, - 0x81, 0x79, 0x0c, 0x40, 0xe6, 0x45, 0xdd, 0xba, 0x71, 0x21, 0x27, 0xde, 0x2b, 0xfb, 0x8f, 0x1f, - 0x45, 0x13, 0xae, 0xd0, 0x7a, 0xf5, 0xe9, 0xeb, 0xdb, 0x39, 0x13, 0xae, 0xb8, 0x2a, 0xdb, 0x15, - 0xbf, 0x07, 0x79, 0xa7, 0x09, 0x58, 0x4c, 0x9f, 0x32, 0xfb, 0x76, 0xad, 0x24, 0x66, 0xc3, 0xbb, - 0x63, 0x29, 0x0a, 0x94, 0x28, 0x5b, 0xd0, 0xce, 0xa2, 0xbc, 0xd0, 0x26, 0xbf, 0x74, 0x8f, 0x29, - 0x26, 0x02, 0x2f, 0xbd, 0x73, 0x53, 0xf0, 0x92, 0xd8, 0x34, 0xbc, 0x9b, 0xc3, 0x3e, 0x0b, 0x0f, - 0x0d, 0x31, 0x37, 0x39, 0x00, 0x99, 0xb9, 0x9d, 0xa2, 0xe5, 0x75, 0x74, 0x9a, 0x96, 0xb7, 0x7d, - 0x9f, 0x85, 0xca, 0x4e, 0xfd, 0xa8, 0xd1, 0x3c, 0xbf, 0xac, 0x18, 0x17, 0x97, 0x15, 0xe3, 0xcb, - 0x65, 0xc5, 0x78, 0x73, 0x55, 0xc9, 0x5d, 0x5c, 0x55, 0x72, 0x9f, 0xaf, 0x2a, 0xb9, 0xa7, 0xbb, - 0x01, 0xe6, 0x47, 0x71, 0xc7, 0xe9, 0xd2, 0xd0, 0x7d, 0x2c, 0xcf, 0x37, 0x8f, 0x7c, 0x4c, 0x92, - 0x5a, 0x43, 0x59, 0x8d, 0x9f, 0x45, 0x88, 0x75, 0xe6, 0xe5, 0x3f, 0xf6, 0x9f, 0xef, 0x01, 0x00, - 0x00, 0xff, 0xff, 0x46, 0x02, 0x07, 0x5c, 0xec, 0x07, 0x00, 0x00, + // 829 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xcf, 0x6b, 0xe3, 0x46, + 0x14, 0xb6, 0x62, 0x93, 0x38, 0x63, 0xbc, 0x4e, 0x26, 0xdb, 0x5d, 0x45, 0x04, 0xdb, 0x0c, 0x85, + 0x7a, 0x29, 0x48, 0xb5, 0x7b, 0x2b, 0x85, 0xb2, 0x72, 0x7b, 0x48, 0xc1, 0x4d, 0x50, 0xa0, 0x87, + 0x52, 0x30, 0x63, 0x7b, 0x50, 0x26, 0x95, 0x66, 0x84, 0x67, 0x94, 0xf5, 0x52, 0x7a, 0xe9, 0xb1, + 0xa7, 0x42, 0xff, 0x9e, 0xf6, 0xda, 0x3d, 0x2e, 0xf4, 0xd2, 0x93, 0x29, 0x49, 0xff, 0x02, 0x43, + 0x2f, 0x3d, 0x15, 0xcd, 0x8c, 0x14, 0x79, 0xd7, 0xa9, 0x29, 0x25, 0x37, 0xcd, 0xbc, 0x1f, 0xdf, + 0xfb, 0xbe, 0xf7, 0xde, 0x08, 0xb4, 0x66, 0x64, 0xe1, 0x5d, 0xf7, 0x3d, 0xb9, 0x70, 0x93, 0x39, + 0x97, 0x1c, 0x36, 0x19, 0x9d, 0xd0, 0x79, 0xea, 0xce, 0xc8, 0xc2, 0xbd, 0xee, 0x3b, 0x87, 0xc6, + 0x9e, 0x70, 0x1e, 0x69, 0x0f, 0xe7, 0x71, 0xc8, 0x43, 0xae, 0x3e, 0xbd, 0xec, 0xcb, 0xdc, 0xb6, + 0xa7, 0x5c, 0xc4, 0x5c, 0x78, 0x13, 0x2c, 0x88, 0x77, 0xdd, 0x9f, 0x10, 0x89, 0xfb, 0xde, 0x94, + 0x53, 0x66, 0xec, 0x27, 0x21, 0xe7, 0x61, 0x44, 0x3c, 0x9c, 0x50, 0x0f, 0x33, 0xc6, 0x25, 0x96, + 0x94, 0x33, 0xa1, 0xad, 0xe8, 0x17, 0x0b, 0x34, 0x47, 0x22, 0x1c, 0xce, 0x09, 0x96, 0xe4, 0x9c, + 0xf3, 0x08, 0xda, 0x60, 0x6f, 0x9a, 0x9d, 0xf8, 0xdc, 0xb6, 0xba, 0x56, 0x6f, 0x3f, 0xc8, 0x8f, + 0x30, 0x00, 0x8d, 0xac, 0x9a, 0x71, 0x82, 0xe7, 0x38, 0x16, 0xf6, 0x4e, 0xd7, 0xea, 0x35, 0x06, + 0xc7, 0xee, 0x5a, 0xdd, 0x6e, 0x96, 0xe3, 0x5c, 0x39, 0xf8, 0x4f, 0x56, 0xcb, 0x0e, 0x7c, 0x89, + 0xe3, 0xe8, 0x23, 0x54, 0x8a, 0x43, 0x01, 0x48, 0x0a, 0x1f, 0xf8, 0x89, 0xc9, 0x89, 0x85, 0x20, + 0x52, 0xd8, 0xd5, 0x6e, 0xb5, 0xd7, 0x18, 0xd8, 0x1b, 0x72, 0x3e, 0xcf, 0x1c, 0xfc, 0xda, 0xab, + 0x65, 0xa7, 0xa2, 0x13, 0xa8, 0x0b, 0x81, 0x3e, 0x00, 0xef, 0xac, 0xd5, 0x1f, 0x10, 0x91, 0x70, + 0x26, 0x08, 0x7c, 0x0a, 0xf6, 0x54, 0x66, 0x3a, 0x53, 0x3c, 0x6a, 0xc1, 0x6e, 0x76, 0x3c, 0x9d, + 0xa1, 0xbf, 0x2c, 0xd0, 0x18, 0x89, 0xf0, 0x73, 0x4e, 0x99, 0x22, 0xfc, 0x0c, 0xec, 0x0a, 0xc2, + 0x66, 0xc4, 0xf0, 0xf5, 0x0f, 0x57, 0xcb, 0x4e, 0x53, 0x97, 0xad, 0xef, 0x51, 0x60, 0x1c, 0xe0, + 0xfb, 0x77, 0x39, 0x33, 0xf6, 0x35, 0x1f, 0xae, 0x96, 0x9d, 0x47, 0x25, 0x8a, 0x74, 0x86, 0x72, + 0x1c, 0x78, 0x0e, 0xf6, 0x25, 0xff, 0x86, 0x30, 0x31, 0xa6, 0xcc, 0x10, 0x3b, 0x76, 0x75, 0xb3, + 0xdc, 0xac, 0x59, 0xae, 0x69, 0x96, 0x3b, 0xe4, 0x94, 0xf9, 0x76, 0xc6, 0x6c, 0xb5, 0xec, 0x1c, + 0xe8, 0x6c, 0x45, 0x24, 0x0a, 0xea, 0xfa, 0xfb, 0x94, 0xc1, 0x8f, 0x41, 0x33, 0x15, 0x64, 0x8c, + 0xa3, 0x68, 0x9c, 0x35, 0x58, 0xd8, 0xb5, 0xae, 0xd5, 0xab, 0xfb, 0xf6, 0x6a, 0xd9, 0x79, 0xac, + 0xc3, 0xd6, 0xcc, 0x28, 0x68, 0xa4, 0x82, 0x3c, 0x8f, 0xa2, 0xa1, 0x3a, 0xfd, 0xb0, 0x03, 0x8e, + 0x4a, 0xbc, 0x0b, 0xa1, 0xde, 0x03, 0xb5, 0xac, 0x62, 0xc5, 0xbe, 0x31, 0x38, 0xda, 0xa0, 0x7d, + 0xa0, 0x1c, 0x60, 0x04, 0x8e, 0x58, 0x1a, 0x8f, 0x15, 0x51, 0x71, 0x89, 0xe7, 0x44, 0x8c, 0x79, + 0x2a, 0x8b, 0x39, 0xb8, 0x97, 0x1a, 0x32, 0xd4, 0x1c, 0x5d, 0xe3, 0x86, 0x1c, 0x28, 0x38, 0x60, + 0x69, 0x9c, 0x41, 0x5d, 0xa8, 0xbb, 0xb3, 0x54, 0xc2, 0xaf, 0x41, 0x6b, 0x4e, 0x62, 0x4c, 0x19, + 0x65, 0xa1, 0xa1, 0xfb, 0x3f, 0x44, 0x7c, 0x54, 0xe4, 0xd2, 0x62, 0xfc, 0xac, 0x87, 0xe0, 0xb3, + 0x05, 0x95, 0x0f, 0x3a, 0x04, 0x5f, 0x9a, 0xf9, 0xd6, 0x5c, 0xed, 0xea, 0x36, 0xad, 0x1c, 0xc3, + 0xa0, 0xbc, 0x37, 0x3a, 0xd6, 0xec, 0x8d, 0x16, 0x08, 0x5d, 0xa9, 0x5e, 0xe6, 0xe5, 0x17, 0xbd, + 0xbc, 0x00, 0xc0, 0x90, 0xce, 0x3a, 0xb3, 0x55, 0xaf, 0x63, 0x83, 0x76, 0xb8, 0xa6, 0x97, 0x6a, + 0x88, 0x99, 0xdd, 0xb3, 0x54, 0xa2, 0xbf, 0xf5, 0x1b, 0x71, 0xf1, 0x02, 0x27, 0x7a, 0xe9, 0x1e, + 0x4c, 0xad, 0x11, 0xd0, 0xc3, 0xae, 0x37, 0x66, 0x8b, 0x54, 0x4f, 0x4d, 0xf1, 0xad, 0x52, 0xf1, + 0xaa, 0xd7, 0x7b, 0xea, 0xf3, 0x94, 0x41, 0x1f, 0xb4, 0xf4, 0x2d, 0x4f, 0xe5, 0x78, 0x46, 0x18, + 0x8f, 0xd5, 0xc6, 0xec, 0xfb, 0xce, 0x6a, 0xd9, 0x79, 0x52, 0x0e, 0x2b, 0x1c, 0x50, 0xd0, 0x54, + 0x37, 0x67, 0xa9, 0xfc, 0x54, 0x9d, 0xa9, 0x7a, 0x5f, 0xee, 0xb8, 0x17, 0x52, 0xe7, 0xeb, 0x6d, + 0x94, 0xb6, 0xfe, 0xfb, 0x64, 0x6a, 0xa1, 0xeb, 0x39, 0xde, 0xe0, 0xd7, 0x2a, 0xa8, 0x8e, 0x44, + 0x08, 0xaf, 0x00, 0x28, 0xbd, 0xc7, 0x27, 0x6f, 0x2c, 0xe4, 0xda, 0x6b, 0xe7, 0xbc, 0xfb, 0x6f, + 0xd6, 0xbc, 0x56, 0x64, 0x7f, 0xff, 0xdb, 0x9f, 0x3f, 0xed, 0x40, 0x74, 0xe0, 0x69, 0x6f, 0x2f, + 0xfb, 0xb9, 0xa8, 0x9d, 0x66, 0xa0, 0x5e, 0x3c, 0x84, 0xce, 0xdb, 0xb9, 0x72, 0x9b, 0x83, 0xee, + 0xb7, 0x15, 0x28, 0x48, 0xa1, 0x9c, 0x20, 0xa7, 0x8c, 0xf2, 0xad, 0x69, 0xf2, 0x77, 0xde, 0x15, + 0xa7, 0x2c, 0xc3, 0x2b, 0x76, 0x6e, 0x03, 0x5e, 0x6e, 0xdb, 0x84, 0xf7, 0xe6, 0xb0, 0x6f, 0xc3, + 0x23, 0x0b, 0x2a, 0xa1, 0x04, 0xa0, 0x34, 0xb7, 0x1b, 0xb4, 0xbc, 0xb3, 0x6e, 0xd2, 0xf2, 0xed, + 0xbe, 0x6f, 0x43, 0x15, 0x2f, 0x70, 0xe2, 0x0f, 0x5f, 0xdd, 0xb4, 0xad, 0xd7, 0x37, 0x6d, 0xeb, + 0x8f, 0x9b, 0xb6, 0xf5, 0xe3, 0x6d, 0xbb, 0xf2, 0xfa, 0xb6, 0x5d, 0xf9, 0xfd, 0xb6, 0x5d, 0xf9, + 0xea, 0x59, 0x48, 0xe5, 0x65, 0x3a, 0x71, 0xa7, 0x3c, 0xf6, 0xbe, 0x50, 0xf1, 0xc3, 0x4b, 0x4c, + 0x59, 0x9e, 0x6b, 0xa1, 0xb2, 0xc9, 0x97, 0x09, 0x11, 0x93, 0x5d, 0xf5, 0x87, 0xfe, 0xf0, 0x9f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x4e, 0x90, 0x89, 0x2a, 0x08, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -844,6 +854,16 @@ func (m *MsgJoinPool) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.UseAllCoins { + i-- + if m.UseAllCoins { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if len(m.TokensIn) > 0 { for iNdEx := len(m.TokensIn) - 1; iNdEx >= 0; iNdEx-- { { @@ -1164,6 +1184,9 @@ func (m *MsgJoinPool) Size() (n int) { n += 1 + l + sovTx(uint64(l)) } } + if m.UseAllCoins { + n += 2 + } return n } @@ -1595,6 +1618,26 @@ func (m *MsgJoinPool) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UseAllCoins", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.UseAllCoins = bool(v != 0) default: iNdEx = preIndex skippy, err := skipTx(dAtA[iNdEx:])