Skip to content
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

Feat/add price to limit order #472

Merged
merged 25 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
6b94c06
Add price to placeLimitOrderMessage
jcompagni10 Mar 12, 2024
1bf8f2f
Add tests and msgServer
jcompagni10 Mar 14, 2024
367cb06
more error checking + tests
jcompagni10 Apr 12, 2024
75f62ad
price for placeLimitOrder CLI
jcompagni10 Apr 12, 2024
31bdb92
format
jcompagni10 Apr 12, 2024
48ca473
add deprecation notice to tick_index_in_to_out
jcompagni10 Apr 16, 2024
c754b4e
switch to "LimitSellPrice"
jcompagni10 May 3, 2024
a0f03c2
Add wasmbinding for PrecDec with scientificNotation
jcompagni10 May 8, 2024
3f76c96
formatting
jcompagni10 May 8, 2024
a1afe2e
small fixes + typos
jcompagni10 May 10, 2024
0ec932e
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 10, 2024
a083562
fix typo
jcompagni10 May 10, 2024
03f483f
Merge branch 'feat/sdk-50' into feat/add_price_to_lo
jcompagni10 May 14, 2024
3e218a9
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 14, 2024
7f8ab52
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 17, 2024
5bbb9b7
allow 'e' for scientific notation and allowing omitting '+'
jcompagni10 May 24, 2024
265cd9c
better errors and formatting
jcompagni10 May 24, 2024
8c74cee
format
jcompagni10 May 24, 2024
bae1e62
Update x/dex/utils/math_test.go
swelf19 May 24, 2024
3f3c8f4
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 24, 2024
72d2213
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 29, 2024
dd38c10
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 29, 2024
42badc6
fix test values
jcompagni10 May 29, 2024
0b10314
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 31, 2024
0d92a8d
Merge remote-tracking branch 'origin/feat/sdk-50' into feat/add_price…
jcompagni10 May 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion proto/neutron/dex/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ message MsgPlaceLimitOrder {
string receiver = 2;
string token_in = 3;
string token_out = 4;
int64 tick_index_in_to_out = 5;

// DEPRECATED: tick_index_in_to_out will be removed in future release; limit_sell_price should be used instead.
int64 tick_index_in_to_out = 5 [deprecated = true];
string amount_in = 7 [
(gogoproto.moretags) = "yaml:\"amount_in\"",
(gogoproto.customtype) = "cosmossdk.io/math.Int",
Expand All @@ -126,6 +128,12 @@ message MsgPlaceLimitOrder {
(gogoproto.nullable) = true,
(gogoproto.jsontag) = "max_amount_out"
];
string limit_sell_price = 11 [
(gogoproto.moretags) = "yaml:\"limit_sell_price\"",
(gogoproto.customtype) = "github.com/neutron-org/neutron/v4/utils/math.PrecDec",
(gogoproto.nullable) = true,
(gogoproto.jsontag) = "limit_sell_price"
];
}

message MsgPlaceLimitOrderResponse {
Expand Down
6 changes: 3 additions & 3 deletions utils/math/prec_dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

// NOTE: This file is nearly direct copy from cosmossdk.io/math/dec.go @v1.01
// The Precesion has been changed from 18 to 26
// The Precesion has been changed from 18 to 27

// NOTE: never use new(Dec) or else we will panic unmarshalling into the
// nil embedded big.Int
Expand All @@ -23,11 +23,11 @@ type PrecDec struct {

const (
// number of decimal places
Precision = 26
Precision = 27

// bits required to represent the above precision
// Ceiling[Log2[10^Precision - 1]]
PrecDecimalPrecisionBits = 87
PrecDecimalPrecisionBits = 90

// decimalTruncateBits is the minimum number of bits removed
// by a truncate operation. It is equal to
Expand Down
11 changes: 7 additions & 4 deletions wasmbinding/bindings/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,14 +239,17 @@ type Dex struct {
// MsgPlaceLimitOrder is a copy dextypes.MsgPlaceLimitOrder with altered ExpirationTime field,
// it's a preferable way to pass timestamp as unixtime to contracts
type MsgPlaceLimitOrder struct {
Creator string `json:"creator,omitempty"`
Receiver string `json:"receiver,omitempty"`
TokenIn string `json:"token_in,omitempty"`
TokenOut string `json:"token_out,omitempty"`
Creator string `json:"creator,omitempty"`
Receiver string `json:"receiver,omitempty"`
TokenIn string `json:"token_in,omitempty"`
TokenOut string `json:"token_out,omitempty"`
// Deprecated: tick_index_in_to_out will be removed in future release; limit_sell_price should be used instead.
TickIndexInToOut int64 `json:"tick_index_in_to_out,omitempty"`
AmountIn math.Int `json:"amount_in"`
OrderType string `json:"order_type,omitempty"`
// expirationTime is only valid iff orderType == GOOD_TIL_TIME.
ExpirationTime *uint64 `json:"expiration_time,omitempty"`
MaxAmountOut *math.Int `json:"max_amount_out"`
// Accepts standard decimals and decimals with scientific notation (ie. 1234.23E-7)
LimitSellPrice string `json:"limit_sell_price,omitempty"`
}
19 changes: 15 additions & 4 deletions wasmbinding/message_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

dexkeeper "github.com/neutron-org/neutron/v4/x/dex/keeper"
dextypes "github.com/neutron-org/neutron/v4/x/dex/types"
dexutils "github.com/neutron-org/neutron/v4/x/dex/utils"

contractmanagerkeeper "github.com/neutron-org/neutron/v4/x/contractmanager/keeper"

Expand Down Expand Up @@ -223,10 +224,11 @@ func (m *CustomMessenger) dispatchDexMsg(ctx sdk.Context, contractAddr sdk.AccAd
return handleDexMsg(ctx, dex.Withdrawal, m.DexMsgServer.Withdrawal)
case dex.PlaceLimitOrder != nil:
msg := dextypes.MsgPlaceLimitOrder{
Creator: contractAddr.String(),
Receiver: dex.PlaceLimitOrder.Receiver,
TokenIn: dex.PlaceLimitOrder.TokenIn,
TokenOut: dex.PlaceLimitOrder.TokenOut,
Creator: contractAddr.String(),
Receiver: dex.PlaceLimitOrder.Receiver,
TokenIn: dex.PlaceLimitOrder.TokenIn,
TokenOut: dex.PlaceLimitOrder.TokenOut,
//nolint: staticcheck // TODO: remove in next release
TickIndexInToOut: dex.PlaceLimitOrder.TickIndexInToOut,
AmountIn: dex.PlaceLimitOrder.AmountIn,
MaxAmountOut: dex.PlaceLimitOrder.MaxAmountOut,
Expand All @@ -246,6 +248,15 @@ func (m *CustomMessenger) dispatchDexMsg(ctx sdk.Context, contractAddr sdk.AccAd
t := time.Unix(int64(*(dex.PlaceLimitOrder.ExpirationTime)), 0)
msg.ExpirationTime = &t
}

if limitPriceStr := dex.PlaceLimitOrder.LimitSellPrice; limitPriceStr != "" {
limitPriceDec, err := dexutils.ParsePrecDecScientificNotation(limitPriceStr)
if err != nil {
return nil, nil, errors.Wrapf(err, "cannot parse string %s for limit price", limitPriceStr)
}
msg.LimitSellPrice = &limitPriceDec
}

return handleDexMsg(ctx, &msg, m.DexMsgServer.PlaceLimitOrder)
case dex.CancelLimitOrder != nil:
dex.CancelLimitOrder.Creator = contractAddr.String()
Expand Down
7 changes: 7 additions & 0 deletions x/dex/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (
FlagMaxAmountOut = "max-amount-out"
FlagIncludePoolData = "include-pool-data"
FlagCalcWithdraw = "calc-withdraw"
FlagPrice = "price"
)

func FlagSetMaxAmountOut() *flag.FlagSet {
Expand All @@ -14,6 +15,12 @@ func FlagSetMaxAmountOut() *flag.FlagSet {
return fs
}

func FlagSetPrice() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.String(FlagPrice, "", "Sell price for limit order")
return fs
}

func FlagSetIncludePoolData() *flag.FlagSet {
fs := flag.NewFlagSet("", flag.ContinueOnError)
fs.Bool(FlagIncludePoolData, false, "Include pool data with response")
Expand Down
20 changes: 19 additions & 1 deletion x/dex/client/cli/tx_place_limit_order.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import (
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cobra"

math_utils "github.com/neutron-org/neutron/v4/utils/math"
"github.com/neutron-org/neutron/v4/x/dex/types"
)

func CmdPlaceLimitOrder() *cobra.Command {
cmd := &cobra.Command{
//nolint:lll
Use: "place-limit-order [receiver] [token-in] [token-out] [tick-index] [amount-in] ?[order-type] ?[expirationTime] ?(--max-amout-out)",
Use: "place-limit-order [receiver] [token-in] [token-out] [tick-index] [amount-in] ?[order-type] ?[expirationTime] ?(--max-amout-out) ?(--price)",
Short: "Broadcast message PlaceLimitOrder",
Example: "place-limit-order alice tokenA tokenB [-10] tokenA 50 GOOD_TIL_TIME '01/02/2006 15:04:05' --max-amount-out 20 --from alice",
Args: cobra.RangeArgs(5, 7),
Expand Down Expand Up @@ -65,6 +66,7 @@ func CmdPlaceLimitOrder() *cobra.Command {
if err != nil {
return err
}

var maxAmountOutIntP *math.Int
if maxAmountOutArg != "" {
maxAmountOutInt, ok := math.NewIntFromString(maxAmountOutArg)
Expand All @@ -77,6 +79,20 @@ func CmdPlaceLimitOrder() *cobra.Command {
maxAmountOutIntP = &maxAmountOutInt
}

priceArg, err := cmd.Flags().GetString(FlagPrice)
if err != nil {
return err
}

var priceDecP *math_utils.PrecDec
if priceArg != "" {
priceDec, err := math_utils.NewPrecDecFromStr(priceArg)
if err != nil {
return err
}
priceDecP = &priceDec
}

clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
Expand All @@ -92,6 +108,7 @@ func CmdPlaceLimitOrder() *cobra.Command {
orderType,
goodTil,
maxAmountOutIntP,
priceDecP,
)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
Expand All @@ -100,6 +117,7 @@ func CmdPlaceLimitOrder() *cobra.Command {

flags.AddTxFlagsToCmd(cmd)
cmd.Flags().AddFlagSet(FlagSetMaxAmountOut())
cmd.Flags().AddFlagSet(FlagSetPrice())

return cmd
}
35 changes: 35 additions & 0 deletions x/dex/keeper/integration_placelimitorder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
sdkmath "cosmossdk.io/math"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"

math_utils "github.com/neutron-org/neutron/v4/utils/math"
"github.com/neutron-org/neutron/v4/x/dex/types"
)

Expand Down Expand Up @@ -300,6 +301,40 @@ func (s *DexTestSuite) TestLimitOrderPartialFillDepositCancel() {
s.assertDexBalances(0, 0)
}

func (s *DexTestSuite) TestPlaceLimitOrderWithPrice0To1() {
s.fundAliceBalances(10, 0)
s.fundBobBalances(0, 100)

// GIVEN
// Alice place LO at price ~10.0
trancheKey0 := s.limitSellsWithPrice(s.alice, "TokenA", math_utils.NewPrecDec(10), 10)

// WHEN bob swaps through all of Alice's LO
s.bobLimitSells("TokenB", -23078, 100, types.LimitOrderType_IMMEDIATE_OR_CANCEL)
s.aliceWithdrawsLimitSell(trancheKey0)

// THEN alice gets out ~100 TOKENB and bob gets ~10 TOKENA
s.assertAliceBalancesInt(sdkmath.ZeroInt(), sdkmath.NewInt(99_999_967))
s.assertBobBalancesInt(sdkmath.NewInt(10000000), sdkmath.NewInt(23))
}

func (s *DexTestSuite) TestPlaceLimitOrderWithPrice1To0() {
s.fundAliceBalances(0, 200)
s.fundBobBalances(10, 0)
price := math_utils.MustNewPrecDecFromStr("0.25")
// GIVEN
// Alice place LO at price ~.25
trancheKey0 := s.limitSellsWithPrice(s.alice, "TokenB", price, 200)

// WHEN bob swaps through Alice's LO
s.limitSellsWithPrice(s.bob, "TokenA", math_utils.OnePrecDec().Quo(price), 10)
s.aliceWithdrawsLimitSell(trancheKey0)

// THEN alice gets out ~10 TOKENA and bob gets ~40 TOKENB
s.assertAliceBalancesInt(sdkmath.NewInt(9999998), sdkmath.ZeroInt())
s.assertBobBalancesInt(sdkmath.ZeroInt(), sdkmath.NewInt(40001452))
}

// Fill Or Kill limit orders ///////////////////////////////////////////////////////////
func (s *DexTestSuite) TestPlaceLimitOrderFoKNoLiq() {
s.fundAliceBalances(10, 0)
Expand Down
9 changes: 8 additions & 1 deletion x/dex/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,19 @@ func (k MsgServer) PlaceLimitOrder(
if err != nil {
return &types.MsgPlaceLimitOrderResponse{}, err
}
tickIndex := msg.TickIndexInToOut
if msg.LimitSellPrice != nil {
tickIndex, err = types.CalcTickIndexFromPrice(*msg.LimitSellPrice)
if err != nil {
return &types.MsgPlaceLimitOrderResponse{}, errors.Wrapf(err, "invalid LimitSellPrice %s", msg.LimitSellPrice.String())
}
}
trancheKey, coinIn, _, coinOutSwap, err := k.PlaceLimitOrderCore(
goCtx,
msg.TokenIn,
msg.TokenOut,
msg.AmountIn,
msg.TickIndexInToOut,
tickIndex,
msg.OrderType,
msg.ExpirationTime,
msg.MaxAmountOut,
Expand Down
63 changes: 63 additions & 0 deletions x/dex/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,29 @@ func (s *DexTestSuite) limitSellsWithMaxOut(
return msg.TrancheKey
}

func (s *DexTestSuite) limitSellsWithPrice(
account sdk.AccAddress,
tokenIn string,
price math_utils.PrecDec,
amountIn int,
) string {
tokenIn, tokenOut := dexkeeper.GetInOutTokens(tokenIn, "TokenA", "TokenB")

msg, err := s.msgServer.PlaceLimitOrder(s.Ctx, &types.MsgPlaceLimitOrder{
Creator: account.String(),
Receiver: account.String(),
TokenIn: tokenIn,
TokenOut: tokenOut,
LimitSellPrice: &price,
AmountIn: sdkmath.NewInt(int64(amountIn)).Mul(denomMultiple),
OrderType: types.LimitOrderType_GOOD_TIL_CANCELLED,
})

s.Assert().NoError(err)

return msg.TrancheKey
}

func (s *DexTestSuite) limitSellsInt(
account sdk.AccAddress,
tokenIn string,
Expand Down Expand Up @@ -1888,6 +1911,9 @@ func TestMsgPlaceLimitOrderValidate(t *testing.T) {

ZEROINT := sdkmath.ZeroInt()
ONEINT := sdkmath.OneInt()
TINYDEC := math_utils.MustNewPrecDecFromStr("0.000000000000000000000000494")
HUGEDEC := math_utils.MustNewPrecDecFromStr("2020125331305056766452345.127500016657360222036663652")
FIVEDEC := math_utils.NewPrecDec(5)
tests := []struct {
name string
msg types.MsgPlaceLimitOrder
Expand Down Expand Up @@ -1983,6 +2009,43 @@ func TestMsgPlaceLimitOrderValidate(t *testing.T) {
},
types.ErrTickOutsideRange,
},
{
"price < minPrice",
types.MsgPlaceLimitOrder{
Creator: sample.AccAddress(),
Receiver: sample.AccAddress(),
TokenIn: "TokenA",
TokenOut: "TokenB",
LimitSellPrice: &TINYDEC,
AmountIn: sdkmath.OneInt(),
},
types.ErrPriceOutsideRange,
},
{
"price > maxPrice",
types.MsgPlaceLimitOrder{
Creator: sample.AccAddress(),
Receiver: sample.AccAddress(),
TokenIn: "TokenA",
TokenOut: "TokenB",
LimitSellPrice: &HUGEDEC,
AmountIn: sdkmath.OneInt(),
},
types.ErrPriceOutsideRange,
},
{
"invalid tickIndexInToOut & LimitSellPrice",
types.MsgPlaceLimitOrder{
Creator: sample.AccAddress(),
Receiver: sample.AccAddress(),
TokenIn: "TokenA",
TokenOut: "TokenB",
LimitSellPrice: &FIVEDEC,
TickIndexInToOut: 6,
AmountIn: sdkmath.OneInt(),
},
types.ErrInvalidPriceAndTick,
},
}

for _, tt := range tests {
Expand Down
17 changes: 17 additions & 0 deletions x/dex/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,21 @@ var (
1154,
"Swap amount too small; creates unfair spread for liquidity providers",
)

ErrCalcTickFromPrice = sdkerrors.Register(
ModuleName,
1155,
"Cannot convert price to int64 tick value",
)
ErrPriceOutsideRange = sdkerrors.Register(
ModuleName,
1156,
"Invalid price; 0.00000000000000000000000050 < PRICE > 2020125331305056766451886.728",
)

ErrInvalidPriceAndTick = sdkerrors.Register(
ModuleName,
1157,
"Only LimitSellPrice or TickIndexInToOut should be specified",
)
)
Loading
Loading