1use cosmwasm_schema::{cw_serde, QueryResponses};
2use cosmwasm_std::{
3 to_binary, Addr, Binary, Coin, CosmosMsg, Empty, QueryRequest, StdError, StdResult, Uint128,
4 WasmMsg,
5};
6use cw20::Cw20ReceiveMsg;
7
8#[cw_serde]
9pub enum ExecuteMsg {
10 CollectRequests { to_contract: Addr, msg: Binary },
16
17 SendRequests {
19 requests: Vec<GateRequest>,
20 chain: String,
21 timeout: Option<u64>,
22 },
23
24 SetPermission {
26 permission: Permission,
27 chain: String,
28 },
29
30 SetPermissionFromAdmin {
32 contract: Addr,
33 permission: Permission,
34 chain: String,
35 },
36
37 #[cfg(feature = "gate")]
38 SetVoucherPermission {
39 chain: String,
40 local_voucher_contract: Addr,
41 remote_voucher_contract: String,
42 },
43
44 #[cfg(feature = "gate")]
45 RegisterChainAndChannel {
46 chain: String,
47 src_channel: String,
48 base_denom: String,
49 },
50
51 #[cfg(feature = "gate")]
52 Receive(Cw20ReceiveMsg),
53
54 #[cfg(feature = "gate")]
55 IbcHook(IbcHookMsg),
56
57 #[cfg(feature = "gate")]
58 PrivateSendCollectedMsgs,
59
60 #[cfg(feature = "gate")]
61 PrivateRemoteExecuteRequests {
62 requests_infos: Vec<GateRequestsInfo>,
63 native_denom: Option<String>,
64 from_chain: String,
65 },
66
67 #[cfg(feature = "gate")]
68 PrivateRemoteExecuteQuery {
69 queries: Vec<QueryRequest<Empty>>,
70 from_contract: String,
71 callback_msg: Option<Binary>,
72 },
73}
74
75#[cw_serde]
76#[derive(QueryResponses)]
77pub enum QueryMsg {
78 #[returns(Permission)]
79 Permission { contract: Addr, chain: String },
81 #[returns(ChannelInfo)]
82 ChannelInfo { chain: String },
84 #[returns(Config)]
85 Config {},
87}
88
89#[cfg(feature = "gate")]
90#[cw_serde]
91pub enum IbcHookMsg {
92 ExecutePendingRequest { channel: String, sequence: u64 },
93}
94
95#[cw_serde]
96pub enum GateMsg {
97 ReceivedMsg {
99 sender: String,
100 msg: Binary,
101 },
102 RequestFailed {
103 request: GateRequest,
104 },
105 QueryResponse {
107 queries: Vec<GateQueryResponse>,
108 callback_msg: Option<Binary>,
109 },
110 CollectRequests {
111 sender: Addr,
112 msg: Binary,
113 },
114}
115
116#[cfg(feature = "gate")]
117impl GateMsg {
118 pub fn into_binary(self) -> StdResult<Binary> {
120 let msg = ReceiverExecuteMsg::ReceiveGateMsg(self);
121 to_binary(&msg)
122 }
123
124 pub fn into_cosmos_msg<T: Into<String>>(
126 self,
127 contract_addr: T,
128 funds: Vec<Coin>,
129 ) -> StdResult<CosmosMsg> {
130 let msg = self.into_binary()?;
131 let execute = WasmMsg::Execute {
132 contract_addr: contract_addr.into(),
133 msg,
134 funds,
135 };
136 Ok(execute.into())
137 }
138}
139
140#[cw_serde]
141pub enum ReceiverExecuteMsg {
142 ReceiveGateMsg(GateMsg),
143}
144
145#[cw_serde]
146pub struct Config {
147 pub controller: Addr,
148 pub default_timeout: u64,
149 pub default_gas_limit: Option<u64>,
150 pub cw20_icg_code_id: u64,
151 pub voucher_contract: Option<String>,
152 pub base_denom: String,
153 pub max_gas_amount_per_revert: u64,
155}
156
157#[cw_serde]
159pub enum Permission {
160 Permissioned { addresses: Vec<String> },
162 Permissionless {},
164}
165
166#[cw_serde]
168pub enum GateRequest {
169 SendMsg {
174 msg: Binary,
175 to_contract: String,
176 send_native: Option<SendNativeInfo>,
177 },
178
179 Query {
183 queries: Vec<QueryRequest<Empty>>,
184 callback_msg: Option<Binary>,
185 },
186}
187impl GateRequest {
188 pub fn send_native(&self) -> Option<SendNativeInfo> {
189 match self {
190 GateRequest::SendMsg {
191 send_native: native_token,
192 ..
193 } => native_token.clone(),
194 GateRequest::Query { .. } => None,
195 }
196 }
197}
198
199#[cw_serde]
200#[non_exhaustive]
201#[cfg(feature = "gate")]
202pub struct GateRequestsInfo {
203 pub requests: Vec<GateRequest>,
204 pub sender: String,
205 pub fee: Option<Coin>,
206 pub send_native: Option<SendNativeInfo>,
207}
208
209#[cfg(feature = "gate")]
210impl GateRequestsInfo {
211 pub fn new(
212 requests: Vec<GateRequest>,
213 sender: String,
214 sended_funds: Vec<Coin>,
215 base_denom: String,
216 ) -> StdResult<GateRequestsInfo> {
217 let mut new_send_native_info: Option<SendNativeInfo> = None;
218
219 for request in requests.clone() {
221 new_send_native_info = merge_send_native(&new_send_native_info, &request.send_native())?
222 }
223
224 let mut fee: Option<Coin> = None;
225
226 for fund in sended_funds {
228 let mut var_amount = fund.amount;
229 if let Some(ref with_native) = new_send_native_info {
230 if with_native.coin.denom == fund.denom {
231 var_amount -= with_native.coin.amount;
232 }
233 }
234
235 if base_denom.clone() == fund.denom && var_amount > Uint128::zero() {
236 fee = Some(Coin {
237 denom: base_denom.clone(),
238 amount: var_amount,
239 });
240 var_amount = Uint128::zero()
241 }
242
243 if !var_amount.is_zero() {
244 return Err(StdError::generic_err(format!(
245 "Denom {} recevied but not used",
246 fund.denom
247 )));
248 }
249 }
250 Ok(GateRequestsInfo {
251 requests,
252 sender,
253 fee,
254 send_native: new_send_native_info,
255 })
256 }
257}
258
259#[cw_serde]
279pub struct SendNativeInfo {
280 pub coin: Coin,
281 pub path_middle_forward: Vec<PacketPath>,
282 pub dest_denom: String,
283 pub channel_id: String,
284 pub timeout: Option<u64>,
285}
286
287impl SendNativeInfo {
288 pub fn get_first_channel(self) -> String {
289 match self.path_middle_forward.first() {
290 Some(first) => first.channel_id.to_owned(),
291 None => self.channel_id,
292 }
293 }
294}
295
296#[cw_serde]
297pub struct PacketPath {
298 pub channel_id: String,
300 pub address: String,
302}
303
304#[cw_serde]
305pub struct GateQueryResponse {
306 pub request: QueryRequest<Empty>,
307 pub response: Binary,
308}
309
310#[cw_serde]
311pub struct QueryRequestInfoResponse {
312 pub queries: Vec<GateQueryResponse>,
313 pub from_contract: String,
314 pub callback_msg: Option<Binary>,
315}
316
317#[cw_serde]
318pub struct ChannelInfo {
319 pub src_channel_id: String,
321 pub dest_port_id: String,
323 pub dest_channel_id: String,
325 pub connection_id: String,
327 pub base_denom: Option<String>,
329 pub voucher_contract: Option<String>,
331}
332
333impl ChannelInfo {
334 pub fn get_remote_gate(self) -> StdResult<String> {
335 let slice: Vec<&str> = self.dest_port_id.split('.').collect();
336
337 if slice.len() != 2 {
338 return Err(StdError::generic_err("dest_port_id is invalid"));
339 }
340
341 return Ok(slice.last().unwrap().to_string());
342 }
343}
344
345#[cfg(feature = "gate")]
348pub fn merge_send_native(
349 from: &Option<SendNativeInfo>,
350 with: &Option<SendNativeInfo>,
351) -> StdResult<Option<SendNativeInfo>> {
352 match from {
353 Some(from) => match with.clone() {
354 Some(with) => {
355 if from.coin.denom != with.coin.denom {
356 return Err(StdError::generic_err("Multiple native coin denom detected"));
357 }
358
359 if from.path_middle_forward != with.path_middle_forward {
360 return Err(StdError::generic_err(
361 "Multiple path_middle_forward detected",
362 ));
363 }
364
365 if from.dest_denom != with.dest_denom {
366 return Err(StdError::generic_err("Multiple dest_denom detected"));
367 }
368
369 if from.channel_id != with.channel_id {
370 return Err(StdError::generic_err("Multiple channel_id detected"));
371 }
372
373 Ok(Some(SendNativeInfo {
374 coin: Coin {
375 denom: from.coin.denom.clone(),
376 amount: from.coin.amount + with.coin.amount,
377 },
378 path_middle_forward: from.path_middle_forward.clone(),
379 dest_denom: from.dest_denom.clone(),
380 channel_id: from.channel_id.clone(),
381 timeout: lowest_timeout(from.timeout, with.timeout)?,
382 }))
383 }
384 None => Ok(Some(from.to_owned())),
385 },
386 None => Ok(with.to_owned()),
387 }
388}
389
390#[cfg(feature = "gate")]
391pub fn lowest_timeout(from: Option<u64>, with: Option<u64>) -> StdResult<Option<u64>> {
392 match from {
393 Some(from) => match with {
394 Some(with) => {
395 if with > from {
396 Ok(Some(from))
397 } else {
398 Ok(Some(with))
399 }
400 }
401 None => Ok(Some(from)),
402 },
403 None => Ok(with),
404 }
405}