1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4 DepsMut, Env, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg, IbcChannelOpenMsg,
5 IbcChannelOpenResponse, IbcPacketAckMsg, IbcPacketReceiveMsg, IbcPacketTimeoutMsg,
6 IbcReceiveResponse, Never, Reply, Response, SubMsg,
7};
8use polytone::{accounts, callbacks, handshake::note};
9
10use crate::{
11 error::ContractError,
12 state::{BLOCK_MAX_GAS, CHANNEL, CONNECTION_REMOTE_PORT},
13};
14
15pub(crate) const ERR_GAS_NEEDED: u64 = 101_000;
19
20#[cfg_attr(not(feature = "library"), entry_point)]
21pub fn ibc_channel_open(
22 deps: DepsMut,
23 _env: Env,
24 msg: IbcChannelOpenMsg,
25) -> Result<IbcChannelOpenResponse, ContractError> {
26 let response = note::open(&msg, &["JSON-CosmosMsg"])?;
27 match CONNECTION_REMOTE_PORT.may_load(deps.storage)? {
28 Some((conn, port)) => {
29 if msg.channel().counterparty_endpoint.port_id != port
30 || msg.channel().connection_id != conn
31 {
32 Err(ContractError::AlreadyPaired {
33 suggested_connection: msg.channel().connection_id.clone(),
34 suggested_port: msg.channel().counterparty_endpoint.port_id.clone(),
35 pair_connection: conn,
36 pair_port: port,
37 })
38 } else {
39 Ok(response)
40 }
41 }
42 None => Ok(response),
43 }
44}
45
46#[cfg_attr(not(feature = "library"), entry_point)]
47pub fn ibc_channel_connect(
48 deps: DepsMut,
49 _env: Env,
50 msg: IbcChannelConnectMsg,
51) -> Result<IbcBasicResponse, ContractError> {
52 note::connect(&msg, &["JSON-CosmosMsg"])?;
53 CONNECTION_REMOTE_PORT.save(
54 deps.storage,
55 &(
56 msg.channel().connection_id.clone(),
57 msg.channel().counterparty_endpoint.port_id.clone(),
58 ),
59 )?;
60 CHANNEL.save(deps.storage, &msg.channel().endpoint.channel_id)?;
61 Ok(IbcBasicResponse::new()
62 .add_attribute("method", "ibc_channel_connect")
63 .add_attribute("channel_id", &msg.channel().endpoint.channel_id))
64}
65
66#[cfg_attr(not(feature = "library"), entry_point)]
67pub fn ibc_channel_close(
68 deps: DepsMut,
69 _env: Env,
70 msg: IbcChannelCloseMsg,
71) -> Result<IbcBasicResponse, ContractError> {
72 CHANNEL.remove(deps.storage);
73 Ok(IbcBasicResponse::default()
74 .add_attribute("method", "ibc_channel_close")
75 .add_attribute("connection_id", msg.channel().connection_id.clone())
76 .add_attribute(
77 "counterparty_port_id",
78 msg.channel().counterparty_endpoint.port_id.clone(),
79 ))
80}
81
82#[cfg_attr(not(feature = "library"), entry_point)]
83pub fn ibc_packet_receive(
84 _deps: DepsMut,
85 _env: Env,
86 _msg: IbcPacketReceiveMsg,
87) -> Result<IbcReceiveResponse, Never> {
88 unreachable!("voice should never send a packet")
89}
90
91#[cfg_attr(not(feature = "library"), entry_point)]
92pub fn ibc_packet_ack(
93 deps: DepsMut,
94 _env: Env,
95 ack: IbcPacketAckMsg,
96) -> Result<IbcBasicResponse, ContractError> {
97 let (callback, executed_by) = callbacks::on_ack(deps.storage, &ack);
98 let callback = callback.map(|callback| {
99 SubMsg::reply_on_error(callback, ack.original_packet.sequence).with_gas_limit(
100 BLOCK_MAX_GAS
101 .load(deps.storage)
102 .expect("set during instantiation")
103 - ERR_GAS_NEEDED,
104 )
105 });
106
107 accounts::on_ack(
108 deps.storage,
109 ack.original_packet.src.channel_id.clone(),
110 ack.original_packet.sequence,
111 executed_by,
112 );
113
114 Ok(IbcBasicResponse::default()
115 .add_attribute("method", "ibc_packet_ack")
116 .add_attribute("sequence_number", ack.original_packet.sequence.to_string())
117 .add_submessages(callback))
118}
119
120#[cfg_attr(not(feature = "library"), entry_point)]
121pub fn ibc_packet_timeout(
122 deps: DepsMut,
123 _env: Env,
124 msg: IbcPacketTimeoutMsg,
125) -> Result<IbcBasicResponse, ContractError> {
126 let callback = callbacks::on_timeout(deps.storage, &msg).map(|cosmos_msg| {
127 SubMsg::reply_on_error(cosmos_msg, msg.packet.sequence).with_gas_limit(
128 BLOCK_MAX_GAS
129 .load(deps.storage)
130 .expect("set during instantiation")
131 - ERR_GAS_NEEDED,
132 )
133 });
134
135 accounts::on_timeout(deps.storage, msg.packet.src.channel_id, msg.packet.sequence);
136
137 Ok(IbcBasicResponse::default()
138 .add_attribute("method", "ibc_packet_timeout")
139 .add_attribute("sequence_number", msg.packet.sequence.to_string())
140 .add_submessages(callback))
141}
142
143#[cfg_attr(not(feature = "library"), entry_point)]
144pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
145 let sequence = msg.id;
146 Ok(Response::default()
147 .add_attribute("method", "reply_callback_error")
148 .add_attribute("packet_sequence", sequence.to_string())
149 .add_attribute("callback_error", msg.result.unwrap_err()))
150}