Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions contracts/swap/src/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,26 @@ fn verify_route_exists(deps: Deps<InjectiveQueryWrapper>, route: &SwapRoute) ->
}
);

// A multi-hop route is only tradeable if each market in the chain actually shares a denom
// with the next one. The checks above only look at the very first and very last market, so a
// route with a broken middle hop (e.g. [A/B, C/D] where B != C and B != D) would previously
// pass `set_route` and only fail later, at swap time, when the exchange module rejects the
// order for a market/denom mismatch. Failing fast here gives the admin an immediate, specific
// error instead of a route that looks valid but can never execute.
for pair in denoms.windows(2) {
let (current, next) = (&pair[0], &pair[1]);
let shares_denom = current.quote_denom == next.quote_denom
|| current.quote_denom == next.base_denom
|| current.base_denom == next.quote_denom
|| current.base_denom == next.base_denom;
ensure!(
shares_denom,
CustomError {
val: "Route is not continuous: two consecutive markets do not share a denom".to_string()
}
);
}

Ok(())
}

Expand Down
22 changes: 13 additions & 9 deletions contracts/swap/src/swap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,20 @@ pub fn handle_atomic_order_reply(deps: DepsMut<InjectiveQueryWrapper>, env: Env,
Ok(response)
}

pub fn parse_market_order_response(msg: Reply) -> StdResult<MsgCreateSpotMarketOrderResponse> {
let binding = msg.result.into_result().map_err(ContractError::SubMsgFailure).unwrap();

let first_message = binding.msg_responses.first();
let order_response = MsgCreateSpotMarketOrderResponse::decode(first_message.unwrap().value.as_slice())
.map_err(|err| ContractError::ReplyParseFailure {
id: msg.id,
pub fn parse_market_order_response(msg: Reply) -> Result<MsgCreateSpotMarketOrderResponse, ContractError> {
let reply_id = msg.id;
let binding = msg.result.into_result().map_err(ContractError::SubMsgFailure)?;

let first_message = binding.msg_responses.first().ok_or_else(|| ContractError::ReplyParseFailure {
id: reply_id,
err: "sub-message result contained no msg_responses".to_string(),
})?;

let order_response =
MsgCreateSpotMarketOrderResponse::decode(first_message.value.as_slice()).map_err(|err| ContractError::ReplyParseFailure {
id: reply_id,
err: err.to_string(),
})
.unwrap();
})?;

Ok(order_response)
}