routes: Semicolon-separated list of hop routes, where each route is comma-separated hops (format: route1;route2 where route1=tokenA,tokenB,tokenC)
amount-in: Amount of input tokens
exit-limit-price: Minimum allowable price ratio. If the actual price ratio (amount_out/amount_in) is less than this value, the route will fail. Use very low values (e.g., “0.001”) for maximum flexibility, as this represents the absolute minimum acceptable rate.
pick-best-route: Whether to automatically pick the best route (boolean)
Example:
Copy
Ask AI
neutrond tx dex multi-hop-swap alice "tokenA,tokenB;tokenB,tokenC" 1000000 "0.95" true --from alice
MsgPlaceLimitOrder submits a limit order to the DEX.
Copy
Ask AI
message MsgPlaceLimitOrder { option (amino.name) = "dex/MsgPlaceLimitOrder"; option (cosmos.msg.v1.signer) = "creator"; string creator = 1; string receiver = 2; string token_in = 3; string token_out = 4; // 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", (gogoproto.nullable) = false, (gogoproto.jsontag) = "amount_in" ]; LimitOrderType order_type = 8; // expirationTime is only valid iff orderType == GOOD_TIL_TIME. google.protobuf.Timestamp expiration_time = 9 [ (gogoproto.stdtime) = true, (gogoproto.nullable) = true ]; string max_amount_out = 10 [ (gogoproto.moretags) = "yaml:\"max_amount_out\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (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/v6/utils/math.PrecDec", (gogoproto.nullable) = true, (gogoproto.jsontag) = "limit_sell_price" ]; // min_average_sell_price is an optional parameter that sets a required minimum average price for the entire trade. // if the min_average_sell_price is not met the trade will fail. // If min_average_sell_price is omitted limit_sell_price will be used instead string min_average_sell_price = 12 [ (gogoproto.moretags) = "yaml:\"min_average_sell_price\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v6/utils/math.PrecDec", (gogoproto.nullable) = true, (gogoproto.jsontag) = "min_average_sell_price" ];}
Response:
Copy
Ask AI
message MsgPlaceLimitOrderResponse { string trancheKey = 1; // Total amount of coin used for the limit order cosmos.base.v1beta1.Coin coin_in = 2 [ (gogoproto.moretags) = "yaml:\"coin_in\"", (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin", (gogoproto.jsontag) = "coin_in" ]; // Total amount of coin received from the taker portion of the limit order // This is the amount of coin immediately available in the users account after // executing the limit order. It does not include any future proceeds from the // maker portion which will have withdrawn in the future cosmos.base.v1beta1.Coin taker_coin_out = 3 [ (gogoproto.moretags) = "yaml:\"taker_coin_out\"", (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin", (gogoproto.jsontag) = "taker_coin_out" ]; // Total amount of the token in that was immediately swapped for takerOutCoin cosmos.base.v1beta1.Coin taker_coin_in = 4 [ (gogoproto.moretags) = "yaml:\"taker_coin_in\"", (gogoproto.nullable) = false, (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Coin", (gogoproto.jsontag) = "taker_coin_in" ];}
The DEX module provides REST API endpoints for querying pool information, reserves, limit orders, and user positions. All endpoints use GET requests and return JSON responses.
Endpoint Verification: All endpoints verified against /module-reference/proto/dex/query.proto for 100% accuracy.
Simulates a multi-hop swap operation without executing it.Query Parameters:
msg: MsgMultiHopSwap message in JSON format
exitLimitPrice Usage Example
The exitLimitPrice parameter can be easily misunderstood. Here’s a comprehensive example showing correct usage:
Common Mistake: Using simulation results directly to calculate exitLimitPrice often fails due to the 10^27 decimal format and price precision requirements.
Scenario: Swap 145 DYDX → OSMO with 1% slippage protection
// INCORRECT approach that will fail:const outputAmount = new BigNumber("602731536"); // 602.731536 OSMO (6 decimals)const inputAmount = new BigNumber("145000000000000000000"); // 145 DYDX (18 decimals)// What the code actually calculates as currentPrice:const currentPrice = outputAmount.dividedBy(inputAmount); // 602731536 / 145000000000000000000 = ~4.16e-12// WRONG: User thinks in terms of OSMO per DYDX (human-readable rates)const humanRate = outputAmount.shiftedBy(-6).dividedBy(inputAmount.shiftedBy(-18)); // 602.73 / 145 = ~4.16const badExitPrice = humanRate.multipliedBy(0.99).toFixed(); // "4.11..." // This will FAIL because 4.11 > 4.16e-12 (exitLimitPrice > currentPrice)// CORRECT approaches:// Option 1: Use very low exitLimitPrice for maximum flexibilityconst safeExitPrice = "0.000001"; // Very small minimum price// Option 2: Use neutron-sdk PrecDec for proper decimal handlingimport { PrecDec } from '@neutron-org/neutron-sdk';const properExitPrice = PrecDec.fromUserInput("0.001", 18).toString(); // 0.001 as minimum// Option 3: Calculate conservatively using the actual raw ratioconst conservativePrice = currentPrice.multipliedBy(0.1).toFixed(); // Only 10% of raw currentPrice (~4.16e-13)
3
Execute with proper exitLimitPrice
Copy
Ask AI
const finalSwap = { msg: { creator: "neutron1...", receiver: "neutron1...", routes: [{ hops: [ 'ibc/2CB87BCE0937B1D1DFCEE79BE4501AAF3C265E923509AEAC410AD85D27F35130', 'ibc/B559A80D62249C8AA07A380E2A2BEA6E5CA9A6F079C912C3A9E9B494105E4F81', 'ibc/376222D6D9DAE23092E29740E56B758580935A6D77C24C2ABD57A6A78A1F3955' ] }], amountIn: "145000000000000000000", exitLimitPrice: safeExitPrice, // Use one of the correct approaches above pickBestRoute: false, }};
4
Alternative: Post-execution slippage check
Copy
Ask AI
// Instead of using exitLimitPrice for slippage protection,// simulate first, then check results before executingconst simulation = await query.dex.simulateMultiHopSwap(firstSimulation);const expectedOutput = new BigNumber(simulation.coinOut.amount);const minimumAcceptable = expectedOutput.multipliedBy(0.99); // 1% slippage// Execute the actual swap with low exitLimitPriceconst actualSwap = await tx.dex.multiHopSwap({ ...finalSwap.msg, exitLimitPrice: "1000000"});// Check if actual output meets slippage requirementsconst actualOutput = new BigNumber(actualSwap.coinOut.amount);if (actualOutput.isLessThan(minimumAcceptable)) { throw new Error(`Slippage too high: got ${actualOutput}, expected min ${minimumAcceptable}`);}
Best Practice: For slippage protection, either use very low exitLimitPrice values with post-execution checks, or use the neutron-sdk PrecDec type for proper decimal handling.
exitLimitPrice Format: The value uses PrecDec format and can be provided as simple decimal strings like “0.9” or “0.001”. It represents the minimum acceptable price ratio (amountOut/amountIn) using raw token amounts.
The DEX module emits events during transaction processing to provide information about deposits, withdrawals, limit orders, and swaps. All events use the standard Cosmos SDK event format with module name “dex”.
The DEX module maintains state to track pools, reserves, limit orders, and user positions. All state objects are defined in protocol buffer files and stored in the module’s key-value store.
Represents a batch of limit orders at the same price point.
Copy
Ask AI
message LimitOrderTranche { LimitOrderTrancheKey key = 1; string reserves_maker_denom = 2 [ (gogoproto.moretags) = "yaml:\"reserves_maker_denom\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, (gogoproto.jsontag) = "reserves_maker_denom" ]; string reserves_taker_denom = 3 [ (gogoproto.moretags) = "yaml:\"reserves_taker_denom\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, (gogoproto.jsontag) = "reserves_taker_denom" ]; string total_maker_denom = 4 [ (gogoproto.moretags) = "yaml:\"total_maker_denom\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, (gogoproto.jsontag) = "total_maker_denom" ]; string total_taker_denom = 5 [ (gogoproto.moretags) = "yaml:\"total_taker_denom\"", (gogoproto.customtype) = "cosmossdk.io/math.Int", (gogoproto.nullable) = false, (gogoproto.jsontag) = "total_taker_denom" ]; // LimitOrders with expiration_time set are valid as long as blockTime <= expiration_time // JIT orders also use expiration_time to handle deletion but represent a special case // All JIT orders have a expiration_time of 0 and an exception is made to still treat these orders as live // Order deletion still functions the same and the orders will be deleted at the end of the block google.protobuf.Timestamp expiration_time = 6 [ (gogoproto.moretags) = "yaml:\"expiration_time\"", (gogoproto.stdtime) = true, (gogoproto.nullable) = true, (gogoproto.jsontag) = "expiration_time" ]; // DEPRECATED: price_taker_to_maker will be removed in future release, `maker_price` should always be used. string price_taker_to_maker = 7 [ (gogoproto.moretags) = "yaml:\"price_taker_to_maker\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v6/utils/math.PrecDec", (gogoproto.nullable) = false, (gogoproto.jsontag) = "price_taker_to_maker", deprecated = true ]; // This is the price of the LimitOrder denominated in the opposite token. (ie. 1 TokenA with a maker_price of 10 is worth 10 TokenB ) string maker_price = 8 [ (gogoproto.moretags) = "yaml:\"maker_price\"", (gogoproto.customtype) = "github.com/neutron-org/neutron/v6/utils/math.PrecDec", (gogoproto.nullable) = false, (gogoproto.jsontag) = "maker_price" ];}message LimitOrderTrancheKey { TradePairID trade_pair_id = 1; int64 tick_index_taker_to_maker = 2; string tranche_key = 3;}