1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4 ensure, from_json, wasm_execute, Deps, DepsMut, Env, Ibc3ChannelOpenResponse, IbcBasicResponse,
5 IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg, IbcPacketAckMsg,
6 IbcPacketReceiveMsg, IbcPacketTimeoutMsg, IbcReceiveResponse, Never, StdError, StdResult,
7 Uint128,
8};
9
10use astroport_governance::assembly;
11use astroport_governance::emissions_controller::consts::{IBC_APP_VERSION, IBC_ORDERING};
12use astroport_governance::emissions_controller::hub::OutpostInfo;
13use astroport_governance::emissions_controller::msg::{
14 ack_fail, ack_ok, IbcAckResult, VxAstroIbcMsg,
15};
16
17use crate::error::ContractError;
18use crate::execute::{handle_update_user, handle_vote};
19use crate::state::{get_all_outposts, CONFIG};
20use crate::utils::jail_outpost;
21
22#[cfg_attr(not(feature = "library"), entry_point)]
23pub fn ibc_channel_open(
24 _deps: DepsMut,
25 _env: Env,
26 msg: IbcChannelOpenMsg,
27) -> StdResult<Option<Ibc3ChannelOpenResponse>> {
28 let channel = msg.channel();
29
30 ensure!(
31 channel.order == IBC_ORDERING,
32 StdError::generic_err("Ordering is invalid. The channel must be unordered",)
33 );
34 ensure!(
35 channel.version == IBC_APP_VERSION,
36 StdError::generic_err(format!("Must set version to `{IBC_APP_VERSION}`",))
37 );
38 if let Some(counter_version) = msg.counterparty_version() {
39 if counter_version != IBC_APP_VERSION {
40 return Err(StdError::generic_err(format!(
41 "Counterparty version must be `{IBC_APP_VERSION}`"
42 )));
43 }
44 }
45
46 Ok(Some(Ibc3ChannelOpenResponse {
47 version: IBC_APP_VERSION.to_string(),
48 }))
49}
50
51#[cfg_attr(not(feature = "library"), entry_point)]
52pub fn ibc_channel_connect(
53 _deps: DepsMut,
54 _env: Env,
55 msg: IbcChannelConnectMsg,
56) -> StdResult<IbcBasicResponse> {
57 if let Some(counter_version) = msg.counterparty_version() {
58 if counter_version != IBC_APP_VERSION {
59 return Err(StdError::generic_err(format!(
60 "Counterparty version must be `{IBC_APP_VERSION}`"
61 )));
62 }
63 }
64
65 let channel = msg.channel();
66
67 Ok(IbcBasicResponse::new()
68 .add_attribute("action", "ibc_connect")
69 .add_attribute("channel_id", &channel.endpoint.channel_id))
70}
71
72#[cfg_attr(not(feature = "library"), entry_point)]
73pub fn ibc_packet_receive(
74 deps: DepsMut,
75 env: Env,
76 msg: IbcPacketReceiveMsg,
77) -> Result<IbcReceiveResponse, Never> {
78 do_packet_receive(deps, env, msg).or_else(|err| {
79 Ok(IbcReceiveResponse::new()
80 .add_attribute("action", "ibc_packet_receive")
81 .set_ack(ack_fail(err)))
82 })
83}
84
85fn is_outpost_valid(
88 deps: Deps,
89 outpost: &OutpostInfo,
90 ibc_msg: &VxAstroIbcMsg,
91) -> Result<bool, ContractError> {
92 let escrow_address = outpost
93 .params
94 .as_ref()
95 .expect("Outpost params must be set") .escrow_address
97 .clone();
98
99 let xastro_denom = CONFIG.load(deps.storage)?.xastro_denom;
100
101 let escrow_balance = deps
102 .querier
103 .query_balance(escrow_address, xastro_denom)?
104 .amount;
105
106 match ibc_msg {
107 VxAstroIbcMsg::EmissionsVote {
108 total_voting_power, ..
109 }
110 | VxAstroIbcMsg::UpdateUserVotes {
111 total_voting_power, ..
112 }
113 | VxAstroIbcMsg::GovernanceVote {
114 total_voting_power, ..
115 } => Ok(*total_voting_power <= escrow_balance),
116 VxAstroIbcMsg::RegisterProposal { .. } => {
117 unreachable!("Hub can't receive RegisterProposal message")
118 }
119 }
120}
121
122pub fn do_packet_receive(
123 deps: DepsMut,
124 env: Env,
125 msg: IbcPacketReceiveMsg,
126) -> Result<IbcReceiveResponse, ContractError> {
127 let (prefix, outpost) = get_all_outposts(deps.storage)?
129 .into_iter()
130 .find_map(|(prefix, outpost)| {
131 outpost.params.as_ref().and_then(|params| {
132 if msg.packet.dest.channel_id == params.voting_channel {
133 Some((prefix.clone(), outpost.clone()))
134 } else {
135 None
136 }
137 })
138 })
139 .ok_or_else(|| {
140 StdError::generic_err(format!(
141 "Unknown outpost with {} voting channel",
142 msg.packet.dest.channel_id
143 ))
144 })?;
145
146 let ibc_msg: VxAstroIbcMsg = from_json(&msg.packet.data)?;
147
148 if outpost.jailed {
149 match ibc_msg {
150 VxAstroIbcMsg::UpdateUserVotes {
151 voter,
152 is_unlock: true,
153 ..
154 } => handle_update_user(deps.storage, env, voter.as_str(), Uint128::zero()).map(
155 |orig_response| {
156 IbcReceiveResponse::new()
157 .add_attributes(orig_response.attributes)
158 .set_ack(ack_ok())
159 },
160 ),
161 _ => Err(ContractError::JailedOutpost { prefix }),
162 }
163 } else {
164 if !is_outpost_valid(deps.as_ref(), &outpost, &ibc_msg)? {
167 jail_outpost(deps.storage, &prefix, env)?;
168
169 return Ok(IbcReceiveResponse::default()
170 .set_ack(ack_ok())
171 .add_attributes([("action", "jail_outpost"), ("prefix", &prefix)]));
172 }
173
174 match ibc_msg {
175 VxAstroIbcMsg::EmissionsVote {
176 voter,
177 voting_power,
178 votes,
179 ..
180 } => handle_vote(deps, env, &voter, voting_power, votes).map(|orig_response| {
181 IbcReceiveResponse::new()
182 .add_attributes(orig_response.attributes)
183 .set_ack(ack_ok())
184 }),
185 VxAstroIbcMsg::UpdateUserVotes {
186 voter,
187 voting_power,
188 ..
189 } => handle_update_user(deps.storage, env, voter.as_str(), voting_power).map(
190 |orig_response| {
191 IbcReceiveResponse::new()
192 .add_attributes(orig_response.attributes)
193 .set_ack(ack_ok())
194 },
195 ),
196 VxAstroIbcMsg::GovernanceVote {
197 voter,
198 voting_power,
199 proposal_id,
200 vote,
201 ..
202 } => {
203 let config = CONFIG.load(deps.storage)?;
204 let cast_vote_msg = wasm_execute(
205 config.assembly,
206 &assembly::ExecuteMsg::CastVoteOutpost {
207 voter,
208 voting_power,
209 proposal_id,
210 vote,
211 },
212 vec![],
213 )?;
214
215 Ok(IbcReceiveResponse::new()
216 .add_message(cast_vote_msg)
217 .set_ack(ack_ok()))
218 }
219 VxAstroIbcMsg::RegisterProposal { .. } => {
220 unreachable!("Hub can't receive RegisterProposal message")
221 }
222 }
223 }
224}
225
226#[cfg(not(tarpaulin_include))]
227#[cfg_attr(not(feature = "library"), entry_point)]
228pub fn ibc_packet_ack(
229 _deps: DepsMut,
230 _env: Env,
231 msg: IbcPacketAckMsg,
232) -> StdResult<IbcBasicResponse> {
233 match from_json(msg.acknowledgement.data)? {
234 IbcAckResult::Ok(_) => {
235 Ok(IbcBasicResponse::default().add_attribute("action", "ibc_packet_ack"))
236 }
237 IbcAckResult::Error(err) => Ok(IbcBasicResponse::default().add_attribute("error", err)),
238 }
239}
240
241#[cfg(not(tarpaulin_include))]
242#[cfg_attr(not(feature = "library"), entry_point)]
243pub fn ibc_packet_timeout(
244 _deps: DepsMut,
245 _env: Env,
246 _msg: IbcPacketTimeoutMsg,
247) -> StdResult<IbcBasicResponse> {
248 Ok(IbcBasicResponse::default().add_attribute("action", "ibc_packet_timeout"))
249}
250
251#[cfg(not(tarpaulin_include))]
252#[cfg_attr(not(feature = "library"), entry_point)]
253pub fn ibc_channel_close(
254 _deps: DepsMut,
255 _env: Env,
256 _channel: IbcChannelCloseMsg,
257) -> StdResult<IbcBasicResponse> {
258 unimplemented!()
259}
260
261#[cfg(test)]
262mod unit_tests {
263 use std::collections::HashMap;
264 use std::marker::PhantomData;
265
266 use cosmwasm_std::testing::{mock_dependencies, mock_env, MockQuerier, MockStorage};
267 use cosmwasm_std::{
268 attr, coins, to_json_binary, Addr, Decimal, IbcChannel, IbcEndpoint, IbcOrder, IbcPacket,
269 IbcTimeout, OwnedDeps, Timestamp,
270 };
271 use cw_multi_test::MockApiBech32;
272 use neutron_sdk::bindings::query::NeutronQuery;
273
274 use astroport_governance::assembly::ProposalVoteOption;
275 use astroport_governance::emissions_controller::hub::{
276 Config, OutpostInfo, OutpostParams, VotedPoolInfo,
277 };
278 use astroport_governance::emissions_controller::msg::IbcAckResult;
279 use astroport_governance::utils::determine_ics20_escrow_address;
280
281 use crate::state::{OUTPOSTS, POOLS_WHITELIST, VOTED_POOLS};
282
283 use super::*;
284
285 pub fn mock_custom_dependencies(
286 ) -> OwnedDeps<MockStorage, MockApiBech32, MockQuerier, NeutronQuery> {
287 OwnedDeps {
288 storage: MockStorage::default(),
289 api: MockApiBech32::new("neutron"),
290 querier: MockQuerier::default(),
291 custom_query_type: PhantomData,
292 }
293 }
294
295 #[test]
296 fn test_channel_open() {
297 let mut deps = mock_dependencies();
298
299 let mut ibc_channel = IbcChannel::new(
300 IbcEndpoint {
301 port_id: "doesnt matter".to_string(),
302 channel_id: "doesnt matter".to_string(),
303 },
304 IbcEndpoint {
305 port_id: "doesnt matter".to_string(),
306 channel_id: "doesnt matter".to_string(),
307 },
308 IbcOrder::Unordered,
309 IBC_APP_VERSION,
310 "doesnt matter",
311 );
312 let res = ibc_channel_open(
313 deps.as_mut(),
314 mock_env(),
315 IbcChannelOpenMsg::new_init(ibc_channel.clone()),
316 )
317 .unwrap()
318 .unwrap();
319
320 assert_eq!(res.version, IBC_APP_VERSION);
321
322 ibc_channel.order = IbcOrder::Ordered;
323
324 let res = ibc_channel_open(
325 deps.as_mut(),
326 mock_env(),
327 IbcChannelOpenMsg::new_init(ibc_channel.clone()),
328 )
329 .unwrap_err();
330 assert_eq!(
331 res,
332 StdError::generic_err("Ordering is invalid. The channel must be unordered")
333 );
334
335 ibc_channel.order = IbcOrder::Unordered;
336 ibc_channel.version = "wrong_version".to_string();
337
338 let res = ibc_channel_open(
339 deps.as_mut(),
340 mock_env(),
341 IbcChannelOpenMsg::new_init(ibc_channel.clone()),
342 )
343 .unwrap_err();
344 assert_eq!(
345 res,
346 StdError::generic_err(format!("Must set version to `{IBC_APP_VERSION}`"))
347 );
348
349 ibc_channel.version = IBC_APP_VERSION.to_string();
350
351 let res = ibc_channel_open(
352 deps.as_mut(),
353 mock_env(),
354 IbcChannelOpenMsg::new_try(ibc_channel.clone(), "wrong_version"),
355 )
356 .unwrap_err();
357 assert_eq!(
358 res,
359 StdError::generic_err(format!("Counterparty version must be `{IBC_APP_VERSION}`"))
360 );
361
362 ibc_channel_open(
363 deps.as_mut(),
364 mock_env(),
365 IbcChannelOpenMsg::new_try(ibc_channel.clone(), IBC_APP_VERSION),
366 )
367 .unwrap()
368 .unwrap();
369 }
370
371 #[test]
372 fn test_channel_connect() {
373 let mut deps = mock_dependencies();
374
375 let ibc_channel = IbcChannel::new(
376 IbcEndpoint {
377 port_id: "doesnt matter".to_string(),
378 channel_id: "doesnt matter".to_string(),
379 },
380 IbcEndpoint {
381 port_id: "doesnt matter".to_string(),
382 channel_id: "doesnt matter".to_string(),
383 },
384 IbcOrder::Unordered,
385 IBC_APP_VERSION,
386 "doesnt matter",
387 );
388
389 ibc_channel_connect(
390 deps.as_mut(),
391 mock_env(),
392 IbcChannelConnectMsg::new_ack(ibc_channel.clone(), IBC_APP_VERSION),
393 )
394 .unwrap();
395
396 let err = ibc_channel_connect(
397 deps.as_mut(),
398 mock_env(),
399 IbcChannelConnectMsg::new_ack(ibc_channel.clone(), "wrong version"),
400 )
401 .unwrap_err();
402 assert_eq!(
403 err,
404 StdError::generic_err(format!("Counterparty version must be `{IBC_APP_VERSION}`"))
405 );
406 }
407
408 #[test]
409 fn test_packet_receive() {
410 let mut deps = mock_custom_dependencies();
411
412 const XASTRO_DENOM: &str = "xastro";
413
414 CONFIG
415 .save(
416 deps.as_mut().storage,
417 &Config {
418 owner: Addr::unchecked("".to_string()),
419 assembly: Addr::unchecked("".to_string()),
420 vxastro: Addr::unchecked("".to_string()),
421 factory: Addr::unchecked("".to_string()),
422 astro_denom: "".to_string(),
423 xastro_denom: XASTRO_DENOM.to_string(),
424 staking: Addr::unchecked("".to_string()),
425 incentives_addr: Addr::unchecked("".to_string()),
426 pools_per_outpost: 0,
427 whitelisting_fee: Default::default(),
428 fee_receiver: Addr::unchecked("".to_string()),
429 whitelist_threshold: Default::default(),
430 emissions_multiple: Default::default(),
431 max_astro: Default::default(),
432 },
433 )
434 .unwrap();
435
436 let voting_msg = VxAstroIbcMsg::EmissionsVote {
437 voter: "osmo1voter".to_string(),
438 voting_power: 1000u128.into(),
439 total_voting_power: Default::default(),
440 votes: HashMap::from([("osmo1pool1".to_string(), Decimal::one())]),
441 };
442 let packet = IbcPacket::new(
443 to_json_binary(&voting_msg).unwrap(),
444 IbcEndpoint {
445 port_id: "".to_string(),
446 channel_id: "".to_string(),
447 },
448 IbcEndpoint {
449 port_id: "".to_string(),
450 channel_id: "channel-2".to_string(),
451 },
452 1,
453 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
454 );
455 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
456
457 let resp =
458 ibc_packet_receive(deps.as_mut().into_empty(), mock_env(), ibc_msg.clone()).unwrap();
459 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
460 assert_eq!(
461 ack_err,
462 IbcAckResult::Error(
463 "Generic error: Unknown outpost with channel-2 voting channel".to_string()
464 )
465 );
466
467 let escrow_address =
468 determine_ics20_escrow_address(deps.as_mut().api, "transfer", "channel-2").unwrap();
469
470 OUTPOSTS
472 .save(
473 deps.as_mut().storage,
474 "osmo",
475 &OutpostInfo {
476 params: Some(OutpostParams {
477 emissions_controller: "".to_string(),
478 voting_channel: "channel-2".to_string(),
479 ics20_channel: "".to_string(),
480 escrow_address: escrow_address.clone(),
481 }),
482 astro_denom: "".to_string(),
483 astro_pool_config: None,
484 jailed: false,
485 },
486 )
487 .unwrap();
488 POOLS_WHITELIST
489 .save(deps.as_mut().storage, &vec!["osmo1pool1".to_string()])
490 .unwrap();
491
492 let mut env = mock_env();
493 env.block.time = Timestamp::from_seconds(1724922008);
494
495 VOTED_POOLS
496 .save(
497 deps.as_mut().storage,
498 "osmo1pool1",
499 &VotedPoolInfo {
500 init_ts: env.block.time.seconds(),
501 voting_power: 0u128.into(),
502 },
503 env.block.time.seconds(),
504 )
505 .unwrap();
506
507 let resp =
508 ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg.clone()).unwrap();
509 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
510 assert_eq!(ack_err, IbcAckResult::Ok(b"ok".into()));
511
512 let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
514 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
515 assert_eq!(
516 ack_err,
517 IbcAckResult::Error("Next time you can change your vote is at 1725235200".to_string())
518 );
519
520 let packet = IbcPacket::new(
522 to_json_binary(&voting_msg).unwrap(),
523 IbcEndpoint {
524 port_id: "".to_string(),
525 channel_id: "".to_string(),
526 },
527 IbcEndpoint {
528 port_id: "".to_string(),
529 channel_id: "channel-3".to_string(),
530 },
531 1,
532 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
533 );
534 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
535 let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
536 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
537 assert_eq!(
538 ack_err,
539 IbcAckResult::Error(
540 "Generic error: Unknown outpost with channel-3 voting channel".to_string()
541 )
542 );
543
544 let update_msg = VxAstroIbcMsg::UpdateUserVotes {
546 voter: "osmo1voter".to_string(),
547 voting_power: 2000u128.into(),
548 total_voting_power: Default::default(),
549 is_unlock: false,
550 };
551 let packet = IbcPacket::new(
552 to_json_binary(&update_msg).unwrap(),
553 IbcEndpoint {
554 port_id: "".to_string(),
555 channel_id: "".to_string(),
556 },
557 IbcEndpoint {
558 port_id: "".to_string(),
559 channel_id: "channel-2".to_string(),
560 },
561 1,
562 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
563 );
564 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
565 let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
566 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
567 assert_eq!(ack_err, IbcAckResult::Ok(b"ok".into()));
568
569 deps.querier
573 .update_balance(&escrow_address, coins(100_000, XASTRO_DENOM));
574
575 let voting_msg = VxAstroIbcMsg::EmissionsVote {
577 voter: "osmo1voter2".to_string(),
578 voting_power: 1000u128.into(),
579 total_voting_power: 99_999u128.into(),
580 votes: HashMap::from([("osmo1pool1".to_string(), Decimal::one())]),
581 };
582 let packet = IbcPacket::new(
583 to_json_binary(&voting_msg).unwrap(),
584 IbcEndpoint {
585 port_id: "".to_string(),
586 channel_id: "".to_string(),
587 },
588 IbcEndpoint {
589 port_id: "".to_string(),
590 channel_id: "channel-2".to_string(),
591 },
592 1,
593 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
594 );
595 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
596 let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
597 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
598 assert_eq!(ack_err, IbcAckResult::Ok(b"ok".into()));
599
600 let voting_msg = VxAstroIbcMsg::EmissionsVote {
602 voter: "osmo1voter3".to_string(),
603 voting_power: 1000u128.into(),
604 total_voting_power: 150_000u128.into(),
605 votes: HashMap::from([("osmo1pool1".to_string(), Decimal::one())]),
606 };
607 let packet = IbcPacket::new(
608 to_json_binary(&voting_msg).unwrap(),
609 IbcEndpoint {
610 port_id: "".to_string(),
611 channel_id: "".to_string(),
612 },
613 IbcEndpoint {
614 port_id: "".to_string(),
615 channel_id: "channel-2".to_string(),
616 },
617 1,
618 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
619 );
620 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
621 let resp = ibc_packet_receive(deps.as_mut().into_empty(), env.clone(), ibc_msg).unwrap();
622
623 assert!(resp.messages.is_empty());
624 assert!(resp.events.is_empty());
625 assert_eq!(
626 resp.acknowledgement,
627 to_json_binary(&IbcAckResult::Ok(b"ok".into())).unwrap()
628 );
629 assert_eq!(
630 resp.attributes,
631 vec![attr("action", "jail_outpost"), attr("prefix", "osmo"),]
632 );
633 }
634
635 #[test]
636 fn test_jailed_outpost() {
637 let mut deps = mock_custom_dependencies();
638
639 OUTPOSTS
641 .save(
642 deps.as_mut().storage,
643 "osmo",
644 &OutpostInfo {
645 params: Some(OutpostParams {
646 emissions_controller: "".to_string(),
647 voting_channel: "channel-2".to_string(),
648 ics20_channel: "".to_string(),
649 escrow_address: Addr::unchecked("".to_string()),
650 }),
651 astro_denom: "".to_string(),
652 astro_pool_config: None,
653 jailed: true,
654 },
655 )
656 .unwrap();
657
658 for (msg, is_error) in [
659 (
660 VxAstroIbcMsg::EmissionsVote {
661 voter: "osmo1voter".to_string(),
662 voting_power: 1000u128.into(),
663 total_voting_power: Default::default(),
664 votes: HashMap::from([("osmo1pool1".to_string(), Decimal::one())]),
665 },
666 true,
667 ),
668 (
669 VxAstroIbcMsg::GovernanceVote {
670 voter: "osmo1voter".to_string(),
671 voting_power: 1000u128.into(),
672 total_voting_power: Default::default(),
673 proposal_id: 1,
674 vote: ProposalVoteOption::For,
675 },
676 true,
677 ),
678 (
679 VxAstroIbcMsg::UpdateUserVotes {
680 voter: "osmo1voter".to_string(),
681 voting_power: 2000u128.into(),
682 total_voting_power: Default::default(),
683 is_unlock: false,
684 },
685 true,
686 ),
687 (
688 VxAstroIbcMsg::UpdateUserVotes {
689 voter: "osmo1voter".to_string(),
690 voting_power: 0u128.into(),
691 total_voting_power: Default::default(),
692 is_unlock: true,
693 },
694 false,
695 ),
696 ] {
697 let packet = IbcPacket::new(
698 to_json_binary(&msg).unwrap(),
699 IbcEndpoint {
700 port_id: "".to_string(),
701 channel_id: "".to_string(),
702 },
703 IbcEndpoint {
704 port_id: "".to_string(),
705 channel_id: "channel-2".to_string(),
706 },
707 1,
708 IbcTimeout::with_timestamp(Timestamp::from_seconds(100)),
709 );
710 let ibc_msg = IbcPacketReceiveMsg::new(packet, Addr::unchecked("doesnt matter"));
711
712 let resp = ibc_packet_receive(deps.as_mut().into_empty(), mock_env(), ibc_msg).unwrap();
713 let ack_err: IbcAckResult = from_json(resp.acknowledgement).unwrap();
714
715 if is_error {
716 assert_eq!(
717 ack_err,
718 IbcAckResult::Error(
719 ContractError::JailedOutpost {
720 prefix: "osmo".to_string()
721 }
722 .to_string()
723 )
724 );
725 } else {
726 assert_eq!(ack_err, IbcAckResult::Ok(b"ok".into()));
727 }
728 }
729 }
730}