diff --git a/proto/neutron/dex/limit_order_tranche_user.proto b/proto/neutron/dex/limit_order_tranche_user.proto index d9ff3e853..d49c6c615 100644 --- a/proto/neutron/dex/limit_order_tranche_user.proto +++ b/proto/neutron/dex/limit_order_tranche_user.proto @@ -18,7 +18,10 @@ message LimitOrderTrancheUser { (gogoproto.nullable) = false, (gogoproto.jsontag) = "shares_owned" ]; + + // DEPRECATED: shares_withdrawn will be removed in a future release, `dec_shares_withdrawn` should be used string shares_withdrawn = 6 [ + deprecated = true, (gogoproto.moretags) = "yaml:\"shares_withdrawn\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, @@ -32,4 +35,10 @@ message LimitOrderTrancheUser { (gogoproto.jsontag) = "shares_cancelled" ]; LimitOrderType order_type = 8; + string dec_shares_withdrawn = 9 [ + (gogoproto.moretags) = "yaml:\"dec_shares_withdrawn\"", + (gogoproto.customtype) = "github.com/neutron-org/neutron/v10/utils/math.PrecDec", + (gogoproto.nullable) = false, + (gogoproto.jsontag) = "dec_shares_withdrawn" + ]; } diff --git a/tests/dex/state_withdraw_limit_order_test.go b/tests/dex/state_withdraw_limit_order_test.go index d59f86948..2e28c8afd 100644 --- a/tests/dex/state_withdraw_limit_order_test.go +++ b/tests/dex/state_withdraw_limit_order_test.go @@ -187,8 +187,10 @@ func (s *DexStateTestSuite) assertWithdrawFilledAmount(params withdrawLimitOrder s.False(found) } else { s.True(found) - remainingShares := ut.SharesOwned.Sub(ut.SharesWithdrawn) - s.True(expectedBalanceA.Equal(remainingShares), "Expected Balance A %v != Actual %v", expectedBalanceA, remainingShares) + sharesOwnedDec := math_utils.NewPrecDecFromInt(ut.SharesOwned) + remainingShares := sharesOwnedDec.Sub(ut.DecSharesWithdrawn) + expectedBalanceADec := math_utils.NewPrecDecFromInt(expectedBalanceA) + s.True(expectedBalanceADec.Equal(remainingShares), "Expected Balance A %v != Actual %v", expectedBalanceA, remainingShares) } } } diff --git a/x/dex/genesis_test.go b/x/dex/genesis_test.go index 1c08b5b36..3397aeb5a 100644 --- a/x/dex/genesis_test.go +++ b/x/dex/genesis_test.go @@ -9,6 +9,7 @@ import ( "github.com/neutron-org/neutron/v10/testutil/common/nullify" keepertest "github.com/neutron-org/neutron/v10/testutil/dex/keeper" + math_utils "github.com/neutron-org/neutron/v10/utils/math" "github.com/neutron-org/neutron/v10/x/dex" "github.com/neutron-org/neutron/v10/x/dex/types" ) @@ -26,7 +27,7 @@ func TestGenesis(t *testing.T) { TrancheKey: "0", Address: "fakeAddr", SharesOwned: math.NewInt(10), - SharesWithdrawn: math.NewInt(0), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), }, { TradePairId: &types.TradePairID{ @@ -37,7 +38,7 @@ func TestGenesis(t *testing.T) { TrancheKey: "0", Address: "fakeAddr", SharesOwned: math.NewInt(10), - SharesWithdrawn: math.NewInt(0), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), }, }, TickLiquidityList: []*types.TickLiquidity{ diff --git a/x/dex/keeper/cancel_limit_order.go b/x/dex/keeper/cancel_limit_order.go index a444680e4..583246403 100644 --- a/x/dex/keeper/cancel_limit_order.go +++ b/x/dex/keeper/cancel_limit_order.go @@ -91,8 +91,8 @@ func (k Keeper) ExecuteCancelLimitOrder( tranche.TotalMakerDenom = tranche.TotalMakerDenom.Sub(trancheUser.SharesOwned) // Calculate total number of shares removed previously withdrawn by the user (denominated in takerDenom) - sharesWithdrawnTakerDenom := math_utils.NewPrecDecFromInt(trancheUser.SharesWithdrawn). - Quo(tranche.PriceTakerToMaker) + sharesWithdrawnTakerDenom := trancheUser.DecSharesWithdrawn. + Mul(tranche.MakerPrice) // Calculate the total amount removed including prior withdrawals (denominated in takerDenom) totalAmountOutTakerDenom := sharesWithdrawnTakerDenom.Add(takerAmountOut) @@ -101,7 +101,7 @@ func (k Keeper) ExecuteCancelLimitOrder( tranche.SetTotalTakerDenom(tranche.DecTotalTakerDenom.Sub(totalAmountOutTakerDenom)) // Set TrancheUser to 100% shares withdrawn - trancheUser.SharesWithdrawn = trancheUser.SharesOwned + trancheUser.SetSharesWithdrawn(math_utils.NewPrecDecFromInt(trancheUser.SharesOwned)) if !makerAmountToReturn.IsPositive() && !takerAmountOut.IsPositive() { return types.PrecDecCoin{}, types.PrecDecCoin{}, sdkerrors.Wrapf(types.ErrCancelEmptyLimitOrder, "%s", tranche.Key.TrancheKey) diff --git a/x/dex/keeper/grpc_query_limit_order_tranche_user.go b/x/dex/keeper/grpc_query_limit_order_tranche_user.go index 30c387522..92bce2e27 100644 --- a/x/dex/keeper/grpc_query_limit_order_tranche_user.go +++ b/x/dex/keeper/grpc_query_limit_order_tranche_user.go @@ -3,13 +3,13 @@ package keeper import ( "context" - "cosmossdk.io/math" "cosmossdk.io/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + math_utils "github.com/neutron-org/neutron/v10/utils/math" "github.com/neutron-org/neutron/v10/x/dex/types" ) @@ -47,7 +47,7 @@ func (k Keeper) LimitOrderTrancheUserAll( }, nil } -func (k Keeper) CalcWithdrawableShares(ctx sdk.Context, trancheUser types.LimitOrderTrancheUser) (amount math.Int, err error) { +func (k Keeper) CalcWithdrawableShares(ctx sdk.Context, trancheUser types.LimitOrderTrancheUser) (amount math_utils.PrecDec, err error) { tradePairID, tickIndex := trancheUser.TradePairId, trancheUser.TickIndexTakerToMaker tranche, _, found := k.FindLimitOrderTranche( @@ -60,7 +60,7 @@ func (k Keeper) CalcWithdrawableShares(ctx sdk.Context, trancheUser types.LimitO ) if !found { - return math.ZeroInt(), status.Error(codes.NotFound, "Tranche not found") + return math_utils.ZeroPrecDec(), status.Error(codes.NotFound, "Tranche not found") } withdrawableShares, _ := tranche.CalcWithdrawAmount(&trancheUser) @@ -89,7 +89,8 @@ func (k Keeper) LimitOrderTrancheUser(c context.Context, if err != nil { return nil, err } - resp.WithdrawableShares = &withdrawAmt + withdrawAmtInt := withdrawAmt.TruncateInt() + resp.WithdrawableShares = &withdrawAmtInt } return resp, nil diff --git a/x/dex/keeper/integration_cancellimitorder_test.go b/x/dex/keeper/integration_cancellimitorder_test.go index 41551d42f..5a0e29e03 100644 --- a/x/dex/keeper/integration_cancellimitorder_test.go +++ b/x/dex/keeper/integration_cancellimitorder_test.go @@ -532,9 +532,9 @@ func (s *DexTestSuite) TestWithdrawThenCancelLowTick() { s.assertAliceBalancesInt(sdkmath.NewInt(13058413), sdkmath.NewInt(4999999)) s.bobWithdrawsLimitSell(trancheKey) - s.assertBobBalancesInt(sdkmath.ZeroInt(), sdkmath.NewInt(4999999)) + s.assertBobBalances(0, 5) s.bobCancelsLimitSell(trancheKey) - s.assertBobBalancesInt(sdkmath.NewInt(13058413), sdkmath.NewInt(4999999)) + s.assertBobBalancesInt(sdkmath.NewInt(13058413), sdkmath.NewInt(5_000_000)) } func (s *DexTestSuite) TestWrongSharesProtectionCancel() { @@ -553,3 +553,51 @@ func (s *DexTestSuite) TestWrongSharesProtectionCancel() { s.assertAliceBalances(1, 0) } + +func (s *DexTestSuite) TestCanceLimitOrderClearsPosition() { + s.fundAccountBalancesInt(s.alice, sdkmath.NewInt(10_000), sdkmath.ZeroInt()) + s.fundAccountBalancesInt(s.bob, sdkmath.NewInt(500_000), sdkmath.ZeroInt()) + s.fundAccountBalancesInt(s.carol, sdkmath.ZeroInt(), sdkmath.NewInt(22_015)) + + // GIVEN alice and bob place GTC limit sells at tick -100000 + trancheKey := s.limitSellsIntSuccess(s.alice, "TokenA", -100000, sdkmath.NewInt(10_000)) + s.limitSellsIntSuccess(s.bob, "TokenA", -100000, sdkmath.NewInt(500_000)) + + // AND carol swaps 22,015 TokenB for TokeA + s.limitSellsIntSuccess(s.carol, "TokenB", -100001, sdkmath.NewInt(22_015), types.LimitOrderType_FILL_OR_KILL) + + // WHEN Alice withdraws then cancels + s.aliceWithdrawsLimitSell(trancheKey) + s.aliceCancelsLimitSell(trancheKey) + + // THEN totalTakerDenom == ReservesTakerDenom + tranche, _, found := s.App.DexKeeper.FindLimitOrderTranche( + s.Ctx, + &types.LimitOrderTrancheKey{ + TradePairId: types.MustNewTradePairID("TokenB", "TokenA"), + TickIndexTakerToMaker: 100000, + TrancheKey: trancheKey, + }, + ) + s.True(found) + + s.Equal(tranche.DecTotalTakerDenom, tranche.DecReservesTakerDenom) + + // WHEN bob cancels his limit order, the tranche only contains dust + s.bobCancelsLimitSell(trancheKey) + + // Final tranche state + tranche, _, found = s.App.DexKeeper.FindLimitOrderTranche( + s.Ctx, + &types.LimitOrderTrancheKey{ + TradePairId: types.MustNewTradePairID("TokenB", "TokenA"), + TickIndexTakerToMaker: 100000, + TrancheKey: trancheKey, + }, + ) + s.True(found) + + // NOTE: we are using the Int fields just to ensure only dust is left + s.Assert().Equal(tranche.ReservesTakerDenom, sdkmath.ZeroInt()) + s.Assert().Equal(tranche.TotalTakerDenom, sdkmath.ZeroInt()) +} diff --git a/x/dex/keeper/limit_order_tranche_user.go b/x/dex/keeper/limit_order_tranche_user.go index 017279bd3..fdda299ac 100644 --- a/x/dex/keeper/limit_order_tranche_user.go +++ b/x/dex/keeper/limit_order_tranche_user.go @@ -7,6 +7,7 @@ import ( storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" + math_utils "github.com/neutron-org/neutron/v10/utils/math" "github.com/neutron-org/neutron/v10/x/dex/types" ) @@ -33,6 +34,7 @@ func (k Keeper) GetOrInitLimitOrderTrancheUser( Address: receiver, SharesOwned: math.ZeroInt(), SharesWithdrawn: math.ZeroInt(), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), TickIndexTakerToMaker: tickIndex, TradePairId: tradePairID, OrderType: orderType, diff --git a/x/dex/keeper/limit_order_tranche_user_test.go b/x/dex/keeper/limit_order_tranche_user_test.go index 46143710b..8fb93ec0a 100644 --- a/x/dex/keeper/limit_order_tranche_user_test.go +++ b/x/dex/keeper/limit_order_tranche_user_test.go @@ -11,6 +11,7 @@ import ( "github.com/neutron-org/neutron/v10/testutil/common/nullify" keepertest "github.com/neutron-org/neutron/v10/testutil/dex/keeper" + math_utils "github.com/neutron-org/neutron/v10/utils/math" "github.com/neutron-org/neutron/v10/x/dex/keeper" "github.com/neutron-org/neutron/v10/x/dex/types" ) @@ -24,7 +25,7 @@ func createNLimitOrderTrancheUser(keeper *keeper.Keeper, ctx sdk.Context, n int) TradePairId: &types.TradePairID{MakerDenom: "TokenA", TakerDenom: "TokenB"}, TickIndexTakerToMaker: int64(i), SharesOwned: math.NewInt(100), - SharesWithdrawn: math.ZeroInt(), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), } items[i] = val keeper.SetLimitOrderTrancheUser(ctx, items[i]) @@ -42,7 +43,7 @@ func createNLimitOrderTrancheUserWithAddress(keeper *keeper.Keeper, ctx sdk.Cont TradePairId: &types.TradePairID{MakerDenom: "TokenA", TakerDenom: "TokenB"}, TickIndexTakerToMaker: 0, SharesOwned: math.ZeroInt(), - SharesWithdrawn: math.ZeroInt(), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), } items[i] = val keeper.SetLimitOrderTrancheUser(ctx, items[i]) @@ -91,7 +92,8 @@ func (s *DexTestSuite) TestGetAllLimitOrders() { TrancheKey: trancheKeyA, Address: s.alice.String(), SharesOwned: math.NewInt(10_000_000), - SharesWithdrawn: math.NewInt(0), + SharesWithdrawn: math.ZeroInt(), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), SharesCancelled: math.ZeroInt(), }, LOList[0], @@ -102,7 +104,8 @@ func (s *DexTestSuite) TestGetAllLimitOrders() { TrancheKey: trancheKeyB, Address: s.alice.String(), SharesOwned: math.NewInt(10_000_000), - SharesWithdrawn: math.NewInt(0), + SharesWithdrawn: math.ZeroInt(), + DecSharesWithdrawn: math_utils.ZeroPrecDec(), SharesCancelled: math.ZeroInt(), }, LOList[1], diff --git a/x/dex/keeper/migrations.go b/x/dex/keeper/migrations.go index e9c7e42ab..c14916e17 100644 --- a/x/dex/keeper/migrations.go +++ b/x/dex/keeper/migrations.go @@ -7,6 +7,7 @@ import ( v4 "github.com/neutron-org/neutron/v10/x/dex/migrations/v4" v5 "github.com/neutron-org/neutron/v10/x/dex/migrations/v5" v6 "github.com/neutron-org/neutron/v10/x/dex/migrations/v6" + v7 "github.com/neutron-org/neutron/v10/x/dex/migrations/v7" ) // Migrator is a struct for handling in-place store migrations. @@ -38,3 +39,8 @@ func (m Migrator) Migrate4to5(ctx sdk.Context) error { func (m Migrator) Migrate5to6(ctx sdk.Context) error { return v6.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) } + +// Migrate6to7 migrates from version 6 to 7. +func (m Migrator) Migrate6to7(ctx sdk.Context) error { + return v7.MigrateStore(ctx, m.keeper.cdc, m.keeper.storeKey) +} diff --git a/x/dex/keeper/withdraw_filled_limit_order.go b/x/dex/keeper/withdraw_filled_limit_order.go index 31a980941..41b90d24c 100644 --- a/x/dex/keeper/withdraw_filled_limit_order.go +++ b/x/dex/keeper/withdraw_filled_limit_order.go @@ -4,7 +4,6 @@ import ( "context" sdkerrors "cosmossdk.io/errors" - "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" math_utils "github.com/neutron-org/neutron/v10/utils/math" @@ -82,7 +81,7 @@ func (k Keeper) ExecuteWithdrawFilledLimitOrder( remainingTokenIn := math_utils.ZeroPrecDec() // It's possible that a TrancheUser exists but tranche does not if LO was filled entirely through a swap if found { - var amountOutTokenIn math.Int + var amountOutTokenIn math_utils.PrecDec amountOutTokenIn, amountOutTokenOut, err = tranche.Withdraw(trancheUser) if err != nil { return types.PrecDecCoin{}, types.PrecDecCoin{}, err @@ -97,12 +96,13 @@ func (k Keeper) ExecuteWithdrawFilledLimitOrder( k.UpdateInactiveTranche(ctx, tranche) // Since the order has already been filled we treat this as a complete withdrawal - trancheUser.SharesWithdrawn = trancheUser.SharesOwned + trancheUser.SetSharesWithdrawn(math_utils.NewPrecDecFromInt(trancheUser.SharesOwned)) } else { // This was an active tranche (still has MakerReserves) and we have only removed TakerReserves; we will save it as an active tranche k.UpdateTranche(ctx, tranche) - trancheUser.SharesWithdrawn = trancheUser.SharesWithdrawn.Add(amountOutTokenIn) + totalSharesWithdrawn := trancheUser.DecSharesWithdrawn.Add(amountOutTokenIn) + trancheUser.SetSharesWithdrawn(totalSharesWithdrawn) } } diff --git a/x/dex/migrations/v7/store.go b/x/dex/migrations/v7/store.go new file mode 100644 index 000000000..66550f1c0 --- /dev/null +++ b/x/dex/migrations/v7/store.go @@ -0,0 +1,61 @@ +package v7 + +import ( + errorsmod "cosmossdk.io/errors" + "cosmossdk.io/store/prefix" + storetypes "cosmossdk.io/store/types" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + + math_utils "github.com/neutron-org/neutron/v10/utils/math" + "github.com/neutron-org/neutron/v10/x/dex/types" +) + +// MigrateStore performs in-place store migrations. +// Add DecSharesWithdrawn field to all LimitOrderTrancheUser +func MigrateStore(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { + if err := migrateLimitOrderTrancheUsers(ctx, cdc, storeKey); err != nil { + return err + } + + return nil +} + +type migrationUpdate struct { + key []byte + val []byte +} + +func migrateLimitOrderTrancheUsers(ctx sdk.Context, cdc codec.BinaryCodec, storeKey storetypes.StoreKey) error { + ctx.Logger().Info("Migrating LimitOrderTrancheUser fields...") + + // Iterate through all LimitOrderTrancheUser + store := prefix.NewStore(ctx.KVStore(storeKey), types.KeyPrefix(types.LimitOrderTrancheUserKeyPrefix)) + iterator := storetypes.KVStorePrefixIterator(store, []byte{}) + trancheUserUpdates := make([]migrationUpdate, 0) + + for ; iterator.Valid(); iterator.Next() { + var limitOrderTrancheUser types.LimitOrderTrancheUser + cdc.MustUnmarshal(iterator.Value(), &limitOrderTrancheUser) + + limitOrderTrancheUser.DecSharesWithdrawn = math_utils.NewPrecDecFromInt(limitOrderTrancheUser.SharesWithdrawn) + + bz := cdc.MustMarshal(&limitOrderTrancheUser) + trancheUserUpdates = append(trancheUserUpdates, migrationUpdate{key: iterator.Key(), val: bz}) + + } + + err := iterator.Close() + if err != nil { + return errorsmod.Wrap(err, "iterator failed to close during migration") + } + + // Store the updated LimitOrderTrancheUser + for _, v := range trancheUserUpdates { + store.Set(v.key, v.val) + } + + ctx.Logger().Info("Finished migrating LimitOrderTrancheUser fields...") + + return nil +} diff --git a/x/dex/migrations/v7/store_test.go b/x/dex/migrations/v7/store_test.go new file mode 100644 index 000000000..b79ec6841 --- /dev/null +++ b/x/dex/migrations/v7/store_test.go @@ -0,0 +1,59 @@ +package v7_test + +import ( + "testing" + + "cosmossdk.io/math" + v7 "github.com/neutron-org/neutron/v10/x/dex/migrations/v7" + "github.com/stretchr/testify/suite" + + "github.com/neutron-org/neutron/v10/testutil" + math_utils "github.com/neutron-org/neutron/v10/utils/math" + "github.com/neutron-org/neutron/v10/x/dex/types" +) + +type V7DexMigrationTestSuite struct { + testutil.IBCConnectionTestSuite +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(V7DexMigrationTestSuite)) +} + +func (suite *V7DexMigrationTestSuite) TestFieldUpdates() { + var ( + app = suite.GetNeutronZoneApp(suite.ChainA) + storeKey = app.GetKey(types.StoreKey) + ctx = suite.ChainA.GetContext() + cdc = app.AppCodec() + ) + + trancheUser0 := &types.LimitOrderTrancheUser{ + TrancheKey: "123", + Address: "alice", + SharesOwned: math.NewInt(100), + SharesWithdrawn: math.ZeroInt(), + } + + trancheUser1 := &types.LimitOrderTrancheUser{ + TrancheKey: "123", + Address: "bob", + SharesOwned: math.NewInt(100), + SharesWithdrawn: math.NewInt(10), + } + + app.DexKeeper.SetLimitOrderTrancheUser(ctx, trancheUser0) + app.DexKeeper.SetLimitOrderTrancheUser(ctx, trancheUser1) + + suite.NoError(v7.MigrateStore(ctx, cdc, storeKey)) + + migratedTrancheUser0, found := app.DexKeeper.GetLimitOrderTrancheUser(ctx, trancheUser0.Address, trancheUser0.TrancheKey) + suite.True(found) + migratedTrancheUser1, found := app.DexKeeper.GetLimitOrderTrancheUser(ctx, trancheUser1.Address, trancheUser1.TrancheKey) + suite.True(found) + + suite.Equal(math.ZeroInt(), migratedTrancheUser0.SharesWithdrawn) + suite.Equal(math_utils.ZeroPrecDec(), migratedTrancheUser0.DecSharesWithdrawn) + suite.Equal(math.NewInt(10), migratedTrancheUser1.SharesWithdrawn) + suite.Equal(math_utils.NewPrecDec(10), migratedTrancheUser1.DecSharesWithdrawn) +} diff --git a/x/dex/module.go b/x/dex/module.go index 7b427ee9f..9b6859e25 100644 --- a/x/dex/module.go +++ b/x/dex/module.go @@ -168,6 +168,9 @@ func (am AppModule) RegisterServices(cfg module.Configurator) { if err := cfg.RegisterMigration(types.ModuleName, 5, m.Migrate5to6); err != nil { panic(fmt.Sprintf("failed to migrate x/dex from version 5 to 6: %v", err)) } + if err := cfg.RegisterMigration(types.ModuleName, 6, m.Migrate6to7); err != nil { + panic(fmt.Sprintf("failed to migrate x/dex from version 6 to 7: %v", err)) + } } // RegisterInvariants registers the capability module's invariants. diff --git a/x/dex/types/constants.go b/x/dex/types/constants.go index c6914d566..389c52fca 100644 --- a/x/dex/types/constants.go +++ b/x/dex/types/constants.go @@ -1,3 +1,3 @@ package types -const ConsensusVersion = 6 +const ConsensusVersion = 7 diff --git a/x/dex/types/events.go b/x/dex/types/events.go index 03e711f1f..990a61663 100644 --- a/x/dex/types/events.go +++ b/x/dex/types/events.go @@ -69,6 +69,7 @@ const ( AttributeTakerDenom = "TakerDenom" AttributeSharesOwned = "SharesOwned" AttributeSharesWithdrawn = "SharesWithdrawn" + AttributeDecSharesWithdrawn = "DecSharesWithdrawn" AttributeMinAvgSellPrice = "MinAvgSellPrice" AttributeMaxAmountOut = "MaxAmountOut" AttributeRequestAmountIn = "RequestAmountIn" @@ -521,6 +522,7 @@ func TrancheUserUpdateEvent(trancheUser LimitOrderTrancheUser) sdk.Event { sdk.NewAttribute(AttributeTokenIn, trancheUser.TradePairId.MakerDenom), sdk.NewAttribute(AttributeSharesOwned, trancheUser.SharesOwned.String()), sdk.NewAttribute(AttributeSharesWithdrawn, trancheUser.SharesWithdrawn.String()), + sdk.NewAttribute(AttributeDecSharesWithdrawn, trancheUser.DecSharesWithdrawn.String()), } return sdk.NewEvent(EventTypeTrancheUserUpdate, attrs...) } diff --git a/x/dex/types/limit_order_tranche.go b/x/dex/types/limit_order_tranche.go index 61fccfff5..9758458c5 100644 --- a/x/dex/types/limit_order_tranche.go +++ b/x/dex/types/limit_order_tranche.go @@ -153,31 +153,30 @@ func (t *LimitOrderTranche) RemoveTokenIn( return amountToRemove, nil } -func (t *LimitOrderTranche) CalcWithdrawAmount(trancheUser *LimitOrderTrancheUser) (sharesToWithdraw math.Int, tokenOut math_utils.PrecDec) { +func (t *LimitOrderTranche) CalcWithdrawAmount(trancheUser *LimitOrderTrancheUser) (sharesToWithdraw, tokenOut math_utils.PrecDec) { ratioFilled := t.RatioFilled() maxAllowedToWithdraw := ratioFilled.MulInt(trancheUser.SharesOwned) - sharesToWithdrawDec := maxAllowedToWithdraw.Sub(math_utils.NewPrecDecFromInt(trancheUser.SharesWithdrawn)) + sharesToWithdrawDec := maxAllowedToWithdraw.Sub(trancheUser.DecSharesWithdrawn) // Given rounding it is possible for sharesToWithdrawn > maxAllowedToWithdraw. In this case we just exit. if !sharesToWithdrawDec.IsPositive() { - return math.ZeroInt(), math_utils.ZeroPrecDec() + return math_utils.ZeroPrecDec(), math_utils.ZeroPrecDec() } // Cap the amount out at the reserves of the tranche // Due to decimal limitations its possible amountOutTokenOutDec > t.DecReservesTakerDenom amountOutTokenOutDec := math_utils.MinPrecDec(sharesToWithdrawDec.Mul(t.MakerPrice), t.DecReservesTakerDenom) - // Round shares withdrawn up to ensure math favors dex - return sharesToWithdrawDec.Ceil().TruncateInt(), amountOutTokenOutDec + return sharesToWithdrawDec, amountOutTokenOutDec } -func (t *LimitOrderTranche) Withdraw(trancheUser *LimitOrderTrancheUser) (sharesWithdrawn math.Int, tokenOut math_utils.PrecDec, err error) { +func (t *LimitOrderTranche) Withdraw(trancheUser *LimitOrderTrancheUser) (sharesWithdrawn, tokenOut math_utils.PrecDec, err error) { amountOutTokenIn, amountOutTokenOut := t.CalcWithdrawAmount(trancheUser) t.SetTakerReserves(t.DecReservesTakerDenom.Sub(amountOutTokenOut)) // This error should be impossible, but we check it for safety if t.DecReservesTakerDenom.IsNegative() { - return math.ZeroInt(), math_utils.ZeroPrecDec(), sdkerrors.Wrapf(ErrInsufficientReserves, "%s", t.Key.TrancheKey) + return math_utils.ZeroPrecDec(), math_utils.ZeroPrecDec(), sdkerrors.Wrapf(ErrInsufficientReserves, "%s", t.Key.TrancheKey) } return amountOutTokenIn, amountOutTokenOut, nil } @@ -236,6 +235,9 @@ func (t *LimitOrderTranche) SetTakerReserves(reserves math_utils.PrecDec) { } func (t *LimitOrderTranche) SetTotalTakerDenom(reserves math_utils.PrecDec) { + if reserves.IsNegative() { + panic("TotalTakerDenom cannot be negative") + } t.TotalTakerDenom = reserves.TruncateInt() t.DecTotalTakerDenom = reserves } diff --git a/x/dex/types/limit_order_tranche_user.go b/x/dex/types/limit_order_tranche_user.go index 51f923210..b8a51768e 100644 --- a/x/dex/types/limit_order_tranche_user.go +++ b/x/dex/types/limit_order_tranche_user.go @@ -1,5 +1,14 @@ package types +import ( + math_utils "github.com/neutron-org/neutron/v10/utils/math" +) + func (l LimitOrderTrancheUser) IsEmpty() bool { - return l.SharesWithdrawn.Equal(l.SharesOwned) + return l.DecSharesWithdrawn.Equal(math_utils.NewPrecDecFromInt(l.SharesOwned)) +} + +func (l *LimitOrderTrancheUser) SetSharesWithdrawn(shares math_utils.PrecDec) { + l.SharesWithdrawn = shares.TruncateInt() + l.DecSharesWithdrawn = shares } diff --git a/x/dex/types/limit_order_tranche_user.pb.go b/x/dex/types/limit_order_tranche_user.pb.go index 433523863..a50d79721 100644 --- a/x/dex/types/limit_order_tranche_user.pb.go +++ b/x/dex/types/limit_order_tranche_user.pb.go @@ -8,6 +8,7 @@ import ( fmt "fmt" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" + github_com_neutron_org_neutron_v10_utils_math "github.com/neutron-org/neutron/v10/utils/math" io "io" math "math" math_bits "math/bits" @@ -30,10 +31,12 @@ type LimitOrderTrancheUser struct { TrancheKey string `protobuf:"bytes,3,opt,name=tranche_key,json=trancheKey,proto3" json:"tranche_key,omitempty"` Address string `protobuf:"bytes,4,opt,name=address,proto3" json:"address,omitempty"` SharesOwned cosmossdk_io_math.Int `protobuf:"bytes,5,opt,name=shares_owned,json=sharesOwned,proto3,customtype=cosmossdk.io/math.Int" json:"shares_owned" yaml:"shares_owned"` - SharesWithdrawn cosmossdk_io_math.Int `protobuf:"bytes,6,opt,name=shares_withdrawn,json=sharesWithdrawn,proto3,customtype=cosmossdk.io/math.Int" json:"shares_withdrawn" yaml:"shares_withdrawn"` + // DEPRECATED: shares_withdrawn will be removed in a future release, `dec_shares_withdrawn` should be used + SharesWithdrawn cosmossdk_io_math.Int `protobuf:"bytes,6,opt,name=shares_withdrawn,json=sharesWithdrawn,proto3,customtype=cosmossdk.io/math.Int" json:"shares_withdrawn" yaml:"shares_withdrawn"` // Deprecated: Do not use. // TODO: remove this in next release. It is no longer used - SharesCancelled cosmossdk_io_math.Int `protobuf:"bytes,7,opt,name=shares_cancelled,json=sharesCancelled,proto3,customtype=cosmossdk.io/math.Int" json:"shares_cancelled" yaml:"shares_cancelled"` - OrderType LimitOrderType `protobuf:"varint,8,opt,name=order_type,json=orderType,proto3,enum=neutron.dex.LimitOrderType" json:"order_type,omitempty"` + SharesCancelled cosmossdk_io_math.Int `protobuf:"bytes,7,opt,name=shares_cancelled,json=sharesCancelled,proto3,customtype=cosmossdk.io/math.Int" json:"shares_cancelled" yaml:"shares_cancelled"` + OrderType LimitOrderType `protobuf:"varint,8,opt,name=order_type,json=orderType,proto3,enum=neutron.dex.LimitOrderType" json:"order_type,omitempty"` + DecSharesWithdrawn github_com_neutron_org_neutron_v10_utils_math.PrecDec `protobuf:"bytes,9,opt,name=dec_shares_withdrawn,json=decSharesWithdrawn,proto3,customtype=github.com/neutron-org/neutron/v10/utils/math.PrecDec" json:"dec_shares_withdrawn" yaml:"dec_shares_withdrawn"` } func (m *LimitOrderTrancheUser) Reset() { *m = LimitOrderTrancheUser{} } @@ -113,37 +116,41 @@ func init() { } var fileDescriptor_67e5ffbd487ea05f = []byte{ - // 470 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x92, 0x4d, 0x6f, 0xd3, 0x4e, - 0x10, 0xc6, 0xe3, 0x7f, 0xfb, 0x6f, 0xe9, 0x9a, 0x37, 0x99, 0x46, 0x2c, 0x45, 0xb2, 0xa3, 0x9c, - 0x22, 0x24, 0x6c, 0x28, 0x17, 0x54, 0x71, 0x2a, 0x3d, 0x10, 0x51, 0x54, 0x64, 0x05, 0x21, 0x71, - 0xb1, 0xb6, 0xde, 0x91, 0xbd, 0x4a, 0xec, 0xb5, 0x76, 0x37, 0xc4, 0xfe, 0x16, 0x7c, 0xac, 0x1e, - 0x7b, 0x44, 0x1c, 0x2c, 0x48, 0x6e, 0x1c, 0xfb, 0x09, 0xd0, 0xc6, 0x2f, 0xb2, 0xd5, 0x03, 0xe2, - 0xe4, 0x99, 0xe7, 0x79, 0x66, 0x7e, 0xb6, 0x35, 0xe8, 0x59, 0x0a, 0x4b, 0x25, 0x78, 0xea, 0x51, - 0xc8, 0xbd, 0x05, 0x4b, 0x98, 0x0a, 0xb8, 0xa0, 0x20, 0x02, 0x25, 0x48, 0x1a, 0xc6, 0x10, 0x2c, - 0x25, 0x08, 0x37, 0x13, 0x5c, 0x71, 0xcb, 0xac, 0xb3, 0x2e, 0x85, 0xfc, 0xe8, 0x30, 0xe2, 0x11, - 0xdf, 0xea, 0x9e, 0xae, 0xaa, 0xc8, 0x91, 0xd3, 0x5d, 0xa7, 0x04, 0xa1, 0x10, 0x64, 0x84, 0x89, - 0x80, 0xd1, 0x3a, 0x70, 0xd8, 0x0b, 0xe4, 0x95, 0x3a, 0xfe, 0xb5, 0x8b, 0x86, 0xe7, 0x1a, 0x7e, - 0xa1, 0xd9, 0xb3, 0x0a, 0xfd, 0x49, 0x82, 0xb0, 0xde, 0xa0, 0x7b, 0xbd, 0x35, 0xd8, 0x18, 0x19, - 0x13, 0xf3, 0x18, 0xbb, 0x9d, 0x77, 0x71, 0x67, 0x3a, 0xf1, 0x91, 0x30, 0x31, 0x3d, 0xf3, 0x4d, - 0xd5, 0x36, 0xd4, 0x7a, 0x8d, 0x9e, 0x28, 0x16, 0xce, 0x03, 0x96, 0x52, 0xc8, 0x03, 0x45, 0xe6, - 0xfa, 0xc3, 0x78, 0x90, 0xe8, 0x02, 0xff, 0x37, 0x32, 0x26, 0x3b, 0xfe, 0x50, 0x07, 0xa6, 0xda, - 0x9f, 0x69, 0x75, 0xc6, 0x3f, 0xe8, 0x87, 0xe5, 0x20, 0xb3, 0xf9, 0x03, 0x73, 0x28, 0xf0, 0xce, - 0xc8, 0x98, 0x1c, 0xf8, 0xa8, 0x96, 0xde, 0x43, 0x61, 0x61, 0xb4, 0x4f, 0x28, 0x15, 0x20, 0x25, - 0xde, 0xdd, 0x9a, 0x4d, 0x6b, 0x45, 0xe8, 0xae, 0x8c, 0x89, 0x00, 0x19, 0xf0, 0x55, 0x0a, 0x14, - 0xff, 0xaf, 0xed, 0xd3, 0xb3, 0xab, 0xd2, 0x19, 0xfc, 0x28, 0x9d, 0x61, 0xc8, 0x65, 0xc2, 0xa5, - 0xa4, 0x73, 0x97, 0x71, 0x2f, 0x21, 0x2a, 0x76, 0xa7, 0xa9, 0xfa, 0x5d, 0x3a, 0xbd, 0xa1, 0x9b, - 0xd2, 0x79, 0x54, 0x90, 0x64, 0x71, 0x32, 0xee, 0xaa, 0x63, 0xdf, 0xac, 0xda, 0x0b, 0xdd, 0x59, - 0x2b, 0xf4, 0xb0, 0x76, 0x57, 0x4c, 0xc5, 0x54, 0x90, 0x55, 0x8a, 0xf7, 0xb6, 0xb0, 0xf3, 0xbf, - 0xc1, 0x6e, 0x0d, 0xde, 0x94, 0xce, 0xe3, 0x1e, 0xb0, 0x75, 0xc6, 0xfe, 0x83, 0x4a, 0xfa, 0xdc, - 0x28, 0x1d, 0x70, 0x48, 0xd2, 0x10, 0x16, 0x0b, 0xa0, 0x78, 0xff, 0xdf, 0xc0, 0xed, 0xe0, 0x2d, - 0x70, 0xeb, 0xb4, 0xe0, 0xb7, 0x8d, 0x62, 0x9d, 0x20, 0x54, 0x5f, 0x67, 0x91, 0x01, 0xbe, 0x33, - 0x32, 0x26, 0xf7, 0x8f, 0x9f, 0xf6, 0x4e, 0xa1, 0x73, 0x45, 0x45, 0x06, 0xfe, 0x01, 0x6f, 0xca, - 0xd3, 0x77, 0x57, 0x6b, 0xdb, 0xb8, 0x5e, 0xdb, 0xc6, 0xcf, 0xb5, 0x6d, 0x7c, 0xdb, 0xd8, 0x83, - 0xeb, 0x8d, 0x3d, 0xf8, 0xbe, 0xb1, 0x07, 0x5f, 0xdc, 0x88, 0xa9, 0x78, 0x79, 0xe9, 0x86, 0x3c, - 0xf1, 0xea, 0x5d, 0xcf, 0xb9, 0x88, 0x9a, 0xda, 0xfb, 0xfa, 0xf2, 0x85, 0x97, 0x57, 0x07, 0x5b, - 0x64, 0x20, 0x2f, 0xf7, 0xb6, 0x47, 0xfb, 0xea, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x81, - 0x52, 0x7e, 0x3c, 0x03, 0x00, 0x00, + // 529 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x53, 0xcd, 0x6e, 0xd3, 0x40, + 0x10, 0x8e, 0x29, 0xb4, 0x64, 0xc3, 0x9f, 0x4c, 0x22, 0x96, 0x56, 0xb2, 0x23, 0x9f, 0x22, 0x24, + 0x6c, 0x28, 0x42, 0x42, 0x15, 0xa7, 0x92, 0x03, 0x11, 0x85, 0x56, 0x26, 0x08, 0x89, 0x8b, 0xb5, + 0xf5, 0x8e, 0x92, 0x55, 0x62, 0x6f, 0xb4, 0xbb, 0x21, 0xf1, 0x3b, 0x70, 0xe0, 0x29, 0x78, 0x96, + 0x1e, 0x7b, 0x44, 0x1c, 0x2c, 0x94, 0xdc, 0x7a, 0xcc, 0x13, 0xa0, 0xf5, 0x4f, 0x14, 0x2b, 0x95, + 0xe8, 0xc9, 0x33, 0xdf, 0xf7, 0xed, 0x7c, 0x33, 0xa3, 0x31, 0x7a, 0x16, 0xc3, 0x54, 0x09, 0x1e, + 0x7b, 0x14, 0xe6, 0xde, 0x98, 0x45, 0x4c, 0x05, 0x5c, 0x50, 0x10, 0x81, 0x12, 0x24, 0x0e, 0x87, + 0x10, 0x4c, 0x25, 0x08, 0x77, 0x22, 0xb8, 0xe2, 0x66, 0xa3, 0xd0, 0xba, 0x14, 0xe6, 0xfb, 0xcd, + 0x01, 0x1f, 0xf0, 0x0c, 0xf7, 0x74, 0x94, 0x4b, 0xf6, 0xed, 0xcd, 0x72, 0x4a, 0x10, 0x0a, 0xc1, + 0x84, 0x30, 0x11, 0x30, 0x5a, 0x08, 0x9a, 0x15, 0xc1, 0x3c, 0x47, 0x9d, 0x1f, 0xbb, 0xa8, 0x75, + 0xa2, 0xcd, 0x4f, 0xb5, 0x77, 0x3f, 0xb7, 0xfe, 0x22, 0x41, 0x98, 0x6f, 0xd1, 0xfd, 0x4a, 0x19, + 0x6c, 0xb4, 0x8d, 0x4e, 0xe3, 0x10, 0xbb, 0x1b, 0xbd, 0xb8, 0x7d, 0xad, 0x38, 0x23, 0x4c, 0xf4, + 0xba, 0x7e, 0x43, 0xad, 0x13, 0x6a, 0xbe, 0x41, 0x4f, 0x15, 0x0b, 0x47, 0x01, 0x8b, 0x29, 0xcc, + 0x03, 0x45, 0x46, 0x7a, 0x30, 0x1e, 0x44, 0x3a, 0xc0, 0xb7, 0xda, 0x46, 0x67, 0xc7, 0x6f, 0x69, + 0x41, 0x4f, 0xf3, 0x7d, 0x8d, 0xf6, 0xf9, 0x47, 0xfd, 0x31, 0x6d, 0xd4, 0x28, 0x37, 0x30, 0x82, + 0x04, 0xef, 0xb4, 0x8d, 0x4e, 0xdd, 0x47, 0x05, 0xf4, 0x01, 0x12, 0x13, 0xa3, 0x3d, 0x42, 0xa9, + 0x00, 0x29, 0xf1, 0xed, 0x8c, 0x2c, 0x53, 0x73, 0x80, 0xee, 0xc9, 0x21, 0x11, 0x20, 0x03, 0x3e, + 0x8b, 0x81, 0xe2, 0x3b, 0x9a, 0x3e, 0xee, 0x5e, 0xa4, 0x76, 0xed, 0x4f, 0x6a, 0xb7, 0x42, 0x2e, + 0x23, 0x2e, 0x25, 0x1d, 0xb9, 0x8c, 0x7b, 0x11, 0x51, 0x43, 0xb7, 0x17, 0xab, 0xab, 0xd4, 0xae, + 0x3c, 0x5a, 0xa5, 0xf6, 0xe3, 0x84, 0x44, 0xe3, 0x23, 0x67, 0x13, 0x75, 0xfc, 0x46, 0x9e, 0x9e, + 0xea, 0xcc, 0x4c, 0xd0, 0xa3, 0x82, 0x9d, 0x31, 0x35, 0xa4, 0x82, 0xcc, 0x62, 0xbc, 0x9b, 0x99, + 0x7d, 0xfa, 0x9f, 0xd9, 0xd6, 0xc3, 0x55, 0x6a, 0x3f, 0xa9, 0x18, 0xae, 0x19, 0x07, 0x1b, 0xfe, + 0xc3, 0x1c, 0xfc, 0x5a, 0x62, 0xe6, 0x6c, 0x6d, 0x1d, 0x92, 0x38, 0x84, 0xf1, 0x18, 0x28, 0xde, + 0xcb, 0xac, 0x4f, 0x6e, 0x6a, 0xbd, 0x7e, 0xb8, 0x65, 0xbd, 0x66, 0x9c, 0xd2, 0xf8, 0x5d, 0x89, + 0x98, 0x47, 0x08, 0x15, 0xf7, 0x99, 0x4c, 0x00, 0xdf, 0x6d, 0x1b, 0x9d, 0x07, 0x87, 0x07, 0x95, + 0x63, 0xd8, 0xb8, 0xa3, 0x64, 0x02, 0x7e, 0x9d, 0x97, 0xa1, 0xf9, 0xcb, 0x40, 0x4d, 0x0a, 0x61, + 0xb0, 0xb5, 0xb4, 0x7a, 0xd6, 0xb9, 0x2a, 0x3a, 0x7f, 0x3d, 0x60, 0x6a, 0x38, 0x3d, 0x77, 0x43, + 0x1e, 0x79, 0x45, 0xe1, 0xe7, 0x5c, 0x0c, 0xca, 0xd8, 0xfb, 0xfe, 0xf2, 0x85, 0x37, 0x55, 0x6c, + 0x2c, 0xf3, 0xa9, 0xce, 0x04, 0x84, 0x5d, 0x08, 0xaf, 0x52, 0xfb, 0xda, 0xe2, 0xab, 0xd4, 0x3e, + 0xc8, 0xa7, 0xbb, 0x8e, 0x75, 0x7c, 0x93, 0x42, 0xf8, 0xb9, 0xba, 0xdd, 0xe3, 0xf7, 0x17, 0x0b, + 0xcb, 0xb8, 0x5c, 0x58, 0xc6, 0xdf, 0x85, 0x65, 0xfc, 0x5c, 0x5a, 0xb5, 0xcb, 0xa5, 0x55, 0xfb, + 0xbd, 0xb4, 0x6a, 0xdf, 0xdc, 0x1b, 0xf4, 0x36, 0xcf, 0xff, 0xad, 0x64, 0x02, 0xf2, 0x7c, 0x37, + 0xfb, 0xbf, 0x5e, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xf8, 0x53, 0x5c, 0x8e, 0xe7, 0x03, 0x00, + 0x00, } func (m *LimitOrderTrancheUser) Marshal() (dAtA []byte, err error) { @@ -166,6 +173,16 @@ func (m *LimitOrderTrancheUser) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.DecSharesWithdrawn.Size() + i -= size + if _, err := m.DecSharesWithdrawn.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintLimitOrderTrancheUser(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x4a if m.OrderType != 0 { i = encodeVarintLimitOrderTrancheUser(dAtA, i, uint64(m.OrderType)) i-- @@ -276,6 +293,8 @@ func (m *LimitOrderTrancheUser) Size() (n int) { if m.OrderType != 0 { n += 1 + sovLimitOrderTrancheUser(uint64(m.OrderType)) } + l = m.DecSharesWithdrawn.Size() + n += 1 + l + sovLimitOrderTrancheUser(uint64(l)) return n } @@ -554,6 +573,40 @@ func (m *LimitOrderTrancheUser) Unmarshal(dAtA []byte) error { break } } + case 9: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DecSharesWithdrawn", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowLimitOrderTrancheUser + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthLimitOrderTrancheUser + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthLimitOrderTrancheUser + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.DecSharesWithdrawn.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipLimitOrderTrancheUser(dAtA[iNdEx:])