-
Notifications
You must be signed in to change notification settings - Fork 608
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
Allow supplying final hop memos in XCS contracts #8895
base: main
Are you sure you want to change the base?
Changes from all commits
c4c7a61
5f8bc96
9e10658
c8b56e0
9b30c63
162b568
c43f951
5e1aeda
394c378
594edd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,8 @@ pub enum ExecuteMsg { | |
into_chain: Option<String>, | ||
#[serde(default = "String::new")] | ||
with_memo: String, | ||
#[serde(default = "String::new")] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be |
||
final_memo: String, | ||
}, | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -34,12 +34,14 @@ fn ibc_message_event<T: Debug>(context: &str, message: T) -> cosmwasm_std::Event | |||||||||||||||||||||||||||||||||||||
/// valid channel), it will just proceed to swap and forward. If it's not, then | ||||||||||||||||||||||||||||||||||||||
/// it will send an IBC message to unwrap it first and provide a callback to | ||||||||||||||||||||||||||||||||||||||
/// ensure the right swap_and_forward gets called after the unwrap succeeds | ||||||||||||||||||||||||||||||||||||||
#[allow(clippy::too_many_arguments)] | ||||||||||||||||||||||||||||||||||||||
pub fn unwrap_or_swap_and_forward( | ||||||||||||||||||||||||||||||||||||||
ctx: (DepsMut, Env, MessageInfo), | ||||||||||||||||||||||||||||||||||||||
output_denom: String, | ||||||||||||||||||||||||||||||||||||||
slippage: swaprouter::Slippage, | ||||||||||||||||||||||||||||||||||||||
receiver: &str, | ||||||||||||||||||||||||||||||||||||||
next_memo: Option<SerializableJson>, | ||||||||||||||||||||||||||||||||||||||
final_memo: Option<SerializableJson>, | ||||||||||||||||||||||||||||||||||||||
failed_delivery_action: FailedDeliveryAction, | ||||||||||||||||||||||||||||||||||||||
route: Option<Vec<SwapAmountInRoute>>, | ||||||||||||||||||||||||||||||||||||||
) -> Result<Response, ContractError> { | ||||||||||||||||||||||||||||||||||||||
|
@@ -73,17 +75,19 @@ pub fn unwrap_or_swap_and_forward( | |||||||||||||||||||||||||||||||||||||
env.contract.address.to_string(), | ||||||||||||||||||||||||||||||||||||||
env.block.time, | ||||||||||||||||||||||||||||||||||||||
build_memo(None, env.contract.address.as_str())?, | ||||||||||||||||||||||||||||||||||||||
String::new(), | ||||||||||||||||||||||||||||||||||||||
Some(Callback { | ||||||||||||||||||||||||||||||||||||||
contract: env.contract.address.clone(), | ||||||||||||||||||||||||||||||||||||||
msg: serde_cw_value::to_value(&ExecuteMsg::OsmosisSwap { | ||||||||||||||||||||||||||||||||||||||
output_denom, | ||||||||||||||||||||||||||||||||||||||
receiver: receiver.to_string(), | ||||||||||||||||||||||||||||||||||||||
slippage, | ||||||||||||||||||||||||||||||||||||||
next_memo, | ||||||||||||||||||||||||||||||||||||||
final_memo, | ||||||||||||||||||||||||||||||||||||||
Comment on lines
+78
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider passing through the final_memo parameter. Right now, this line sets the final memo to an empty string, effectively discarding any user input. Consider passing through the final_memo parameter to maintain consistency with the feature's intent. - String::new(),
+ final_memo.map(|m| serde_json_wasm::to_string(&m)).transpose()?.unwrap_or_default(), 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||
on_failed_delivery: failed_delivery_action.clone(), | ||||||||||||||||||||||||||||||||||||||
route, | ||||||||||||||||||||||||||||||||||||||
})? | ||||||||||||||||||||||||||||||||||||||
.into(), | ||||||||||||||||||||||||||||||||||||||
.try_into()?, | ||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||
false, | ||||||||||||||||||||||||||||||||||||||
)?; | ||||||||||||||||||||||||||||||||||||||
|
@@ -125,6 +129,7 @@ pub fn unwrap_or_swap_and_forward( | |||||||||||||||||||||||||||||||||||||
slippage, | ||||||||||||||||||||||||||||||||||||||
receiver, | ||||||||||||||||||||||||||||||||||||||
next_memo, | ||||||||||||||||||||||||||||||||||||||
final_memo, | ||||||||||||||||||||||||||||||||||||||
failed_delivery_action, | ||||||||||||||||||||||||||||||||||||||
route, | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
|
@@ -142,6 +147,7 @@ pub fn swap_and_forward( | |||||||||||||||||||||||||||||||||||||
slippage: swaprouter::Slippage, | ||||||||||||||||||||||||||||||||||||||
receiver: &str, | ||||||||||||||||||||||||||||||||||||||
next_memo: Option<SerializableJson>, | ||||||||||||||||||||||||||||||||||||||
final_memo: Option<SerializableJson>, | ||||||||||||||||||||||||||||||||||||||
failed_delivery_action: FailedDeliveryAction, | ||||||||||||||||||||||||||||||||||||||
route: Option<Vec<SwapAmountInRoute>>, | ||||||||||||||||||||||||||||||||||||||
) -> Result<Response, ContractError> { | ||||||||||||||||||||||||||||||||||||||
|
@@ -152,16 +158,11 @@ pub fn swap_and_forward( | |||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// Check that the received is valid and retrieve its channel | ||||||||||||||||||||||||||||||||||||||
let (valid_chain, valid_receiver) = validate_receiver(deps.as_ref(), receiver)?; | ||||||||||||||||||||||||||||||||||||||
// If there is a memo, check that it is valid (i.e. a valud json object that | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// If there are memos, check that they are valid (i.e. a valid json object that | ||||||||||||||||||||||||||||||||||||||
// doesn't contain the key that we will insert later) | ||||||||||||||||||||||||||||||||||||||
let memo = if let Some(memo) = &next_memo { | ||||||||||||||||||||||||||||||||||||||
// Ensure the json is an object ({...}) and that it does not contain the CALLBACK_KEY | ||||||||||||||||||||||||||||||||||||||
deps.api.debug(&format!("checking memo: {memo:?}")); | ||||||||||||||||||||||||||||||||||||||
ensure_key_missing(memo.as_value(), CALLBACK_KEY)?; | ||||||||||||||||||||||||||||||||||||||
serde_json_wasm::to_string(&memo)? | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
String::new() | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
let s_next_memo = validate_user_provided_memo(&deps, next_memo.as_ref())?; | ||||||||||||||||||||||||||||||||||||||
let s_final_memo = validate_user_provided_memo(&deps, final_memo.as_ref())?; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// Validate that the swapped token can be unwrapped. If it can't, abort | ||||||||||||||||||||||||||||||||||||||
// early to avoid swapping unnecessarily | ||||||||||||||||||||||||||||||||||||||
|
@@ -172,7 +173,8 @@ pub fn swap_and_forward( | |||||||||||||||||||||||||||||||||||||
Some(&valid_chain), | ||||||||||||||||||||||||||||||||||||||
env.contract.address.to_string(), | ||||||||||||||||||||||||||||||||||||||
env.block.time, | ||||||||||||||||||||||||||||||||||||||
memo, | ||||||||||||||||||||||||||||||||||||||
s_next_memo, | ||||||||||||||||||||||||||||||||||||||
s_final_memo, | ||||||||||||||||||||||||||||||||||||||
None, | ||||||||||||||||||||||||||||||||||||||
false, | ||||||||||||||||||||||||||||||||||||||
)?; | ||||||||||||||||||||||||||||||||||||||
|
@@ -208,6 +210,7 @@ pub fn swap_and_forward( | |||||||||||||||||||||||||||||||||||||
chain: valid_chain, | ||||||||||||||||||||||||||||||||||||||
receiver: valid_receiver, | ||||||||||||||||||||||||||||||||||||||
next_memo, | ||||||||||||||||||||||||||||||||||||||
final_memo, | ||||||||||||||||||||||||||||||||||||||
on_failed_delivery: failed_delivery_action, | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
|
@@ -256,7 +259,13 @@ pub fn handle_swap_reply( | |||||||||||||||||||||||||||||||||||||
// If the memo is provided we want to include it in the IBC message. If not, | ||||||||||||||||||||||||||||||||||||||
// we default to an empty object. The resulting memo will always include the | ||||||||||||||||||||||||||||||||||||||
// callback so this contract can track the IBC send | ||||||||||||||||||||||||||||||||||||||
let memo = build_memo(swap_msg_state.forward_to.next_memo, contract_addr.as_str())?; | ||||||||||||||||||||||||||||||||||||||
let next_memo = build_memo(swap_msg_state.forward_to.next_memo, contract_addr.as_str())?; | ||||||||||||||||||||||||||||||||||||||
let final_memo = swap_msg_state | ||||||||||||||||||||||||||||||||||||||
.forward_to | ||||||||||||||||||||||||||||||||||||||
.final_memo | ||||||||||||||||||||||||||||||||||||||
.map(|final_memo| serde_json_wasm::to_string(&final_memo)) | ||||||||||||||||||||||||||||||||||||||
.transpose()? | ||||||||||||||||||||||||||||||||||||||
.unwrap_or_default(); | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
let registry = get_registry(deps.as_ref())?; | ||||||||||||||||||||||||||||||||||||||
let ibc_transfer = registry.unwrap_coin_into( | ||||||||||||||||||||||||||||||||||||||
|
@@ -268,7 +277,8 @@ pub fn handle_swap_reply( | |||||||||||||||||||||||||||||||||||||
Some(&swap_msg_state.forward_to.chain), | ||||||||||||||||||||||||||||||||||||||
env.contract.address.to_string(), | ||||||||||||||||||||||||||||||||||||||
env.block.time, | ||||||||||||||||||||||||||||||||||||||
memo, | ||||||||||||||||||||||||||||||||||||||
next_memo, | ||||||||||||||||||||||||||||||||||||||
final_memo, | ||||||||||||||||||||||||||||||||||||||
None, | ||||||||||||||||||||||||||||||||||||||
false, | ||||||||||||||||||||||||||||||||||||||
)?; | ||||||||||||||||||||||||||||||||||||||
|
@@ -314,7 +324,9 @@ pub fn handle_forward_reply( | |||||||||||||||||||||||||||||||||||||
deps.api.debug(&format!("handle_forward_reply")); | ||||||||||||||||||||||||||||||||||||||
// Parse the result from the underlying chain call (IBC send) | ||||||||||||||||||||||||||||||||||||||
let SubMsgResult::Ok(SubMsgResponse { data: Some(b), .. }) = msg.result else { | ||||||||||||||||||||||||||||||||||||||
return Err(ContractError::FailedIBCTransfer { msg: format!("failed reply: {:?}", msg.result) }) | ||||||||||||||||||||||||||||||||||||||
return Err(ContractError::FailedIBCTransfer { | ||||||||||||||||||||||||||||||||||||||
msg: format!("failed reply: {:?}", msg.result), | ||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
// The response contains the packet sequence. This is needed to be able to | ||||||||||||||||||||||||||||||||||||||
|
@@ -442,6 +454,20 @@ pub fn set_swap_contract( | |||||||||||||||||||||||||||||||||||||
Ok(Response::new().add_attribute("action", "set_swaps_contract")) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
fn validate_user_provided_memo( | ||||||||||||||||||||||||||||||||||||||
deps: &DepsMut, | ||||||||||||||||||||||||||||||||||||||
user_memo: Option<&SerializableJson>, | ||||||||||||||||||||||||||||||||||||||
) -> Result<String, ContractError> { | ||||||||||||||||||||||||||||||||||||||
Ok(if let Some(memo) = user_memo { | ||||||||||||||||||||||||||||||||||||||
// Ensure the json is an object ({...}) and that it does not contain the CALLBACK_KEY | ||||||||||||||||||||||||||||||||||||||
deps.api.debug(&format!("checking memo: {memo:?}")); | ||||||||||||||||||||||||||||||||||||||
ensure_key_missing(memo.as_value(), CALLBACK_KEY)?; | ||||||||||||||||||||||||||||||||||||||
serde_json_wasm::to_string(&memo)? | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
String::new() | ||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
#[cfg(test)] | ||||||||||||||||||||||||||||||||||||||
mod tests { | ||||||||||||||||||||||||||||||||||||||
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info}; | ||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider passing through the newly introduced
final_memo
.Right now, this line sets the final memo to an empty string, effectively discarding any user input. Given that the PR introduces a new
final_memo
feature in related modules, it may be beneficial to accept and forward the user-supplied final memo here (or confirm this is intentional if no final hop memo is desired inpropose_pfm
).Below is an illustrative diff, assuming a new function parameter
final_memo: String
is made available: