1use cosmwasm_schema::cw_serde;
2use cosmwasm_std::{
3 to_json_binary, Addr, Api, Binary, CosmosMsg, HexBinary, IbcPacketAckMsg, IbcPacketTimeoutMsg,
4 StdResult, Storage, Uint64, WasmMsg,
5};
6use cw_storage_plus::Map;
7
8use crate::{
9 ack::Ack,
10 ibc::{ExecuteResponsePacket, ExecuteResult},
11};
12
13#[cw_serde]
15pub struct CallbackRequest {
16 pub receiver: String,
17 pub msg: Binary,
18}
19
20#[cw_serde]
31pub struct CallbackMessage {
32 pub initiator: Addr,
34 pub initiator_msg: Binary,
37 pub result: Callback,
39}
40
41#[cw_serde]
42pub enum Callback {
43 Execute(Result<ExecutionResponse, String>),
52
53 FatalError(String),
63}
64
65#[cw_serde]
66pub struct ExecutionResponse {
67 pub executed_by: String,
69 pub result: Vec<ExecuteResult>,
72}
73
74#[cw_serde]
75pub struct ErrorResponse {
76 pub message_index: Uint64,
78 pub error: String,
80}
81
82#[cw_serde]
85pub enum CallbackRequestType {
86 Execute,
87 }
89
90pub fn request_callback(
93 storage: &mut dyn Storage,
94 api: &dyn Api,
95 channel_id: String,
96 sequence_number: u64,
97 initiator: Addr,
98 request: Option<CallbackRequest>,
99 request_type: CallbackRequestType,
100) -> StdResult<()> {
101 if let Some(request) = request {
102 let receiver = api.addr_validate(&request.receiver)?;
103 let initiator_msg = request.msg;
104
105 CALLBACKS.save(
106 storage,
107 (channel_id, sequence_number),
108 &PendingCallback {
109 initiator,
110 initiator_msg,
111 receiver,
112 request_type,
113 },
114 )?;
115 }
116
117 Ok(())
118}
119
120const ACK_SUCCESS: u8 = 0;
121#[allow(unused)]
122const ACK_FAILURE: u8 = 1;
123
124pub fn on_ack(
130 storage: &mut dyn Storage,
131 IbcPacketAckMsg {
133 acknowledgement,
134 original_packet,
135 ..
136 }: &IbcPacketAckMsg,
137) -> (Option<CosmosMsg>, Option<String>, Ack) {
138 let (ack_res, ack_res_data) = acknowledgement.data.split_at(1);
139
140 let mut executed_by = None;
141
142 let maybe_response = ExecuteResponsePacket::decode_bytes(ack_res_data);
143 let result: Ack = match maybe_response {
144 Ok(decoded) => {
145 executed_by = match &decoded {
146 ExecuteResponsePacket { executed_by, .. } => Some(executed_by),
147 }
148 .cloned();
149 Ack::Execute(Ok(ExecutionResponse {
150 executed_by: executed_by.clone().unwrap(),
152 result: decoded.result,
153 }))
154 }
155 Err(err) => {
156 if ack_res[0] != ACK_SUCCESS {
157 Ack::FatalError(HexBinary::from(ack_res_data).to_string())
158 } else {
159 Ack::Execute(Err(err.to_string()))
160 }
161 }
162 };
163
164 let callback_message = dequeue_callback(
165 storage,
166 original_packet.src.channel_id.clone(),
167 original_packet.sequence,
168 )
169 .map(|request| callback_message(request, result.clone()));
170
171 (callback_message, executed_by, result)
172}
173
174pub fn on_timeout(
177 storage: &mut dyn Storage,
178 IbcPacketTimeoutMsg { packet, .. }: &IbcPacketTimeoutMsg,
179) -> Option<CosmosMsg> {
180 let request = dequeue_callback(storage, packet.src.channel_id.clone(), packet.sequence)?;
181 let timeout = "timeout".to_string();
182 let result = match request.request_type {
183 CallbackRequestType::Execute => Callback::Execute(Err(timeout)),
184 };
189 Some(callback_message(request, result))
190}
191
192fn callback_message(request: PendingCallback, result: Callback) -> CosmosMsg {
193 #[cw_serde]
196 enum C {
197 Callback(CallbackMessage),
198 }
199 WasmMsg::Execute {
200 contract_addr: request.receiver.into_string(),
201 msg: to_json_binary(&C::Callback(CallbackMessage {
202 initiator: request.initiator,
203 initiator_msg: request.initiator_msg,
204 result,
205 }))
206 .expect("fields are known to be serializable"),
207 funds: vec![],
208 }
209 .into()
210}
211
212fn dequeue_callback(
213 storage: &mut dyn Storage,
214 channel_id: String,
215 sequence_number: u64,
216) -> Option<PendingCallback> {
217 let request = CALLBACKS
218 .may_load(storage, (channel_id.clone(), sequence_number))
219 .unwrap()?;
220 CALLBACKS.remove(storage, (channel_id, sequence_number));
221 Some(request)
222}
223
224#[cw_serde]
225struct PendingCallback {
226 initiator: Addr,
227 initiator_msg: Binary,
228 receiver: Addr,
230 request_type: CallbackRequestType,
232}
233
234const CALLBACKS: Map<(String, u64), PendingCallback> = Map::new("polytone-callbacks");