Skip to content

Commit

Permalink
Custom hooks, metadata as optional params for transferRemote() (#128)
Browse files Browse the repository at this point in the history
* add hooks, metadata optional values to transferRemote in cw20 and native warps

* add custom hook addr validation; add unit tests for custom hook and custom metadata
  • Loading branch information
udit-gulati authored May 6, 2024
1 parent 25a3181 commit 3c9fd3b
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ context/*.config.json
tmp/
dist/
node_modules/
wasm_codes.zip
41 changes: 32 additions & 9 deletions contracts/warp/cw20/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,9 @@ pub fn execute(
dest_domain,
recipient,
amount,
} => transfer_remote(deps, env, info, dest_domain, recipient, amount),
hook,
metadata,
} => transfer_remote(deps, env, info, dest_domain, recipient, amount, hook, metadata),
}
}

Expand Down Expand Up @@ -177,6 +179,8 @@ fn transfer_remote(
dest_domain: u32,
recipient: HexBinary,
transfer_amount: Uint128,
hook: Option<String>,
metadata: Option<HexBinary>,
) -> Result<Response, ContractError> {
let token = TOKEN.load(deps.storage)?;
let mode = MODE.load(deps.storage)?;
Expand All @@ -186,6 +190,11 @@ fn transfer_remote(
.route
.expect("route not found");

// validate hook if present
if let Some(ref custom_hook) = hook {
let _ = deps.api.addr_validate(&custom_hook)?;
}

let mut msgs: Vec<CosmosMsg> = vec![];

// push token transfer msg
Expand Down Expand Up @@ -218,8 +227,8 @@ fn transfer_remote(
metadata: HexBinary::default(),
}
.into(),
get_hook(deps.storage)?.map(|v| v.into()),
None,
hook.clone().or(get_hook(deps.storage)?.map(|v| v.into())),
metadata.clone(),
info.funds,
)?);

Expand All @@ -229,7 +238,9 @@ fn transfer_remote(
.add_attribute("dest_domain", dest_domain.to_string())
.add_attribute("recipient", recipient.to_hex())
.add_attribute("token", token)
.add_attribute("amount", transfer_amount),
.add_attribute("amount", transfer_amount)
.add_attribute("hook", hook.unwrap_or_default())
.add_attribute("metadata", metadata.unwrap_or_default().to_string()),
))
}

Expand Down Expand Up @@ -292,6 +303,7 @@ mod test {
const OWNER: &str = "owner";
const MAILBOX: &str = "mailbox";
const TOKEN: &str = "token";
const CUSTOM_HOOK: &str = "custom_hook";

const CW20_BRIDGED_CODE_ID: u64 = 1;
const CW20_BRIDGED_NAME: &str = "cw20-created";
Expand Down Expand Up @@ -509,15 +521,17 @@ mod test {
}

#[rstest]
#[case(1, gen_bz(32), token_mode_bridged())]
#[case(1, gen_bz(32), token_mode_collateral())]
#[case(1, gen_bz(32), token_mode_bridged(), Some(CUSTOM_HOOK), None)]
#[case(1, gen_bz(32), token_mode_collateral(), None, Some(gen_bz(100)))]
#[should_panic(expected = "route not found")]
#[case(2, gen_bz(32), token_mode_collateral())]
#[case(2, gen_bz(32), token_mode_collateral(), None, None)]
fn test_transfer_remote(
#[values("osmo", "neutron")] hrp: &str,
#[case] domain: u32,
#[case] route: HexBinary,
#[case] token_mode: Cw20TokenMode,
#[case] custom_hook: Option<&str>,
#[case] custom_metadata: Option<HexBinary>,
) {
let (mut deps, _) = deps(
vec![(1, route.clone())],
Expand All @@ -536,6 +550,8 @@ mod test {
dest_domain: domain,
recipient: recipient.clone(),
amount: Uint128::new(100),
hook: custom_hook.map(|h| h.to_string()),
metadata: custom_metadata.clone(),
},
vec![],
);
Expand All @@ -558,8 +574,15 @@ mod test {
metadata: HexBinary::default(),
};

let dispatch_msg =
mailbox::dispatch(MAILBOX, domain, route, warp_msg.into(), None, None, vec![]).unwrap();
let dispatch_msg = mailbox::dispatch(
MAILBOX,
domain,
route,
warp_msg.into(),
custom_hook.map(|h| h.to_string()),
custom_metadata,
vec![],
).unwrap();

match token_mode {
TokenModeMsg::Bridged(_) => {
Expand Down
38 changes: 27 additions & 11 deletions contracts/warp/native/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ pub fn execute(
dest_domain,
recipient,
amount,
} => transfer_remote(deps, env, info, dest_domain, recipient, amount),
hook,
metadata,
} => transfer_remote(deps, env, info, dest_domain, recipient, amount, hook, metadata),
}
}

Expand Down Expand Up @@ -190,6 +192,8 @@ fn transfer_remote(
dest_domain: u32,
recipient: HexBinary,
transfer_amount: Uint128,
hook: Option<String>,
metadata: Option<HexBinary>,
) -> Result<Response, ContractError> {
let token = TOKEN.load(deps.storage)?;
let mode = MODE.load(deps.storage)?;
Expand All @@ -213,6 +217,11 @@ fn transfer_remote(
.route
.expect("route not found");

// validate hook if present
if let Some(ref custom_hook) = hook {
let _ = deps.api.addr_validate(&custom_hook)?;
}

let mut msgs: Vec<CosmosMsg> = vec![];

if mode == TokenMode::Bridged {
Expand All @@ -232,8 +241,8 @@ fn transfer_remote(
dest_domain,
dest_router,
dispatch_payload.into(),
get_hook(deps.storage)?.map(|v| v.into()),
None,
hook.clone().or(get_hook(deps.storage)?.map(|v| v.into())),
metadata.clone(),
funds,
)?);

Expand All @@ -242,7 +251,9 @@ fn transfer_remote(
.add_attribute("sender", info.sender)
.add_attribute("recipient", recipient.to_hex())
.add_attribute("token", token)
.add_attribute("amount", transfer_amount.to_string()),
.add_attribute("amount", transfer_amount.to_string())
.add_attribute("hook", hook.unwrap_or_default())
.add_attribute("metadata", metadata.unwrap_or_default().to_string()),
))
}

Expand Down Expand Up @@ -314,6 +325,7 @@ mod test {
const OWNER: &str = "owner";
const MAILBOX: &str = "mailbox";
const DENOM: &str = "utest";
const CUSTOM_HOOK: &str = "custom_hook";

#[fixture]
fn metadata(#[default(true)] empty: bool) -> Option<Metadata> {
Expand Down Expand Up @@ -508,20 +520,22 @@ mod test {
}

#[rstest]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM), coin(100, "uatom")])]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)], Some(CUSTOM_HOOK), None)]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, DENOM), coin(100, "uatom")], None, Some(gen_bz(100)))]
#[should_panic(expected = "route not found")]
#[case(2, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)])]
#[case(2, gen_bz(32), gen_bz(32), vec![coin(100, DENOM)], None, None)]
#[should_panic(expected = "no funds sent")]
#[case(1, gen_bz(32), gen_bz(32), vec![])]
#[case(1, gen_bz(32), gen_bz(32), vec![], None, None)]
#[should_panic(expected = "no funds sent")]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, "uatom")])]
#[case(1, gen_bz(32), gen_bz(32), vec![coin(100, "uatom")], None, None)]
fn test_transfer_remote(
mut deps: TestDeps,
#[case] dest_domain: u32,
#[case] dest_router: HexBinary,
#[case] dest_recipient: HexBinary,
#[case] funds: Vec<Coin>,
#[case] custom_hook: Option<&str>,
#[case] custom_metadata: Option<HexBinary>,
) {
set_route(
deps.as_mut().storage,
Expand All @@ -540,6 +554,8 @@ mod test {
dest_domain,
recipient: dest_recipient.clone(),
amount: Uint128::new(50),
hook: custom_hook.map(|h| h.to_string()),
metadata: custom_metadata.clone(),
},
funds.clone(),
);
Expand All @@ -559,8 +575,8 @@ mod test {
metadata: HexBinary::default(),
}
.into(),
None,
None,
custom_hook.map(|h| h.to_string()),
custom_metadata,
[
vec![coin(50, DENOM)],
funds.into_iter().filter(|v| v.denom != DENOM).collect()
Expand Down
2 changes: 2 additions & 0 deletions packages/interface/src/warp/cw20.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ pub enum ExecuteMsg {
dest_domain: u32,
recipient: HexBinary,
amount: Uint128,
hook: Option<String>,
metadata: Option<HexBinary>,
},
}

Expand Down
2 changes: 2 additions & 0 deletions packages/interface/src/warp/native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ pub enum ExecuteMsg {
dest_domain: u32,
recipient: HexBinary,
amount: Uint128,
hook: Option<String>,
metadata: Option<HexBinary>,
},
}

Expand Down

0 comments on commit 3c9fd3b

Please sign in to comment.