levana_perpswap_cosmos/
response.rs

1use std::collections::HashMap;
2
3use anyhow::Result;
4use cosmwasm_std::{
5    from_json, to_json_binary, wasm_execute, CosmosMsg, Empty, Event, IbcBasicResponse,
6    IbcReceiveResponse, Response, SubMsg, WasmMsg,
7};
8use cw2::ContractVersion;
9use serde::de::DeserializeOwned;
10use serde::Serialize;
11
12use crate::ibc::{ack_fail, ack_success};
13
14/// Helper data type, following builder pattern, for constructing a [Response].
15pub struct ResponseBuilder {
16    resp: Response,
17    event_type: EventType,
18    event_type_count: HashMap<String, u32>,
19}
20
21enum EventType {
22    MuteEvents,
23    EmitEvents {
24        common_attrs: Vec<(&'static str, String)>,
25    },
26}
27
28fn standard_event_attributes(
29    ContractVersion { contract, version }: ContractVersion,
30) -> Vec<(&'static str, String)> {
31    vec![
32        ("levana_protocol", "perps".to_string()),
33        ("contract_version", version),
34        ("contract_name", contract),
35    ]
36}
37
38impl ResponseBuilder {
39    /// Initialize a new builder.
40    pub fn new(contract_version: ContractVersion) -> Self {
41        ResponseBuilder {
42            resp: Response::new(),
43            event_type: EventType::EmitEvents {
44                common_attrs: standard_event_attributes(contract_version),
45            },
46            event_type_count: HashMap::new(),
47        }
48    }
49
50    /// Create a response where the event methods are no-ops.
51    pub fn new_mute_events() -> Self {
52        ResponseBuilder {
53            resp: Response::new(),
54            event_type: EventType::MuteEvents,
55            event_type_count: HashMap::new(),
56        }
57    }
58
59    /// Finalize the builder and generate the final response.
60    pub fn into_response(self) -> Response {
61        self.resp
62    }
63
64    /// Add a new [CosmosMsg] to the response.
65    pub fn add_message(&mut self, msg: impl Into<CosmosMsg<Empty>>) {
66        self.resp.messages.push(SubMsg::new(msg.into()));
67    }
68
69    /// Add a submessage for instantiating a new contract.
70    pub fn add_instantiate_submessage<
71        I: Into<u64>,
72        A: Into<String>,
73        L: Into<String>,
74        T: Serialize,
75    >(
76        &mut self,
77        id: I,
78        admin: A,
79        code_id: u64,
80        label: L,
81        msg: &T,
82    ) -> Result<()> {
83        let payload = to_json_binary(msg)?;
84
85        // the common case
86        // more fine-grained control via raw submessage
87        let msg = WasmMsg::Instantiate {
88            admin: Some(admin.into()),
89            code_id,
90            msg: payload,
91            funds: vec![],
92            label: label.into(),
93        };
94        self.add_raw_submessage(
95            // the common case
96            // more fine-grained control via raw submessage
97            SubMsg::reply_on_success(msg, id.into()),
98        );
99
100        Ok(())
101    }
102
103    /// Add a new one-shot submessage execution.
104    pub fn add_execute_submessage_oneshot<C: Into<String>, T: Serialize>(
105        &mut self,
106        contract: C,
107        msg: &T,
108    ) -> Result<()> {
109        self.add_raw_submessage(
110            // the common case
111            // more fine-grained control via raw submessage
112            SubMsg::new(wasm_execute(
113                contract,
114                msg,
115                // the common case, no coins
116                vec![],
117            )?),
118        );
119
120        Ok(())
121    }
122
123    /// Add a raw submsg. Helpful if you need to handle a reply.
124    pub fn add_raw_submessage(&mut self, msg: SubMsg<Empty>) {
125        self.resp.messages.push(msg);
126    }
127
128    /// Add an event to the response.
129    pub fn add_event(&mut self, event: impl Into<Event>) {
130        let event: Event = event.into();
131        match &self.event_type {
132            EventType::MuteEvents => (),
133            EventType::EmitEvents { common_attrs } => {
134                let mut event = event.add_attributes(common_attrs.clone());
135
136                let event_type_count = self.event_type_count.entry(event.ty.clone()).or_default();
137
138                if *event_type_count > 0 {
139                    event.ty = format!("{}-{}", event.ty, *event_type_count);
140                }
141
142                *event_type_count += 1;
143
144                self.resp.events.push(event)
145            }
146        }
147    }
148
149    /// Set response data
150    pub fn set_data(&mut self, data: &impl Serialize) -> Result<()> {
151        match self.resp.data {
152            None => {
153                let data = to_json_binary(data)?;
154                self.resp.data = Some(data);
155            }
156            Some(_) => anyhow::bail!("data already exists, use update_data instead"),
157        }
158
159        Ok(())
160    }
161
162    /// Get response data
163    pub fn get_data<T: DeserializeOwned>(&self) -> Result<Option<T>> {
164        match &self.resp.data {
165            None => Ok(None),
166            Some(data) => Ok(Some(from_json(data)?)),
167        }
168    }
169
170    /// Remove response data
171    pub fn remove_data(&mut self) {
172        self.resp.data = None;
173    }
174
175    /// Update response data
176    pub fn update_data<T: Serialize + DeserializeOwned>(
177        &mut self,
178        f: impl FnOnce(Option<T>) -> T,
179    ) -> Result<()> {
180        let data = self.get_data()?;
181        let updated = f(data);
182        self.resp.data = Some(to_json_binary(&updated)?);
183
184        Ok(())
185    }
186
187    /// Turn the accumulated response into an IBC Basic response
188    pub fn into_ibc_response(self) -> IbcBasicResponse {
189        let mut resp = IbcBasicResponse::default();
190        resp.messages = self.resp.messages;
191        resp.attributes = self.resp.attributes;
192        resp.events = self.resp.events;
193
194        resp
195    }
196
197    /// Turn the accumulated response into an IBC Receive success response
198    pub fn into_ibc_recv_response_success(self) -> IbcReceiveResponse {
199        let mut resp = IbcReceiveResponse::new(ack_success());
200        resp.messages = self.resp.messages;
201        resp.attributes = self.resp.attributes;
202        resp.events = self.resp.events;
203        resp
204    }
205
206    /// Turn the accumulated response into an IBC Receive fail response
207    pub fn into_ibc_recv_response_fail(self, error: anyhow::Error) -> IbcReceiveResponse {
208        let mut resp = IbcReceiveResponse::new(ack_fail(error));
209        resp.messages = self.resp.messages;
210        resp.attributes = self.resp.attributes;
211        resp.events = self.resp.events;
212        resp
213    }
214}