1#[cfg(not(feature = "library"))]
2use cosmwasm_std::entry_point;
3use cosmwasm_std::{
4 DepsMut, Env, HexBinary, IbcBasicResponse, IbcChannelCloseMsg, IbcChannelConnectMsg,
5 IbcChannelOpenMsg, IbcChannelOpenResponse, IbcPacketAckMsg, IbcPacketReceiveMsg,
6 IbcPacketTimeoutMsg, IbcReceiveResponse, Never, Reply, Response, SubMsg,
7};
8use polytone_evm::{accounts, ack::Ack, callbacks, handshake::note, ibc::Packet};
9
10use crate::{
11 error::ContractError,
12 state::{BLOCK_MAX_GAS, CHANNEL, CONNECTION_REMOTE_PORT},
13};
14
15pub(crate) mod temp_test {
17 use cosmwasm_schema::cw_serde;
18 use cosmwasm_std::HexBinary;
19 use cw_storage_plus::Map;
20 use polytone_evm::ibc::ExecuteResponsePacket;
21
22 use super::*;
23
24 #[cw_serde]
25 pub enum SentPacketData {
26 Binary(HexBinary),
27 Request(Packet),
28 }
29
30 #[cw_serde]
31 pub enum ReceivedPacketData {
32 Binary(HexBinary),
33 Result(ExecuteResponsePacket),
34 }
35
36 #[cw_serde]
37 pub struct AckInfo {
38 pub height: u64,
39 pub index: Option<u32>,
40 pub sent: SentPacketData,
41 pub result: Ack,
42 }
43
44 pub const ACK_DATAS: Map<u64, AckInfo> = Map::new("ack_datas");
45}
46
47pub(crate) const ERR_GAS_NEEDED: u64 = 101_000;
51
52#[cfg_attr(not(feature = "library"), entry_point)]
53pub fn ibc_channel_open(
54 deps: DepsMut,
55 _env: Env,
56 msg: IbcChannelOpenMsg,
57) -> Result<IbcChannelOpenResponse, ContractError> {
58 let response = note::open(&msg, &["EVM"])?;
59 match CONNECTION_REMOTE_PORT.may_load(deps.storage)? {
60 Some((conn, port)) => {
61 if msg.channel().counterparty_endpoint.port_id != port
62 || msg.channel().connection_id != conn
63 {
64 Err(ContractError::AlreadyPaired {
65 suggested_connection: msg.channel().connection_id.clone(),
66 suggested_port: msg.channel().counterparty_endpoint.port_id.clone(),
67 pair_connection: conn,
68 pair_port: port,
69 })
70 } else {
71 Ok(response)
72 }
73 }
74 None => Ok(response),
75 }
76}
77
78#[cfg_attr(not(feature = "library"), entry_point)]
79pub fn ibc_channel_connect(
80 deps: DepsMut,
81 _env: Env,
82 msg: IbcChannelConnectMsg,
83) -> Result<IbcBasicResponse, ContractError> {
84 note::connect(&msg, &["EvmMsg"])?;
85 CONNECTION_REMOTE_PORT.save(
86 deps.storage,
87 &(
88 msg.channel().connection_id.clone(),
89 msg.channel().counterparty_endpoint.port_id.clone(),
90 ),
91 )?;
92 CHANNEL.save(deps.storage, &msg.channel().endpoint.channel_id)?;
93 Ok(IbcBasicResponse::new()
94 .add_attribute("method", "ibc_channel_connect")
95 .add_attribute("channel_id", &msg.channel().endpoint.channel_id))
96}
97
98#[cfg_attr(not(feature = "library"), entry_point)]
99pub fn ibc_channel_close(
100 deps: DepsMut,
101 _env: Env,
102 msg: IbcChannelCloseMsg,
103) -> Result<IbcBasicResponse, ContractError> {
104 CHANNEL.remove(deps.storage);
105 Ok(IbcBasicResponse::default()
106 .add_attribute("method", "ibc_channel_close")
107 .add_attribute("connection_id", msg.channel().connection_id.clone())
108 .add_attribute(
109 "counterparty_port_id",
110 msg.channel().counterparty_endpoint.port_id.clone(),
111 ))
112}
113
114#[cfg_attr(not(feature = "library"), entry_point)]
115pub fn ibc_packet_receive(
116 _deps: DepsMut,
117 _env: Env,
118 _msg: IbcPacketReceiveMsg,
119) -> Result<IbcReceiveResponse, Never> {
120 unreachable!("voice should never send a packet")
121}
122
123#[cfg_attr(not(feature = "library"), entry_point)]
124pub fn ibc_packet_ack(
125 deps: DepsMut,
126 _env: Env,
127 ack: IbcPacketAckMsg,
128) -> Result<IbcBasicResponse, ContractError> {
129 let maybe_packet = Packet::decode(&ack.original_packet.data);
130
131 let packet = match maybe_packet {
133 Ok(decoded) => temp_test::SentPacketData::Request(decoded),
134 Err(err) => {
135 deps.api.debug(&format!("Error decoding packet: {:?}", err));
136 temp_test::SentPacketData::Binary(HexBinary::from(ack.original_packet.data.clone()))
137 }
138 };
139
140 let (callback, executed_by, result) = callbacks::on_ack(deps.storage, &ack);
141
142 let callback_msg = callback.map(|callback| {
143 SubMsg::reply_on_error(callback, ack.original_packet.sequence).with_gas_limit(
144 BLOCK_MAX_GAS
145 .load(deps.storage)
146 .expect("set during instantiation")
147 - ERR_GAS_NEEDED,
148 )
149 });
150
151 accounts::on_ack(
152 deps.storage,
153 ack.original_packet.src.channel_id.clone(),
154 ack.original_packet.sequence,
155 executed_by,
156 );
157
158 let ack_info = temp_test::AckInfo {
159 height: _env.block.height,
160 index: _env.transaction.map(|tx| tx.index),
161 sent: packet,
162 result,
163 };
164
165 temp_test::ACK_DATAS
166 .save(deps.storage, ack.original_packet.sequence, &ack_info)
167 .ok();
168
169 Ok(IbcBasicResponse::default()
170 .add_attribute("method", "ibc_packet_ack")
171 .add_attribute("sequence_number", ack.original_packet.sequence.to_string())
172 .add_submessages(callback_msg))
173}
174
175#[cfg_attr(not(feature = "library"), entry_point)]
176pub fn ibc_packet_timeout(
177 deps: DepsMut,
178 _env: Env,
179 msg: IbcPacketTimeoutMsg,
180) -> Result<IbcBasicResponse, ContractError> {
181 let callback = callbacks::on_timeout(deps.storage, &msg).map(|cosmos_msg| {
182 SubMsg::reply_on_error(cosmos_msg, msg.packet.sequence).with_gas_limit(
183 BLOCK_MAX_GAS
184 .load(deps.storage)
185 .expect("set during instantiation")
186 - ERR_GAS_NEEDED,
187 )
188 });
189
190 accounts::on_timeout(deps.storage, msg.packet.src.channel_id, msg.packet.sequence);
191
192 Ok(IbcBasicResponse::default()
193 .add_attribute("method", "ibc_packet_timeout")
194 .add_attribute("sequence_number", msg.packet.sequence.to_string())
195 .add_submessages(callback))
196}
197
198#[cfg_attr(not(feature = "library"), entry_point)]
199pub fn reply(_deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
200 let sequence = msg.id;
201 Ok(Response::default()
202 .add_attribute("method", "reply_callback_error")
203 .add_attribute("packet_sequence", sequence.to_string())
204 .add_attribute("callback_error", msg.result.unwrap_err()))
205}